fix(handler/forward): split readTimeout from pipe idleTimeout to prevent 15s download abort

The readTimeout field (default 15s) was being applied via xnet.Pipe to both
directions of a bidirectional proxy connection. During asymmetric transfers
(e.g. HTTP file download), the direction reading from the tunnel sees no
data after the initial request is forwarded, causing SetReadDeadline to fire
after 15s and abort the entire transfer.

Fix: add a separate idleTimeout field (default 0=disabled) to the metadata
structs in both forward/local and forward/remote handlers, and switch
xnet.Pipe to use idleTimeout instead of readTimeout. The readTimeout field
now only applies to the initial protocol sniffing/handshake phase.

Also document readTimeout vs idleTimeout semantics across all 24 locations
in the x/ module where these timeouts appear:
- readTimeout: handshake sniffing deadline (handlers), upstream response
  header timeout (http.Transport), or transport-level read deadline
- idleTimeout: idle read deadline per Pipe direction (0=disabled)
- ReadTimeout on Sniffer/SnifferBuilder: upstream response header/TLS
  handshake read timeout during sniffing
This commit is contained in:
ginuerzh
2026-05-31 17:05:21 +08:00
parent b8d9c79f72
commit 722dde5cfc
24 changed files with 128 additions and 6 deletions
+3
View File
@@ -230,6 +230,9 @@ type SnifferBuilder struct {
ALPN string
CertPool tls_util.CertPool
MitmBypass bypass.Bypass
// ReadTimeout is the timeout for reading upstream HTTP response headers
// and TLS ServerHello during sniffing. Passed through to sniffing.Sniffer.
// See sniffing.Sniffer.ReadTimeout for details.
ReadTimeout time.Duration
}
+2 -2
View File
@@ -23,8 +23,8 @@ const (
// Fields are populated by parseMetadata and read throughout the handler's
// request-processing methods.
type metadata struct {
readTimeout time.Duration // read timeout for upstream responses; default 15s, negative disables
idleTimeout time.Duration // idle timeout for pipe forwarding; 0 means disabled
readTimeout time.Duration // deadline for upstream response headers (http.Transport.ResponseHeaderTimeout); 0=15s default, negative=disabled
idleTimeout time.Duration // idle read deadline per Pipe direction during CONNECT/forwarding; 0 or negative = disabled
keepalive bool // enable HTTP keep-alive on the upstream transport
compression bool // enable HTTP compression on the upstream transport