Skip to content

fix(ext/node): flush GOAWAY before destroying http2 socket and expose internal/http2/core#33566

Merged
littledivy merged 1 commit into
denoland:mainfrom
divybot:claude/test-http2-options-max-headers-exceeds-nghttp2
Apr 27, 2026
Merged

fix(ext/node): flush GOAWAY before destroying http2 socket and expose internal/http2/core#33566
littledivy merged 1 commit into
denoland:mainfrom
divybot:claude/test-http2-options-max-headers-exceeds-nghttp2

Conversation

@divybot

@divybot divybot commented Apr 27, 2026

Copy link
Copy Markdown
Contributor

Summary

Enables test-http2-options-max-headers-exceeds-nghttp2 in node_compat suite.

Test plan

  • cargo test --test node_compat -- test-http2-options-max-headers-exceeds-nghttp2

… internal/http2/core

Enables tests/node_compat/runner/suite/test/parallel/test-http2-options-max-headers-exceeds-nghttp2.js

Co-authored-by: Divy Srivastava <[email protected]>

@fibibot fibibot 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.

LGTM. Two coordinated changes that together unblock the test:

socket.destroy()socket.end(cb) then destroy — correct fix for a real bug. handle.hasPendingData() returns false once nghttp2's internal want_read/want_write are both 0 (session terminated, GOAWAY drained from nghttp2's side), but the GOAWAY bytes are still in the JS socket's writable-stream buffer waiting for the OS write. socket.destroy() would discard those, so the peer never sees the COMPRESSION_ERROR(9) GOAWAY and never surfaces ERR_HTTP2_SESSION_ERROR. socket.end(cb) flushes first, then FIN, then the cb destroys for good measure (some peers don't surface an error on a half-open FIN alone). The !socket.destroyed re-check inside the cb handles the race where the socket got destroyed by another path while the writable was draining.

The condition guarding this block (!handle.hasPendingData() && !this.destroyed, fired from socket.on('data')) only triggers post-session-termination — for a normal idle HTTP/2 connection want_read stays non-zero, so this won't fire on every quiet receive. Verified has_pending_data in ext/node/ops/http2/session.rs:2124 is want_write != 0 || want_read != 0.

Internal internal/http2/core shim — the upstream test does require('internal/http2/core').ServerHttp2Session for an instanceof assertion. Standard pattern for --expose-internals Node compat tests. The new file just re-exports the four classes (ClientHttp2Session, Http2Session, Http2Stream, ServerHttp2Session) which all already exist in ext/node/polyfills/http2.ts (lines 1363, 3048, 3512, 3638) — verified the names line up.

Test enrollment alphabetically positioned correctly between no-wanttrailers-listener and options-server-request.

CI still building, no failures on completed jobs.

@littledivy
littledivy merged commit 3a187c1 into denoland:main Apr 27, 2026
112 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.

3 participants