4. 线路格式 (Wire Format)
4.1 基元 (Primitives)
4.1.1 前缀整数 (Prefixed Integers)
本文档广泛使用 [RFC7541] 第 5.1 节中的 prefixed integer. [RFC7541] 中的格式按原样使用. 但需注意, QPACK 使用了一些 HPACK 中并未实际使用的前缀大小.
QPACK 实现 MUST 能够解码长度最大为 62 位 (含 62 位) 的整数.
4.1.2 字符串字面量 (String Literals)
[RFC7541] 第 5.2 节定义的 string literal 也在本文档中通篇使用. 该字符串格式包含可选的 Huffman 编码.
HPACK 定义 string literals 从字节边界开始. 它们以一个单 bit 标志开始, 本文档称其为 'H' (表示字符串是否经过 Huffman 编码), 后跟编码为 7-bit prefix integer 的 Length, 最后是 Length 字节的数据. 启用 Huffman 编码时, 按原样使用 [RFC7541] Appendix B 中的 Huffman table, 且 Length 表示已编码字符串的大小.
本文档扩展了 string literals 的定义, 允许它们从非字节边界开始. "N-bit prefix string literal" 从字节中间开始, 前 (8-N) 位分配给前一个字段. 该字符串使用一位作为 Huffman 标志, 后跟编码为 (N-1)-bit prefix integer 的 Length. 前缀大小 N 的值可以在 2 到 8 之间 (含 2 和 8). string literal 的其余部分保持不变.
未标注前缀长度的 string literal 是 8-bit prefix string literal, 并按原样遵循 [RFC7541] 中的定义.
4.2 Encoder 和 Decoder 流 (Encoder and Decoder Streams)
QPACK 定义了两种单向流类型:
-
Encoder stream 是类型为 0x02 的单向流. 它承载从 encoder 到 decoder 的无帧 encoder instructions 序列.
-
Decoder stream 是类型为 0x03 的单向流. 它承载从 decoder 到 encoder 的无帧 decoder instructions 序列.
HTTP/3 endpoints 包含一个 QPACK encoder 和一个 QPACK decoder. 每个 endpoint MUST 最多发起一个 encoder stream 和一个 decoder stream. 收到任一流类型的第二个实例 MUST 被视为 H3_STREAM_CREATION_ERROR 类型的连接错误.
sender MUST NOT 关闭这两个流中的任意一个, receiver MUST NOT 请求 sender 关闭这两个流中的任意一个. 任一单向流类型被关闭 MUST 被视为 H3_CLOSED_CRITICAL_STREAM 类型的连接错误.
如果不会使用 encoder stream, endpoint MAY 避免创建它 (例如, 其 encoder 不希望使用 dynamic table, 或 peer 允许的 dynamic table 最大大小为零).
如果 decoder 将 dynamic table 的最大容量设置为零, endpoint MAY 避免创建 decoder stream.
即使连接设置阻止使用这些流, endpoint 也 MUST 允许其 peer 创建 encoder stream 和 decoder stream.
4.3 Encoder 指令 (Encoder Instructions)
encoder 在 encoder stream 上发送 encoder instructions, 用于设置 dynamic table 的容量并添加 dynamic table 条目. 添加表条目的指令可以使用现有条目来避免传输冗余信息. 名称可以作为对 static table 或 dynamic table 中现有条目的引用传输, 也可以作为 string literal 传输. 对于 dynamic table 中已经存在的条目, 也可以通过引用完整条目来创建一个 duplicate entry.
4.3.1 设置 Dynamic Table 容量 (Set Dynamic Table Capacity)
encoder 使用以 '001' 3-bit pattern 开头的指令通知 decoder dynamic table capacity 已发生变化. 其后是表示为 5-bit prefix 整数的新 dynamic table capacity; 见 Section 4.1.1.
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 0 | 0 | 1 | Capacity (5+) |
+---+---+---+-------------------+
Figure 5: Set Dynamic Table Capacity
新容量 MUST 小于或等于 Section 3.2.3 中描述的限制. 在 HTTP/3 中, 此限制是从 decoder 收到的 SETTINGS_QPACK_MAX_TABLE_CAPACITY 参数 (Section 5) 的值. decoder MUST 将超过此限制的新 dynamic table capacity 值视为 QPACK_ENCODER_STREAM_ERROR 类型的连接错误.
减小 dynamic table capacity 可能导致条目被驱逐; 见 Section 3.2.2. 这 MUST NOT 导致不可驱逐的条目被驱逐; 见 Section 2.1.1. 更改 dynamic table 的容量不会被确认, 因为它不会插入条目.
4.3.2 使用名称引用插入 (Insert with Name Reference)
encoder 使用以 '1' 1-bit pattern 开头的指令向 dynamic table 添加条目, 其中 field name 与 static table 或 dynamic table 中某个条目的 field name 匹配. 第二个 ('T') bit 表示该引用指向 static table 还是 dynamic table. 随后的 6-bit prefix integer (Section 4.1.1) 用于定位该 field name 的表条目. 当 T=1 时, 该数字表示 static table index; 当 T=0 时, 该数字是 dynamic table 中条目的 relative index.
field name reference 后跟表示为 string literal 的 field value; 见 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 使用 Literal Name 插入 (Insert with Literal Name)
encoder 使用以 '01' 2-bit pattern 开头的指令向 dynamic table 添加条目, 其中 field name 和 field value 均表示为 string literals.
其后是表示为 6-bit prefix string literal 的名称, 以及表示为 8-bit prefix string literal 的值; 见 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
encoder 使用以 '000' 3-bit pattern 开头的指令复制 dynamic table 中的现有条目. 其后是表示为 5-bit prefix 整数的现有条目的 relative index; 见 Section 4.1.1.
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 0 | 0 | 0 | Index (5+) |
+---+---+---+-------------------+
Figure 8: Duplicate
现有条目被重新插入 dynamic table, 不会重新发送名称或值. 这有助于避免添加对较旧条目的引用, 这种引用可能阻塞新条目的插入.
4.4 Decoder 指令 (Decoder Instructions)
decoder 在 decoder stream 上发送 decoder instructions, 向 encoder 告知 field sections 和 table updates 的处理情况, 以确保 dynamic table 一致.
4.4.1 Section Acknowledgment
处理声明的 Required Insert Count 非零的已编码 field section 后, decoder 发出 Section Acknowledgment 指令. 该指令以 '1' 1-bit pattern 开头, 后跟编码为 7-bit prefix integer 的该 field section 关联 stream ID; 见 Section 4.1.1.
此指令按 Section 2.1.4 和 Section 2.2.2 中的描述使用.
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 1 | Stream ID (7+) |
+---+---------------------------+
Figure 9: Section Acknowledgment
如果 encoder 收到的 Section Acknowledgment 指令引用了某个流, 而该流上每个 Required Insert Count 非零的已编码 field section 都已经被确认, 则 MUST 将此视为 QPACK_DECODER_STREAM_ERROR 类型的连接错误.
Section Acknowledgment 指令可能增加 Known Received Count; 见 Section 2.1.4.
4.4.2 Stream Cancellation
当某个流被重置或读取被放弃时, decoder 发出 Stream Cancellation 指令. 该指令以 '01' 2-bit pattern 开头, 后跟编码为 6-bit prefix integer 的受影响流的 stream ID.
此指令按 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
Insert Count Increment 指令以 '00' 2-bit pattern 开头, 后跟编码为 6-bit prefix integer 的 Increment. 此指令按 Increment 参数的值增加 Known Received Count (Section 2.1.4). decoder 应发送一个 Increment 值, 将 Known Received Count 增加到目前已处理的 dynamic table 插入和复制总数.
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 0 | 0 | Increment (6+) |
+---+---+-----------------------+
Figure 11: Insert Count Increment
encoder 如果收到等于零的 Increment 字段, 或收到会使 Known Received Count 超过 encoder 已发送数量的 Increment 字段, MUST 将此视为 QPACK_DECODER_STREAM_ERROR 类型的连接错误.
4.5 Field Line Representations
已编码 field section 由一个 prefix 和本节定义的可能为空的 representations 序列组成. 每个 representation 对应一个 field line. 这些 representations 引用特定状态下的 static table 或 dynamic table, 但不会修改该状态.
已编码 field section 承载在外层协议定义的某个流上的帧中.
4.5.1 已编码 Field Section 前缀 (Encoded Field Section Prefix)
每个已编码 field section 都以前缀中的两个整数开始. Required Insert Count 使用 Section 4.5.1.1 中描述的编码, 编码为带 8-bit prefix 的整数. Base 被编码为一个符号位 ('S') 和一个带 7-bit prefix 的 Delta Base 值; 见 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 标识处理已编码 field section 所需的 dynamic table 状态. 会阻塞的 decoders 使用 Required Insert Count 判断何时可以安全处理 field section 的其余部分.
encoder 在编码前按如下方式转换 Required Insert Count:
if ReqInsertCount == 0:
EncInsertCount = 0
else:
EncInsertCount = (ReqInsertCount mod (2 * MaxEntries)) + 1
其中 MaxEntries 是 dynamic table 可以拥有的最大条目数. 最小条目的名称和值字符串为空, 大小为 32. 因此, MaxEntries 计算如下:
MaxEntries = floor( MaxTableCapacity / 32 )
MaxTableCapacity 是 decoder 指定的 dynamic table 最大容量; 见 Section 3.2.3.
这种编码限制了长生命周期连接上的前缀长度.
decoder 可以使用如下算法重构 Required Insert Count. 如果 decoder 遇到一个合规 encoder 不可能生成的 EncodedInsertCount, 它 MUST 将此视为 QPACK_DECOMPRESSION_FAILED 类型的连接错误.
TotalNumberOfInserts 是插入 decoder 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
例如, 如果 dynamic table 为 100 字节, 则 Required Insert Count 会按模 6 编码. 如果 decoder 已收到 10 次插入, 则编码值 4 表示该 field section 的 Required Insert Count 为 9.
4.5.1.2 Base
Base 用于按 Section 3.2.5 中的描述解析 dynamic table 中的引用.
为节省空间, Base 使用一位符号 ('S' in Figure 12) 和 Delta Base 值, 相对于 Required Insert Count 进行编码. 符号位为 0 表示 Base 大于或等于 Required Insert Count 的值; decoder 将 Delta Base 的值加到 Required Insert Count 上以确定 Base 的值. 符号位为 1 表示 Base 小于 Required Insert Count; decoder 从 Required Insert Count 中减去 Delta Base 的值, 再减去一, 以确定 Base 的值. 即:
if Sign == 0:
Base = ReqInsertCount + DeltaBase
else:
Base = ReqInsertCount - DeltaBase - 1
单遍 encoder 在编码 field section 之前确定 Base. 如果 encoder 在编码 field section 时向 dynamic table 插入了条目并引用这些条目, Required Insert Count 将大于 Base, 因而编码差值为负且符号位设置为 1. 如果 field section 不是使用引用表中最新条目的 representations 编码, 并且没有插入任何新条目, Base 将大于 Required Insert Count, 因而编码差值为正且符号位设置为 0.
Base 的值 MUST NOT 为负. 尽管使用负 Base 和 post-Base indexing 时协议仍能正确运行, 但这是不必要的低效做法. 如果 Required Insert Count 的值小于或等于 Delta Base 的值, endpoint MUST 将符号位为 1 的 field block 视为无效.
在编码 field section 之前生成 table updates 的 encoder 可能会将 Base 设置为 Required Insert Count 的值. 在这种情况下, 符号位和 Delta Base 都会设置为零.
未引用 dynamic table 编码的 field section 可以为 Base 使用任意值; 将 Delta Base 设置为零是最高效的编码之一.
例如, 当 Required Insert Count 为 9, 符号位为 1, Delta Base 为 2 时, Base 为 6, 并允许对三个条目进行 post-Base indexing. 在此示例中, relative index 1 指向添加到表中的第五个条目; post-Base index 1 指向第八个条目.
4.5.2 Indexed Field Line
indexed field line representation 标识 static table 中的一个条目, 或 dynamic table 中 absolute index 小于 Base 值的一个条目.
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 1 | T | Index (6+) |
+---+---+-----------------------+
Figure 13: Indexed Field Line
此 representation 以 '1' 1-bit pattern 开头, 后跟 'T' bit, 表示引用指向 static table 还是 dynamic table. 随后的 6-bit prefix integer (Section 4.1.1) 用于定位该 field line 的表条目. 当 T=1 时, 该数字表示 static table index; 当 T=0 时, 该数字是 dynamic table 中条目的 relative index.
4.5.3 带 Post-Base Index 的 Indexed Field Line (Indexed Field Line with Post-Base Index)
带 post-Base index 的 indexed field line representation 标识 dynamic table 中 absolute index 大于或等于 Base 值的一个条目.
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 0 | 0 | 0 | 1 | Index (4+) |
+---+---+---+---+---------------+
Figure 14: Indexed Field Line with Post-Base Index
此 representation 以 '0001' 4-bit pattern 开头. 其后是匹配 field line 的 post-Base index (Section 3.2.6), 表示为带 4-bit prefix 的整数; 见 Section 4.1.1.
4.5.4 带名称引用的 Literal Field Line (Literal Field Line with Name Reference)
带 name reference 的 literal field line representation 编码一个 field line, 其 field name 与 static table 中某个条目的 field name 匹配, 或与 dynamic table 中 absolute index 小于 Base 值的某个条目的 field name 匹配.
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
此 representation 以 '01' 2-bit pattern 开头. 随后的 bit 'N' 表示是否允许 intermediary 在后续跳上将此 field line 添加到 dynamic table. 当 'N' bit 被设置时, 已编码 field line MUST 始终使用 literal representation 编码. 特别是, 当 peer 发送一个它收到的、以设置了 'N' bit 的 literal field line 表示的 field line 时, 它 MUST 使用 literal representation 转发该 field line. 此 bit 旨在保护那些不应因压缩而承受风险的 field values; 更多细节见 Section 7.1.
第四个 ('T') bit 表示引用指向 static table 还是 dynamic table. 随后的 4-bit prefix integer (Section 4.1.1) 用于定位该 field name 的表条目. 当 T=1 时, 该数字表示 static table index; 当 T=0 时, 该数字是 dynamic table 中条目的 relative index.
只有 field name 取自动 dynamic table 条目; field value 编码为 8-bit prefix string literal; 见 Section 4.1.2.
4.5.5 带 Post-Base 名称引用的 Literal Field Line (Literal Field Line with Post-Base Name Reference)
带 post-Base name reference 的 literal field line representation 编码一个 field line, 其 field name 与 dynamic table 中 absolute index 大于或等于 Base 值的某个条目的 field name 匹配.
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
此 representation 以 '0000' 4-bit pattern 开头. 第五个 bit 是 Section 4.5.4 中描述的 'N' bit. 其后是 dynamic table 条目的 post-Base index (Section 3.2.6), 编码为带 3-bit prefix 的整数; 见 Section 4.1.1.
只有 field name 取自动 dynamic table 条目; field value 编码为 8-bit prefix string literal; 见 Section 4.1.2.
4.5.6 带 Literal Name 的 Literal Field Line (Literal Field Line with Literal Name)
带 literal name 的 literal field line representation 将 field name 和 field value 编码为 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
此 representation 以 '001' 3-bit pattern 开头. 第四个 bit 是 Section 4.5.4 中描述的 'N' bit. 后续为名称, 表示为 4-bit prefix string literal; 再后续为值, 表示为 8-bit prefix string literal; 见 Section 4.1.2.