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 来终止连接。