3. 胶囊 (Capsules)
扩展 HTTP 的一种机制是引入新的 HTTP upgrade token; 见 [HTTP] 第 16.7 节. 在 HTTP/1.x 中, 这些 token 通过 Upgrade 机制使用; 见 [HTTP] 第 7.8 节. 在 HTTP/2 和 HTTP/3 中, 这些 token 通过 Extended CONNECT 机制使用; 见 [EXT-CONNECT2] 和 [EXT-CONNECT3].
本规范引入 Capsule Protocol. Capsule Protocol 是一个类型-长度-值元组序列, 新 HTTP upgrade token 的定义可以选择使用它. 即使存在 HTTP 中间节点, 它也允许端点在 HTTP 请求流上端到端可靠地传递与请求相关的信息. Capsule Protocol 可用于交换 HTTP Datagram, 当 HTTP 运行在不支持 QUIC DATAGRAM 帧的传输之上时这是必要的. 即使正在使用 HTTP/3 Datagram, Capsule Protocol 也可用于传递与基于数据报的协议相关的可靠双向控制消息.
3.1. HTTP 数据流 (HTTP Data Streams)
本规范将 HTTP 请求的 "data stream" 定义为请求消息头部字段段之后, 以及最终响应消息之后的双向字节流; 该最终响应消息要么成功 (即 2xx), 要么已升级 (即 101).
在 HTTP/1.x 中, 数据流由连接上跟随空行之后的所有字节组成, 该空行用于结束请求头部字段段或最终响应头部字段段. 因此, 在 HTTP/1.x 连接上只有最后一个 HTTP 请求可以启动 Capsule Protocol.
在 HTTP/2 和 HTTP/3 中, 给定 HTTP 请求的数据流由带有相应流 ID 的 DATA 帧中发送的所有字节组成.
数据流这一概念对 CONNECT 等方法尤其相关, 因为这类方法在头部之后没有 HTTP 消息内容.
可以使用任何适合流或请求优先级的方式为数据流确定优先级. 例如, 见 [PRIORITY] 第 11 节.
数据流受底层的流量控制机制约束; 示例包括 HTTP/2 流流量控制, HTTP/2 连接流量控制以及 TCP 流量控制.
3.2. Capsule Protocol
新的 HTTP upgrade token 定义可以声明其关联请求的数据流使用 Capsule Protocol. 如果这样声明, 则关联请求的数据流内容使用以下格式:
Capsule Protocol {
Capsule (..) ...,
}
图 2: Capsule Protocol 流格式
Capsule {
Capsule Type (i),
Capsule Length (i),
Capsule Value (..),
}
图 3: Capsule 格式
Capsule Type: 一个可变长度整数, 指示 capsule 的类型. Capsule Type 的分配由 IANA 注册表管理; 见第 5.4 节.
Capsule Length: Capsule Value 字段的长度, 单位为字节, 该字段紧随此字段之后, 并编码为可变长度整数. 注意, 此字段可以为零.
Capsule Value: 此 Capsule 的负载. 其语义由 Capsule Type 字段的值决定.
中间节点可以通过 Capsule-Protocol 头部字段 (第 3.4 节) 的存在, 或通过理解所选 HTTP Upgrade token, 来识别 Capsule Protocol 的使用.
由于新协议或扩展可能定义新的 Capsule Type, 希望允许未来扩展性的中间节点应当原样转发 Capsules, 除非所用 Capsule Type 的定义指定了额外的中间节点处理. DATAGRAM Capsule 就是这样一种 Capsule Type; 见第 3.5 节. 尤其是, 中间节点应当原样转发带有未知 Capsule Type 的 Capsules.
端点如果收到带有未知 Capsule Type 的 Capsule, 必须静默丢弃该 Capsule, 并跳过它以解析下一个 Capsule.
根据数据流的定义:
-
除非响应包含 2xx (Successful) 或 101 (Switching Protocols) 状态码, 否则 Capsule Protocol 未被使用.
-
当使用 Capsule Protocol 时, 关联的 HTTP 请求和响应不携带 HTTP 内容. 未来扩展可以定义新的 Capsule Type 来携带 HTTP 内容.
Capsule Protocol 只适用于新 HTTP upgrade token 的定义; 因此, 在 HTTP/2 和 HTTP/3 中, 它只能与 CONNECT 方法一起使用. 所以, 一旦两个端点都同意使用 Capsule Protocol, 该流的帧使用要求就会按 [HTTP/2] 第 8.5 节和 [HTTP/3] 第 4.4 节中的规定发生变化.
Capsule Protocol 禁止与包含 Content-Length, Content-Type 或 Transfer-Encoding 头部字段的消息一起使用. 此外, 使用 Capsule Protocol 的响应中禁止发送 HTTP 状态码 204 (No Content), 205 (Reset Content) 和 206 (Partial Content). 接收方如果观察到违反这些要求的情况, 必须将该 HTTP 消息视为格式错误.
处理 Capsules 时, 接收方可能会倾向于先在数据流中累积完整长度的 Capsule Value 字段, 再对其进行处理. 这种做法应当避免, 因为它可能消耗底层的流量控制额度, 并且如果 Capsule 数据耗尽流量控制窗口, 可能导致死锁.
3.3. 错误处理 (Error Handling)
当接收方在处理 Capsule Protocol 时遇到错误, 接收方必须按收到格式错误或不完整的 HTTP 消息来处理. 对 HTTP/3, 格式错误消息的处理见 [HTTP/3] 第 4.1.2 节. 对 HTTP/2, 格式错误消息的处理见 [HTTP/2] 第 8.1.1 节. 对 HTTP/1.x, 不完整消息的处理见 [HTTP/1.1] 第 8 节.
每个 Capsule 的负载必须只包含其说明中标识出的字段. 如果 Capsule 负载在已标识字段之后还包含额外字节, 或者在已标识字段结束之前终止, 则必须按格式错误或不完整消息处理. 尤其是, 冗余长度编码必须验证为自洽.
如果承载 Capsules 的流的接收侧被干净地终止 (例如在 HTTP/3 中, 这定义为收到设置了 FIN 位的 QUIC STREAM 帧), 并且该流上的最后一个 Capsule 被截断, 则必须按格式错误或不完整消息处理.
3.4. Capsule-Protocol 头部字段 (The Capsule-Protocol Header Field)
Capsule-Protocol 头部字段是一个 Item Structured Field; 见 [STRUCTURED-FIELDS] 第 3.3 节. 其值必须为 Boolean; 接收方必须把任何其他值类型当作该字段不存在来处理 (例如, 如果该字段被包含多次, 其类型将变为 List, 该字段会被忽略). 本文档没有为 Capsule-Protocol 头部字段值定义任何参数, 但未来文档可能会定义参数. 接收方必须忽略未知参数.
端点通过发送值为 true 的 Capsule-Protocol 头部字段, 指示某个数据流正在使用 Capsule Protocol. 值为 false 的 Capsule-Protocol 头部字段与不存在该头部字段具有相同语义.
中间节点可以使用此头部字段来允许处理未知 HTTP upgrade token 的 HTTP Datagrams. 注意, 这只可能用于 HTTP Upgrade 或 Extended CONNECT.
Capsule-Protocol 头部字段禁止用于状态码既不是 101 (Switching Protocols) 且又不在 2xx (Successful) 范围内的 HTTP 响应.
使用 Capsule Protocol 时, HTTP 端点应当发送 Capsule-Protocol 头部字段以简化中间节点处理. 使用 Capsule Protocol 的新 HTTP upgrade token 定义可以改变此建议.
3.5. DATAGRAM Capsule
本文档定义 DATAGRAM (0x00) Capsule Type. 该 Capsule 允许使用 Capsule Protocol 在流上发送 HTTP Datagrams. 当 HTTP 运行在不支持 QUIC DATAGRAM 帧的传输之上时, 这尤其有用.
Datagram Capsule {
Type (i) = 0x00,
Length (i),
HTTP Datagram Payload (..),
}
图 4: DATAGRAM Capsule 格式
HTTP Datagram Payload: 数据报的负载, 其语义由使用 HTTP Datagrams 的扩展定义. 注意, 此字段可以为空.
使用 DATAGRAM Capsule 发送的 HTTP Datagrams 与在 QUIC DATAGRAM 帧中发送的 HTTP Datagrams 具有相同语义. 尤其是, 第 2.1 节中关于何时允许发送 HTTP Datagram 以及如何处理它们的限制, 同样适用于使用 DATAGRAM Capsule 发送和接收的 HTTP Datagrams.
中间节点可以在转发 HTTP Datagrams 时重新编码它们. 换言之, 中间节点可以发送 DATAGRAM Capsule 来转发在 QUIC DATAGRAM 帧中收到的 HTTP Datagram, 反之亦然. 除非中间节点已经识别出对应请求流正在使用 Capsule Protocol, 否则禁止执行这种重新编码; 见第 3.2 节.
注意, 发送在流上的 DATAGRAM Capsules 会按顺序可靠交付, 但中间节点在转发消息时可以把 DATAGRAM Capsules 重新编码为 QUIC DATAGRAM 帧, 这可能导致丢失或重排序.
如果中间节点在 QUIC DATAGRAM 帧中收到 HTTP Datagram, 并且要在支持 QUIC DATAGRAM 帧的连接上转发它, 则中间节点不应将该 HTTP Datagram 转换为 DATAGRAM Capsule. 如果该 HTTP Datagram 太大, 无法放入 DATAGRAM 帧 (例如, 因为该 QUIC 连接的路径 MTU (PMTU) 太低, 或该连接上通告的最大 UDP 负载大小太低), 中间节点应当丢弃该 HTTP Datagram, 而不是将其转换为 DATAGRAM Capsule. 这会保留 Datagram Packetization Layer PMTU Discovery (DPLPMTUD) 等方法所依赖的端到端不可靠特性 [DPLPMTUD]. 将 QUIC DATAGRAM 帧转换为 DATAGRAM Capsules 的中间节点会允许 HTTP Datagrams 任意大而不遭受任何丢失. 这可能错误呈现真实路径属性, 从而破坏 DPLPMTUD 等方法.
虽然 DATAGRAM Capsules 理论上可以承载长度为 2^62-1 的负载, 但大多数使用 HTTP Datagrams 的 HTTP 扩展会对实际可行的数据报负载大小有自己的限制. 实现解析 DATAGRAM Capsules 时应当考虑这些限制. 如果传入 DATAGRAM Capsule 的长度已知大到不可用, 实现应当丢弃该 Capsule, 而不是把其内容缓冲进内存.
由于 QUIC DATAGRAM 帧必须能放入一个 QUIC 数据包, 把 DATAGRAM Capsules 重新编码为 QUIC DATAGRAM 帧的实现可能会倾向于先在流中累积整个 Capsule, 再重新编码. 这种做法应当避免, 因为它可能造成流量控制问题; 见第 3.2 节.
注意, HTTP 扩展可以在不使用 Capsule Protocol 的情况下使用 HTTP Datagrams. 例如, 如果某个使用 HTTP Datagrams 的 HTTP 扩展只定义在支持 QUIC DATAGRAM 帧的传输之上, 它可能不需要流编码. 此外, HTTP 扩展也可以结合自己的数据流协议使用 HTTP Datagrams. 但是, 希望使用 HTTP Datagrams 的新 HTTP 扩展应当使用 Capsule Protocol, 因为不这样做会使该 HTTP 扩展更难支持 HTTP/3 以外的 HTTP 版本, 并会阻止其与只支持 Capsule Protocol 的中间节点互操作.