Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions docs/DEPLOYMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,15 @@ shutdown context error and initiates request cancellation and socket cleanup;
it does not wait indefinitely for custom handlers that ignore cancellation.
This change is not included in v1.0.1.

Source builds also cancel pending SOCKS5 TCP CONNECT setup when its tracked
client connection is forcibly closed, without waiting for the full dial
timeout. Successful setup still cancels the temporary dialing context without
tying the established tunnel to that timeout. There is no competing TCP reader:
early payload and write-half-close remain usable. Cancellation affects only
that caller's wait, not a shared native client's physical dial. This does not
add immediate peer-disconnect detection during a pending TCP dial, and is not
included in v1.0.1.

For SOCKS5 TCP and HTTP CONNECT tunnels, `--idle-timeout` (default `5m`)
measures inactivity across both directions: an active download or upload does
not need reverse-direction application traffic to stay open. A blocked write
Expand Down
9 changes: 5 additions & 4 deletions internal/proxy/lifecycle.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,11 @@ type trackedConn struct {
connectionCancel context.CancelFunc
}

// connectionContext binds net/http's connection context to explicit socket
// closure. Its background reader can stop after caching a pipelined byte, so
// closing the socket alone need not produce a read error that cancels a request.
// ConnContext calls this once per accepted connection. Registration and Close
// connectionContext binds frontend work to explicit socket closure. A caller
// waiting for upstream setup need not be reading the socket; net/http's reader
// can also stop after caching a pipelined byte. Socket closure alone therefore
// need not cancel that work. The frontend calls this once per accepted
// connection, from HTTP ConnContext or SOCKS TCP setup. Registration and Close
// may race during shutdown; either ordering must return a canceled context.
func (c *trackedConn) connectionContext(parent context.Context) context.Context {
ctx, cancel := context.WithCancel(parent)
Expand Down
16 changes: 14 additions & 2 deletions internal/proxy/socks5.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,11 +137,23 @@ func (s *SOCKS5Server) serveConn(client net.Conn) {

func (s *SOCKS5Server) serveConnect(client net.Conn, request socksRequest) {
ctx := context.Background()
cancel := func() {}
if tracked, ok := client.(*trackedConn); ok {
// Bind only this caller's setup wait to full socket close. Do not add
// a reader that could consume early payload or treat half-close as
// abandonment, and do not close the shared upstream dialer.
ctx = tracked.connectionContext(ctx)
}
var cancel context.CancelFunc
if s.cfg.dialTimeout > 0 {
ctx, cancel = context.WithTimeout(ctx, s.cfg.dialTimeout)
} else {
ctx, cancel = context.WithCancel(ctx)
}
var upstream net.Conn
err := ctx.Err()
if err == nil {
upstream, err = s.cfg.dialer.DialContext(ctx, "tcp", request.address)
}
upstream, err := s.cfg.dialer.DialContext(ctx, "tcp", request.address)
cancel()
if s.cfg.handshakeTimeout > 0 {
_ = client.SetWriteDeadline(time.Now().Add(s.cfg.handshakeTimeout))
Expand Down
Loading
Loading