Skip to content

test: track connections so HTTP/2 tests terminate in node 24.20.0 - #6285

Merged
qw-in merged 2 commits into
mainfrom
quinn/transport-connect-hang-fix
Sep 15, 2026
Merged

qw-in merged 2 commits into
mainfrom
quinn/transport-connect-hang-fix

Conversation

@qw-in

@qw-in qw-in commented Sep 15, 2026 •

Copy link
Copy Markdown
Member

Update @connectrpc/connect to 2.2.0 and fix a change where the http2 session would hang in the tests:

This patch changes the test helper only. It changes no source file. Before, the
helper tracked the HTTP/2 sessions of the test server. Now it tracks the TCP
sockets that the server accepts, and close() destroys those sockets.

@arcjet-rei I'd appreciate you having a look at the http2 handling change!

qw-in and others added 2 commits September 15, 2026 13:45
@bufbuild/protobuf 2.14.0 -> 2.14.1 and @connectrpc/connect,
connect-node, connect-web 2.1.2 -> 2.2.0, moving together in
@arcjet/guard, protocol, transport and arcjet (dev). No peer ranges
changed, so no consumer-visible constraint moves.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
`test/wire.test.ts` hung indefinitely on "HTTP fallback", taking the
whole file down with `Promise resolution is still pending but the event
loop has already resolved`. The request itself was fine — it rejected
with the expected `[unavailable] HTTP 503` in ~5ms — but the
`close(server)` in `withWireServer`'s `finally` never settled.

`trackHttp2Sessions` tracked server-side `Http2Session` objects and
`close` called `session.destroy()` on each. By the time it runs, the
session has already reached `closed: true` while still reporting
`destroyed: false`, and in that state `destroy()` is a no-op and
`session.socket` no longer refers to anything whose teardown closes the
connection. The TCP socket stayed open, so `server.close()` never
invoked its callback.

Track the sockets from the server's own `connection` event instead and
destroy those. That works for both `createServer` and
`createSecureServer`, since dropping the TCP layer takes the TLS session
with it. Renamed to `trackHttp2Connections` to match what it now holds.

Confirmed the hang is specific to this code, not the connect-es version
in the prior commit: reverting these three files while keeping
@connectrpc/connect-node at 2.2.0 reproduces the identical hang; the fix
also reproduces the same hang (and the same fix) against 2.1.2.

Transport suite goes from an indefinite hang to 54 passing.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@qw-in qw-in self-assigned this Sep 15, 2026
@qw-in
qw-in requested a review from a team as a code owner September 15, 2026 20:48
@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​bufbuild/​protobuf@​2.14.11001008796100
Updated@​connectrpc/​connect-web@​2.1.2 ⏵ 2.2.099 +11008789 +1100
Updated@​connectrpc/​connect@​2.1.2 ⏵ 2.2.0100 +110091 +189 +1100
Updated@​connectrpc/​connect-node@​2.1.2 ⏵ 2.2.0100 +1100100 +189 +1100

View full report

@arcjet-review arcjet-review Bot 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.

Arcjet Review — 🟡 Medium Risk

Decision: Approved

Rationale: This PR fires the dependency-changes escalation trigger because it updates package.json dependencies across several packages. The dependency updates are patch/minor bumps to existing, pinned dependencies rather than new packages, and the code change is limited to test infrastructure for HTTP/2 server teardown. The HTTP/2 change is consistent with the stated issue: tracking accepted sockets and destroying them during close is a reasonable way to release idle HTTP/2 connections that may otherwise keep the test runner alive. No hardcoded secrets, auth changes, injection risks, or other security issues were identified. I am approving despite Medium risk because the changed behavior is test-only and the dependency bumps are narrow and exact-version pinned.

Summary of Changes

Updates @bufbuild/protobuf from 2.14.0 to 2.14.1 and @connectrpc/connect, connect-node, and connect-web from 2.1.2 to 2.2.0 across affected packages. Renames the HTTP/2 test helper from trackHttp2Sessions to trackHttp2Connections and changes teardown to track and destroy accepted sockets instead of HTTP/2 sessions so tests do not hang on idle HTTP/2 connections.

Escalation Triggers

  • Dependency Changes: Multiple package.json files update @bufbuild/protobuf and @connectrpc packages.

Review Focus Areas

Notes

Path filtering: 1 file excluded by ignore paths. 7 of 8 files included in review.

Review: e1c1cece | Model: openai/gpt-5.5 | Powered by Arcjet Review

@arcjet-rei arcjet-rei left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I couldn't understand why a dependency bump would lead to changes in the functions used, so I had Claude take a look at the change. It spent a loooooooong time looking at everything, and the tl;dr is that this is actually a patch for an upstream regression in Node 24 (less tl;dr version appended). This fixes that regression, and the dependency bump is worth taking, but the PR title and description should be updated to reflect what's actually going on here. Everything else is fine!

What this patch does

This patch changes the test helper only. It changes no source file. Before, the
helper tracked the HTTP/2 sessions of the test server. Now it tracks the TCP
sockets that the server accepts, and close() destroys those sockets.

The cause is Node.js, not the dependency update

I put the lockfile back to @connectrpc/connect 2.1.2 and kept the old test
helper. The test run did not complete, in the same way. The new version of
connect-es is therefore not the cause.

I then ran transport/test/wire.test.ts on different versions of Node.js:

  • Node 22.23.2 — 21 tests pass
  • Node 24.19.0 — 21 tests pass
  • Node 24.20.0 — 21 tests pass
  • Node 24.21.0 — the test run does not complete
  • Node 26.8.2 — 21 tests pass

The release date of Node.js 24.21.0 is 2026-09-07. With this patch, all 54
transport tests pass on Node 24.21.0.

What Node.js 24.21.0 changed

lib/internal/http2/core.js is identical in 24.20.0 and 24.21.0. The change is
in src/node_http2.cc, from nodejs/node#65093, "http2: adapt receive deferral
for Node.js 24". Http2Session::Close() now defers its work when JavaScript
calls it from inside nghttp2_session_mem_recv(). The same commit also stops
SendPendingData() while the session receives data. Node.js 26 does not have
that second condition, and Node.js 26 passes the tests.

What you see in the test

On Node 24.20.0 and on Node 24.21.0, session.destroy() leaves the socket open.
Its state is { destroyed: false, readable: true, writable: false }. On 24.20.0,
each socket closes 1 to 5 milliseconds later. On 24.21.0, the socket of the
"HTTP fallback" test never closes. server.close() waits for that socket, and
its callback never runs. In that test, the client resets the stream after the
503 response, which is the path that nodejs/node#65093 changed.

CI

The last test run on main used Node 24.20.0 from the runner tool cache. The
tests on main pass for that reason. When the runner image gets 24.21.0, the
transport tests will not complete, on all three operating systems. This patch
prevents a failure that is not yet visible.

One correction to the new comment

The comment in proxy.ts says that a session in the closed state ignores
destroy(). That is not correct. destroy() runs, and it sets destroyed to
true. It disconnects the session from the socket, but it does not close the
socket. session.socket is a JavaScript Proxy, and after destroy() every
property of that Proxy throws ERR_HTTP2_SOCKET_UNBOUND. A reference to
Node.js 24.21.0 and nodejs/node#65093 in that comment would help the next reader.

@qw-in qw-in changed the title fix(transport): destroy tracked connections so HTTP/2 tests terminate test: track connections so HTTP/2 tests terminate in node 24.20.0 Sep 15, 2026
@qw-in
qw-in added this pull request to the merge queue Sep 15, 2026
Merged via the queue into main with commit 567dc00 Sep 15, 2026
67 of 68 checks passed
@qw-in
qw-in deleted the quinn/transport-connect-hang-fix branch September 15, 2026 22:00
@arcjet-review arcjet-review Bot removed the ready Ready to merge label Sep 15, 2026
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