Skip to content

http2: implement internalBinding('http2') for the vendored node test shim (+2 upstream tests) - #34499

Draft
cirospaciari wants to merge 3 commits into
mainfrom
claude/http2-internalbinding-shim
Draft

http2: implement internalBinding('http2') for the vendored node test shim (+2 upstream tests)#34499
cirospaciari wants to merge 3 commits into
mainfrom
claude/http2-internalbinding-shim

Conversation

@cirospaciari

Copy link
Copy Markdown
Member

What does this PR do?

internal/test/binding threw internalBinding("http2") is not implemented in Bun, so every vendored node http2 test gated on --expose-internals died on the import before running a single assertion. This adds the http2 case and vendors the two node v26.3.0 tests it unblocks.

The case is backed by real state wherever Bun has it:

  • constants is the public node:http2 table. All 240 keys already match node v26.3.0 exactly — I diffed them against the v26.3.0 binary and found zero value mismatches — so test-http2-binding becomes a genuine compat assertion rather than a check against a hand-written copy.
  • nghttp2ErrorString is a transcription of nghttp2's nghttp2_strerror, machine-diffed entry-by-entry against deps/nghttp2/lib/nghttp2_helper.c (42/42 keys and strings match).
  • Http2Session / Http2Stream are inert stand-ins for the native handle layer Bun does not have (Bun's streams drive the Rust engine directly). In node these are the native classes underneath the JS ones, and the tests stub a method on them to neuter the native call. Bun has no native call to neuter, so the stub is inert and the test ends up asserting Bun's real behavior — test-http2-respond-errors exercises the actual ERR_HTTP2_HEADERS_SENT / ERR_HTTP2_INVALID_STREAM paths in http2.ts.

Tests added, both byte-identical to node v26.3.0:

  • test-http2-binding.js
  • test-http2-respond-errors.js

Scope note

Seven other http2 tests are blocked on this same import but are not included, because they can only pass by faking node internals rather than testing Bun:

  • client-onconnect-errors, info-headers-errors, respond-nghttperrors, respond-with-fd-errors, server-push-stream-errors assert that a native return code is translated into an NghttpError. Bun's respond()/request() throw directly and there is no native return code to inject, so the shim itself would have to perform the translation the test is checking.
  • util-headers-list needs node's buildNgHeaderString (nghttp2 wire-format packing) and util-update-options-buffer needs node's optionsBuffer marshalling. Bun has neither; it packs headers via its own materializer.

One consequence worth knowing for future ports: because the stand-ins are unrelated to any real Bun object, a future test that stubs a method on them will silently no-op rather than fail loudly — check the stub actually took effect.

How did you verify your code works?

  • Both new tests pass with the CI invocation (bun run <file>), 5/5 on repeat runs.
  • Full regression sweep of all 259 already-vendored test-http2* files: no regressions. (test-http2-socket-proxy-handler-for-has.js flaked once under 6-way parallelism and passes 3/3 standalone; it does not reference internalBinding.)
  • Cross-checked against the node v26.3.0 binary: constants object identity (binding.constants === require('http2').constants) is true in both; nghttp2ErrorString of an unknown code returns "Unknown error code" in both.
  • Confirmed stubbing binding.Http2Stream.prototype.respond leaves Bun's real ServerHttp2Stream.prototype.respond untouched.
  • A plain h2 round-trip produces byte-identical output to node, and internalBinding of an unknown name still throws.

`internal/test/binding` threw for "http2", so every vendored node http2
test gated on `--expose-internals` died on the import instead of running.

Add the case, backed by real state where Bun has it:
- `constants` is the public node:http2 table (all 240 keys already match
  node v26.3.0 exactly, verified against the v26.3.0 binary)
- `nghttp2ErrorString` transcribes nghttp2's `nghttp2_strerror`
- `Http2Session`/`Http2Stream` are inert stand-ins for the native handle
  layer Bun does not have, so a test stubbing a handle method ends up
  asserting Bun's real behavior instead of a native return code

Vendor the two node v26.3.0 tests this unblocks, byte-identical:
test-http2-binding and test-http2-respond-errors.
@robobun

robobun commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator
Updated 6:35 PM PT - Aug 3rd, 2026

@robobun, your commit 35ea754 is building: #88581

@github-actions

Copy link
Copy Markdown
Contributor

This PR may be a duplicate of:

  1. node:http2: populate internal/http2/util for --expose-internals tests [1tpjlb] #29825 - Also adds a case "http2" to internalBinding() in src/js/internal/test/binding.ts with nghttp2ErrorString and stub classes, for unblocking vendored Node http2 tests using --expose-internals

🤖 Generated with Claude Code

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