Skip to main content

4. Wire Format

4.1 Primitives

4.1.1 Prefixed Integers

This document makes extensive use of the prefixed integer from Section 5.1 of [RFC7541]. The format from [RFC7541] is used unmodified. Note, however, that QPACK uses some prefix sizes not actually used in HPACK.

QPACK implementations MUST be able to decode integers up to and including 62 bits long.

4.1.2 String Literals

The string literal defined by Section 5.2 of [RFC7541] is also used throughout this document. This string format includes optional Huffman encoding.

HPACK defines string literals to begin on a byte boundary. They begin with a single bit flag, referred to in this document as 'H' (indicating whether the string is Huffman encoded), followed by the Length encoded as a 7-bit prefix integer, and finally Length bytes of data. When Huffman encoding is enabled, the Huffman table from Appendix B of [RFC7541] is used without modification, and the Length indicates the size of the encoded string.

This document expands the definition of string literals by permitting them to begin other than on a byte boundary. An "N-bit prefix string literal" begins mid-byte, with the first (8-N) bits allocated to a previous field. The string uses one bit for the Huffman flag, followed by the Length encoded as an (N-1)-bit prefix integer. The value of the prefix size N can be between 2 and 8 inclusive. The remainder of the string literal is unmodified.

A string literal without a prefix length noted is an 8-bit prefix string literal and follows the definitions in [RFC7541] without modification.

4.2 Encoder and Decoder Streams

QPACK defines two unidirectional stream types:

  • Encoder stream is a unidirectional stream of type 0x02. It carries an unframed sequence of encoder instructions from encoder to decoder.

  • Decoder stream is a unidirectional stream of type 0x03. It carries an unframed sequence of decoder instructions from decoder to encoder.

HTTP/3 endpoints contain a QPACK encoder and decoder. Each endpoint MUST initiate at most one encoder stream and at most one decoder stream. Receipt of a second instance of either stream type MUST be treated as a connection error of type H3_STREAM_CREATION_ERROR.

The sender MUST NOT close either of these streams, and the receiver MUST NOT request that the sender close either of these streams. Closure of either unidirectional stream type MUST be treated as a connection error of type H3_CLOSED_CRITICAL_STREAM.

An endpoint MAY avoid creating an encoder stream if it will not be used (for example, if its encoder does not wish to use the dynamic table or if the maximum size of the dynamic table permitted by the peer is zero).

An endpoint MAY avoid creating a decoder stream if the decoder sets the maximum capacity of the dynamic table to zero.

An endpoint MUST allow its peer to create an encoder stream and a decoder stream even if the connection's settings prevent their use.

4.3 Encoder Instructions

An encoder sends encoder instructions on the encoder stream to set the capacity of the dynamic table and add dynamic table entries. Instructions adding table entries can use existing entries to avoid transmitting redundant information. The name can be transmitted as a reference to an existing entry in the static or the dynamic table or as a string literal. For entries that already exist in the dynamic table, the full entry can also be used by reference, creating a duplicate entry.

4.3.1 Set Dynamic Table Capacity

An encoder uses an instruction beginning with the '001' 3-bit pattern to inform the decoder of a change to the dynamic table capacity. Following this is the new dynamic table capacity represented as an integer with a 5-bit prefix; see Section 4.1.1.

  0   1   2   3   4   5   6   7
+---+---+---+---+---+---+---+---+
| 0 | 0 | 1 | Capacity (5+) |
+---+---+---+-------------------+

Figure 5: Set Dynamic Table Capacity

The new capacity MUST be lower than or equal to the limit described in Section 3.2.3. In HTTP/3, this limit is the value of the SETTINGS_QPACK_MAX_TABLE_CAPACITY parameter (Section 5) received from the decoder. The decoder MUST treat a new dynamic table capacity value that exceeds this limit as a connection error of type QPACK_ENCODER_STREAM_ERROR.

Reducing the dynamic table capacity can cause entries to be evicted; see Section 3.2.2. This MUST NOT cause the eviction of entries that are not evictable; see Section 2.1.1. Changing the capacity of the dynamic table is not acknowledged, as it does not insert an entry.

4.3.2 Insert with Name Reference

An encoder adds an entry to the dynamic table where the field name matches the field name of an entry stored in the static or the dynamic table using an instruction that begins with the '1' 1-bit pattern. The second ('T') bit indicates whether the reference is to the static or dynamic table. The following 6-bit prefix integer (Section 4.1.1) is used to locate the table entry for the field name. When T=1, the number represents the static table index; when T=0, the number is the relative index of the entry in the dynamic table.

The field name reference is followed by the field value represented as a string literal; see Section 4.1.2.

     0   1   2   3   4   5   6   7
+---+---+---+---+---+---+---+---+
| 1 | T | Name Index (6+) |
+---+---+-----------------------+
| H | Value Length (7+) |
+---+---------------------------+
| Value String (Length bytes) |
+-------------------------------+

Figure 6: Insert Field Line -- Indexed Name

4.3.3 Insert with Literal Name

An encoder adds an entry to the dynamic table where both the field name and the field value are represented as string literals using an instruction that begins with the '01' 2-bit pattern.

This is followed by the name represented as a 6-bit prefix string literal, and the value represented as an 8-bit prefix string literal; see Section 4.1.2.

     0   1   2   3   4   5   6   7
+---+---+---+---+---+---+---+---+
| 0 | 1 | H | Name Length (5+) |
+---+---+---+-------------------+
| Name String (Length bytes) |
+---+---------------------------+
| H | Value Length (7+) |
+---+---------------------------+
| Value String (Length bytes) |
+-------------------------------+

Figure 7: Insert Field Line -- New Name

4.3.4 Duplicate

An encoder duplicates an existing entry in the dynamic table using an instruction that begins with the '000' 3-bit pattern. This is followed by the relative index of the existing entry represented as an integer with a 5-bit prefix; see Section 4.1.1.

     0   1   2   3   4   5   6   7
+---+---+---+---+---+---+---+---+
| 0 | 0 | 0 | Index (5+) |
+---+---+---+-------------------+

Figure 8: Duplicate

The existing entry is re-inserted into the dynamic table without resending either the name or the value. This is useful to avoid adding a reference to an older entry, which might block inserting new entries.

4.4 Decoder Instructions

A decoder sends decoder instructions on the decoder stream to inform the encoder about the processing of field sections and table updates to ensure consistency of the dynamic table.

4.4.1 Section Acknowledgment

After processing an encoded field section whose declared Required Insert Count is not zero, the decoder emits a Section Acknowledgment instruction. The instruction begins with the '1' 1-bit pattern, followed by the field section's associated stream ID encoded as a 7-bit prefix integer; see Section 4.1.1.

This instruction is used as described in Sections 2.1.4 and 2.2.2.

  0   1   2   3   4   5   6   7
+---+---+---+---+---+---+---+---+
| 1 | Stream ID (7+) |
+---+---------------------------+

Figure 9: Section Acknowledgment

If an encoder receives a Section Acknowledgment instruction referring to a stream on which every encoded field section with a non-zero Required Insert Count has already been acknowledged, this MUST be treated as a connection error of type QPACK_DECODER_STREAM_ERROR.

The Section Acknowledgment instruction might increase the Known Received Count; see Section 2.1.4.

4.4.2 Stream Cancellation

When a stream is reset or reading is abandoned, the decoder emits a Stream Cancellation instruction. The instruction begins with the '01' 2-bit pattern, followed by the stream ID of the affected stream encoded as a 6-bit prefix integer.

This instruction is used as described in Section 2.2.2.

  0   1   2   3   4   5   6   7
+---+---+---+---+---+---+---+---+
| 0 | 1 | Stream ID (6+) |
+---+---+-----------------------+

Figure 10: Stream Cancellation

4.4.3 Insert Count Increment

The Insert Count Increment instruction begins with the '00' 2-bit pattern, followed by the Increment encoded as a 6-bit prefix integer. This instruction increases the Known Received Count (Section 2.1.4) by the value of the Increment parameter. The decoder should send an Increment value that increases the Known Received Count to the total number of dynamic table insertions and duplications processed so far.

  0   1   2   3   4   5   6   7
+---+---+---+---+---+---+---+---+
| 0 | 0 | Increment (6+) |
+---+---+-----------------------+

Figure 11: Insert Count Increment

An encoder that receives an Increment field equal to zero or one that increases the Known Received Count beyond what the encoder has sent MUST treat this as a connection error of type QPACK_DECODER_STREAM_ERROR.

4.5 Field Line Representations

An encoded field section consists of a prefix and a possibly empty sequence of representations defined in this section. Each representation corresponds to a single field line. These representations reference the static table or the dynamic table in a particular state, but they do not modify that state.

An encoded field section is carried in a frame on a stream defined by the enclosing protocol.

4.5.1 Encoded Field Section Prefix

Each encoded field section is prefixed with two integers. The Required Insert Count is encoded as an integer with an 8-bit prefix using the encoding described in Section 4.5.1.1. The Base is encoded as a sign bit ('S') and a Delta Base value with a 7-bit prefix; see Section 4.5.1.2.

  0   1   2   3   4   5   6   7
+---+---+---+---+---+---+---+---+
| Required Insert Count (8+) |
+---+---------------------------+
| S | Delta Base (7+) |
+---+---------------------------+
| Encoded Field Lines ...
+-------------------------------+

Figure 12: Encoded Field Section

4.5.1.1 Required Insert Count

Required Insert Count identifies the state of the dynamic table needed to process the encoded field section. Blocking decoders use the Required Insert Count to determine when it is safe to process the rest of the field section.

The encoder transforms the Required Insert Count as follows before encoding:

if ReqInsertCount == 0:
EncInsertCount = 0
else:
EncInsertCount = (ReqInsertCount mod (2 * MaxEntries)) + 1

Here MaxEntries is the maximum number of entries that the dynamic table can have. The smallest entry has empty name and value strings and has a size of 32. Hence, MaxEntries is calculated as:

MaxEntries = floor( MaxTableCapacity / 32 )

MaxTableCapacity is the maximum capacity of the dynamic table as specified by the decoder; see Section 3.2.3.

This encoding limits the length of the prefix on long-lived connections.

The decoder can reconstruct the Required Insert Count using an algorithm such as the following. If the decoder encounters an EncodedInsertCount that could not have been produced by a conformant encoder, it MUST treat this as a connection error of type QPACK_DECOMPRESSION_FAILED.

TotalNumberOfInserts is the total number of inserts into the decoder's dynamic table.

FullRange = 2 * MaxEntries
if EncodedInsertCount == 0:
ReqInsertCount = 0
else:
if EncodedInsertCount > FullRange:
Error
MaxValue = TotalNumberOfInserts + MaxEntries

# MaxWrapped is the largest possible value of
# ReqInsertCount that is 0 mod 2 * MaxEntries
MaxWrapped = floor(MaxValue / FullRange) * FullRange
ReqInsertCount = MaxWrapped + EncodedInsertCount - 1

# If ReqInsertCount exceeds MaxValue, the Encoder's value
# must have wrapped one fewer time
if ReqInsertCount > MaxValue:
if ReqInsertCount <= FullRange:
Error
ReqInsertCount -= FullRange

# Value of 0 must be encoded as 0.
if ReqInsertCount == 0:
Error

For example, if the dynamic table is 100 bytes, then the Required Insert Count will be encoded modulo 6. If a decoder has received 10 inserts, then an encoded value of 4 indicates that the Required Insert Count is 9 for the field section.

4.5.1.2 Base

The Base is used to resolve references in the dynamic table as described in Section 3.2.5.

To save space, the Base is encoded relative to the Required Insert Count using a one-bit sign ('S' in Figure 12) and the Delta Base value. A sign bit of 0 indicates that the Base is greater than or equal to the value of the Required Insert Count; the decoder adds the value of Delta Base to the Required Insert Count to determine the value of the Base. A sign bit of 1 indicates that the Base is less than the Required Insert Count; the decoder subtracts the value of Delta Base from the Required Insert Count and also subtracts one to determine the value of the Base. That is:

if Sign == 0:
Base = ReqInsertCount + DeltaBase
else:
Base = ReqInsertCount - DeltaBase - 1

A single-pass encoder determines the Base before encoding a field section. If the encoder inserted entries in the dynamic table while encoding the field section and is referencing them, the Required Insert Count will be greater than the Base, so the encoded difference is negative and the sign bit is set to 1. If the field section was not encoded using representations that reference the most recent entry in the table and did not insert any new entries, the Base will be greater than the Required Insert Count, so the encoded difference is positive and the sign bit is set to 0.

The value of the Base MUST NOT be negative. Though the protocol would operate correctly with a negative Base and post-Base indexing, this is needlessly inefficient. If the value of the Required Insert Count is less than or equal to the value of the Delta Base, an endpoint MUST treat a field block with a sign bit of 1 as invalid.

An encoder that produces table updates before encoding a field section might set the Base to the value of the Required Insert Count. In such a case, both the sign bit and the Delta Base will be set to zero.

A field section that was encoded without references to the dynamic table can use any value for the Base; setting Delta Base to zero is one of the most efficient encodings.

For example, with a Required Insert Count of 9, a sign bit of 1, and a Delta Base of 2, the Base is 6 and enables post-Base indexing for three entries. In this example, a relative index of 1 refers to the fifth entry that was added to the table; a post-Base index of 1 refers to the eighth entry.

4.5.2 Indexed Field Line

An indexed field line representation identifies an entry in the static table or an entry in the dynamic table with an absolute index less than the value of the Base.

  0   1   2   3   4   5   6   7
+---+---+---+---+---+---+---+---+
| 1 | T | Index (6+) |
+---+---+-----------------------+

Figure 13: Indexed Field Line

This representation begins with the '1' 1-bit pattern, followed by the 'T' bit indicating whether the reference is into the static or dynamic table. The following 6-bit prefix integer (Section 4.1.1) is used to locate the table entry for the field line. When T=1, the number represents the static table index; when T=0, the number is the relative index of the entry in the dynamic table.

4.5.3 Indexed Field Line with Post-Base Index

An indexed field line with post-Base index representation identifies an entry in the dynamic table with an absolute index greater than or equal to the value of the Base.

  0   1   2   3   4   5   6   7
+---+---+---+---+---+---+---+---+
| 0 | 0 | 0 | 1 | Index (4+) |
+---+---+---+---+---------------+

Figure 14: Indexed Field Line with Post-Base Index

This representation begins with the '0001' 4-bit pattern. This is followed by the post-Base index (Section 3.2.6) of the matching field line, represented as an integer with a 4-bit prefix; see Section 4.1.1.

4.5.4 Literal Field Line with Name Reference

A literal field line with name reference representation encodes a field line where the field name matches the field name of an entry in the static table or the field name of an entry in the dynamic table with an absolute index less than the value of the Base.

     0   1   2   3   4   5   6   7
+---+---+---+---+---+---+---+---+
| 0 | 1 | N | T |Name Index (4+)|
+---+---+---+---+---------------+
| H | Value Length (7+) |
+---+---------------------------+
| Value String (Length bytes) |
+-------------------------------+

Figure 15: Literal Field Line with Name Reference

This representation begins with the '01' 2-bit pattern. The following bit, 'N', indicates whether an intermediary is permitted to add this field line to the dynamic table on subsequent hops. When the 'N' bit is set, the encoded field line MUST always be encoded with a literal representation. In particular, when a peer sends a field line that it received represented as a literal field line with the 'N' bit set, it MUST use a literal representation to forward this field line. This bit is intended to protect field values that are not to be put at risk by compressing them; see Section 7.1 for more details.

The fourth ('T') bit indicates whether the reference is to the static or dynamic table. The following 4-bit prefix integer (Section 4.1.1) is used to locate the table entry for the field name. When T=1, the number represents the static table index; when T=0, the number is the relative index of the entry in the dynamic table.

Only the field name is taken from the dynamic table entry; the field value is encoded as an 8-bit prefix string literal; see Section 4.1.2.

4.5.5 Literal Field Line with Post-Base Name Reference

A literal field line with post-Base name reference representation encodes a field line where the field name matches the field name of a dynamic table entry with an absolute index greater than or equal to the value of the Base.

     0   1   2   3   4   5   6   7
+---+---+---+---+---+---+---+---+
| 0 | 0 | 0 | 0 | N |NameIdx(3+)|
+---+---+---+---+---+-----------+
| H | Value Length (7+) |
+---+---------------------------+
| Value String (Length bytes) |
+-------------------------------+

Figure 16: Literal Field Line with Post-Base Name Reference

This representation begins with the '0000' 4-bit pattern. The fifth bit is the 'N' bit as described in Section 4.5.4. This is followed by a post-Base index (Section 3.2.6) of the dynamic table entry, encoded as an integer with a 3-bit prefix; see Section 4.1.1.

Only the field name is taken from the dynamic table entry; the field value is encoded as an 8-bit prefix string literal; see Section 4.1.2.

4.5.6 Literal Field Line with Literal Name

A literal field line with literal name representation encodes a field name and a field value as string literals.

     0   1   2   3   4   5   6   7
+---+---+---+---+---+---+---+---+
| 0 | 0 | 1 | N | H |NameLen(3+)|
+---+---+---+---+---+-----------+
| Name String (Length bytes) |
+---+---------------------------+
| H | Value Length (7+) |
+---+---------------------------+
| Value String (Length bytes) |
+-------------------------------+

Figure 17: Literal Field Line with Literal Name

This representation begins with the '001' 3-bit pattern. The fourth bit is the 'N' bit as described in Section 4.5.4. The name follows, represented as a 4-bit prefix string literal, then the value, represented as an 8-bit prefix string literal; see Section 4.1.2.