From 6a026fd64aff8dc18be75c3e2a28a74db09658ef Mon Sep 17 00:00:00 2001 From: Alan Frindell Date: Sun, 18 Mar 2018 05:32:44 -0700 Subject: [PATCH 1/3] Move QPACK control instructions to dedicated streams 1. Give QPACK encoders and decoders their own unidirectional streams (hardcoded to 6,7,10,11 for now) 2. Define simple framing for encoder stream 3. Move HEADER_ACK from HTTP over QUIC frame to QPACK instruction 4. Introduce Table State Synchronize instruction Closes #1121 and #1122 --- draft-ietf-quic-http.md | 46 ++++---------------- draft-ietf-quic-qpack.md | 91 +++++++++++++++++++++++++++------------- 2 files changed, 70 insertions(+), 67 deletions(-) diff --git a/draft-ietf-quic-http.md b/draft-ietf-quic-http.md index 7ac84490fc..b5b85e13cb 100644 --- a/draft-ietf-quic-http.md +++ b/draft-ietf-quic-http.md @@ -253,9 +253,12 @@ QUIC reserves the first client-initiated, bidirectional stream (Stream 0) for cryptographic operations. HTTP over QUIC reserves the first unidirectional stream sent by either peer (Streams 2 and 3) for sending and receiving HTTP control frames. This pair of unidirectional streams is analogous to HTTP/2's -Stream 0. The data sent on these streams is critical to the HTTP connection. -If either control stream is closed for any reason, this MUST be treated as a -connection error of type QUIC_CLOSED_CRITICAL_STREAM. +Stream 0. HTTP over QUIC also reserves the second and third unidirectional +streams for each peer's QPACK encoder and decoder. The client's QPACK encoder +uses stream 6 and decoder uses stream 10. The server's encoder and decoder use +streams 7 and 11, respectively. The data sent on these streams is critical to +the HTTP connection. If any control stream is closed for any reason, this +MUST be treated as a connection error of type QUIC_CLOSED_CRITICAL_STREAM. When HTTP headers and data are sent over QUIC, the QUIC layer handles most of the stream management. @@ -568,8 +571,7 @@ The HEADERS frame defines no flags. ~~~~~~~~~~ {: #fig-headers title="HEADERS frame payload"} -HEADERS frames can be sent on the Control Stream as well as on request / push -streams. +HEADERS frames can only be sent on request / push streams. ### PRIORITY {#frame-priority} @@ -950,38 +952,6 @@ using an Immediate Close (see {{QUIC-TRANSPORT}}). An endpoint that completes a graceful shutdown SHOULD use the QUIC APPLICATION_CLOSE frame with the HTTP_NO_ERROR code. -### HEADER_ACK {#frame-header-ack} - -The HEADER_ACK frame (type=0x8) is used by header compression to ensure -consistency. The frames are sent from the QPACK decoder to the QPACK encoder; -that is, the server sends them to the client to acknowledge processing of the -client's header blocks, and the client sends them to the server to acknowledge -processing of the server's header blocks. - -The HEADER_ACK frame is sent on the Control Stream when the QPACK decoder has -fully processed a header block. It is used by the peer's QPACK encoder to -determine whether subsequent indexed representations that might reference that -block are vulnerable to head-of-line blocking, and to prevent eviction races. -See [QPACK] for more details on the use of this information. - -The HEADER_ACK frame indicates the stream on which the header block was -processed by encoding the Stream ID as a variable-length integer. The same -Stream ID can be identified multiple times, as multiple header-containing blocks -can be sent on a single stream in the case of intermediate responses, trailers, -pushed requests, etc. as well as on the Control Streams. Since header frames on -each stream are received and processed in order, this gives the encoder precise -feedback on which header blocks within a stream have been fully processed. - -~~~~~~~~~~ - 0 1 2 3 4 5 6 7 -+---+---+---+---+---+---+---+---+ -| Stream ID (i) ... -+---+---+---+---+---+---+---+---+ -~~~~~~~~~~ -{: title="HEADER_ACK frame"} - -The HEADER_ACK frame does not define any flags. - ### MAX_PUSH_ID {#frame-max-push-id} @@ -1407,7 +1377,7 @@ The entries in the following table are registered by this document. | PUSH_PROMISE | 0x5 | {{frame-push-promise}} | | Reserved | 0x6 | N/A | | GOAWAY | 0x7 | {{frame-goaway}} | -| HEADER_ACK | 0x8 | {{frame-header-ack}} | +| Reserved | 0x8 | N/A | | Reserved | 0x9 | N/A | | MAX_PUSH_ID | 0xD | {{frame-max-push-id}} | |----------------|------|--------------------------| diff --git a/draft-ietf-quic-qpack.md b/draft-ietf-quic-qpack.md index b006a1dccc..61bed06a0c 100644 --- a/draft-ietf-quic-qpack.md +++ b/draft-ietf-quic-qpack.md @@ -119,11 +119,11 @@ and can always be processed immediately. QPACK instructions occur in three locations, each of which uses a separate instruction space: - - Table updates are carried by HEADERS frames on the control stream, as defined - by {{QUIC-HTTP}}. Frames on this stream modify the dynamic table state - without generating output to any particular request. - - Acknowledgement of header frame processing is carried by HEADER_ACK frames on - the control stream, running from decoder to encoder. + - Table updates are carried by a unidirectional stream from encoder to decoder. + Instructions on this stream modify the dynamic table state without generating + output to any particular request. + - Acknowledgements of table modifications and header processing are carried by + a unidirectional stream from decoder to encoder. - Finally, the contents of HEADERS and PUSH_PROMISE frames on request streams reference the QPACK table state. @@ -233,7 +233,7 @@ error code. If this reference occurs on the control stream, this MUST be treated as a session error. -## HEADERS Frames on the Control Stream +## QPACK Encoder Stream Table updates can add a table entry, possibly using existing entries to avoid transmitting redundant information. The name can be transmitted as a reference @@ -241,6 +241,19 @@ to an existing entry in the static or the dynamic table or as a string literal. For entries which already exist in the dynamic table, the full entry can also be used by reference, creating a duplicate entry. +Each set of encoder insructions is prefaced by its length, encoded as a variable +length integer with an 8-bit prefix. + +~~~~~~~~~~ drawing + 0 1 2 3 4 5 6 7 + +---+---+---+---+---+---+---+---+ + | Block Length (8+) | + +-------------------------------+ + | Instruction Block (*) ... + +-------------------------------+ +~~~~~~~~~~ +{: title="Insert Header Field -- Indexed Name"} + ### Insert With Name Reference An addition to the header table where the header field name matches the header @@ -335,36 +348,56 @@ Reducing the maximum size of the dynamic table can cause entries to be evicted (see Section 4.3 of [RFC7541]). This MUST NOT cause the eviction of entries with outstanding references (see {{reference-tracking}}). -## HEADER_ACK Frames {#feedback} +## QPACK Decoder Stream {#feedback} -HEADER_ACK frames on the control stream carry information used to ensure -consistency of the dynamic table. Information is sent from the QPACK decoder to -the QPACK encoder; that is, the server informs the client about the processing -of the client's header blocks and table updates, and the client informs the -server about the processing of the server's header blocks and table updates. +The decoder stream carries information used to ensure consistency of the dynamic +table. Information is sent from the QPACK decoder to the QPACK encoder; that is, +the server informs the client about the processing of the client's header blocks +and table updates, and the client informs the server about the processing of the +server's header blocks and table updates. -Each frame represents a header block or table update which the QPACK decoder has -fully processed. It is used by the peer's QPACK encoder to determine whether -subsequent indexed representations that might reference impacted entries are -vulnerable to head-of-line blocking, and to prevent eviction races. +### Table Size Synchronize -The frame payload contains contains a variable-length integer (as defined in -{{QUIC-TRANSPORT}}) which indicates the stream on which the header block was -processed. The same Stream ID can be identified in multiple frames, as multiple -header blocks can be sent on a single request or push stream. (Requests can -have trailers; responses can have intermediate status codes and PUSH_PROMISE -frames.) As the control stream carries multiple table updates, the control -stream can also be identified in multiple frames. +After processing a set of instructions on the encoder stream, the decoder will +emit a Table State Synchronize instruction on the decoder stream. The +instruction specifies the total number of dynamic table inserts and duplications +since the last Table State Synchronize. The encoder uses this value to +determine which table entries are vulnerable to head-of-line blocking. A +decoder MAY coalesce multiple synchronization updates into a single update. -Since header frames on each stream are received and processed in -order, this gives the encoder precise feedback on which header blocks within a -stream have been fully processed. +~~~~~~~~~~ drawing + 0 1 2 3 4 5 6 7 ++---+---+---+---+---+---+---+---+ +| 1 | Insert Count (7+) | ++---+---+---+-------------------+ +~~~~~~~~~~ +{:#fig-size-sync title="Table Size Synchronize"} + +### Header Acknowledgement + +After processing a header block on a request or push stream, the decoder emits a +Header Ack instruction on the decoder stream with the request stream's stream +ID. It is used by the peer's QPACK encoder to prevent eviction races. + +The same Stream ID can be identified multiple times, as multiple header blocks +can be sent on a single stream in the case of intermediate responses, trailers, +and pushed requests. Since header frames on each stream are received and +processed in order, this gives the encoder precise feedback on which header +blocks within a stream have been fully processed. + +~~~~~~~~~~ drawing + 0 1 2 3 4 5 6 7 ++---+---+---+---+---+---+---+---+ +| 1 | Stream ID (7+) | ++---+---+---+-------------------+ +~~~~~~~~~~ +{:#fig-header-ack title="Header Acknowledgement"} -## Request Streams +## Request and Push Streams HEADERS and PUSH_PROMISE frames on request and push streams reference the -dynamic table in a particular state without modifying it, but emit the headers -for an HTTP request or response. +dynamic table in a particular state without modifying it. Frames on these +streams emit the headers for an HTTP request or response. ### Header Data Prefix {#absolute-index} From dd1c678909e00c133621ac7e0fceded854ceeaab Mon Sep 17 00:00:00 2001 From: Mike Bishop Date: Tue, 24 Apr 2018 11:14:27 -0700 Subject: [PATCH 2/3] +----+ => ----- --- draft-ietf-quic-qpack.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/draft-ietf-quic-qpack.md b/draft-ietf-quic-qpack.md index 61bed06a0c..a9038f8258 100644 --- a/draft-ietf-quic-qpack.md +++ b/draft-ietf-quic-qpack.md @@ -369,7 +369,7 @@ decoder MAY coalesce multiple synchronization updates into a single update. 0 1 2 3 4 5 6 7 +---+---+---+---+---+---+---+---+ | 1 | Insert Count (7+) | -+---+---+---+-------------------+ ++---+---------------------------+ ~~~~~~~~~~ {:#fig-size-sync title="Table Size Synchronize"} @@ -389,7 +389,7 @@ blocks within a stream have been fully processed. 0 1 2 3 4 5 6 7 +---+---+---+---+---+---+---+---+ | 1 | Stream ID (7+) | -+---+---+---+-------------------+ ++---+---------------------------+ ~~~~~~~~~~ {:#fig-header-ack title="Header Acknowledgement"} From e8d22aae942d5f13b17ff22abfa1cff8ffecf58d Mon Sep 17 00:00:00 2001 From: Alan Frindell Date: Thu, 26 Apr 2018 13:38:29 -0700 Subject: [PATCH 3/3] Fix Header Ack bit pattern, describe wire format using text --- draft-ietf-quic-qpack.md | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/draft-ietf-quic-qpack.md b/draft-ietf-quic-qpack.md index a9038f8258..337dde2b94 100644 --- a/draft-ietf-quic-qpack.md +++ b/draft-ietf-quic-qpack.md @@ -241,8 +241,9 @@ to an existing entry in the static or the dynamic table or as a string literal. For entries which already exist in the dynamic table, the full entry can also be used by reference, creating a duplicate entry. -Each set of encoder insructions is prefaced by its length, encoded as a variable -length integer with an 8-bit prefix. +Each set of encoder instructions is prefaced by its length, encoded as a +variable length integer with an 8-bit prefix. Instructions MUST NOT span more +than one block. ~~~~~~~~~~ drawing 0 1 2 3 4 5 6 7 @@ -252,7 +253,7 @@ length integer with an 8-bit prefix. | Instruction Block (*) ... +-------------------------------+ ~~~~~~~~~~ -{: title="Insert Header Field -- Indexed Name"} +{: title="Encoder instruction block"} ### Insert With Name Reference @@ -360,10 +361,11 @@ server's header blocks and table updates. After processing a set of instructions on the encoder stream, the decoder will emit a Table State Synchronize instruction on the decoder stream. The -instruction specifies the total number of dynamic table inserts and duplications -since the last Table State Synchronize. The encoder uses this value to -determine which table entries are vulnerable to head-of-line blocking. A -decoder MAY coalesce multiple synchronization updates into a single update. +instruction begins with the '1' one-bit pattern. The instruction specifies the +total number of dynamic table inserts and duplications since the last Table +State Synchronize, encoded as a 7-bit prefix integer. The encoder uses this +value to determine which table entries are vulnerable to head-of-line blocking. +A decoder MAY coalesce multiple synchronization updates into a single update. ~~~~~~~~~~ drawing 0 1 2 3 4 5 6 7 @@ -376,8 +378,10 @@ decoder MAY coalesce multiple synchronization updates into a single update. ### Header Acknowledgement After processing a header block on a request or push stream, the decoder emits a -Header Ack instruction on the decoder stream with the request stream's stream -ID. It is used by the peer's QPACK encoder to prevent eviction races. +Header Acknowledgement instruction on the decoder stream. The instruction +begins with the '0' one-bit pattern and includes the request stream's stream ID, +encoded as a 7-bit prefix integer. It is used by the peer's QPACK encoder to +know when it is safe to evict an entry. The same Stream ID can be identified multiple times, as multiple header blocks can be sent on a single stream in the case of intermediate responses, trailers, @@ -388,7 +392,7 @@ blocks within a stream have been fully processed. ~~~~~~~~~~ drawing 0 1 2 3 4 5 6 7 +---+---+---+---+---+---+---+---+ -| 1 | Stream ID (7+) | +| 0 | Stream ID (7+) | +---+---------------------------+ ~~~~~~~~~~ {:#fig-header-ack title="Header Acknowledgement"}