4. HTTP 帧

优质
小牛编辑
122浏览
2023-12-01

一旦建立了HTTP/2连接, 终端之间就可以开始交换帧了.

4.1 帧格式

所有的帧都以一个9字节的报头开始, 后接变长的载荷:

+-----------------------------------------------+
|                 Length (24)                   |
+---------------+---------------+---------------+
|   Type (8)    |   Flags (8)   |
+-+-------------+---------------+-------------------------------+
|R|                 Stream Identifier (31)                      |
+=+=============================================================+
|                   Frame Payload (0...)                      ...
+---------------------------------------------------------------+

报头部分的字段定义如下:

  • Length: 载荷的长度, 无符号24位整型. 对于发送值大于2^14 (长度大于16384字节)的载荷, 只有在接收方设置SETTINGS_MAX_FRAME_SIZE为更大的值时才被允许

    注: 帧的报头9字节不算在length里.

  • Type: 8位的值表示帧类型, 决定了帧的格式和语义. 协议实现上必须忽略任何未知类型的帧.
  • Flags: 为Type保留的bool标识, 大小是8位. 对确定的帧类型赋予特定的语义, 否则发送时必须忽略(设置为0x0).
  • R: 1位的保留字段, 尚未定义语义. 发送和接收必须忽略(0x0).
  • Stream Identifier: 31位无符号整型的流标示符. 其中0x0作为保留值, 表示与连接相关的frames作为一个整体而不是一个单独的流.

4.2 帧大小

载荷的大小受接收方设置的SETTINGS_MAX_FRAME_SIZE所限, 而这个值的取值区间是[2^14 (16384), 2^24-1 (16777215)] 字节.

必须能够接收并且至少能够处理2^14字节大小的帧, 外加9字节的报头. 在描述帧的大小时, 不包含帧的报头部分.

注: 某些类型的帧, 如PING, 会在允许传输的载荷大小上附加额外的限制.

终端必须响应一个错误码FRAME_SIZE_ERROR ----如果帧大小超过了SETTINGS_MAX_FRAME_SIZE中的定义或超过了对特定类型帧的大小限制, 抑或是帧太小以至于无法容纳下必要的元信息. 如果一个帧的帧大小错误可能改变整个连接的状态, 那么必须作为连接错误来对待; 这包括任何携带报头区块(HEADERS, PUSH_PROMISE, CONTINUATION)的帧, SETTINGS帧, 以及任何带有流标识符为0x0的帧.

终端不必完整填充一个帧的所有空间. 更小的帧能提高响应速度. 对于时间敏感的帧来讲(如RST_STREAM, WINDOW_UPDATE, PRIORITY), 发送大块的帧可能导致延迟, 如果传输链路被一个大块帧阻塞, 很可能会带来性能损耗.

4.3 Header压缩和解压

HTTP/2与HTTP/1的报头结构一样, 一键多值. 除了用于HTTP请求和响应之外还用于server push操作.

压缩&分片

报头列表包含0个或多个报头字段. 传输过程是: 先用压缩把报头列表序列化成一个报头区块. 然后将这个区块分割成一个或多个字节序列, 称之为区块分片. 分片可以作为HEADERS帧, PUSH_PROMISE帧或者CONTINUATION帧的载荷.

其中Cookie字段通过HTTP mapping特殊处理.

解压&重组

报文接收端将分片拼接起来以重组报头区块, 然后解压区块得到原始的报头列表.

一个完整地报头区块可以由下面任意一种结构组成:

  • 一个设置了END_HEADERS标记的HEADERSPUSH_PROMISE帧.
  • 一个END_HEADERS标记置空的HEADERSPUSH_PROMISE 帧, 后接一个或多个CONTINUATION帧, 并且最后一个CONTINUATIONEND_HEADERS标记.

报头压缩是个有状态的过程. 整个连接复用同一个压缩和解压上下文. 报头区块中发生的解码错误必须当成连接错误处理, 类型为COMPRESSION_ERROR.

每个报头区块被当做离散的单元处理. 必须确保这些区块传输的连续性, 期间不能交叉其他类型的或来自任何其他流的帧. HEADERSPUSH_PROMISECONTINUATION传输序列中最后一帧一定设置了END_HEADERS标记. 这保证了报头区块在逻辑上等价于单个帧.

报头区块的分片只能作为HEADERS, PUSH_PROMISE以及CONTINUATION帧的载荷存在. 因为这些帧携带的数据可能修改接收方维护的压缩上下文: 接收方拿到这三种类型之一的帧之后需要重组区块并解压, 即便这些帧将被丢弃. 如果无法解压区块, 接收方必须以一个类型为COMPRESSION_ERROR的错误断开连接.