Implement receive-side WebTransport per-session stream flow control (draft-15 §5.6.2)
Background
draft-ietf-webtrans-http3-15 §5.6.2 defines a cumulative per-session stream limit, analogous to QUIC's MAX_STREAMS: the total number of streams of a given type an endpoint may open over the session's lifetime, including
closed streams. The limit starts at the peer's SETTINGS_WT_INITIAL_MAX_STREAMS_{UNI,BIDI} and is raised over time by
WT_MAX_STREAMS capsules.
Today neqo only partially implements this:
- It enforces the peer's initial SETTINGS value when we open streams (one-way send enforcement), but that value is static — we never process
WT_MAX_STREAMS, so the limit can never grow.
- It does not enforce, against incoming streams, the limit we advertised to the peer. Note: right now we always send 0, which means no limit, so this is not a current problem. In the Draft-15 patches I plan to expose settings for the max streams values, and this could come into play. If we decide not the implement the settings values, then this issue becomes moot until we do
This issue covers the receive side. Sending WT_MAX_STREAMS (the flow-control provider that grows a peer's limit) is tracked separately.
Tasks
1. WT_MAX_STREAMS capsule
- Add the capsule to
WebTransportFrame with the two type codepoints (§9.3): 0x190B4D3F (bidi), 0x190B4D40 (uni). Decode the Maximum Streams varint.
- On receipt, raise our send limit for that stream type. The effective send limit becomes
max(peer SETTINGS initial value, highest WT_MAX_STREAMS received), and the existing stream-creation check must use it.
2. WT_MAX_STREAMS validation (§5.6.2)
Maximum Streams > 2^60 MUST be treated as H3_DATAGRAM_ERROR (connection error). Requires an H3_DATAGRAM_ERROR (0x33, RFC 9297 §5.5) error code.
- A
Maximum Streams value less than a previously received one MUST close the WebTransport session with WT_FLOW_CONTROL_ERROR.
3. Receiver-side enforcement (§5.6.2)
- Track the cumulative count of remote-initiated streams per type (never decremented on close — the limit is cumulative).
- When an incoming stream would exceed the limit we advertised (
wt_initial_max_streams_*; 0 = not advertised = unlimited), close the WebTransport session with WT_FLOW_CONTROL_ERROR. This means tearing down the session's sub-streams, notifying the application (session_end with the error reason), and resetting the CONNECT stream so the peer learns.
4. Error codes
WT_FLOW_CONTROL_ERROR = 0x045d4487 (draft-15 §9.5).
H3_DATAGRAM_ERROR = 0x33.
Acceptance criteria
- A peer that sends
WT_MAX_STREAMS raising the limit lets us open more streams than its initial SETTINGS value permitted; the cumulative semantics hold (closing a stream does not free a slot).
- An incoming stream beyond our advertised limit closes the session with
WT_FLOW_CONTROL_ERROR, surfaced to the application.
- A decreasing
WT_MAX_STREAMS closes the session with WT_FLOW_CONTROL_ERROR; a value > 2^60 is an H3_DATAGRAM_ERROR. Unit tests cover capsule round-trip, the limit being raised, and the receiver enforcement path. (A test-only injection helper is acceptable for exercising the receive path, since we don't yet send these capsules.)
Out of scope
- Sending
WT_MAX_STREAMS / WT_STREAMS_BLOCKED (this should be a separate "flow-control provider" issue).
- Exact decrease detection against the SETTINGS-advertised initial value when the very first capsule is received (the session may not retain that baseline); acceptable to compare only against prior capsules initially, and note it.
Implement receive-side WebTransport per-session stream flow control (draft-15 §5.6.2)
Background
draft-ietf-webtrans-http3-15§5.6.2 defines a cumulative per-session stream limit, analogous to QUIC'sMAX_STREAMS: the total number of streams of a given type an endpoint may open over the session's lifetime, includingclosed streams. The limit starts at the peer's
SETTINGS_WT_INITIAL_MAX_STREAMS_{UNI,BIDI}and is raised over time byWT_MAX_STREAMScapsules.Today neqo only partially implements this:
WT_MAX_STREAMS, so the limit can never grow.This issue covers the receive side. Sending
WT_MAX_STREAMS(the flow-control provider that grows a peer's limit) is tracked separately.Tasks
1.
WT_MAX_STREAMScapsuleWebTransportFramewith the two type codepoints (§9.3):0x190B4D3F(bidi),0x190B4D40(uni). Decode theMaximum Streamsvarint.max(peer SETTINGS initial value, highest WT_MAX_STREAMS received), and the existing stream-creation check must use it.2.
WT_MAX_STREAMSvalidation (§5.6.2)Maximum Streams > 2^60MUST be treated asH3_DATAGRAM_ERROR(connection error). Requires anH3_DATAGRAM_ERROR(0x33, RFC 9297 §5.5) error code.Maximum Streamsvalue less than a previously received one MUST close the WebTransport session withWT_FLOW_CONTROL_ERROR.3. Receiver-side enforcement (§5.6.2)
wt_initial_max_streams_*; 0 = not advertised = unlimited), close the WebTransport session withWT_FLOW_CONTROL_ERROR. This means tearing down the session's sub-streams, notifying the application (session_endwith the error reason), and resetting the CONNECT stream so the peer learns.4. Error codes
WT_FLOW_CONTROL_ERROR=0x045d4487(draft-15 §9.5).H3_DATAGRAM_ERROR=0x33.Acceptance criteria
WT_MAX_STREAMSraising the limit lets us open more streams than its initial SETTINGS value permitted; the cumulative semantics hold (closing a stream does not free a slot).WT_FLOW_CONTROL_ERROR, surfaced to the application.WT_MAX_STREAMScloses the session withWT_FLOW_CONTROL_ERROR; a value> 2^60is anH3_DATAGRAM_ERROR. Unit tests cover capsule round-trip, the limit being raised, and the receiver enforcement path. (A test-only injection helper is acceptable for exercising the receive path, since we don't yet send these capsules.)Out of scope
WT_MAX_STREAMS/WT_STREAMS_BLOCKED(this should be a separate "flow-control provider" issue).