Subject: [QUIC] h2 mapping - hpack dynamic table by reference

From: Patrick McManus <[email-masked]>
Date: Thu, 3 Nov 2016 17:29:09 -0400

Hi Robbie, Mike, et al. Thanks for getting the ball rolling with
https://tools.ietf.org/html/draft-shade-quic-http2-mapping-00..

I have a thought on the mapping - Putting all of the header frames onto one
in-order stream is a pretty big hammer.

Is there stomach for looking at a strategy where hpack references the
dynamic table state via some kind of instruction counter? That way the
headers could be on the data (>=5) stream and just the dynamic table
manipulation stuff from hpack could be on stream 3.. implementations would
be able to choose whether to generate hpack that referenced the current
counter or an older one (within some window).. an older one is going to be
more robust to reordering happening on stream 3 so the dep between the
streams is loosened. Anecdotally the rate of change of the dynamic table
slows down (though I have no data - could collect it if we're serious)
after an initial burst and at that point not using table updates until
they've been acked could be a reasonable thing to do... and this even
allows pre-populating the dynamic table at setup time with site-local
values rather than in response to a query. Rewinding to any particular
instruction seems to be a big burden, but we could probably do some things
with checkpoints and acks to bound it all.

this is a fairly significant bit of surgery to h2's formats, but given that
one of the real quic opportunities is to perform better under loss it could
be worthwhile to sever ordering dependencies even further. Seems like a
reasonable thing to investigate to me.

Thanks
-Patrick

---

From: Mike Bishop <[email-masked]>
Date: Thu, 3 Nov 2016 22:35:53 +0000

Jana and I were talking about that at the HTTP Workshop.  The current solution is probably the best that can be achieved without modifying HPACK, because HPACK takes a hard dependency on reliable in-order delivery, which a dedicate stream can provide.  If we were to define HPACKv2 (or QPACK, if you prefer) and make its semantics more out-of-order-friendly, we could do a number of things, as you suggest.

Some approaches we talked about:

  *   Establish a static dictionary in the first flight and never change it.
  *   Newly-added entries have explicit and never-reused indices.  All HPACK frames are on-stream and only block if they reference an entry you don’t yet have.
  *   Split HPACK into two different frame types – one only modifies the header table; one only emits headers.  Each “modify” frame increments a generation number and records the number of “emit” frames in the previous generation.  Emitting frames block until the header table reaches its generation; the old header state can be discarded when you know you’ve seen all emitting frames from that generation.

Your idea is similar to the last of these, but choosing to encode to an old state for better reliability is an interesting wrinkle.  You’d have to add an explicit signal to evict the old state for that to work.  Defining an endpoint’s memory commitment and management gets a little fuzzier in this world, no matter what approach we take.

I think we’ll need to seriously consider something in this space.  It also implies changing HPACK implementations to be more cautious about adding to the table.  I did some prototypes during HTTP/2 and concluding that “index everything” was not substantially worse than the hypothetical ideal given a reasonable table size, so we went with that to save time.  With QPACK, that will be a decidedly sub-optimal strategy, and there will be some art in designing compressors to be as effective as possible in the presence of reordering.

From: QUIC [mailto:[email-masked]] On Behalf Of Patrick McManus
Sent: Thursday, November 3, 2016 2:29 PM
To: [email-masked]
Subject: [QUIC] h2 mapping - hpack dynamic table by reference

Hi Robbie, Mike, et al. Thanks for getting the ball rolling with https://tools.ietf.org/html/draft-shade-quic-http2-mapping-00..
I have a thought on the mapping - Putting all of the header frames onto one in-order stream is a pretty big hammer.

Is there stomach for looking at a strategy where hpack references the dynamic table state via some kind of instruction counter? That way the headers could be on the data (>=5) stream and just the dynamic table manipulation stuff from hpack could be on stream 3.. implementations would be able to choose whether to generate hpack that referenced the current counter or an older one (within some window).. an older one is going to be more robust to reordering happening on stream 3 so the dep between the streams is loosened. Anecdotally the rate of change of the dynamic table slows down (though I have no data - could collect it if we're serious) after an initial burst and at that point not using table updates until they've been acked could be a reasonable thing to do... and this even allows pre-populating the dynamic table at setup time with site-local values rather than in response to a query. Rewinding to any particular instruction seems to be a big burden, but we could probably do some things with checkpoints and acks to bound it all.

this is a fairly significant bit of surgery to h2's formats, but given that one of the real quic opportunities is to perform better under loss it could be worthwhile to sever ordering dependencies even further. Seems like a reasonable thing to investigate to me.
Thanks
-Patrick

---

From: Martin Thomson <[email-masked]>
Date: Fri, 4 Nov 2016 11:54:10 +1100

> reasonable thing to investigate to me.

I agree.  We risk negating much of the performance wins by creating
head-of-line blocking in header fields.  With many resources, a lot of
the critical early interactions are entirely header fields, and hpack
is strictly serialized.

Maybe this is a good time to raise the possibility that we might need
hpack modifications and that those modifications might benefit from a
new document.  We split hpack out in h2, and it was a neat break that
we never regretted.

---

From: Patrick McManus <[email-masked]>
Date: Thu, 3 Nov 2016 21:07:19 -0400

On Thu, Nov 3, 2016 at 6:35 PM, Mike Bishop <[email-masked]>
wrote:

> Jana and I were talking about that at the HTTP Workshop.
>

good!

>
> Your idea is similar to the last of these, but choosing to encode to an
> old state for better reliability is an interesting wrinkle.  You’d have to
> add an explicit signal to evict the old state for that to work.
>

yep.. and because of the out-of-order use of the state it gets tricky if
you're aggressive about that.. but retry with REFUSED_STREAM might provide
an escape valve in the really bad corner case. all handwaving of course.

> Defining an endpoint’s memory commitment and management gets a little
> fuzzier in this world, no matter what approach we take.
>
>
>
> I think we’ll need to seriously consider something in this space.  It also
> implies changing HPACK implementations to be more cautious about adding to
> the table.  I did some prototypes during HTTP/2 and concluding that “index
> everything” was not substantially worse than the hypothetical ideal given a
> reasonable table size, so we went with that to save time.  With QPACK, that
> will be a decidedly sub-optimal strategy, and there will be some art in
> designing compressors to be as effective as possible in the presence of
> reordering.
>
>
When we get to the github issue stage I'll open one. Something to tackle
early I would hope.

Thanks!

---

From: Robbie Shade <[email-masked]>
Date: Fri, 4 Nov 2016 09:06:48 -0400

Agreed that fixing the HPACK head of line blocking problem is well worth
tackling, thanks for bringing this up.

+Buck who has been thinking about this as well.

On Thu, Nov 3, 2016 at 9:07 PM, Patrick McManus <[email-masked]>
wrote:

>> I think we’ll need to seriously consider something in this space.  It
>> also implies changing HPACK implementations to be more cautious about
>> adding to the table.  I did some prototypes during HTTP/2 and concluding
>> that “index everything” was not substantially worse than the hypothetical
>> ideal given a reasonable table size, so we went with that to save time.
>> With QPACK, that will be a decidedly sub-optimal strategy, and there will
>> be some art in designing compressors to be as effective as possible in the
>> presence of reordering.
>>
>>

---

From: "Charles 'Buck' Krasic" <[email-masked]>
Date: Fri, 4 Nov 2016 10:28:26 -0700

I have indeed been thinking about this very issue.  Here is an outline of
what I've got so far.  I think it's not so far from what others are
thinking.

I call the new scheme QUIC HPACK (as opposed to HTTP/2 HPACK for status
quo).

   - Premise:  certain indexed entries should be avoided, namely indexed
   entries that would refer to entries that originate from headers *that
   are still in flight*, because packet loss on those other headers would
   HOL block the current stream.
   - The idea of QUIC HPACK then is to mark updates to the dynamic table as
   "pending" initially, and commit them later when the header they originate
   from is no longer in flight (i.e. acked to sender).   Indexed entries would
   only be generated against dynamic tables entries that are committed.  I.e.,
   when encoding an entry, if there is a  matching but pending entry in the
   dynamic table, generate a literal instead of indexed.
      - I have collected some data to measure the time between when entries
      are added to the table, and the creation of subsequent indexed
entries that
      refer to them.   It suggests that a small fraction of HTTP/2
HPACK indexed
      entries (under standard HPACK) would become literals under QUIC HPACK.
        ~50% are > 10s apart, and ~90% are > 50ms apart.
   - the new behavior could be optional, individual HTTP/2 transactions to
   select between this new scheme and the status quo, trading between
   compression and HOL freedom.   Status quo transactions would still use
   stream 3 (HTTP/2 HPACK).

Algorithm/design details:

   - instead using the shared headers stream (#3), each HTTP/2 transaction
   has a dedicated stream for headers
      - i.e. each HTTP/2 transaction has two QUIC streams: 1 for headers
      (incl. trailers if used), 1 for body.
      - 1 bit of the stream id will be used to classify the QUIC stream as
      headers or body.
         - like the current global header stream, these new headers streams
         will protected from RSTs.
            - *QUIC HPACK algorithm will process all headers, even if the
            corresponding HTTP/2 transaction may have been cancelled
and corresponding
            body stream RST*.
         - QUIC HPACK encode and decode are divided into two phases each:
   transmit and commit.
      - in addition to the info already included in HTTP/2 header frames,
      each header transmitted from encoder to decoder will include a pair of
      sequence numbers, the encode sequence number and the commit epoch,
      described below.
         -  *the pair will always have the property that commit epoch <
         encode sequence number.*
      - first phase (transmit):  header is encoded/decoded, but updates to
      dynamic table are marked pending.
         - when processing a header, the encoder will decide which entries
         should be added to the dynamic table, as in current HPACK,
but the entry in
         the dynamic table will have a "pending" flag set, preventing
it from being
         a reference for indexed entries yet.
         - the encoder assigns a sequence number to each header (block),
         the total number of headers encoded so far (encode epoch).
         - the encoder/decoder enters each header into a commit sequencer
         (ordered queue) to be used in the second phase.
         - *the header is transmitted from encoder to decoder.  the decoder
         can always decode immediately, because this algorithm
prevents a header
         from referring to other headers that might still be in flight.*
      - second phase (commit):  once fully transmitted,  pending updates
      are committed, in the same order they were encoded.
         - at encoder, when a previously transmitted header has been acked,
         the entry in the commit-sequencer is marked committed.
            - the front of the sequencer is popped while the front is a
            committed entry. (loop)
            - the commit epoch tracks the highest sequence number popped.
         - at decoder, when a header is received, it will pop off the front
         of the commit sequencer while its sequence number is <= the
just received
         commit epoch (loop).
         - each popped entry is marked committed.
            - now the received header is decoded.
         - a special case that is handled by the dynamic table in HTTP/2
      HPACK but I think needs to be treated differently for QUIC HPACK, that is
      entries that refer to other entries from the same header.    For this I
      propose to define a new distinct table, and corresponding distinct entry
      types, "Intra".   This would be an ephemeral per-header table that exists
      only during the transmit phase.
         - *I don't have data yet for how many entries fall into this
         category.*

-- 
Charles 'Buck' Krasic | Software Engineer | [email-masked] | +1 (408)
412-1141

---

From: Mike Bishop <[email-masked]>
Date: Fri, 4 Nov 2016 18:16:19 +0000

This is certainly interesting, but feels more like a wise strategy for an encoder to employ (if you want to avoid blocking, don’t reference a header that hasn’t been ACKed) than an independent change to the protocol.  Think about using, say, the unique-index-per-entry approach (which requires blocking on unknown indices) – an encoder that wanted to be smart could do as you describe below and emit literals until the frame has been ACK’d.  (Unless the frame was going in the same packet as the original value, if it wants to be even smarter….)  An encoder that didn’t want the complexity would just emit the most compact representation it could, knowing it might cause some blockage on the receiver.

The two-streams-per-request feels jarring to me.  For HTTP, at least, you have a reasonably well-defined series of “Headers+, Body?, Headers?” in each direction for each transaction.  I don’t think you’ll get rid of the need to have some framing around the headers; I’m not convinced (but willing to be) that the complexity of managing two affiliated streams is greater than that of needing framing around the body as well because it shares a stream with the headers.

From: QUIC [mailto:[email-masked]] On Behalf Of Charles 'Buck' Krasic
Sent: Friday, November 4, 2016 10:28 AM
To: Martin Thomson <[email-masked]>
Cc: [email-masked]; Patrick McManus <[email-masked]>
Subject: Re: [QUIC] h2 mapping - hpack dynamic table by reference

I have indeed been thinking about this very issue.  Here is an outline of what I've got so far.  I think it's not so far from what others are thinking.

I call the new scheme QUIC HPACK (as opposed to HTTP/2 HPACK for status quo).

  *   Premise:  certain indexed entries should be avoided, namely indexed entries that would refer to entries that originate from headers that are still in flight, because packet loss on those other headers would HOL block the current stream.
  *   The idea of QUIC HPACK then is to mark updates to the dynamic table as "pending" initially, and commit them later when the header they originate from is no longer in flight (i.e. acked to sender).   Indexed entries would only be generated against dynamic tables entries that are committed.  I.e., when encoding an entry, if there is a  matching but pending entry in the dynamic table, generate a literal instead of indexed.

     *   I have collected some data to measure the time between when entries are added to the table, and the creation of subsequent indexed entries that refer to them.   It suggests that a small fraction of HTTP/2 HPACK indexed entries (under standard HPACK) would become literals under QUIC HPACK.   ~50% are > 10s apart, and ~90% are > 50ms apart.

  *   the new behavior could be optional, individual HTTP/2 transactions to select between this new scheme and the status quo, trading between compression and HOL freedom.   Status quo transactions would still use stream 3 (HTTP/2 HPACK).
Algorithm/design details:

  *   instead using the shared headers stream (#3), each HTTP/2 transaction has a dedicated stream for headers

     *   i.e. each HTTP/2 transaction has two QUIC streams: 1 for headers (incl. trailers if used), 1 for body.
     *   1 bit of the stream id will be used to classify the QUIC stream as headers or body.

        *   like the current global header stream, these new headers streams will protected from RSTs.

           *   QUIC HPACK algorithm will process all headers, even if the corresponding HTTP/2 transaction may have been cancelled and corresponding body stream RST.

  *   QUIC HPACK encode and decode are divided into two phases each: transmit and commit.

     *   in addition to the info already included in HTTP/2 header frames, each header transmitted from encoder to decoder will include a pair of sequence numbers, the encode sequence number and the commit epoch, described below.

        *    the pair will always have the property that commit epoch < encode sequence number.

     *   first phase (transmit):  header is encoded/decoded, but updates to dynamic table are marked pending.

        *   when processing a header, the encoder will decide which entries should be added to the dynamic table, as in current HPACK, but the entry in the dynamic table will have a "pending" flag set, preventing it from being a reference for indexed entries yet.
        *   the encoder assigns a sequence number to each header (block), the total number of headers encoded so far (encode epoch).
        *   the encoder/decoder enters each header into a commit sequencer (ordered queue) to be used in the second phase.
        *   the header is transmitted from encoder to decoder.  the decoder can always decode immediately, because this algorithm prevents a header from referring to other headers that might still be in flight.

     *   second phase (commit):  once fully transmitted,  pending updates are committed, in the same order they were encoded.

        *   at encoder, when a previously transmitted header has been acked, the entry in the commit-sequencer is marked committed.

           *   the front of the sequencer is popped while the front is a committed entry. (loop)
           *   the commit epoch tracks the highest sequence number popped.

        *   at decoder, when a header is received, it will pop off the front of the commit sequencer while its sequence number is <= the just received commit epoch (loop).

           *   each popped entry is marked committed.
           *   now the received header is decoded.

     *   a special case that is handled by the dynamic table in HTTP/2 HPACK but I think needs to be treated differently for QUIC HPACK, that is entries that refer to other entries from the same header.    For this I propose to define a new distinct table, and corresponding distinct entry types, "Intra".   This would be an ephemeral per-header table that exists only during the transmit phase.

        *   I don't have data yet for how many entries fall into this category.

On Thu, Nov 3, 2016 at 5:54 PM, Martin Thomson <[email-masked]<mailto:[email-masked]>> wrote:

--
Charles 'Buck' Krasic | Software Engineer | [email-masked]<mailto:[email-masked]> | +1 (408) 412-1141

---

From: Patrick McManus <[email-masked]>
Date: Fri, 4 Nov 2016 14:29:34 -0400

I'm thinking more along the lines of Mike -but I'm excited there is energy
around this. Its definitely nice if we have a format that lends itself to a
range of implementation choices and complexities.

If I take martin's suggestion just a tad bit further, we might be able to
spitball some designs as reworked id's of a 7541 update (i.e. add a 5th
document, qpack, to the quic suite) and then see if it can be applied
fairly succinctly to the quic-h2-mapping document..

On Fri, Nov 4, 2016 at 2:16 PM, Mike Bishop <[email-masked]>
wrote:

> This is certainly interesting, but feels more like a wise strategy for an
> encoder to employ (if you want to avoid blocking, don’t reference a header
> that hasn’t been ACKed) than an independent change to the protocol.  Think
> about using, say, the unique-index-per-entry approach (which requires
> blocking on unknown indices) – an encoder that wanted to be smart could do
> as you describe below and emit literals until the frame has been ACK’d.
> (Unless the frame was going in the same packet as the original value, if it
> wants to be even smarter….)  An encoder that didn’t want the complexity
> would just emit the most compact representation it could, knowing it might
> cause some blockage on the receiver.
>
>
>
> The two-streams-per-request feels jarring to me.  For HTTP, at least, you
> have a reasonably well-defined series of “Headers+, Body?, Headers?” in
> each direction for each transaction.  I don’t think you’ll get rid of the
> need to have some framing around the headers; I’m not convinced (but
> willing to be) that the complexity of managing two affiliated streams is
> greater than that of needing framing around the body as well because it
> shares a stream with the headers.
>
>
>
> *From:* QUIC [mailto:[email-masked]] *On Behalf Of *Charles 'Buck'
> Krasic
> *Sent:* Friday, November 4, 2016 10:28 AM
> *To:* Martin Thomson <[email-masked]>
> *Cc:* [email-masked]; Patrick McManus <[email-masked]>
> *Subject:* Re: [QUIC] h2 mapping - hpack dynamic table by reference
>
>
>
>    - The idea of QUIC HPACK then is to mark updates to the dynamic table
>    as "pending" initially, and commit them later when the header they
>    originate from is no longer in flight (i.e. acked to sender).   Indexed
>    entries would only be generated against dynamic tables entries that are
>    committed.  I.e., when encoding an entry, if there is a  matching but
>    pending entry in the dynamic table, generate a literal instead of indexed.
>
>
>       are added to the table, and the creation of subsequent indexed entries that
>       refer to them.   It suggests that a small fraction of HTTP/2 HPACK indexed
>    - the new behavior could be optional, individual HTTP/2 transactions
>    to select between this new scheme and the status quo, trading between
>    - instead using the shared headers stream (#3), each HTTP/2
>    transaction has a dedicated stream for headers
>
>
>       - 1 bit of the stream id will be used to classify the QUIC stream
>       as headers or body.
>
>
>             corresponding HTTP/2 transaction may have been cancelled and corresponding
>             body stream RST*.
>
>
>    -  *the pair will always have the property that commit epoch < encode
>          sequence number.*
>
>
>          should be added to the dynamic table, as in current HPACK, but the entry in
>          the dynamic table will have a "pending" flag set, preventing it from being
>          - *the header is transmitted from encoder to decoder.  the
>          decoder can always decode immediately, because this algorithm prevents a
>          header from referring to other headers that might still be in flight.*
>
>
>    - second phase (commit):  once fully transmitted,  pending updates are
>       committed, in the same order they were encoded.
>
>
>    - at encoder, when a previously transmitted header has been acked, the
>          entry in the commit-sequencer is marked committed.
>
>
>    - the front of the sequencer is popped while the front is a committed
>             entry. (loop)
>    - at decoder, when a header is received, it will pop off the front of
>          the commit sequencer while its sequence number is <= the just received
>          commit epoch (loop).
>
>
>    - a special case that is handled by the dynamic table in HTTP/2 HPACK
>       but I think needs to be treated differently for QUIC HPACK, that is entries
>       that refer to other entries from the same header.    For this I propose to
>       define a new distinct table, and corresponding distinct entry types,
>       "Intra".   This would be an ephemeral per-header table that exists only
>       during the transmit phase.
>
>
>    - *I don't have data yet for how many entries fall into this category.*
>
>
>

---

From: "Charles 'Buck' Krasic" <[email-masked]>
Date: Fri, 4 Nov 2016 12:35:47 -0700

On Fri, Nov 4, 2016 at 11:16 AM, Mike Bishop <[email-masked]>
wrote:

> that hasn’t been ACKed) than an independent change to the protocol.
>

I'd be happy if it could be done with just encoder strategy, but I don't
think it can.   Hence the extra sequence numbers sent, and the modified
logic at the decoder.    That said, I have definitely trying to make the
delta as small and sharply focused on HOL as possible.

Think about using, say, the unique-index-per-entry approach (which requires
> wants to be even smarter….)
>

The same packet as original value is interesting, but I think possibly
super hard.  There's a circular dependency of estimating the compressed
header size as part of knowing whether they will go in the same packet, in
order decide on representation strategy that will turn effect compressed
header size.

Maybe alternative, more or less crazy, approach would be use some
combination of pseudo requests/pushes to prime the dictionary?

> An encoder that didn’t want the complexity would just emit the most
> compact representation it could, knowing it might cause some blockage on
> the receiver.
>

That's what I was saying wrt "status quo transactions".

just sticking with the current headers over stream 3 would allow for the
prefers more compact representation option.

> shares a stream with the headers.

Right.  Our QUIC implementation does layer HTTP/2 HEADER frames above QUIC
STREAM frames.  That would be true in this design also.  However, the
status quo does allow the body data to be sent without any extra framing
above QUIC STREAM frames.  I didn't see an obvious way not to lose that
latter property if headers and body are in the same stream.

---

From: Mike Bishop <[email-masked]>
Date: Fri, 4 Nov 2016 23:20:35 +0000

Oh, granted that it’s not sufficient to call it an encoder strategy and just keep using HPACK.  I’m just making the argument that it need not be the basis of the protocol design – the protocol can enable an encoder that wants to do such a strategy to do so without mandating it.  I really don’t want a dual HPACK-and-QPACK approach – maybe we can define a legacy version where HPACK on stream 3 is used, but at worst case, you should select and commit to one for a connection.

I also agree that judging whether you’ll be within the same packet could lead to loops.  Perhaps better left alone for most implementations, that’s fair.

The thing that strikes me is that the HTTP/2 framing layer is essentially message-based (frames), while from the application layer, QUIC is purely stream-based.  I’m thinking that for HTTP-over-QUIC, we define a light-weight framing to differentiate headers from body and move the headers back on-stream, if we can.  Maybe just a couple bytes of type identification, length, and enough context to differentiate intermediate header blocks from final response headers from body from trailers…?  PUSH_PROMISE probably lives here, too.

From: Charles 'Buck' Krasic [mailto:[email-masked]]
Sent: Friday, November 4, 2016 12:36 PM
To: Mike Bishop <[email-masked]>
Cc: Martin Thomson <[email-masked]>; [email-masked]; Patrick McManus <[email-masked]>
Subject: Re: [QUIC] h2 mapping - hpack dynamic table by reference

On Fri, Nov 4, 2016 at 11:16 AM, Mike Bishop <[email-masked]<mailto:[email-masked]>> wrote:
This is certainly interesting, but feels more like a wise strategy for an encoder to employ (if you want to avoid blocking, don’t reference a header that hasn’t been ACKed) than an independent change to the protocol.

I'd be happy if it could be done with just encoder strategy, but I don't think it can.   Hence the extra sequence numbers sent, and the modified logic at the decoder.    That said, I have definitely trying to make the delta as small and sharply focused on HOL as possible.

Think about using, say, the unique-index-per-entry approach (which requires blocking on unknown indices) – an encoder that wanted to be smart could do as you describe below and emit literals until the frame has been ACK’d.  (Unless the frame was going in the same packet as the original value, if it wants to be even smarter….)

The same packet as original value is interesting, but I think possibly super hard.  There's a circular dependency of estimating the compressed header size as part of knowing whether they will go in the same packet, in order decide on representation strategy that will turn effect compressed header size.

Maybe alternative, more or less crazy, approach would be use some combination of pseudo requests/pushes to prime the dictionary?

An encoder that didn’t want the complexity would just emit the most compact representation it could, knowing it might cause some blockage on the receiver.

just sticking with the current headers over stream 3 would allow for the prefers more compact representation option.

Right.  Our QUIC implementation does layer HTTP/2 HEADER frames above QUIC STREAM frames.  That would be true in this design also.  However, the status quo does allow the body data to be sent without any extra framing above QUIC STREAM frames.  I didn't see an obvious way not to lose that latter property if headers and body are in the same stream.

From: QUIC [mailto:[email-masked]<mailto:[email-masked]>] On Behalf Of Charles 'Buck' Krasic
Sent: Friday, November 4, 2016 10:28 AM
To: Martin Thomson <[email-masked]<mailto:[email-masked]>>
Cc: [email-masked]<mailto:[email-masked]>; Patrick McManus <[email-masked]<mailto:[email-masked]>>
Subject: Re: [QUIC] h2 mapping - hpack dynamic table by reference

--
Charles 'Buck' Krasic | Software Engineer | [email-masked]<mailto:[email-masked]> | +1 (408) 412-1141<tel:(408)%20412-1141>

---

From: "Charles 'Buck' Krasic" <[email-masked]>
Date: Fri, 4 Nov 2016 21:04:10 -0700

On Fri, Nov 4, 2016 at 4:20 PM, Mike Bishop <[email-masked]>
wrote:

> Oh, granted that it’s not sufficient to call it an encoder strategy and
> just keep using HPACK.  I’m just making the argument that it need not be
> the basis of the protocol design – the protocol can enable an encoder that
> wants to do such a strategy to do so without mandating it.  I really don’t
> want a dual HPACK-and-QPACK approach – maybe we can define a legacy version
> where HPACK on stream 3 is used, but at worst case, you should select and
> commit to one for a connection.
>
Yea, all or nothing per connection might be the simplest option.  That's
certainly my plan for my initial experimentation.

>
>
> I also agree that judging whether you’ll be within the same packet could
> lead to loops.  Perhaps better left alone for most implementations, that’s
> fair.
>
>
>
> The thing that strikes me is that the HTTP/2 framing layer is essentially
> message-based (frames), while from the application layer, QUIC is purely
> stream-based.
>

Right, but streams are so lightweight, the philosophy is that you can use
QUIC streams as messages. You're of course free to frame messages on your
own within a stream.

> I’m thinking that for HTTP-over-QUIC, we define a light-weight framing to
> differentiate headers from body and move the headers back on-stream, if we
> can.  Maybe just a couple bytes of type identification, length, and enough
> context to differentiate intermediate header blocks from final response
> headers from body from trailers…?  PUSH_PROMISE probably lives here, too.
>
>
>
Yes, you could imagine something like that, maybe even use something very
close to HTTP/2 DATA frames.   I don't think overhead is such a big deal
either way.

I think the big simplification of separate headers streams are around
cancellation.  Keeping the encoder/decoder state perfectly in sync will be
much harder if the headers share fate with the body.   I believe a whole
lot of complexity will be avoided by having headers reliably delivered,
even when HTTP/2 transactions and corresponding body streams are
cancelled/RST.

> *From:* Charles 'Buck' Krasic [mailto:[email-masked]]
> *Sent:* Friday, November 4, 2016 12:36 PM
> *To:* Mike Bishop <[email-masked]>
> *Cc:* Martin Thomson <[email-masked]>; [email-masked]; Patrick
> McManus <[email-masked]>
>
> Think about using, say, the unique-index-per-entry approach (which
> requires blocking on unknown indices) – an encoder that wanted to be smart
> could do as you describe below and emit literals until the frame has been
> ACK’d.  (Unless the frame was going in the same packet as the original
> value, if it wants to be even smarter….)
>
>
>