Skip to content

fix(tls): start reads on adopted paused duplex transports - #38

Merged
steipete merged 1 commit into
mainfrom
codex/tls-paused-duplex
Sep 30, 2026
Merged

steipete merged 1 commit into
mainfrom
codex/tls-paused-duplex

Conversation

@steipete

@steipete steipete commented Sep 30, 2026 •

Copy link
Copy Markdown

Paused transports handed to TLS now complete their handshake. Previously, tls.connect({ socket: duplex }) and a paused HTTP CONNECT socket injected into tls.Server could wait indefinitely even though the encrypted bytes were buffered or available.

The stream upgrade attached TLS listeners but left the underlying Duplex paused. Its native resume_stream hook is currently a no-op, so reading the outer TLSSocket does not start that transport. Resume the transport after assigning the TLS handle and installing all data/end/drain/close/error listeners, in all four stream-upgrade paths. Native fd adoption stays unchanged. This matches Node 24: TLS takes ownership and starts reading, while the previous owner can keep bytes paused until adoption.

Validation:

  • Four new tests cover a paused Duplex pair, buffered ClientHello before server adoption, paused CONNECT after its plaintext acknowledgement, and CONNECT with an acknowledgement still corked at adoption. All four fail on unpatched fork e8105cb349 at the explicit handshake deadline; all pass on Node 24.21.0 and the patched release build (268 ms combined).
  • Release builds, using --expose-internals for surrounding suites: TLS: 461 passed, 32 skipped, 6 TODO; net: 266 passed, 37 skipped. HTTP: 1,662 passed, 29 skipped, 3 TODO, with one existing Node-reference test failure in node-http-req-complete.test.ts; the same test fails on the baseline and with Node 24. All Bun assertions in that file pass.
  • JS lint and source TypeScript checks pass; formatting checked. GitHub Lint and Format jobs pass for e5df1ee. Full test-tree typechecking reports pre-existing repository errors; the six helper type errors it exposed in the new tests were corrected with type annotations, with no runtime change. Independent P2 autoreview found no findings; the type-only final pass was also clean.
  • No Rust changes.

OpenClaw consumer proof uses detached main 95ed4ee478cd8, the patched binary first on PATH, OPENCLAW_VITEST_RUNTIME=bun, one worker, private 0700 TMPDIR, disabled disposable compile caching, 15-second test/hook deadlines, and a 300-second outer file guard. The three full proxy files pass: proxy-server.test.ts 38/38 in 38.76 s; proxy-server.websocket.test.ts 18/18 in 29.48 s; proxy-server.lifecycle.test.ts 38/38 in 38.20 s. Unpatched: main proxy file hit the 300-second outer guard (306.50 s including shutdown); websocket finished with 5 passed / 13 failed in 250.14 s; lifecycle hit the outer guard (310.03 s including shutdown). Patched: all 94 tests pass. These are failure-removal checks on a shared Darwin host, not isolated throughput benchmarks.

W15 attributed roughly 900–937 seconds of aggregate invocation-wall opportunity to this defect in the old Linux baseline (secrets config: Node 92.242 s, Bun 1029.090 s). That estimate is not claimed as measured patched savings.

Upstream search: no exact initial-read fix found among oven-sh/bun issue/PR searches for TLS, paused Duplex and CONNECT. Open oven-sh#42332 concerns ongoing Duplex backpressure; open oven-sh#38076 concerns server native-adoption fallbacks; merged oven-sh#39830 concerns net pause/read semantics. None supplies this missing initial stream resume.

@steipete
steipete merged commit a0ef910 into main Sep 30, 2026
6 of 7 checks passed
@steipete
steipete deleted the codex/tls-paused-duplex branch September 30, 2026 10:03
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.

1 participant