Repository navigation
fix(tunnel): bound deferred handshakes by the TCP dial timeout - #3439
Open
polaris-arch wants to merge 5 commits into
Open
polaris-arch wants to merge 5 commits into
polaris-arch wants to merge 5 commits into
Conversation
This was referenced Nov 9, 2026
polaris-arch
marked this pull request as draft
November 9, 2026 11:43
polaris-arch
marked this pull request as ready for review
November 9, 2026 12:26
polaris-arch
marked this pull request as draft
November 9, 2026 14:23
polaris-arch
marked this pull request as ready for review
November 10, 2026 14:26
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Merge prerequisite: merge #3436 first. This change can be reviewed independently, but must not be merged ahead of the lazy-TFO lifetime fix: cancellation during the initial Write may call Close concurrently with lazy socket publication. The recorded combined validation includes that fix. No ready-to-merge claim is made for this PR in isolation.
Keep deferred first-write handshakes within the existing TCP dial/preparation budget. Use the existing connection-context interrupter during preparation, reject late successes, and detach it before relay ownership.
Concrete bug and normal-path reachability
handleTCPConncreates the dial-timeout context, callsproxy.DialContext, then performs an initialWriteforNeedHandshaketransports. WebSocket early-data construction returns a lazy connection whose own context is derived fromBackground. Its HTTP Upgrade runs on the first write, so a peer that accepts the request but never returns HTTP 101 can keep that write blocked after the tunnel's 5-second dial budget has expired.This is reachable through ordinary configured transports: VMess/VLESS/Trojan WebSocket options with
max-early-data > 0reachStreamWebsocketConn, which selects the deferred wrapper. A stalled upgrade is an adverse network/peer condition, not the healthy-handshake fast path. No production incidence rate is established. The timeout begins at the existing preparation stage; DNS/sniffing and established relay lifetime are outside this change.Source: old tunnel handler, WebSocket lazy context and first write, configured adapter example.
Executable reproduction and observed result
Run in a disposable checkout with Go 1.26.10 (the recorded validation toolchain):
To test the same relevant fixture against unchanged Alpha:
The baseline command is expected to fail an assertion, not fail to compile.
Fault injection and defensive coverage
net.Pipe, a real loopback TCP frontend andhandleTCPConn. Route/proxy selection is replaced with a test seam; the peer deliberately withholds HTTP 101. This is controlled peer-fault reproduction, not a complete configured-node or physical-TUN end-to-end test.LateDialSuccessClosedandLateWriteSuccessCloseddeliberately return success after cancellation/closure. These are noncooperative-adapter fault injection and defensive ownership checks, not proof that a normal adapter behaves that way.Validation passes independently and with related fixes: focused/race tests, full default/with_gvisor suites, vet and Windows/macOS tunnel compilation. This is a timeout/ownership result, not an inferred whole-process resource saving.
Merge after #3436 for safe lazy-TFO closure; the old-TFO integration negative control is explicitly a late-success injected backend. #3437 handles cancellation attribution separately. The optional native API is not required; #3438 is the separate native frontend integration.
Head
4f6bd36bchanges tests only relative to28b58eb0; current standalone deferred-handshake focused/race suites pass in final frozen-batch validation.Healthy WebSocket lifetime control
TestDeferredHandshakeWebSocketWinnerLifetimeenters productionhandleTCPConnthrough a real TCP frontend, performs a real HTTP 101/early-data-header upgrade against a loopback WebSocket server, and checks framed data in both directions. It observes the actual preparation context expire withDeadlineExceeded, then writes and reads again on the accepted connection. This passes both independently and in the frozen integration.Route/proxy selection remains a fixture seam. This is WebSocket transport success, not a VMess authentication, REALITY or configured physical-TUN test. The context-recording channel is nonblocking on retries and fixture globals are restored even if bounded cleanup fails. No new runtime change was needed for this positive control.
Final frozen-batch validation also passes full root
./...default/with_gvisor, vet and Windows/macOS changed-package compilation in the declared combination: tunnel98d8db81, TFOb32780aa, groupe8ffb994, context completion23285c8f, and native API5b6706ccthrough a test-only modfile. These full-suite results bind that combination, not a new standalone full-suite run for every PR.SKIP_INTEROP_TEST=1; the nestedtest/module is outside root./.... Older validation links retain their original source identities. No physical-TUN, kernel Fast Open SYN/cookie, production incidence or whole-process resource claim is made.