TsgcWebTransportSession › Properties › QuarterStreamId
The session identity in the compact form WebTransport datagrams use: SessionId divided by 4.
property QuarterStreamId: Int64 read GetQuarterStreamId;
Int64 (__int64 in C++Builder, long in C#).
Computed on every read as SessionId div 4. It has no stored value and no default of its own.
Read-only. This property is opaque unless you know why the RFC divides by four, so here is the reasoning.
Where the four comes from. QUIC does not hand out stream ids sequentially. It encodes two bits of meaning into the low end of every stream id: bit 0 says who opened the stream (0 = client, 1 = server) and bit 1 says whether it is bidirectional (0) or unidirectional (1). So the four kinds of stream advance in steps of four:
| Stream id | Kind |
|---|---|
0, 4, 8, ... | client-initiated, bidirectional |
1, 5, 9, ... | server-initiated, bidirectional |
2, 6, 10, ... | client-initiated, unidirectional |
3, 7, 11, ... | server-initiated, unidirectional |
A WebTransport session id is always the id of a client-initiated bidirectional stream, the Extended CONNECT request. Those ids are therefore always multiples of four: 0, 4, 8, 12 and so on. The bottom two bits carry no information in this context, they are always zero.
Why it matters. Every WebTransport datagram must name its session, and a datagram has no stream to identify it. Since a datagram is unreliable and often small, spending bytes on a redundant identifier is wasteful. RFC 9297 (HTTP Datagrams) therefore prefixes each datagram with a Quarter-Stream-ID: the session id shifted right by two, dropping the two bits that are always zero. Divide by four to encode, multiply by four to decode, nothing is lost, and the varint is frequently one byte shorter.
What you do with it. Normally, nothing. The library encodes the prefix in SendDatagram and decodes it on the inbound path, matching the value against each session's QuarterStreamId to find the right session. The property is exposed for diagnostics and packet-level tracing. Since datagrams are inert as shipped (see SendDatagram), QuarterStreamId is mostly of interest when reading a packet capture.
Note that a session id of 0 and one of 3 would both give a Quarter-Stream-ID of 0. That is not a collision in practice, because a session id is always a multiple of four by construction.
// SessionId 4 -> QuarterStreamId 1, encoded as varint(1) in a datagram prefix
memoLog.Lines.Add(Format('session %d has quarter-stream-id %d',
[oSession.SessionId, oSession.QuarterStreamId]));