Skip to content

Accept negotiated server-initiated streams in the H3 client - #2

Open
xenorune wants to merge 2 commits into
adopted/webtransportfrom
runewarp/server-initiated-streams
Open

xenorune wants to merge 2 commits into
adopted/webtransportfrom
runewarp/server-initiated-streams

Conversation

@xenorune

@xenorune xenorune commented Oct 24, 2026 •

Copy link
Copy Markdown
Member

What

Add bounded client acceptance of server-initiated bidirectional streams after extension negotiation. Ordinary HTTP/3 still rejects unnegotiated streams with H3_STREAM_CREATION_ERROR.

This is layer 2 of the fork stack, based on adopted/webtransport at ea95c4b3eeed5e41b9ef39612a0091a69c0e37f2. Layer 1 is #1.

Why

runewarp/runewarp#255 requires generic stream dispatch beneath the later extension implementation, following the fork layering decision. RFC 9114 section 6.1 permits server bidirectional streams only after an extension is negotiated.

This layer contains no WebTransport or Runewarp dispatch policy. Client WebTransport negotiation, stream framing and exact session validation, server session opening, session lifecycle fixes, and the top-branch patch inventory remain deferred to layer 3.

How

  • Add Connection::server_initiated_streams(capacity, rejection_code, negotiated) and a cancellation-safe IncomingStreams receiver. Export Settings so extension crates can name the negotiation predicate's arguments.
  • Call the extension-supplied function pointer with the local advertised settings and actual peer settings only after peer SETTINGS arrive. The extension owns bilateral negotiation rules. No registration or a false predicate preserves the ordinary H3 connection error. Streams arriving before peer SETTINGS are rejected rather than buffered as negotiated streams.
  • Use one bounded FIFO with a mutex and atomic waker, without background tasks. Reject the newest stream on saturation. Explicitly reset and stop rejected streams and queues abandoned by receiver drop, registration replacement, driver drop, or driver termination. A dropped receiver retains the negotiation gate and rejects subsequent negotiated streams individually. Once received, raw streams and their validation/cancellation belong to the extension.
  • Limit client control processing and bidirectional dispatch to 32 items each per driver poll, then schedule another poll. Preserve transport errors instead of relabelling them as stream-creation errors.
  • Add nine timed tests covering negotiated dispatch and GET/PUT coexistence; missing local or peer settings evidence; absent peer SETTINGS; absent registration; FIFO saturation and recovery; RESET_STREAM and STOP_SENDING on abandonment; receiver cancellation and wakeups; transport closure; and yielding through a complete ready burst using a real Quinn transport adapter.

Validation on macOS ARM64 with Rust 1.96.0: cargo test --workspace --all-features and cargo test --workspace each pass 293 tests, including 24 existing network integration tests and nine new timed tests. cargo fmt --all -- --check, cargo clippy --workspace --lib --all-features -- -D warnings, and ci/example_test.sh pass. Native h3spec passes 43 cases using the five exclusions from ci/h3spec.sh. All-target Clippy reports exactly the 16 layer-1 diagnostics in unchanged upstream files, with none in changed code. Crate names, manifests, lockfile, and master are unchanged; no unsafe code was added.

Claude Opus 5.5 reviewed ad7d1cd38c55af550a18d977a73998058e9b8093 and found no blocking defects. The review identified a generic compatibility change: spawning the client driver now requires C::BidiStream: Send, because the driver owns the dispatch queue. This is accepted for the fork and will be recorded in the top-layer PATCHES.md; concrete Quinn streams already satisfy it. The reviewer independently ran the nine dispatch tests, then repeated them 25 times without failures. Extensions must arrange negotiation ordering before the Server opens streams: an incoming stream before peer SETTINGS retains the ordinary H3 connection error. The predicate sees configured local settings; callers suppressing SETTINGS with send_settings(false) must not treat configuration as wire negotiation evidence.

🤖 Written by @dtcristo's agent (GPT-6.1-Sol, Claude Opus 5.5)

dtcristo and others added 2 commits October 24, 2026 21:30
Co-authored-by: GPT-6.1 Sol <noreply@openai.com>
Co-authored-by: GPT-6.1 Sol <noreply@openai.com>
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