4. HTTP 帧
一旦建立了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
标记的HEADERS
或PUSH_PROMISE
帧. - 一个
END_HEADERS
标记置空的HEADERS
或PUSH_PROMISE
帧, 后接一个或多个CONTINUATION
帧, 并且最后一个CONTINUATION
帧END_HEADERS
标记.
报头压缩是个有状态的过程. 整个连接复用同一个压缩和解压上下文. 报头区块中发生的解码错误必须当成连接错误处理, 类型为COMPRESSION_ERROR
.
每个报头区块被当做离散的单元处理. 必须确保这些区块传输的连续性, 期间不能交叉其他类型的或来自任何其他流的帧. HEADERS
与PUSH_PROMISE
的CONTINUATION
传输序列中最后一帧一定设置了END_HEADERS
标记. 这保证了报头区块在逻辑上等价于单个帧.
报头区块的分片只能作为HEADERS
, PUSH_PROMISE
以及CONTINUATION
帧的载荷存在. 因为这些帧携带的数据可能修改接收方维护的压缩上下文: 接收方拿到这三种类型之一的帧之后需要重组区块并解压, 即便这些帧将被丢弃. 如果无法解压区块, 接收方必须以一个类型为COMPRESSION_ERROR
的错误断开连接.