Subject: HTTP/3 Priorities and Push

From: Mike Bishop <[email-masked]>
Date: Mon, 3 Dec 2018 19:33:08 +0000

I'd like to call the working groups' attention to a couple of related HTTP/3 PRs which will be merged soon unless I hear objections.

In HTTP/3, a single pushed response can be matched with PUSH_PROMISE frames on multiple streams, but the frames are required to contain identical request headers.  Since the QPACK state can vary, the frame payloads will not be byte-for-byte identical, but the uncompressed header set MUST be.  This creates some wrinkles:

  *   Issue #1864<https://github.com/quicwg/base-drafts/issues/1864>:  The server is compressing the same headers multiple times.  The client is obligated to compare headers and verify that they're actually identical.  How long is the client required to keep such history?  PR #2072<https://github.com/quicwg/base-drafts/pull/2072> avoids the duplication of pushed request headers by returning to a single PUSH_PROMISE frame (with request headers) and zero or more DUPLICATE_PUSH frames which provide additional references.  Sending the headers only once guarantees they can't differ.
  *   Issue #1959<https://github.com/quicwg/base-drafts/issues/1959>:  The initial prioritization of a pushed response is as a child of the parent request.  If there are multiple PUSH_PROMISEs, which one is that?  PRs #2072<https://github.com/quicwg/base-drafts/pull/2072> and #2074<https://github.com/quicwg/base-drafts/pull/2074> fix that by saying that it's the child of the request that got the PUSH_PROMISE, not the requests that got the DUPLICATE_PUSH.

Note that DUPLICATE_PUSH isn't a pure win - this means it's possible to know there's a pushed resource coming, but not yet know what it is, while processing further headers and data on the stream.  That has some hairy implications for clients - I think it's net better than comparing duplicate compressed headers, but it's painful.  (The same situation exists today for a PUSH_PROMISE frame blocked on QPACK dynamic table references.)

Interrelated with these is another PRIORITY issue:  We moved PRIORITY frames to the control stream to avoid the most obvious races from reordering, but new ones continue to crop up (Issue #1865<https://github.com/quicwg/base-drafts/issues/1865>).  The common culprit in most of the races is the Exclusive flag, which permits reprioritizing streams other than the two actually mentioned in the frame; non-racy use of the Exclusive flag requires that both sides know the peer's state of the priority tree immediately before sending, and that's not guaranteed in QUIC.

The one remaining race was between default priority and arrival of the PRIORITY frame on the control stream - if the control stream gets head-of-line blocked, the request can be completed at default priority before the PRIORITY frame gets a chance to arrive.  PR#2075<https://github.com/quicwg/base-drafts/pull/2075> removes the Exclusive flag and adds an INITIAL_PRIORITY frame which can only be the first frame on a stream; it's always superseded by PRIORITY frames on the control stream, so that a reliable ordering can be established.  (Compare to HTTP/2's Priority region in HEADERS frames.)

Comments appreciated either on GitHub or on-list.

Thanks,
Mike Bishop