Skip to content

Research: WebTransport (HTTP/3) and WebSocket-over-HTTP/3 (RFC 9220) as successors to WebSocket #133

Description

@EdmondDantes

Context

We have just shipped WebSocket topics / pub-sub (#2, #118, #120). Question raised: is there anything newer than WebSocket worth targeting, and what would it cost us?

Short answer: WebTransport over HTTP/3. It went Baseline in March 2026 (Safari 26.4 finally shipped it, closing the iOS gap), so it is now production-viable, not a Chromium experiment.

What WebTransport buys over WebSocket

  • Independent streams in one connection — no head-of-line blocking. A WebSocket is always one TCP connection = one ordered frame stream.
  • Unreliable datagrams (fire-and-forget) — what people previously dragged in WebRTC DataChannel (SDP/ICE) for.
  • QUIC properties: 0-RTT reconnect, connection migration across networks.

Browser support: Chrome 97+, Edge 98+, Firefox 114+, Safari 26.4+ (macOS/iOS), Opera 83+, Samsung Internet 18+.

Is it HTTP/3-only?

In practice, yes. draft-ietf-webtrans-http2 defines a TCP fallback over HTTP/2 (same extended CONNECT, SETTINGS_WT_MAX_SESSIONS), but neither Chrome nor Firefox implements it — both only speak WebTransport over HTTP/3 (draft-02). The HTTP/2 draft is also still not an RFC.

Consequence: WebTransport = QUIC/UDP only. Corporate proxies that block UDP block WebTransport. WebSocket does not go away — it stays the compatibility transport.

What our NG stack already provides

Checked the installed libraries (nghttp3 1.15.0, ngtcp2):

Building block Status
Extended CONNECT (:protocol), RFC 9220 available — nghttp3_settings.enable_connect_protocol
HTTP/3 Datagrams, RFC 9297 available — nghttp3_settings.h3_datagram
QUIC DATAGRAM frame available — ngtcp2_conn_writev_datagram
WebTransport layer proper absent — no symbol anywhere

nghttp3 deliberately leaves WebTransport to the application: no SETTINGS_WT_MAX_SESSIONS, no WEBTRANSPORT_STREAM stream type (0x54), no CLOSE_WEBTRANSPORT_SESSION capsule.

Are there ready-made C implementations?

Per our "ready-made libs only" preference — checked, and for our stack the answer is no:

  • picoquic + picowt (pico_webtransport.h, picowt_connect(), wt_baton demo) — the only full WebTransport implementation in plain C, and it is good. But it sits on h3zero, picoquic's own HTTP/3. Reusing it means replacing our entire QUIC stack. Not an option.
  • owt-sdk-quic (Intel OWT) — C++ WebTransport server/client API, but on top of Chromium QUIC. Pulling a slice of Chromium into the server: no.
  • Everything else alive is not C: webtransport-go (quic-go), wtransport (Rust/quinn).

So: WebTransport on our stack means writing the layer ourselves on top of ngtcp2/nghttp3.

Implementation cost estimate

Medium. The QUIC foundation is already in production; this is a layer on top, not a transport rewrite.

What we would have to write:

  1. Stream demultiplexer (the hard part). WebTransport streams bypass HTTP framing — uni streams start with type 0x54 + session ID, bidi streams with signal value 0x41. nghttp3 discards these as unknown. So inside our recv_stream_data callback from ngtcp2 we must peek the varint prefix ourselves and route the stream to either nghttp3 or the WT layer.
  2. Write scheduler. The send loop currently drives nghttp3_conn_writev_stream. WT streams must be interleaved with nghttp3's streams by hand, with flow-control accounting.
  3. Capsule protocol on the CONNECT stream (CLOSE_WEBTRANSPORT_SESSION, DRAIN) — easy, the body arrives via the normal recv_data callback.
  4. Datagrams — parse the quarter-stream-ID varint and map it to a session.

Roughly comparable in scope to the H3 static work, but with tighter ngtcp2 ↔ nghttp3 coupling.

The existing topics/pub-sub API maps onto WebTransport almost one-to-one — only the "connection → message stream" level changes.

Cheap first step: WebSocket over HTTP/3 (RFC 9220)

nghttp3's header explicitly cites RFC 9220 for enable_connect_protocol. Extended CONNECT is therefore already supported out of the box — our existing wslay can ride a QUIC stream.

  • Client API does not change at all.
  • We still gain QUIC benefits: connection migration, no TCP head-of-line blocking between separate WS connections, 0-RTT.
  • Effort: wiring an upgrade path, not a new protocol layer.

Proposal

  1. Phase 1 (cheap): WebSocket over HTTP/3 via RFC 9220 extended CONNECT. Validates our H3 + wslay plumbing under a persistent-stream workload.
  2. Phase 2: WebTransport over HTTP/3 as a second transport behind the same topics API, with WebSocket as the fallback for UDP-hostile networks.

Opening this as a research/design issue — no code yet, looking for a decision on whether Phase 1 is worth scheduling.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions