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消息 (请求或响应) 由以下部分组成:
-
一个HEADERS帧 (后跟零个或多个CONTINUATION帧),包含头部段 (参见 [HTTP] 第6.3节)
-
零个或多个DATA帧,包含消息内容 (参见 [HTTP] 第6.4节)
-
可选地,一个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节中描述的握手方法被认为足以协商替代协议的使用。
8.2.3. Compressing the Cookie Header Field (压缩Cookie头部字段)
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流错误
无效字段 → 格式错误
违反伪头部规则 → 格式错误