Skip to content

Webtransport draft-15 support#104

Open
aran wants to merge 13 commits intogoogle:mainfrom
aran:wt-draft-15
Open

Webtransport draft-15 support#104
aran wants to merge 13 commits intogoogle:mainfrom
aran:wt-draft-15

Conversation

@aran
Copy link
Copy Markdown

@aran aran commented Apr 1, 2026

Implements IETF WebTransport draft-15, which is now in WG Last Call.

The headline feature since draft-07 is the implementation of session-level flow control. Some constants are changed, the :protocol token changes to "webtransport-h3", reset-stream-at becomes mandatory, session limiting moves to being a http 429 from SETTINGS. The changes provide support for TLS keying material exporters for per-session auth.

There is one known "MUST" in the spec which is not implemented, which is from Section 3.2: "When the request contains the Origin header, the WebTransport server MUST verify the Origin header to ensure that the specified origin is allowed to access the server in question. If the verification fails, the server SHOULD reply with status code 403.". This stack of commits does not enforce this requirement. This is left to the application integration layer.

The spec also includes a "SHOULD" that these changes hide behind a constant gate, from section 4.6: "Endpoints SHOULD buffer streams and datagrams that arrive before the WebTransport session they belong to is fully established, then deliver them once the session is ready." It's gated for now because implementing it requires a handful of opinionated decisions.

Reference: https://datatracker.ietf.org/doc/draft-ietf-webtrans-http3/15/

aran added 13 commits April 1, 2026 14:15
Wire format definitions for draft-ietf-webtrans-http3-15, no dispatch logic.

Capsule types (capsule.h/cc, Section 5):
- Session-level flow control: WT_MAX_DATA, WT_MAX_STREAM_DATA,
  WT_DATA_BLOCKED, WT_STREAM_DATA_BLOCKED, WT_MAX_STREAMS_BIDI/UNIDI,
  WT_STREAMS_BLOCKED_BIDI/UNIDI
- Serialization and deserialization for all new capsule types

HTTP/3 SETTINGS identifiers (http_constants.h, Sections 3.1-3.2):
- SETTINGS_WT_ENABLED (0x2c7cf000) — replaces SETTINGS_WEBTRANS_MAX_SESSIONS
- SETTINGS_WT_INITIAL_MAX_STREAMS_UNI/BIDI, SETTINGS_WT_INITIAL_MAX_DATA

Error codes (http_constants.h, Section 9.5):
- Session: kWtFlowControlError (0x045d4487), kWtAlpnError (0x0817b3dd)
- Stream: kWtSessionGone (0x170d7b68), kWtBufferedStreamRejected (0x3994bd84)
- Connection: kWtRequirementsNotMet (0x212c0d48)
- kH3DatagramError (0x33, RFC 9297 Section 9.1)
Refactor only — no production behavior change.

- Extract ~530 lines of reusable test fixtures (TestSession, mock helpers,
  ReceiveWebTransportSession, etc.) from quic_spdy_session_test.cc into
  quic_spdy_session_test_utils.h for sharing across draft-15 test files
- Add QuicSpdySessionPeer::EnableWebTransport(session, versions) overload
  to allow tests to specify exact version sets
- Add draft15_constants.h with spec codepoints for test assertions
  (SETTINGS values, error codes, capsule types from Sections 5, 9.5)
Prepare for draft-15 counterparts by making 4 existing tests explicitly
draft-07-only. Each test now constructs a draft07_only version set and
passes it to EnableWebTransport() and QuicSpdySessionPeer::EnableWebTransport().

Renamed tests (quic_spdy_stream_test.cc):
- ProcessWebTransportHeadersAsClient -> *Draft07
- WebTransportIgnoreSubprotocolsThatWereNotOffered -> *Draft07
- WebTransportInvalidSubprotocolResponse -> *Draft07
- ProcessWebTransportHeadersAsServer -> *Draft07

No behavioral change — same assertions, same coverage.
Replace set_enable_web_transport(bool) with
set_supported_web_transport_versions(WebTransportHttp3VersionSet) on the
QUIC client stack, so tests and applications can control which WebTransport
draft versions the client advertises during SETTINGS negotiation.

No-op refactor — existing callers pass kDefaultSupportedWebTransportVersions
(equivalent to the previous true) or empty set (equivalent to false).

Files changed:
- quic_spdy_client_base.h: bool -> VersionSet member and setter
- quic_simple_client_session.h/cc: constructor takes VersionSet
- quic_default_client.cc: passes VersionSet to session
- moqt_client.cc: uses kDefaultSupportedWebTransportVersions
- end_to_end_test.cc: wt_versions_ member, uses kDefaultSupportedWebTransportVersions
Mirror the client-side set_supported_web_transport_versions() pattern on
the server side. QuicSimpleServerBackend gains a virtual
SupportedWebTransportVersions() that returns the version set (defaulting
to kDefaultSupportedWebTransportVersions when SupportsWebTransport() is
true). QuicTestBackend stores a configurable version set, and
QuicSimpleServerSession delegates to the backend instead of hardcoding
kDefaultSupportedWebTransportVersions.
Draft-15 uses `:protocol: webtransport-h3` (Section 3), not
`webtransport`. Pick the token based on the negotiated version.

Also enable `reset_stream_at` transport parameter when draft-15 is
in the client's version set (Section 3.1 requires it).
Production changes:
- Add kDraft15 to WebTransportHttp3Version enum (not yet in defaults)
- FillSettingsFrame(): emit SETTINGS_WT_ENABLED for kDraft15 (Section 3.1)
- OnSetting(): handle SETTINGS_WT_ENABLED, SETTINGS_WT_INITIAL_MAX_STREAMS_*,
  SETTINGS_WT_INITIAL_MAX_DATA with 0-RTT anti-reduction checks (Section 3.2)
- Upgrade token: accept ":protocol: webtransport-h3" for draft-15,
  retain "webtransport" for older drafts (Section 3)
- Validate :scheme=https, :authority, :path on draft-15 CONNECT (Section 3.2)
- ALPN validation: close session with kWtAlpnError on subprotocol
  mismatch (Section 3.3)
- Validate session IDs are client-initiated bidi stream IDs (Section 4)
- Use WT_BUFFERED_STREAM_REJECTED for excess buffered streams in draft-15
  (Section 4.6)
- Use WT_REQUIREMENTS_NOT_MET instead of SETTINGS_ERROR for missing
  prerequisites in draft-15 (Section 3.1)
- Add WebTransportHttp3::OnInternalError() for implementation-detected
  protocol errors
- Refactor QuicSession::ResetStream() to accept QuicResetStreamError
  directly (needed for IETF error codes on stream resets)

Test changes (4 new test files, 1 updated):
- web_transport_draft15_test_utils.h: Draft15SessionTest fixture
- web_transport_session_establishment_draft15_test.cc (638 lines)
- web_transport_version_negotiation_draft15_test.cc (172 lines)
- web_transport_headers_draft15_test.cc (308 lines)
- quic_spdy_stream_test.cc: 4 new *Draft15 header processing tests
Production changes:
- Extract ResetAssociatedStreams() and call on close-sent, close-received,
  drain-received, and connect-stream-closing (Section 6: "endpoint MUST
  reset all streams associated with the session")
- Use WT_SESSION_GONE (kWtSessionGone) for stream resets on termination
- Block post-termination stream creation, datagram sending via
  IsTerminated() guard (Section 6)
- Enforce 1024-byte limit on WT_CLOSE_SESSION error messages (Section 6)
- Send STOP_SENDING + StopReading on close-received to prevent
  additional data on the CONNECT stream (Section 6)
- Track active session count (OnWebTransportSessionCreated/Destroyed)
  for session limiting
- Call ProcessBufferedWebTransportStreamsForSession after server-side
  session creation to deliver pre-buffered streams (Section 4.6)
- Add WebTransportStreamAdapter::SetWebTransportSession() for direct
  WT session pointer (needed by FC in later commit)
- Expose web_transport_stream_adapter() accessor on QuicSpdyStream

Test changes (3 new test files):
- web_transport_streams_draft15_test.cc (336 lines)
- web_transport_buffering_draft15_test.cc (196 lines)
- web_transport_error_codes_draft15_test.cc (300 lines)
Tests for the flow control capsule types added in commit 1:
WT_MAX_DATA, WT_MAX_STREAM_DATA, WT_MAX_STREAMS_BIDI/UNIDI,
WT_DATA_BLOCKED, WT_STREAM_DATA_BLOCKED, WT_STREAMS_BLOCKED_BIDI/UNIDI.

- New file: capsule_draft15_test.cc (251 lines)
- Move 48 lines of capsule-level tests out of
  web_transport_capsule_dispatch_draft15_test.cc (capsule wire format
  tests belong with the capsule library, not session-level dispatch)
Draft-15 replaces SETTINGS_WEBTRANS_MAX_SESSIONS with per-session flow
control. Until FC is negotiated, at most one session is allowed (Section
5.1). Excess sessions are rejected with H3_REQUEST_REJECTED.

Production changes:
- Add CanCreateNewWebTransportSession() to QuicSpdySession: checks
  active_web_transport_sessions_ < 1 for draft-15 without FC
- Server path (MaybeProcessReceivedWebTransportHeaders): reject excess
  CONNECT requests with H3_REQUEST_REJECTED (Section 5.1)
- Client path (MaybeProcessSentWebTransportHeaders): skip WT session
  creation when limit exceeded
- Track session_counted_ flag on WebTransportHttp3 to ensure the
  active count is decremented exactly once (on close-sent,
  close-received, drain-received, or connect-stream-closing)

Test changes:
- web_transport_session_limiting_draft15_test.cc (156 lines)
Implements session-level stream and data limits exchanged via capsules
on the CONNECT stream, layered above QUIC connection-level flow control.

Production changes — QuicSpdySession (Sections 3.1-3.2):
- FillSettingsFrame(): emit WT_INITIAL_MAX_STREAMS_BIDI/UNI and
  WT_INITIAL_MAX_DATA when flow control is configured
- Add local/peer FC limit fields and accessors
- wt_flow_control_enabled() predicate

Production changes — WebTransportHttp3 (Sections 5.1-5.5):
- Stream limits: SetInitialStreamLimits, CanOpenNextOutgoingStream,
  OnIncomingStreamAssociated, OnMaxStreamsCapsuleReceived
- Data limits: SetInitialDataLimit, CanSendData, OnDataSent,
  OnIncomingDataReceived, OnMaxDataCapsuleReceived
- Wire FC setup into session creation via ApplyWebTransportFlowControlLimits
- Update CanCreateNewWebTransportSession to allow multiple sessions
  when FC is negotiated

Production changes — QuicSpdyStream (Section 5):
- Capsule dispatch for WT_MAX_STREAMS_BIDI/UNIDI, WT_MAX_DATA
- Silently accept WT_DATA_BLOCKED, WT_STREAMS_BLOCKED_BIDI/UNIDI
- WT_MAX_STREAM_DATA and WT_STREAM_DATA_BLOCKED are prohibited in
  draft-15 (Section 5.4): receipt triggers kWtFlowControlError

Production changes — WebTransportStreamAdapter:
- FC-aware CanSendData check before writes

Test changes (1 new file, 6 updated):
- web_transport_flow_control_draft15_test.cc (2171 lines)
- Updates to buffering, capsule dispatch, error code, session
  establishment, session limiting, and version negotiation tests
Implements WebTransportHttp3::GetKeyingMaterial() per Section 4.8 of
draft-ietf-webtrans-http3-15. Derives per-session TLS keying material
using the "EXPORTER-WebTransport" label and a context struct containing
the session ID, application label, and application context.

Production changes:
- Add GetKeyingMaterial() to WebTransportHttp3 (web_transport_http3.h/cc)
- Add ExportKeyingMaterial() forwarding on QuicSpdySession
  (quic_spdy_session.h) — needed because GetMutableCryptoStream() is
  protected on QuicSession
- Add absl/status/statusor.h include to web_transport_http3.h

Test infrastructure:
- TestCryptoStream::ExportKeyingMaterial() now returns true with
  deterministic input-dependent output and captures label/context
  arguments for byte-level verification

Unit tests (web_transport_keying_material_draft15_test.cc, 60 tests):
- Context struct serialization (endianness, field sizes, max lengths)
- Wire-level correctness (captures actual args to ExportKeyingMaterial)
- Session isolation, determinism, error conditions, boundary values
- Draft-07 unavailability

E2E test (end_to_end_test.cc):
- WebTransportKeyingMaterial: real BoringSSL TLS exporter
- WebTransportDraft15SessionEstablishment: version assertion
- WebTransportDraft15SessionLimiting: second session blocked without FC
- WebTransportDraft15NoDatagramsAfterClose: post-close rejection
- WebTransportDraft15FlowControlLimits: stream limit enforcement
- WebTransportDraft15DataFlowControl: data limit enforcement
- WebTransportDraft15ResetStreamAt: RESET_STREAM_AT on stream reset

Removes mock-based web_transport_draft15_end_to_end_test.cc — all its
tests are now covered by real e2e tests or existing e2e tests that
negotiate draft-15 via the version set API.
Add kDraft15 to kDefaultSupportedWebTransportVersions so that new
sessions negotiate draft-15 when both endpoints support it (Section
7.1: highest mutually supported version wins).

Also sort draft-15 test file entries in source_list.bzl alphabetically.

Single-line production change, separately revertable.
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