Skip to main content

8. Expressing HTTP Semantics in HTTP/2

HTTP/2是HTTP消息抽象的一个实例化 ([HTTP] 第6节)。

8.1. HTTP Message Framing (HTTP消息帧化)

客户端使用先前未使用的流标识符 (第5.1.1节) 在新流上发送HTTP请求。服务器在与请求相同的流上发送HTTP响应。

HTTP消息组成

HTTP消息 (请求或响应) 由以下部分组成:

  1. 一个HEADERS帧 (后跟零个或多个CONTINUATION帧),包含头部段 (参见 [HTTP] 第6.3节)

  2. 零个或多个DATA帧,包含消息内容 (参见 [HTTP] 第6.4节)

  3. 可选地,一个HEADERS帧 (后跟零个或多个CONTINUATION帧),包含尾部段 (如果存在) (参见 [HTTP] 第6.5节)

临时响应

仅对于响应,服务器可以 (MAY) 在包含最终响应的HEADERS帧之前发送任意数量的临时响应。临时响应由包含临时 (1xx) HTTP响应的控制数据和头部段的HEADERS帧 (可能后跟零个或多个CONTINUATION帧) 组成 (参见 [HTTP] 第15节)。

携带信息性状态码的设置了END_STREAM标志的HEADERS帧是格式错误的 (第8.1.1节)。

消息结束

序列中的最后一帧带有END_STREAM标志,注意设置了END_STREAM标志的HEADERS帧可以后跟携带字段块任何剩余片段的CONTINUATION帧。

在HEADERS帧和可能跟随的任何CONTINUATION帧之间,禁止 (MUST NOT) 出现其他帧 (来自任何流)。

重要规则

分块传输编码: HTTP/2使用DATA帧携带消息内容。[HTTP/1.1] 第7.1节中定义的分块传输编码不能在HTTP/2中使用;参见第8.2.2节。

尾部字段: 在也终止流的字段块中携带。也就是说,尾部字段包括一个以HEADERS帧开始的序列,后跟零个或多个CONTINUATION帧,其中HEADERS帧带有END_STREAM标志。尾部禁止 (MUST NOT) 包含伪头部字段 (第8.3节)。

请求/响应交换: 完全消耗单个流。请求从将流置于"open"状态的HEADERS帧开始。请求以设置了END_STREAM标志的帧结束,这导致流对客户端变为"half-closed (local)",对服务器变为"half-closed (remote)"。


8.1.1. Malformed Messages (格式错误的消息)

格式错误的请求或响应 是一个在其他方面有效的HTTP/2帧序列,但由于以下原因而无效:

  • 存在多余的帧
  • 禁止的字段或伪头部字段
  • 缺少强制伪头部字段
  • 包含大写字段名
  • 无效的字段名和/或值 (在某些情况下;参见第8.2节)

Content-Length验证

包含消息内容的请求或响应可以包含content-length头部字段。如果content-length头部字段的值不等于构成内容的DATA帧载荷长度之和,则请求或响应也是格式错误的,除非消息被定义为没有内容。例如,204或304响应不包含内容,对HEAD请求的响应也是如此。

处理规则

  • 处理HTTP请求或响应的中介 (即任何不充当隧道的中介) 禁止 (MUST NOT) 转发格式错误的请求或响应。

  • 检测到的格式错误的请求或响应必须 (MUST) 被视为类型为PROTOCOL_ERROR的流错误 (第5.4.2节)。

  • 对于格式错误的请求,服务器可以 (MAY) 在关闭或重置流之前发送HTTP响应。

  • 客户端禁止 (MUST NOT) 接受格式错误的响应。


8.2. HTTP Fields (HTTP字段)

HTTP字段 ([HTTP] 第5节) 由HTTP/2在HEADERS、CONTINUATION和PUSH_PROMISE帧中传达,使用HPACK [COMPRESSION] 压缩。

字段名必须 (MUST) 在构建HTTP/2消息时转换为小写。

8.2.1. Field Validity (字段有效性)

HTTP中字段名和值的定义禁止HPACK可能传达的某些字符。HTTP/2实现应该 (SHOULD) 根据 [HTTP] 第5.1节和第5.5节中的定义验证字段名和值,并将包含禁止字符的消息视为格式错误 (第8.1.1节)。

最小验证要求

实现必须 (MUST) 执行以下字段名和值的最小验证:

字段名:

  • 禁止 (MUST NOT) 包含0x00-0x20、0x41-0x5a或0x7f-0xff范围内的字符 (所有范围包括)
  • 这特别排除了所有不可见的ASCII字符、ASCII SP (0x20) 和大写字符 ('A'到'Z', ASCII 0x41到0x5a)
  • 除了伪头部字段 (第8.3节),字段名禁止 (MUST NOT) 包含冒号 (ASCII COLON, 0x3a)

字段值:

  • 禁止 (MUST NOT) 在任何位置包含零值 (ASCII NUL, 0x00)、换行 (ASCII LF, 0x0a) 或回车 (ASCII CR, 0x0d)
  • 禁止 (MUST NOT) 以ASCII空白字符 (ASCII SP或HTAB, 0x20或0x09) 开始或结束

违反这些条件的请求或响应必须 (MUST) 被视为格式错误 (第8.1.1节)。


8.2.2. Connection-Specific Header Fields (连接特定的头部字段)

HTTP/2不使用Connection头部字段 ([HTTP] 第7.6.1节) 来指示连接特定的头部字段;在此协议中,连接特定的元数据通过其他方式传达。

端点禁止 (MUST NOT) 生成包含连接特定头部字段的HTTP/2消息。这包括Connection头部字段以及 [HTTP] 第7.6.1节中列出的具有连接特定语义的那些 (即Proxy-Connection、Keep-Alive、Transfer-Encoding和Upgrade)。

包含连接特定头部字段的任何消息必须 (MUST) 被视为格式错误 (第8.1.1节)。

唯一的例外 是TE头部字段,它可以 (MAY) 出现在HTTP/2请求中;当它出现时,它禁止 (MUST NOT) 包含除"trailers"之外的任何值。

注意: HTTP/2故意不支持升级到另一个协议。第3节中描述的握手方法被认为足以协商替代协议的使用。


Cookie头部字段 [COOKIE] 使用分号 (";") 来分隔cookie-pairs (或"crumbs")。此头部字段包含多个值,但不使用逗号 (",") 作为分隔符,从而阻止cookie-pairs在多个字段行上发送 (参见 [HTTP] 第5.2节)。这可能会显著降低压缩效率,因为对单个cookie-pairs的更新将使存储在HPACK表中的任何字段行失效。

为了实现更好的压缩效率,Cookie头部字段可以 (MAY) 被拆分为单独的头部字段,每个字段包含一个或多个cookie-pairs。

示例 - 以下两个Cookie头部字段列表在语义上是等效的:

cookie: a=b; c=d; e=f

等同于:

cookie: a=b
cookie: c=d
cookie: e=f

8.3. HTTP Control Data (HTTP控制数据)

HTTP/2使用以':'字符 (ASCII 0x3a) 开头的特殊伪头部字段 (Pseudo-Header Fields) 来传达消息控制数据 (参见 [HTTP] 第6.2节)。

伪头部字段规则

定义:

  • 伪头部字段不是HTTP头部字段
  • 端点禁止 (MUST NOT) 生成除本文档中定义的伪头部字段之外的伪头部字段
  • 扩展可以协商使用额外的伪头部字段;参见第5.5节

有效性:

  • 伪头部字段仅在定义它们的上下文中有效
  • 为请求定义的伪头部字段禁止 (MUST NOT) 出现在响应中
  • 为响应定义的伪头部字段禁止 (MUST NOT) 出现在请求中
  • 伪头部字段禁止 (MUST NOT) 出现在尾部段中

顺序:

  • 所有伪头部字段必须 (MUST) 在字段块中的所有常规字段行之前出现
  • 在常规字段行之后的字段块中出现的伪头部字段必须 (MUST) 被视为格式错误

唯一性:

  • 相同的伪头部字段名禁止 (MUST NOT) 在字段块中出现多次
  • 包含重复伪头部字段名的字段块必须 (MUST) 被视为格式错误

8.3.1. Request Pseudo-Header Fields (请求伪头部字段)

为HTTP/2请求定义以下伪头部字段:

:method

包含HTTP方法 ([HTTP] 第9节)。

示例: :method: GET, :method: POST

:scheme

包含请求目标的方案部分。方案取自目标URI (Section 3.1 of [RFC3986])。对于CONNECT请求省略方案 (第8.5节)。

:scheme不限于"http"和"https"方案的URI。代理或网关可以翻译非HTTP方案的请求,从而能够使用HTTP与非HTTP服务交互。

示例: :scheme: https, :scheme: ftp

:authority

传达目标URI的授权部分 (Section 3.2 of [RFC3986])。HTTP/2请求的接收方在存在:authority时,禁止 (MUST NOT) 使用Host头部字段来确定目标URI。

重要规则:

  • 直接生成HTTP/2请求的客户端必须 (MUST) 使用:authority伪头部字段来传达授权信息
  • 客户端禁止 (MUST NOT) 生成与:authority伪头部字段不同的Host头部字段的请求
  • :authority禁止 (MUST NOT) 包含"http"或"https"方案URI的已弃用userinfo子组件

示例: :authority: www.example.com, :authority: example.com:8080

:path

包含目标URI的路径和查询部分 (absolute-path生产,可选地加上'?'字符后跟query生产)。星号形式的请求 (对于OPTIONS) 为:path伪头部字段包含值'*'。

规则:

  • 对于"http"或"https" URI,此伪头部字段禁止 (MUST NOT) 为空
  • 不包含路径组件的"http"或"https" URI必须 (MUST) 包含值'/'
  • 例外: OPTIONS请求使用'*',CONNECT请求省略:path

示例: :path: /index.html, :path: /api?q=search, :path: *

请求伪头部字段要求

所有HTTP/2请求必须 (MUST) 包含:method:scheme:path伪头部字段的恰好一个有效值,除非它们是CONNECT请求 (第8.5节)。


8.3.2. Response Pseudo-Header Fields (响应伪头部字段)

为HTTP/2响应定义一个伪头部字段:

:status

携带HTTP状态码字段 ([HTTP] 第15节)。

示例: :status: 200, :status: 404, :status: 500


8.4. Server Push (服务器推送)

HTTP/2允许服务器在相应请求之前优先向客户端发送响应。服务器推送是可选的;客户端可以选择禁用它。

服务器推送使用PUSH_PROMISE帧 (第6.6节) 通知客户端。PUSH_PROMISE包括服务器生成的请求的头部,允许客户端在接收响应之前了解推送。

关键特性:

  • 推送的流始终由服务器发起
  • PUSH_PROMISE帧在客户端发起的流上发送
  • 推送的响应在保留的推送流上发送
  • 客户端可以通过SETTINGS_ENABLE_PUSH设置禁用服务器推送
  • 客户端可以通过RST_STREAM拒绝推送的流

8.5. The CONNECT Method (CONNECT方法)

CONNECT方法 ([HTTP] 第9.3.6节) 主要与HTTP代理一起使用,以建立到源服务器的TLS会话,以便与"https"资源交互。

在HTTP/2中,CONNECT方法用于通过单个HTTP/2流在HTTP/2客户端和远程主机之间建立隧道。

CONNECT请求的特殊性:

  • 禁止 (MUST NOT) 包含:scheme:path伪头部字段
  • 必须 (MUST) 包含:authority伪头部字段
  • :authority的值包含要连接的主机和端口

8.6. The Upgrade Header Field (Upgrade头部字段)

HTTP/2不支持101 (Switching Protocols)信息状态码 ([HTTP] 第15.2.2节)。

Upgrade头部字段的语义特定于HTTP/1.1,不适用于HTTP/2。


🔑 关键要点总结

HTTP/2消息结构

请求: HEADERS (+ CONTINUATION) → DATA* → [HEADERS (trailer)]
响应: [HEADERS (1xx)]* → HEADERS (+ CONTINUATION) → DATA* → [HEADERS (trailer)]

伪头部字段

请求:

  • :method - HTTP方法 (必需)
  • :scheme - URI方案 (必需,CONNECT除外)
  • :authority - 授权信息 (必需)
  • :path - 路径和查询 (必需,CONNECT除外)

响应:

  • :status - HTTP状态码 (必需)

字段规则

字段名: 必须小写,不能包含特殊字符
字段值: 不能包含NUL、CR、LF,不能以空白开始/结束
伪头部: 必须在常规字段之前,不能重复

禁止的头部字段

  • Connection
  • Keep-Alive
  • Proxy-Connection
  • Transfer-Encoding
  • Upgrade (除TE: trailers外)

Cookie特殊处理

可以拆分为多个字段行以提高压缩效率:

cookie: a=b
cookie: c=d

服务器推送

  • 使用PUSH_PROMISE通知
  • 客户端可通过SETTINGS禁用
  • 客户端可通过RST_STREAM拒绝
  • 推送流使用偶数ID

错误处理

格式错误的消息 → PROTOCOL_ERROR流错误
无效字段 → 格式错误
违反伪头部规则 → 格式错误