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.
This commit is contained in:
ginuerzh
2026-05-22 16:31:37 +08:00
parent ed93d60004
commit 174bc082d1
2 changed files with 7 additions and 4 deletions
+5 -2
View File
@@ -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
}
+2 -2
View File
@@ -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: