HTTP 帧
HTTP/2 连接一旦建立,端点间即可开始交换帧。
帧格式
所有的帧以 9 字节的报头开始,后面跟着可变长度的主题。
+-----------------------------------------------+
| Length (24) |
+---------------+---------------+---------------+
| Type (8) | Flags (8) |
+-+-------------+---------------+-------------------------------+
|R| Stream Identifier (31) |
+=+=============================================================+
| Frame Payload (0...) ...
+---------------------------------------------------------------+
图 1:帧布局
帧报头中各字段定义如下:
长度(Length):24位无符号整型表示帧主体的长度。值超过 $$2^{14}$$(16,384) 的帧将禁止(MUST NOT)被发送,除非接收端将 SETTINGS_MAX_FRAME_SIZE 设置为了更大的值。
9 字节长度的帧报头信息不计算在此长度内。
类型(Type):帧的类型,长度为 8 bit。帧类型决定了帧的格式和语法。具体实现在收到未知类型的帧的时候,必须(MUST)忽略并丢弃该帧。
标记(Flags):一个 8 bit 的字段,服务于具体帧类型。
标记(Flags)在不同的帧类型中被赋予不同的语义。未定义语法的帧类型必须(MUST)忽略它,并且必须(MUST)在发送时不设置(0x0)该属性。
R:1 bit 保留字段。未定义该字段的语义,发送时,这个比特必须(MUST)保持未设置(0x0),接受时,这个比特必须被忽略。
- 流标识符(Stream Identifier):流标识符(参考章节 5.1.1)以 31 位的无符号整型表示。值 0x0 是保留值,用于关联连接,与独立的流相对。
帧主体的结构和内容完全取决于帧类型。
帧尺寸
帧主体的尺寸受限于帧的最大长度,其值由接收端在 SETTINGS_MAX_FRAME_SIZE 设置中指定。这个配置可以是 $$2^{14}$$(16,384)字节 ~ $$2^{24}-1$$(16,777,215)字节之间的任何值(闭区间)。
所有实现必须(MUST)能够接收帧,并且至少处理$$2^{14}$$ 字节长度,以及 9 字节的帧报头(参考章节 4.1)。当描述帧尺寸时,并没有包含帧报头的尺寸。
注意:某些帧类型,比如 PING(参考章节 6.7),对帧主体大小有额外的限制。
如果帧的尺寸超过了 SETTINGS_MAX_FRAME_SIZE 中所定义的尺寸,或者超过了任何帧类型所定义的约束,或者尺寸太小以至于无法承载数据,端点必须(MUST)发送一个 FRAME_SIZE_ERROR 错误码。一个帧尺寸错误可能修改整个连接状态,必须作为一个连接错误(参考章节 5.4.1)处理;这包括所有携带报头区块的帧(参考章节 4.3)(即:HEADERS、PUSH_PROMISE 和 CONTINUATION),SETTINGS 帧,以及标识符为 0 的流。
端点并没有义务使用帧中所有的可用空间。可以通过使用比允许的最大尺寸更小的帧来提升响应能力。如果在时间敏感的帧(比如 RST_STREAM、WINDOW_UPDATE、PRIORITY)中发送较大的帧,并在传输过程中被阻塞,则可能导致性能问题。
报头压缩和解压缩
HTTP/2 和 HTTP/2 一样,一个报头字段包含一个名称,以及关联的一个或多个值。报头字段在 HTTP 请求和响应的消息中,以及服务端推送(参考章节 8.2)中使用。
包头列表是0个或多个报头字段的集合。当它通过连接传输时,报头列表使用 HTTP 包头压缩(COMPRESSION)序列化到报头块中。序列化的报头块之后被分割为一个或多个字节序列,称为报头片段,并在 HEADERS 帧(参考章节 6.2)、PUSH_PROMISE 帧(参考章节 6.6)、CONTINUATION 帧(参考章节 6.10)的主体中传输。
报文头 Cookie 字段通过 HTTP 映射(参考章节 8.1.2.5)进行特殊处理。
接收端点通过连接报头片段重新组装报头块,并解压缩报头块,重新构建报头列表。
一个完整的包头区块包含:
- 一个包含 END_HEADERS 标记集合的 HEADERS 或者 PUSH_PROMISE 帧。或者
- 一个 END_HEADERS 标记被清除的 HEADERS 或 PUSH_PROMISE 帧,以及一个或多个 CONTINUATION 帧,最后一个 CONTINUATION 帧被设置了 END_HEADERS 标记。
包头压缩是有状态的。一个压缩上下文和一个解压缩上下文用于整个连接。在报头块中出现的解码错误必须(MUST)被当做 COMPRESSION_ERROR 类型的连接错误(参考章节 5.4.1)处理。
每个报头字段被当做一个独立单元处理。报头字段必须(MUST)作为一个连续的帧序列传输,之间没有穿插任何其他类型的帧或或任何其他的流。一个 HEADERS 或 CONTINUATIN 帧序列的最后一帧必须设置了 END_HEADERS 标记。这允许一个报头块逻辑上等价于一个单独的帧。
报头块片段只能被当做 HEADERS、PUSH_PROMISE 或 CONTINUATION 帧的主体发送,因为这些帧中懈怠了能够被接收端修改的压缩上下文数据。一个端点接受 HEADERS、PUSH_PROMISE 或 CONTINUATION 帧需要重新组装报头块并且执行解压缩,即便这些帧将会被丢弃。如果接收端无法解压缩报头块,它必须(MUST)通过 COMPRESSION_ERROR 来终止连接。