browser wasm

This commit is contained in:
Jorijn van der Graaf 2026-05-19 02:53:50 +02:00
commit e8630528af
24 changed files with 2490 additions and 100 deletions

View file

@ -43,11 +43,33 @@ namespace Crafter::HTTP3 {
export inline constexpr std::uint64_t kFrameData = 0x00;
export inline constexpr std::uint64_t kFrameHeaders = 0x01;
export inline constexpr std::uint64_t kFrameSettings = 0x04;
// WebTransport bidirectional stream frame type (draft-ietf-webtrans-http3).
// Distinct from normal HTTP/3 frames — its body is unbounded (runs to FIN)
// rather than length-prefixed, and the first bytes of the body are the
// session id varint.
export inline constexpr std::uint64_t kFrameWtStream = 0x41;
// ---------------- Unidirectional stream types (RFC 9114 §6.2) ----------------
export inline constexpr std::uint64_t kStreamControl = 0x00;
export inline constexpr std::uint64_t kStreamQpackEnc = 0x02;
export inline constexpr std::uint64_t kStreamQpackDec = 0x03;
// WebTransport unidirectional stream type (draft-ietf-webtrans-http3).
// After this varint comes a session id varint, then opaque payload to FIN.
export inline constexpr std::uint64_t kStreamWt = 0x54;
// ---------------- SETTINGS parameter identifiers ----------------
// Required to negotiate WebTransport over HTTP/3 + HTTP/3 datagrams.
export inline constexpr std::uint64_t kSettingQpackMaxTableCapacity = 0x01; // RFC 9204
export inline constexpr std::uint64_t kSettingQpackBlockedStreams = 0x07; // RFC 9204
export inline constexpr std::uint64_t kSettingEnableConnectProtocol = 0x08; // RFC 9220
export inline constexpr std::uint64_t kSettingH3Datagram = 0x33; // RFC 9297
// Legacy identifiers from older WebTransport / H3-DATAGRAM drafts. Chrome
// (as of M120-ish) advertises and looks for the draft-02 / draft-04 ids
// alongside the RFC ones; if we only send the modern ids it decides we
// don't support WebTransport and aborts with ERR_METHOD_NOT_SUPPORTED.
export inline constexpr std::uint64_t kSettingH3DatagramDraft04 = 0xffd277; // draft-ietf-masque-h3-datagram-04
export inline constexpr std::uint64_t kSettingEnableWebTransport = 0x2b603742; // draft-02 boolean
export inline constexpr std::uint64_t kSettingWtMaxSessions = 0xc671706a; // draft-ietf-webtrans-http3 (-07+)
// ---------------- Errors ----------------
export class HTTP3ProtocolError : public std::runtime_error {
@ -575,4 +597,55 @@ namespace Crafter::HTTP3 {
EncodeVarint(0, out); // frame length 0
return out;
}
// Server-side variant that advertises WebTransport-over-HTTP/3 support
// to the peer. Without these three SETTINGS the browser silently rejects
// the extended CONNECT and the WebTransport.ready promise never resolves.
// `maxSessions` becomes the value of SETTINGS_WT_MAX_SESSIONS.
export inline std::vector<std::uint8_t> BuildWebTransportControlStreamPrelude(
std::uint64_t maxSessions = 1)
{
// Encode the SETTINGS body first so we can write its length. The two
// QPACK settings declare we run with no dynamic table — sent
// explicitly because some HTTP/3 stacks (Chrome among them) refuse
// to consider the peer ready for extended-CONNECT until they have
// seen a baseline QPACK configuration. The draft-02 ENABLE_WEBTRANSPORT
// and draft-04 H3_DATAGRAM ids are sent alongside their RFC counterparts
// for compatibility with current Chrome (which still negotiates the
// draft form even when advertising RFC support).
std::vector<std::uint8_t> body;
EncodeVarint(kSettingQpackMaxTableCapacity, body); EncodeVarint(0, body);
EncodeVarint(kSettingQpackBlockedStreams, body); EncodeVarint(0, body);
EncodeVarint(kSettingEnableConnectProtocol, body); EncodeVarint(1, body);
EncodeVarint(kSettingH3Datagram, body); EncodeVarint(1, body);
EncodeVarint(kSettingH3DatagramDraft04, body); EncodeVarint(1, body);
EncodeVarint(kSettingEnableWebTransport, body); EncodeVarint(1, body);
EncodeVarint(kSettingWtMaxSessions, body); EncodeVarint(maxSessions, body);
std::vector<std::uint8_t> out;
EncodeVarint(kStreamControl, out);
WriteFrame(out, kFrameSettings, body.data(), body.size());
return out;
}
// Prefix bytes that go on the front of an outgoing WT bidi stream — the
// peer reads these to know which session the stream belongs to. After
// this prefix the stream contains opaque WebTransport payload until FIN
// (there is no length field — WT_STREAM is the only HTTP/3 frame whose
// body runs to end-of-stream).
export inline std::vector<std::uint8_t> BuildWtBidiPrefix(std::uint64_t sessionId) {
std::vector<std::uint8_t> out;
EncodeVarint(kFrameWtStream, out);
EncodeVarint(sessionId, out);
return out;
}
// Prefix bytes that go on the front of an outgoing WT unidi stream
// (server-initiated → client). Stream-type varint then session id.
export inline std::vector<std::uint8_t> BuildWtUnidiPrefix(std::uint64_t sessionId) {
std::vector<std::uint8_t> out;
EncodeVarint(kStreamWt, out);
EncodeVarint(sessionId, out);
return out;
}
}