HTTP/3 帧格式

变长帧头、七种帧类型、四类单向流,以及被删除的 h2 帧。

作者 David更新于 第 12 篇(共 16 篇)

帧格式

HTTP/3 Frame {
  Type   (变长整数),
  Length (变长整数),
  Frame Payload (..),
}

对照 h2 的 9 字节定长头,三处差异:

h2h3
头部长度固定 9 字节通常 2 字节(变长)
流 ID在帧头里不在——由 QUIC 流承载
标志位8 位 Flags无——语义改由帧类型或 QUIC 表达

h3 帧头不含流 ID,因为帧已经写在某条 QUIC 流上了,流身份由传输层提供。同理 END_STREAM 标志也没了——改用 QUIC 的流 FIN 位。

七种帧类型

Type名称允许出现在
0x00DATA请求流
0x01HEADERS请求流、推送流
0x03CANCEL_PUSH控制流
0x04SETTINGS控制流(必须是首帧)
0x05PUSH_PROMISE请求流
0x07GOAWAY控制流
0x0dMAX_PUSH_ID控制流

未知帧类型 MUST 被忽略(扩展点,同 h2)。

被删除的 h2 帧

这些帧类型在 h3 中被永久保留为非法——收到即 H3_FRAME_UNEXPECTED:

h2 帧类型值为什么没了
PRIORITY0x02优先级树已废弃,改用 priority 字段
PING0x06QUIC 有 PING 帧
WINDOW_UPDATE0x08QUIC 有 MAX_DATA/MAX_STREAM_DATA
CONTINUATION0x09不再需要——见下
RST_STREAM—QUIC 有 RESET_STREAM 帧

保留这些值而非复用,是为了让「h2 帧被误发到 h3 连接上」这种协议混淆能被立即检出。

CONTINUATION 消失的意义

h2 中字段块超过 MAX_FRAME_SIZE 就要拆成 HEADERS + 若干 CONTINUATION,且中间不得插入任何其他帧——巨大的头部会短暂独占整条连接。

h3 的 HEADERS 帧长度是变长整数(上限 2^62−1),一个帧就能装下任意大的字段块;而且它写在自己的 QUIC 流上,天然不影响其他流。CONTINUATION Flood 这类攻击面在 h3 上不存在。

四类单向流

h3 用 QUIC 的单向流承载连接级信令。每条单向流的第一个变长整数是流类型:

流类型名称作用
0x00控制流SETTINGS、GOAWAY、MAX_PUSH_ID、CANCEL_PUSH
0x01推送流服务端推送的响应(已无实际使用)
0x02QPACK 编码器流动态表插入指令
0x03QPACK 解码器流确认与取消指令

规则:

  • 双方各 MUST 开一条控制流,且在连接早期开启。
  • 控制流上的首帧 MUST 是 SETTINGS,否则 H3_MISSING_SETTINGS。
  • 控制流、QPACK 两条流每种只能有一条,重复开启 → H3_STREAM_CREATION_ERROR。
  • 这些流MUST NOT 被关闭。关闭任何一条 → H3_CLOSED_CRITICAL_STREAM,整条连接终止。
  • 未知流类型 MUST 被忽略(可安全地停止读取)。

对照 h2:h2 用流 0 承载所有连接级帧;h3 没有流 0,改用专门的单向流——因为 QUIC 的流 ID 空间里没有「0 号连接级流」这个概念。

没有连接前言

h2 有 24 字节的 PRI * HTTP/2.0 魔数,用于防止把 h2 帧发给 1.1 服务端。

h3 不需要:ALPN 在 QUIC 握手中已经确定了协议是 h3,且 QUIC 本身与 TCP 不兼容,不存在「误发给 1.1 服务端」的可能。取而代之的是「控制流首帧必须是 SETTINGS」这一约定。

SETTINGS

同样是单向声明 + 不可重发,但与 h2 有三点不同:

h2h3
承载流 0 的 SETTINGS 帧控制流首帧
是否需 ACK需要(ACK 标志)不需要
可否重发可以,随时更新不可——一条连接只发一次,再发 → H3_FRAME_UNEXPECTED

h3 的三个标准参数:

ID名称作用
0x01QPACK_MAX_TABLE_CAPACITYQPACK 动态表容量,默认 0(即默认禁用动态表)
0x06MAX_FIELD_SECTION_SIZE字段列表未压缩大小上限(对应 h2 的 MAX_HEADER_LIST_SIZE)
0x07QPACK_BLOCKED_STREAMS允许因 QPACK 而阻塞的最大流数,默认 0

注意 h2 的 INITIAL_WINDOW_SIZE、MAX_CONCURRENT_STREAMS、MAX_FRAME_SIZE 在 h3 中都不存在——分别由 QUIC 的传输参数 initial_max_stream_data_*、initial_max_streams_bidi 承担,帧大小则无需限制。

请求流的帧序列

客户端在一条双向流上:
  HEADERS                 请求头(QPACK 编码)
  DATA (可选,可多个)      请求体
  HEADERS (可选)          trailer 字段
  → QUIC 流 FIN 位        相当于 h2 的 END_STREAM

服务端在同一条流上:
  HEADERS (可选,多个)     1xx 中间响应
  HEADERS                 最终响应头
  DATA (可选,可多个)      响应体
  HEADERS (可选)          trailer
  → QUIC 流 FIN 位

约束:

  • 请求流上必须恰好有一个请求与一个最终响应。
  • 客户端流上收到 DATA 而无先行 HEADERS → H3_FRAME_UNEXPECTED。
  • 请求流未完整(缺响应就 FIN)→ H3_REQUEST_INCOMPLETE。

错误码

h3 错误码从 0x0100 起,与 QUIC 传输层错误码共用同一空间但不重叠:

码名称触发
0x0100H3_NO_ERROR正常关闭
0x0101H3_GENERAL_PROTOCOL_ERROR未归类的协议违规
0x0102H3_INTERNAL_ERROR实现内部错误
0x0103H3_STREAM_CREATION_ERROR流创建违规(如重复控制流)
0x0104H3_CLOSED_CRITICAL_STREAM关键流被关闭
0x0105H3_FRAME_UNEXPECTED帧出现在不允许的位置
0x0106H3_FRAME_ERROR帧格式或长度错
0x0107H3_EXCESSIVE_LOAD对端负载过大(对应 h2 的 ENHANCE_YOUR_CALM)
0x0109H3_SETTINGS_ERRORSETTINGS 违规
0x010aH3_MISSING_SETTINGS控制流首帧不是 SETTINGS
0x010bH3_REQUEST_REJECTED未处理即拒绝,客户端可安全重试(对应 REFUSED_STREAM)
0x010cH3_REQUEST_CANCELLED请求被取消
0x010eH3_MESSAGE_ERROR消息 malformed(如伪头违规)
0x0110H3_VERSION_FALLBACK要求客户端改用 HTTP/1.1 重试

下一篇:QPACK 头部压缩。

本页目录