Skip to content

h3: enable Safari (WebKit) WebTransport by advertising RFC-track SETTINGS_WT_MAX_SESSIONS - #363

Open
dgarnier wants to merge 1 commit into
hyperium:masterfrom
openstarnz:wt-modern-settings-pr
Open

dgarnier wants to merge 1 commit into
hyperium:masterfrom
openstarnz:wt-modern-settings-pr

Conversation

@dgarnier

Copy link
Copy Markdown

Problem

Safari cannot establish WebTransport sessions against h3-webtransport servers. Its extended CONNECTs arrive and are then cancelled by the browser itself (H3_REQUEST_CANCELLED), while Chrome succeeds against the same server.

The cause is settings-dialect negotiation. h3 currently advertises and recognizes only the legacy draft-02 experiment codepoints:

  • SETTINGS_ENABLE_WEBTRANSPORT = 0x2b603742
  • WEBTRANSPORT_MAX_SESSIONS = 0x2b603743

The RFC-track spec (draft-ietf-webtrans-http3, draft-07 and later) negotiates via a different codepoint, SETTINGS_WT_MAX_SESSIONS = 0xc671706a, and deprecates the legacy pair. WebKit implements only the RFC-track dialect: when the server's SETTINGS lack 0xc671706a, Safari concludes the server does not support WebTransport and abandons the CONNECT. Chrome still accepts either dialect for compatibility.

Change

17 insertions across h3/src/proto/frame.rs and h3/src/config.rs:

  • Define SettingId::WT_MAX_SESSIONS = 0xc671706a and mark it supported.
  • Send it (value = max_webtransport_sessions) alongside the legacy pair, so both dialects are advertised.
  • Parse it from peer settings: treat WT_MAX_SESSIONS > 0 as WebTransport support, and prefer its value for max_webtransport_sessions. The parse side matters independently of the send side: WebTransportSession::accept rejects sessions when the client's settings lack WebTransport support, and WebKit clients send only the RFC-track codepoint.

Not included: the capsule-protocol session close/drain semantics from the later drafts. This change is only the settings negotiation — the minimal fix that unblocks WebKit interop. In testing, Safari's close() still tears the session down cleanly at the QUIC layer.

Related but out of scope: #355 (the max_webtransport_sessions value is advertised but not enforced at runtime). This PR changes which codepoint the value is negotiated under, not its enforcement — the value remains declarative-only under both dialects, exactly as before.

Verification

  • Safari 26.6.2 (macOS, locally-trusted certificate): before the patch, every WT session attempt fails with the client cancelling its CONNECT; after the patch, sessions establish, bidirectional streams round-trip application data, and WebTransport.close() tears down cleanly. Same server, certificate, and page either side of the patch — the settings are the only variable.
  • Chrome remains green with both dialects advertised.
  • cargo test -p h3 --lib passes (230 tests), cargo fmt --check clean.
  • Also exercised end to end by a QUIC data server that pins this revision: its integration suite — including a wtransport-based interop client, i.e. an independent implementation that itself sends both dialects — passes against the patched crates.

Attribution

This investigation and patch were done together with Claude Code (Claude Fable 5): it isolated the failure to settings negotiation by A/B-testing Safari against draft-02-only and dual-dialect servers, wrote the patch, and verified it in a real browser. The commit carries a Co-Authored-By trailer accordingly. A human (me) reviewed and takes responsibility for the change.

WebKit-based browsers (Safari 26) negotiate WebTransport only via the
RFC-track codepoint (draft-ietf-webtrans-http3, draft-07+) and cancel
their extended CONNECTs when the server's SETTINGS carry just the
legacy draft-02 pair (0x2b603742/0x2b603743). Chrome accepts either
dialect.

Send both dialects, and treat an incoming WT_MAX_SESSIONS > 0 as
WebTransport support when parsing peer settings — the peer-side half
matters because WebTransportSession::accept rejects sessions when the
client's settings lack WebTransport support, and WebKit clients send
only the RFC-track codepoint.

Verified against Safari 26.6.2 (macOS, trusted local cert): session
establishes, bidirectional streams round-trip, and Chrome remains
green with both dialects advertised.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@dgarnier
dgarnier force-pushed the wt-modern-settings-pr branch from b3aefe6 to 9358b6e Compare October 24, 2026 18:13
@dgarnier

Copy link
Copy Markdown
Author

Rebased onto current master now that #356 has merged. There were no conflicts and the patch is unchanged (git range-diff shows the single commit identical: b3aefe6 = 9358b6e).

Re-verified on the rebased revision: Safari 27.0.1 and Chrome both establish sessions and stream data, including several concurrent streams per session, and cargo test passes for the workspace and for a downstream server pinned to this revision. The original verification in the description, on the Safari 26 line where WebTransport support arrived with 26.2, still stands.

@dgarnier dgarnier changed the title h3: advertise and accept RFC-track SETTINGS_WT_MAX_SESSIONS (0xc671706a) h3: enable Safari (WebKit) WebTransport by advertising RFC-track SETTINGS_WT_MAX_SESSIONS Oct 24, 2026
dgarnier added a commit to openstarnz/h3 that referenced this pull request Nov 9, 2026
Manual port of the SETTINGS-wait part of spotdemo4/h3@9564e2e
(spotdemo4@9564e2e, "Negotiate WebTransport
drafts 02 to 16"). Original author: trev <me@trev.xyz>.

A WebTransport CONNECT can arrive before the client's control stream, so
before its SETTINGS. WebTransportSession::accept checked
shared.settings() immediately, which returns defaults (WebTransport and
datagrams off) until the SETTINGS frame is read, and closed the
connection with H3_SETTINGS_ERROR "webtransport is not supported by
client". draft-ietf-webtrans-http3 section 3.1 says the server MUST NOT
process WebTransport requests until the client's SETTINGS have been
received.

Taken from 9564e2e:
- ConnectionState::peer_settings(): the peer's SETTINGS if received
- server::Connection::poll_peer_settings(): polls the control stream
  until the SETTINGS arrive
- WebTransportSession::accept awaits it before validating settings
  (no timeout, as upstream of this port; callers should bound it)

Left out of 9564e2e: WebTransport draft negotiation (WebTransportDraft,
webtransport_drafts, draft-specific codepoints and response headers,
webtransport-h3 token), the 16-entry SETTINGS frame and control-stream
buffer sizing.

Why a manual port: 9564e2e does not cherry-pick onto this branch; it is
bundled with draft negotiation and conflicts with our modern-WT-settings
patch (hyperium#363) in config.rs and frame.rs.

Adds h3-webtransport/tests/settings_race.rs: a raw quinn client sends
the extended CONNECT first and delays its control stream; the test
asserts accept stays pending, then succeeds once SETTINGS arrive (it
fails without the fix).

Co-authored-by: Darren Garnier <garnier@mit.edu>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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.

1 participant