Skip to content

fix: close failed TCP dial results in proxy frontends - #36

Merged
cppla merged 1 commit into
mainfrom
codex/tcp-dial-error-ownership
Oct 2, 2026
Merged

cppla merged 1 commit into
mainfrom
codex/tcp-dial-error-ownership

Conversation

@cppla

@cppla cppla commented Oct 2, 2026 •

Copy link
Copy Markdown
Owner

Summary

Close a non-nil TCP connection returned alongside a custom Dialer's setup error, before discarding that failed result. Cover SOCKS5 CONNECT, HTTP CONNECT and ordinary HTTP forwarding.

  • Preserve the original dial error, including the existing SOCKS/HTTP timeout classification, even when connection cleanup also fails.
  • Reject custom (nil, nil) results before a success reply or invalid connection wrapper.
  • Preserve healthy early data, TCP half-close, reverse replies and HTTP origin connection reuse.
  • Do not close the shared Dialer, add a downstream reader, or change default budgets.

Validation completed on dae73c6a7e84e30a0b9b1c6e390d4536e4f4265e

  • make check and make race: full repository, exit 0.
  • Same final ownership/error/nil-result tests against old production: expected failure, 2.821s. Six real TCP entry/error cases remain unclosed with Close=0 and peer read timeout; direct HTTP pipe and invalid-result controls also fail.
  • Candidate ownership/error identity/real peer EOF/worker joins/tracker cleanup: race ×3, pass (2.414s).
  • Healthy early-byte/half-close/reverse-response, two HTTP requests using one origin dial, and real frontend nil-result controls: race ×3, pass (1.652s).
  • Linux/arm64 isolated existing Docker image: six new top-level tests ×3 plus 23 existing cancellation/shared-ownership/HTTP/relay/UDP controls, 41 top-level passes across 29 tests. Both containers exit 0 and are removed. Full source/module/five-file inventories match before compilation, after compilation and after execution. Binary SHA-256 1dd6fdf846682b560a64749cea014116c90961451c330275ffee73e88cdc6127.
  • Independent production/documentation reviews: no blocker.

Exact-head GitHub verification completed on attempt 1:

  • CI 37033309529: all 11 jobs passed, including Go 1.25.13/1.27, macOS, Windows UDP continuity, full race, reachable-vulnerability scan, four platform builds, container integration and multi-platform OCI export. Actual container log includes integration and OCI-export success markers.
  • CodeQL 37033309538: passed.
  • Linux netem 37033309574: passed; actual namespace/netem log includes its success marker.
  • Automated review reported no findings; there are no inline review threads.

Post-merge main verification

PR #36 is squash-merged as a6c878ae2c4121155e1b2506c31a9a50343bd754. Local main, origin/main and the live remote main ref match; main tree 03136e2b3ea04fc1985681867acc27f966188715 matches the tested PR head exactly. Working tree is clean.

  • Main focused ownership/nil-result/preservation suite: race, shuffle, count 3, exit 0 (2.124s).
  • Main CI 37033765054: all 11 jobs passed on attempt 1. Actual container job 110926791409 includes both integration and OCI-export success markers.
  • Main CodeQL 37033765038: passed on attempt 1.
  • Main Linux netem 37033765045: passed on attempt 1; actual job 110926791132 includes its success marker.
  • All three main workflows are push events against the exact merge SHA. No rerun or relaxed gate.

The next independently reproduced ownership gap is on the custom destination Dialer; it is not fixed in this frontend patch. No new release/tag is published.

Evidence limits and fixture corrections

The triggering (conn, error) combination was produced by a custom Dialer after a real successful TCP dial; no built-in transport was demonstrated to return that combination, and no indefinite descriptor leak is claimed.

Two exploratory fixtures incorrectly expected SOCKS TTL-expired for DNS or generic Timeout errors. Those logs are retained. The final tests use a typed net.OpError wrapping the existing deadline sentinel, with no production mapping change.

An original diagnostic also encountered the existing HTTP duplicate-listener-close Shutdown return before its ownership oracle. The final fixture explicitly logs/accepts only closed-listener error leaves; a deadline or unrelated joined error still fails. This patch does not change Shutdown semantics. Independent endpoint cleanup and completion joins remain required.

This is source-build behavior, not included in v1.0.1. No dependency, release/tag, remote host or large capture campaign is changed; no traffic-equivalence or comparative-performance claim is made.

Copilot AI balanced review requested due to automatic review settings October 2, 2026 16:20

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟢 Approval recommended

The focused implementation is consistent across frontends and comprehensively covers failure and success paths.

Review effort: Balanced
Findings: None

What changed in this PR

Closes failed custom TCP dial results across proxy frontends while preserving original error classification and successful connection behavior.

Changes:

  • Close non-nil connections returned with dial errors.
  • Reject (nil, nil) dial results.
  • Add ownership, reuse, half-close, and error-preservation tests and documentation.
File Description
internal/​proxy/​socks5.go Handles invalid and failed SOCKS5 dial results safely.
internal/​proxy/​http.go Handles failed HTTP upstream dial results safely.
internal/​proxy/​tcp_dial_error_ownership_test.go Verifies cleanup ownership and original errors.
internal/​proxy/​tcp_dial_result_preserve_test.go Verifies successful tunnel and reuse behavior.
docs/​DEPLOYMENT.md Documents failed-result ownership semantics.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@cppla
cppla merged commit a6c878a into main Oct 2, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants