From 174bc082d15bb2484964617f945fd1b1f191a59a Mon Sep 17 00:00:00 2001 From: ginuerzh Date: Fri, 22 May 2026 16:31:37 +0800 Subject: [PATCH] fix(tunnel): data race in CloseOnIdle and goroutine leak in sniffingWebsocketFrame - Use write lock (Lock) instead of read lock (RLock) in CloseOnIdle since it modifies the close channel, preventing a race with Close() that could panic on double-close of channel. - Buffer sniffingWebsocketFrame errc to capacity 2 and close both connections on first error to ensure the remaining copy goroutine unblocks and exits cleanly. --- handler/tunnel/entrypoint.go | 7 +++++-- handler/tunnel/tunnel.go | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/handler/tunnel/entrypoint.go b/handler/tunnel/entrypoint.go index 074ed2d6..0ec16549 100644 --- a/handler/tunnel/entrypoint.go +++ b/handler/tunnel/entrypoint.go @@ -440,8 +440,8 @@ func (ep *entrypoint) handleUpgradeResponse(ctx context.Context, rw io.ReadWrite return xnet.Pipe(ctx, rw, backConn) } -func (ep *entrypoint) sniffingWebsocketFrame(ctx context.Context, rw, cc io.ReadWriter, ro *xrecorder.HandlerRecorderObject, log logger.Logger) error { - errc := make(chan error, 1) +func (ep *entrypoint) sniffingWebsocketFrame(ctx context.Context, rw, cc io.ReadWriteCloser, ro *xrecorder.HandlerRecorderObject, log logger.Logger) error { + errc := make(chan error, 2) sampleRate := ep.websocketSampleRate if sampleRate == 0 { @@ -503,6 +503,9 @@ func (ep *entrypoint) sniffingWebsocketFrame(ctx context.Context, rw, cc io.Read } }() + <-errc + rw.Close() + cc.Close() <-errc return nil } diff --git a/handler/tunnel/tunnel.go b/handler/tunnel/tunnel.go index d1e407ef..3c2e12b1 100644 --- a/handler/tunnel/tunnel.go +++ b/handler/tunnel/tunnel.go @@ -230,8 +230,8 @@ func (t *Tunnel) Close() error { } func (t *Tunnel) CloseOnIdle() bool { - t.mu.RLock() - defer t.mu.RUnlock() + t.mu.Lock() + defer t.mu.Unlock() select { case <-t.close: