tcp.go raw
1 // Package tcp provides a plain HTTP transport for the relay.
2 package tcp
3
4 import (
5 "context"
6 "fmt"
7 "net/http"
8 "sync"
9
10 "next.orly.dev/pkg/lol/log"
11 )
12
13 // Config holds TCP transport configuration.
14 type Config struct {
15 // Addr is the listen address (e.g., "0.0.0.0:3334").
16 Addr string
17 // Handler is the HTTP handler to serve.
18 Handler http.Handler
19 }
20
21 // Transport serves HTTP over plain TCP.
22 type Transport struct {
23 cfg *Config
24 server *http.Server
25 mu sync.Mutex
26 }
27
28 // New creates a new TCP transport.
29 func New(cfg *Config) *Transport {
30 return &Transport{cfg: cfg}
31 }
32
33 func (t *Transport) Name() string { return "tcp" }
34
35 func (t *Transport) Start(ctx context.Context) error {
36 t.mu.Lock()
37 defer t.mu.Unlock()
38
39 t.server = &http.Server{
40 Addr: t.cfg.Addr,
41 Handler: t.cfg.Handler,
42 }
43
44 log.I.F("starting listener on http://%s", t.cfg.Addr)
45
46 errCh := make(chan error, 1)
47 go func() {
48 if err := t.server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
49 errCh <- err
50 }
51 close(errCh)
52 }()
53
54 // Give the server a moment to fail on bind errors
55 select {
56 case err := <-errCh:
57 if err != nil {
58 return fmt.Errorf("tcp listen on %s: %w", t.cfg.Addr, err)
59 }
60 default:
61 }
62
63 return nil
64 }
65
66 func (t *Transport) Stop(ctx context.Context) error {
67 t.mu.Lock()
68 defer t.mu.Unlock()
69
70 if t.server == nil {
71 return nil
72 }
73 return t.server.Shutdown(ctx)
74 }
75
76 func (t *Transport) Addresses() []string {
77 return []string{"ws://" + t.cfg.Addr + "/"}
78 }
79