From b3aefe68d576f33510ac175b0d752f70dd9f1a3d Mon Sep 17 00:00:00 2001 From: Darren Garnier Date: Tue, 25 Aug 2026 19:35:41 -0400 Subject: [PATCH] h3: advertise and accept RFC-track SETTINGS_WT_MAX_SESSIONS (0xc671706a) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- h3/src/config.rs | 14 +++++++++++++- h3/src/proto/frame.rs | 4 ++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/h3/src/config.rs b/h3/src/config.rs index c2a6fe64..1f828ae2 100644 --- a/h3/src/config.rs +++ b/h3/src/config.rs @@ -50,12 +50,21 @@ impl From<&frame::Settings> for Settings { max_field_section_size: settings .get(frame::SettingId::MAX_HEADER_LIST_SIZE) .unwrap_or(defaults.max_field_section_size), + // A peer signals WebTransport support with either the legacy + // draft-02 boolean or the RFC-track WT_MAX_SESSIONS (> 0 means + // supported); WebKit sends only the latter. enable_webtransport: settings .get(frame::SettingId::ENABLE_WEBTRANSPORT) .map(|value| value != 0) + .or_else(|| { + settings + .get(frame::SettingId::WT_MAX_SESSIONS) + .map(|value| value != 0) + }) .unwrap_or(defaults.enable_webtransport), max_webtransport_sessions: settings - .get(frame::SettingId::WEBTRANSPORT_MAX_SESSIONS) + .get(frame::SettingId::WT_MAX_SESSIONS) + .or_else(|| settings.get(frame::SettingId::WEBTRANSPORT_MAX_SESSIONS)) .unwrap_or(defaults.max_webtransport_sessions), enable_datagram: settings .get(frame::SettingId::H3_DATAGRAM) @@ -129,6 +138,9 @@ impl TryFrom for frame::Settings { frame::SettingId::WEBTRANSPORT_MAX_SESSIONS, max_webtransport_sessions, )?; + // Advertise the RFC-track codepoint alongside the legacy pair: + // Chrome accepts either dialect, WebKit requires this one. + settings.insert(frame::SettingId::WT_MAX_SESSIONS, max_webtransport_sessions)?; Ok(settings) } diff --git a/h3/src/proto/frame.rs b/h3/src/proto/frame.rs index a09520bb..9c70a1dc 100644 --- a/h3/src/proto/frame.rs +++ b/h3/src/proto/frame.rs @@ -406,6 +406,7 @@ impl SettingId { | SettingId::ENABLE_CONNECT_PROTOCOL | SettingId::ENABLE_WEBTRANSPORT | SettingId::WEBTRANSPORT_MAX_SESSIONS + | SettingId::WT_MAX_SESSIONS | SettingId::H3_DATAGRAM, ) } @@ -455,6 +456,9 @@ setting_identifiers! { H3_SETTING_ENABLE_DATAGRAM_CHROME_SPECIFIC= 0xFFD277, WEBTRANSPORT_MAX_SESSIONS = 0x2b603743, + // RFC-track codepoint (draft-ietf-webtrans-http3 draft-07+); WebKit + // requires this one and ignores the legacy pair above. + WT_MAX_SESSIONS = 0xc671706a, } const SETTINGS_LEN: usize = 8;