Skip to content

fix: preserve active tunnels and bound connection retirement - #17

Merged
cppla merged 1 commit into
mainfrom
codex/connection-lifecycle-fixes
Sep 22, 2026
Merged

cppla merged 1 commit into
mainfrom
codex/connection-lifecycle-fixes

Conversation

@cppla

@cppla cppla commented Sep 22, 2026 •

Copy link
Copy Markdown
Owner

Summary

Fix three reproduced connection-lifecycle problems without changing authentication formats, transport defaults, or fingerprint profiles:

  1. Active one-way tunnels were closed as idle. Share read-inactivity progress across both relay directions, while retaining a separate bound on each stalled write. Clear completed write deadlines, including HTTP CONNECT's buffered prefix path, so an H2 stream's old upload timer cannot abort an active download.
  2. Retired H2 connections could block the whole client. Replace write-lock-dependent ClientConn.State() cleanup with explicit opening/active ownership. Detach retired sessions under the client mutex, then interrupt the raw wire and close H2 outside that mutex. Full Close/deadline releases ownership once; EOF/half-close preserves the opposite direction.
  3. UDP enqueue was mistaken for H3 recovery. Update fallback health only on a newly verified CONNECT-UDP response, including signed target rejection. Cached datagram enqueue does not reset the TCP cooldown or select a transport.

Reproduction and validation

  • Baseline one-way relay tests failed at the 200 ms idle limit despite data every 40 ms, in both directions.
  • Real loopback WebH2 + HTTP CONNECT: baseline and an isolated missing-write-deadline-clear variant both failed near 250 ms after 64/256 bytes. Fixed implementation transfers all 256 bytes over 800 ms and accepts a reverse acknowledgment, with both stream directions still open. Covers a separate request and a request pipelined with CONNECT.
  • Real x/net H2 SETTINGS ACK wire-write stall reproduced blocked retirement/client Close; regressions now cover drained/active sessions, caller deadline, retired siblings, half-close, deadline and concurrent shutdown.
  • Deterministic cached UDP queue reproduced premature cooldown clearing; real loopback H3 tests cover a new verified response, cached sends, signed target rejection and bad credentials.
  • Local make check, make race, make stealth-tools-check passed. Final proxy/integration race rerun passed. Targeted H2 lifecycle race x10, UDP-health/generation race x20 and real H2 proxy integration race repetitions passed.
  • Independent read-only lifecycle review completed. Historical repro sources/logs retained locally outside tracked code; temporary overlay source extensions do not participate in normal Go package discovery.

Scope

No old remote test hosts were used; Linux/Docker/netem validation was supplied by GitHub CI for the exact head before merge. No formal PCAP corpus, comparative anti-identification result, version tag or release is included. These changes improve continuity and bounded cleanup; they do not prove traffic indistinguishability.

Exact-head checks

949761b8045714863fdacc6b747b58318f340abd: all jobs passed in CI 35727859407, CodeQL 35727859450, and Linux netem 35727859513. This includes Go 1.25/1.27 Linux tests, macOS tests, race, container integration/build, four-platform builds, and reachable vulnerability scan. No open review threads at the merge audit.

Post-merge verification

Merged as c85f70160cd1ad1d20e3789c40172a80ac3ca74b; its source tree exactly matches the validated PR head. All main-push workflows passed for that SHA: CI 35728213574, CodeQL 35728213651, and Linux netem 35728213729. Main CI confirms container integration, both Go versions, race, macOS tests, four-platform builds and vulnerability scan passed. Local main is synchronized and clean. No release/tag created.

Copilot AI lite review requested due to automatic review settings September 22, 2026 12:33
@cppla
cppla merged commit c85f701 into main Sep 22, 2026
14 checks passed

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

All reviewed changes have approval readiness and no unresolved blocking issues.

Review effort: Lite
Findings: None

What changed in this PR

Fixes tunnel idle-timeout handling, H2 connection retirement, and UDP fallback health tracking.

Changes:

  • Shares relay activity across directions while bounding stalled writes.
  • Adds explicit H2 ownership and safe session retirement.
  • Updates UDP health only after verified CONNECT-UDP responses.
File Description
internal/​tunnel/​web_udp.go Reports verified UDP authentication events.
internal/​tunnel/​web_stream.go Releases H2 stream ownership on close.
internal/​tunnel/​web_h2_lifecycle_test.go Adds H2 lifecycle regression coverage.
internal/​tunnel/​web_h2_client.go Tracks ownership and safely retires sessions.
internal/​tunnel/​web_client.go Removes health updates from local UDP enqueue.
internal/​tunnel/​web_client_udp_health_test.go Tests UDP health generations.
internal/​tunnel/​web_client_test.go Updates health-state tests.
internal/​proxy/​relay.go Shares activity deadlines and bounds writes.
internal/​proxy/​relay_test.go Tests relay lifecycle and blocked writes.
internal/​proxy/​http.go Bounds HTTP CONNECT prefix writes.
internal/​proxy/​config.go Documents timeout semantics.
integration/​proxy_activity_test.go Adds real H2 activity coverage.
docs/​WEB_COVER.md Documents UDP recovery behavior.
docs/​DEPLOYMENT.md Documents tunnel timeout behavior.

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

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