@@ -7,9 +7,11 @@ import (
7
7
"bytes"
8
8
"context"
9
9
"encoding/json"
10
+ "errors"
10
11
"fmt"
11
12
"io"
12
13
"mime"
14
+ "net"
13
15
"net/http"
14
16
"net/http/httputil"
15
17
"net/url"
@@ -61,6 +63,9 @@ type TransparentProxy struct {
61
63
62
64
// If mcp server has been initialized
63
65
IsServerInitialized bool
66
+
67
+ // Listener for the HTTP server
68
+ listener net.Listener
64
69
}
65
70
66
71
// NewTransparentProxy creates a new transparent proxy with optional middlewares.
@@ -129,10 +134,13 @@ func (t *tracingTransport) RoundTrip(req *http.Request) (*http.Response, error)
129
134
130
135
resp , err := t .forward (req )
131
136
if err != nil {
137
+ if errors .Is (err , context .Canceled ) {
138
+ // Expected during shutdown or client disconnect—silently ignore
139
+ return nil , err
140
+ }
132
141
logger .Errorf ("Failed to forward request: %v" , err )
133
142
return nil , err
134
143
}
135
-
136
144
if resp .StatusCode == http .StatusOK {
137
145
// check if we saw a valid mcp header
138
146
ct := resp .Header .Get ("Mcp-Session-Id" )
@@ -286,6 +294,11 @@ func (p *TransparentProxy) Start(ctx context.Context) error {
286
294
mux .Handle ("/metrics" , p .prometheusHandler )
287
295
logger .Info ("Prometheus metrics endpoint enabled at /metrics" )
288
296
}
297
+ ln , err := net .Listen ("tcp" , fmt .Sprintf ("%s:%d" , p .host , p .port ))
298
+ if err != nil {
299
+ return fmt .Errorf ("failed to listen: %w" , err )
300
+ }
301
+ p .listener = ln
289
302
290
303
// Create the server
291
304
p .server = & http.Server {
@@ -294,16 +307,17 @@ func (p *TransparentProxy) Start(ctx context.Context) error {
294
307
ReadHeaderTimeout : 10 * time .Second , // Prevent Slowloris attacks
295
308
}
296
309
297
- // Start the server in a goroutine
298
310
go func () {
299
- logger .Infof ("Transparent proxy started for container %s on %s:%d -> %s" ,
300
- p .containerName , p .host , p .port , p .targetURI )
301
-
302
- if err := p .server .ListenAndServe (); err != nil && err != http .ErrServerClosed {
311
+ err := p .server .Serve (ln )
312
+ if err != nil && err != http .ErrServerClosed {
313
+ var opErr * net.OpError
314
+ if errors .As (err , & opErr ) && opErr .Op == "accept" {
315
+ // Expected when listener is closed—silently return
316
+ return
317
+ }
303
318
logger .Errorf ("Transparent proxy error: %v" , err )
304
319
}
305
320
}()
306
-
307
321
// Start health-check monitoring only if health checker is enabled
308
322
if p .healthChecker != nil {
309
323
go p .monitorHealth (ctx )
@@ -312,6 +326,14 @@ func (p *TransparentProxy) Start(ctx context.Context) error {
312
326
return nil
313
327
}
314
328
329
+ // CloseListener closes the listener for the transparent proxy.
330
+ func (p * TransparentProxy ) CloseListener () error {
331
+ if p .listener != nil {
332
+ return p .listener .Close ()
333
+ }
334
+ return nil
335
+ }
336
+
315
337
func (p * TransparentProxy ) monitorHealth (parentCtx context.Context ) {
316
338
ticker := time .NewTicker (10 * time .Second )
317
339
defer ticker .Stop ()
@@ -352,7 +374,13 @@ func (p *TransparentProxy) Stop(ctx context.Context) error {
352
374
353
375
// Stop the HTTP server
354
376
if p .server != nil {
355
- return p .server .Shutdown (ctx )
377
+ err := p .server .Shutdown (ctx )
378
+ if err != nil && err != http .ErrServerClosed && err != context .DeadlineExceeded {
379
+ logger .Warnf ("Error during proxy shutdown: %v" , err )
380
+ return err
381
+ }
382
+ logger .Infof ("Server for %s stopped successfully" , p .containerName )
383
+ p .server = nil
356
384
}
357
385
358
386
return nil
0 commit comments