v158 · webtransport · receive streams

bytesReceived vs bytesRead

Two counters, one diagnosis. bytesReceived is how much contiguous stream data the browser has taken off the network; bytesRead is how much your JavaScript has actually consumed. When the first races ahead of the second, the network is fine — your consumer is the bottleneck, and the gap is sitting in browser memory.

The real counters come from getStats() on a live session's receive stream, which needs an HTTP/3 endpoint this showcase cannot reach — and this build's support is probed for real below. The rig underneath is therefore a clearly-labelled local stand-in: a genuine ReadableStream with a producer and consumer you control, where the two counters are maintained by the page at the exact points the spec defines them. The streams machinery is real; only the network is replaced.

First, the real surface on this build

WebTransportReceiveStream → … WebTransportReceiveStream.prototype.getStats → … stats dictionary fields (spec) → bytesReceived, bytesRead

local model — plain ReadableStream, no network

Watch the gap grow and drain

bytes the "network" hands the browser per second (drives the bytesReceived analogue)
bytes the reader loop pulls per second (drives the bytesRead analogue)

bytesReceived (analogue)0 KB

bytesRead (analogue)0 KB

the gap = buffered in the browser0 KB

Not running. Set the consumer slower than the producer, start, and watch the gap — that is the slow-consumer signature getStats() exists to reveal.

code path — what you would write against the real API

async function watchBackpressure(stream /* WebTransportReceiveStream */) {
  const { bytesReceived, bytesRead } = await stream.getStats();
  const buffered = bytesReceived - bytesRead;      // bytesRead ≤ bytesReceived, always
  if (buffered > HIGH_WATER) {
    // the network is NOT the problem — our consumer is behind;
    // shed work, skip frames, or move parsing off the main thread
  }
}
// Firefox note: getStats() exists but currently rejects with
// NotSupportedError — await it in a try/catch, not bare.

see also