Repository navigation
Conversation
SessionId::from(StreamId) took the stream index (the ID shifted right by two) while the reverse conversion and decoding from the wire use the full stream ID. A session on any CONNECT stream other than 0 got the wrong ID, so streams the server opened for it named a different session. (cherry picked from spotdemo4/h3@fb114cf) Co-authored-by: GPT-6.1 Sol <noreply@openai.com>
h3 only advertised and recognised SETTINGS_ENABLE_WEBTRANSPORT, the codepoint of drafts 02 to 06, so clients that only speak newer drafts, such as Safari 26, could not establish a session (hyperium#347). Drafts 00 to 16 use four distinct SETTINGS codepoints, each shared by a range of drafts with the same wire format. WebTransportDraft names those ranges: Draft02 (02-06), Draft07 (07-11), Draft13 (13-14) and Draft15 (15-16). The server advertises every draft selected with Builder::webtransport_drafts (all of them by default) and WebTransportSession::accept uses the latest draft the client also advertised. Draft 12 shares draft 07's codepoint but requires flow control, which is not implemented, so SETTINGS_WT_MAX_SESSIONS is always 1 to keep flow control disabled for drafts 13 and 14. Other changes needed for this: - WebTransportSession::accept waits for the client's SETTINGS before handling the CONNECT request, as the drafts require. Previously a CONNECT that arrived before the control stream closed the connection with "webtransport is not supported by client". - The webtransport-h3 upgrade token of drafts 15 and 16 is accepted. - The sec-webtransport-http3-draft response header is only sent to draft 02 clients. - A SETTINGS frame can hold 16 entries instead of 8, so a peer sending every recognised setting is not rejected. The entries now live on the heap so that Frame does not grow. - The control stream header buffer is sized for the largest SETTINGS frame. With the new settings, a large max_webtransport_sessions value overflowed the 64 byte buffer and panicked. (cherry picked from spotdemo4/h3@9564e2e) Co-authored-by: GPT-6.1 Sol <noreply@openai.com>
Adds enable_webtransport and webtransport_drafts to the client builder so a client can advertise WebTransport drafts, and uses them to check the draft a server negotiates over a real quinn connection: draft 02 for Chrome and Firefox, draft 13 for Safari 26, draft 15 for a client using the webtransport-h3 upgrade token, and the latest draft the server also supports. Draft 15 can't be tested against a browser yet, and webtransport-go's client refuses servers without the reset_stream_at transport parameter, which quinn does not implement. The Safari job advertising only drafts 02 and 13 is now informational: Safari 26 cancels its CONNECT unless draft 07 is advertised. Adoption: Omit browser CI and manifest edits. Run the Quinn negotiation tests in examples/tests using its existing dependencies, and bound each complete async test with a timeout. The client builder only advertises settings; client session support remains deferred. (cherry picked from spotdemo4/h3@96c4283) Co-authored-by: GPT-6.1 Sol <noreply@openai.com>
WebTransportSession could neither end a session with an error code and message, nor learn that the client ended one, nor ask the client to wind down. It now reads and writes the WT_CLOSE_SESSION and WT_DRAIN_SESSION capsules on the CONNECT stream: - close(code, reason) sends WT_CLOSE_SESSION and finishes the CONNECT stream. The reason is truncated to 1024 bytes at a character boundary. - drain() sends WT_DRAIN_SESSION. - closed() waits for the session to terminate and returns its close code and reason. A CONNECT stream that ends without a capsule counts as code 0 with an empty reason. After a close from the client, the server finishes its side of the CONNECT stream. An invalid capsule resets the CONNECT stream with H3_MESSAGE_ERROR. - draining() waits for the client to send WT_DRAIN_SESSION. Capsules may span DATA frames or share one, and unknown capsules are skipped without buffering them. Once a session terminates, which includes being dropped, its streams are reset and stopped with WT_SESSION_GONE the next time they are used or when they are dropped, and tasks blocked on them are woken. Opening new streams fails. The CONNECT stream is now split, so WebTransportSession requires C::BidiStream: quic::BidiStream<B>, which h3-quinn satisfies. Adoption: Keep manifests unchanged. Relocate integration tests and their shared Quinn fixture to examples/tests, which already supplies the required dependencies. Add whole-test async timeouts. (cherry picked from spotdemo4/h3@2a7174a) Co-authored-by: GPT-6.1 Sol <noreply@openai.com>
After close(), closed() returned as soon as the close capsule and FIN were queued. The example server then dropped the session and with it the QUIC connection, which discards unsent stream data, so browsers saw the connection drop instead of the close (Chrome: "Connection lost"). closed() now also waits for the client to finish or reset its side of the CONNECT stream, as draft 16 recommends before closing the connection. After a close from the client we stop reading the CONNECT stream instead, since that close has already arrived. Adoption: Relocate the regression test under examples/tests and bound the full async test. (cherry picked from spotdemo4/h3@b0ae8fe) Co-authored-by: GPT-6.1 Sol <noreply@openai.com>
The Safari 26 crash on a server close is not caused by the FIN after the WT_CLOSE_SESSION capsule but by the grease frame that h3's finish() sent in between: with grease disabled, capsule and FIN together work in every Safari interop run. Frames of unknown types must be ignored (RFC 9114, section 9), so this is a Safari bug, but it takes down the networking of every tab. close() sends the FIN right after the capsule again, as the drafts require, and WebTransportSession::accept disables grease on the CONNECT stream through a new RequestStream::disable_grease() (behind the i-implement-a-third-party-backend feature). closed() still waits for the client to close its side before returning. Adoption: Apply the final no-grease and close-order changes directly on b0ae8fe, omitting the superseded delayed-FIN workaround 6164649. Keep immediate FIN and wait for the client before allowing closed() to finish. Browser CI and example-reporting changes are omitted. (cherry picked from spotdemo4/h3@43d90ad) Co-authored-by: GPT-6.1 Sol <noreply@openai.com>
Co-authored-by: GPT-6.1 Sol <noreply@openai.com>
Co-authored-by: GPT-6.1 Sol <noreply@openai.com>
Co-authored-by: GPT-6.1 Sol <noreply@openai.com>
Co-authored-by: GPT-6.1 Sol <noreply@openai.com>
Co-authored-by: GPT-6.1 Sol <noreply@openai.com>
This was referenced Oct 24, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Layer 1 of the fork stack for runewarp/runewarp#255. This PR targets
masterfor review only and MUST NEVER BE MERGED INTOmaster. That branch remains an exact mirror ofhyperium/h3; later layers build onadopted/webtransport.Adopt full CONNECT stream IDs, WebTransport draft negotiation, and session close/drain support from spotdemo4/h3. Add regression coverage for GET-before-CONNECT, independent FINs, blocked stream opens, and cancelled lifecycle waiters. Crate names and manifests stay unchanged, and upstream test suites remain enabled.
Why
The accepted carrier decision needs one session alongside ordinary HTTP/3 requests. The fork decision separates adopted fixes from the generic stream-dispatch and WebTransport integration layers.
Stock session IDs incorrectly turn CONNECT stream ID
4into session ID1, and stock WebTransport cannot close or drain sessions. Adopted close handling also needed two local corrections: opens blocked on QUIC credit must wake on session termination, and cancelled waiters must release their task wakers without waiting for a session event.How
Base:
hyperium/h3@43745ad87e95b0c035ac9c547aad467303213425. Six adopted commits retain trev's authorship and exact source provenance in their commit messages:spotdemo4/h3@fb114cfa21d79a668dcd99d4354302638dec1458: full CONNECT stream ID, including its regression test.spotdemo4/h3@9564e2eac819dcd72b7e578f38887425a8112776: draft settings/tokens, peer-SETTINGS wait, and SETTINGS buffer sizing.spotdemo4/h3@96c4283ef733757db46ee7009a63a5965ee1d86d: Quinn negotiation tests and client SETTINGS advertisement hooks. These hooks do not implement client sessions or server-stream dispatch.spotdemo4/h3@2a7174a932cf091c4060fe6c73b0a0a86cbff79b: close/drain capsules, session lifecycle, and stream termination.spotdemo4/h3@b0ae8feb6431d4fe1906d7e9ee1ac344f54d5658: wait for the client's CONNECT close.spotdemo4/h3@43d90ad60756ad6d2d5740d2482af6fdb3d35580: immediate FIN, no CONNECT grease, and final close ordering.Use the negotiation revisions from
wt-session-close;wt-draftscontains the equivalent earlier revisions. Skip browser CI, browser-reporting changes, and publishing changes. Skip the superseded delayed-FIN workaround61646499204706437709876b5c3c18bbcd413a2c; apply the final immediate-FIN/no-grease changes directly on the close-wait fix.Move adopted network tests into
examples/tests, whose existing dependencies avoid all manifest edits. Every async test has an internal timeout. Local fixes cover wire/half-close behavior, close wakeups for credit-blocked opens, and cancellation-owned waiter registrations. No unsafe code is added.Claude Opus 5.5 reviewed
0e89328c17b1b501f4838cc2293a5f135b379da8. Two subsequent commits address its actionable findings:WT_CLOSE_SESSION, whether buffered in the same DATA frame or received in a later frame, using the malformed-capsuleH3_MESSAGE_ERRORreset/stop path. Validate peer capsules after a local close too. Session streams terminate immediately on the close capsule;closed()continues through peer FIN and requires a caller timeout if the peer withholds it. Regressions cover both frame cases, repeated error reporting, local-first close, and cancellation followed by another caller completing FIN validation.The review predates these two fixes; no second review or CI monitoring was performed by this implementation agent.
Final local validation on macOS arm64 with Rust/Cargo 1.96.0:
perl -e 'alarm 300; exec @ARGV' cargo test --workspace --all-features: 284 tests and doctests pass, including 24 network tests. Baseline: 247 pass.cargo fmt --all -- --check: pass.cargo clippy --workspace --all-features --all-targets: pass with the same 16 baseline warnings in unchanged upstream tests andexamples/client.rs; no new warnings.cargo clippy --workspace --all-features --lib -- -D warningsandcargo clippy -p examples --all-features --tests --example webtransport_server -- -D warnings: pass../ci/example_test.sh: passed on the pre-review head; not rerun because these fixes only change WebTransport lifecycle handling.ci/h3spec.sh: 43 cases passed on the pre-review head; not rerun because ordinary HTTP/3 handling is unchanged.4 -> 1regression, prematureclosed()return, credit-blocked opens that never wake, retained cancelled-waiter wakers, 1,000 unnecessary stream wakeups, and accepted trailing data after peer-first and local-first closes.Known limits remain explicit. Draft negotiation is not full current-draft conformance; draft 12 flow control and Quinn's missing
reset_stream_atsupport are not implemented. Close/drain sends retain h3's in-flight-write cancellation constraints, andclosed()needs an application timeout if the peer never responds. Generic client dispatch, client sessions, exact session validation, invalid CONNECT/cap enforcement, the split server opener/driver, andPATCHES.mdbelong to later layers. Security PRs hyperium/h3#366, #377, and #378 remain open and unadopted; their dispositions are deferred to the top-layer inventory. Existing encoded-frame/header-memory risks therefore remain. The settings/draft mismatch currently closes the connection; its stream-error/4xx correction stays deferred to layer 3 invalid-CONNECT work. The optional inlineWriteBufsize optimization andWebTransportDraft::ALLslice API change are left unchanged to keep this patch focused. Concurrent credit-blocked Quinn bidi opens and received-header exact session validation remain explicit layer 3 work. Browser interop and CI monitoring are outside this implementation handoff.🤖 Written by @dtcristo's agent (GPT-6.1-Sol, Claude Opus 5.5)