Repository navigation
docs: add optional HTTP/3 companion example - #5785
brianathere wants to merge 1 commit into
Conversation
|
|
|
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
WalkthroughAdds a standalone HTTP/3 example. An HTTP/3 companion forwards requests to the example’s HTTPS development server, while the server also supports HTTP/2 and HTTP/1.1. The example includes a browser page with HMR, setup documentation, protocol tests for portable and WASM modes, and a Chromium test for HTTP/3, HMR, and HTTP/2 fallback. Priority: Merge Risk:
|
There was a problem hiding this comment.
Actionable comments posted: 1

Review info
-
Run configuration-
- Source mode: Auto
- Repository: Unavailable
- Branch: Unavailable
- File: No configuration file from this repository applied
- Commit: Unavailable
- Layers: Organization UI
- Review profile: CHILL
- Plan: Advanced
- Run ID:
8f6b27c7-b099-424e-8eaf-5ece4e645595
Files ignored due to path filters: 1-
examples/server/http3/package-lock.jsonis excluded by!**/package-lock.json
Files selected for processing: 14-
examples/README.mdexamples/server/http3/.gitignoreexamples/server/http3/README.mdexamples/server/http3/app.jsexamples/server/http3/companion.jsexamples/server/http3/index.htmlexamples/server/http3/message.jsexamples/server/http3/package.jsonexamples/server/http3/server.jsexamples/server/http3/start.jsexamples/server/http3/test/browser.test.jsexamples/server/http3/test/helpers.jsexamples/server/http3/test/protocol.test.jsexamples/server/http3/webpack.config.js
Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 7 remain after this review.
-
| return; | ||
| } | ||
| const forwarded = endToEndHeaders(headers); | ||
| forwarded.host = headers[":authority"]; |
There was a problem hiding this comment.
Handle malformed HTTP/3 requests without throwing from the stream handler.
https.request() throws synchronously for invalid input. Two inputs can trigger this:
forwarded.host = headers[":authority"]isundefinedwhen the client sends aHostheader but no:authority. RFC 9114 allows this. Node then throwsERR_HTTP_INVALID_HEADER_VALUE.- A
:path,:method, or header value with invalid characters throwsERR_UNESCAPED_CHARACTERS,ERR_INVALID_HTTP_TOKEN, orERR_INVALID_CHAR.
The throw happens inside an event handler. Unless the library catches it, the throw becomes an uncaught exception and stops the dev server. The listener is on loopback only, so any local UDP client can send such a request. Use the Host header as a fallback and answer 400 when request creation fails.
🛡️ Proposed fix
-
--- "a/examples/server/http3/companion.js" +++ "b/examples/server/http3/companion.js" @@ -51,10 +51,12 @@ return; } const forwarded = endToEndHeaders(headers); - forwarded.host = headers[":authority"]; + forwarded.host = headers[":authority"] ?? headers.host; let response; - const request = https.request( - { + let request; + try { + request = https.request( + { hostname: "127.0.0.1", port: upstreamPort, servername: "localhost",
Wrap the existing
https.request(...)call intry. Add thiscatchafter the call:} catch { stream.respond({ ":status": "400" }); stream.end(); return; }
Also applies to: 56-56
bjohansebas
left a comment
There was a problem hiding this comment.
HTTP/2 and HTTP/3 use different underlying transports. A simple header won’t make HTTP/3 work in Node.js, and QUIC != TLS, so I’m going to close this. Although Bun supports HTTP/3, I think it’s best to wait for WinterTC to establish a standard.
Developers can exercise HTTP/3 asset requests while webpack-dev-server keeps its normal HTTP/2/HTTP/1.1 listener, middleware, and WSS hot updates. This draft adds a standalone, opt-in example using
@currentspace/http30.9.3. Related to the historical QUIC request in #4976 and its suggested extension approach.The primary server advertises a loopback HTTP/3 companion through
Alt-Svc. The companion forwards requests through verified HTTPS to the existing middleware, preserves compressed bytes and cookies, and closes its upstream streams on cancellation. Stopping it withdraws the advertisement and leaves the primary listener available for HTTP/2 fallback.The example has its own pinned dependencies and npm lockfile. The published webpack-dev-server package, core server implementation, default dependencies, and Node requirement are unchanged. This is a draft for feedback on whether this optional example belongs here; it does not add a new built-in
serveroption.Validation
On macOS ARM64, all six focused tests passed on Node 24.22.1 and again on Node 26.12.1:
h3document, bundle, and hot-update responses; WSS HMR without a page reload; HTTP/2 fallback after stopping the companion.Clean
npm ci --ignore-scripts, the documented CLI with verified HTTPS and SIGTERM shutdown, Prettier 3.2.4, CSpell 10.3.3, andgit diff --checkpassed. The full upstream suite has not been run locally; its ESLint configuration excludes examples. The focused tests are run separately from the root suite.Reproduce from
examples/server/http3:npm ci --ignore-scripts npm test npx playwright install chromium npm run test:browserMeasured results and limits
These are 25 sequential loopback requests per protocol on one machine. HTTP/3 includes an extra HTTPS proxy hop. They are diagnostic measurements, not a general performance claim.
The README documents the local-CA Chromium requirements: a dedicated profile, a leaf SPKI pin and a host-scoped QUIC allowance. The primary origin still uses Alt-Svc discovery and can fall back to HTTP/2. The tests do not change OS trust settings or globally disable TLS verification.
The optional transport requires Node 24+, and its root package is about 46.8 MB compressed / 151.9 MB unpacked. This prototype is qualified only on macOS ARM64. Windows, other browsers, dynamic SNI, HTTP/3 WebSockets, WebTransport, trailers, and public/LAN deployment are outside its validated scope. No changeset is needed for this example-only contribution.
Summary by CodeRabbit