v157 · networking · webtransport
WebTransport draining
Chrome 157 implements WebTransport.prototype.draining, a Promise that resolves when the server sends an HTTP/3 DRAIN_WEBTRANSPORT_SESSION capsule (0x78ae). Instead of abruptly tearing down active streams during a server deployment or load-balancer scale-in, the server signals that the session is retiring so the client can open a replacement session for new work while existing streams finish cleanly.
concepts
-
Graceful session handover
Run the live
WebTransport.prototype.drainingIDL probe, spawn bidirectional streams on Session A, and fire aDRAIN_WEBTRANSPORT_SESSIONsignal. Watch active streams finish uninterrupted on Session A while new streams cut over automatically to replacement Session B. -
Capsule timeline inspector
Compare the HTTP/3
DRAIN_WEBTRANSPORT_SESSION(0x78ae) andCLOSE_WEBTRANSPORT_SESSION(0x2843) wire capsules alongside the three-stageready → draining → closedPromise lifecycle and in-flight stream survival counts. -
Load-balancer drain budget
Model active WebTransport stream duration distributions (p50, p95, p99) against your edge load-balancer or Kubernetes drain grace window to size the gap between
DRAIN_WEBTRANSPORT_SESSIONand final termination.
why it shipped
Long-lived WebTransport sessions multiplex dozens of bidirectional and unidirectional streams plus datagrams over a single HTTP/3 connection. Before WebTransport.prototype.draining, a server node preparing for a rolling restart or autoscaling scale-in had no standard WebTransport API surface to tell the browser "stop opening new streams on this session, but finish the ones you already started." Calling CLOSE_WEBTRANSPORT_SESSION (0x2843) or closing the underlying QUIC connection immediately aborts in-flight media segments, RPC calls, or state sync transfers.
HTTP/3 WebTransport defines the DRAIN_WEBTRANSPORT_SESSION capsule (type = 0x78ae, empty payload) sent on the CONNECT control stream. When Chrome 157 receives this capsule, it resolves the wt.draining Promise without closing existing streams. Application code awaiting wt.draining can immediately dial a warm replacement session to a healthy backend instance, route all subsequent createBidirectionalStream() and createUnidirectionalStream() calls to the new session, and close the draining session once its remaining streams drain to zero.
the API
let activeTransport = new WebTransport("https://edge.example.com/wt");
await activeTransport.ready;
// React to the server's HTTP/3 DRAIN_WEBTRANSPORT_SESSION (0x78ae) capsule:
activeTransport.draining.then(async () => {
console.info("Session draining; opening replacement WebTransport session...");
const replacement = new WebTransport("https://edge.example.com/wt");
await replacement.ready;
// Cut over new stream creation to the replacement session while
// in-flight streams on the old session complete naturally.
activeTransport = replacement;
});
WebTransport.prototype.draining is a getter returning a Promise<undefined>. Each WebTransport instance initializes an internal [[Draining]] promise on construction, resolves it when the DRAIN_WEBTRANSPORT_SESSION capsule arrives on the session's HTTP/3 CONNECT stream, and rejects it if the session is abruptly closed or fails before draining.
research notes
- Wire capsule format: Per the IETF WebTransport over HTTP/3 draft,
DRAIN_WEBTRANSPORT_SESSIONuses capsule type0x78aewith a length of0x00(zero value bytes) on the extended CONNECT data stream. - Lifecycle ordering: A graceful retirement resolves
wt.readyfirst, thenwt.drainingupon receiving capsule0x78ae, and finallywt.closedwith{ closeCode, reason }when either peer closes the session viaCLOSE_WEBTRANSPORT_SESSION(0x2843). - Feature detection: Check
'draining' in WebTransport.prototypeor inspectObject.getOwnPropertyDescriptor(WebTransport.prototype, 'draining')?.getbefore relying on automatic session handover.