跳到主要内容

RFC 9114 - HTTP/3

  • 状态: Proposed Standard
  • 发布日期: June 2022
  • Stream: IETF
  • 勘误: 无勘误

摘要 (Abstract)​

QUIC 传输协议具有在 HTTP 传输中需要的多个功能, 例如流多路复用、每流流量控制和低延迟连接建立.本文档描述了 HTTP 语义在 QUIC 上的映射.本文档还标识了 QUIC 包含的 HTTP/2 功能, 并描述了如何将 HTTP/2 扩展移植到 HTTP/3.


本备忘录的状态 (Status of This Memo)​

这是一份互联网标准跟踪文档.

本文档是互联网工程任务组 (IETF) 的产品.它代表了 IETF 社区的共识.它已经过公众审查, 并已获得互联网工程指导小组 (IESG) 的发布批准.有关互联网标准的更多信息, 请参见 RFC 7841 的第 2 节.

有关本文档当前状态、任何勘误以及如何提供反馈的信息, 可以从以下网址获取: https://www.rfc-editor.org/info/rfc9114


目录 (Table of Contents)​

主要章节 (Main Chapters)​

附录 (Appendices)​



关键特性 (Key Features)​

HTTP/3 的主要特性包括:

  • 基于 QUIC: 使用 QUIC 作为传输协议, 提供流多路复用和低延迟连接建立
  • QPACK 压缩: 使用 QPACK 替代 HPACK 进行头部压缩, 支持无序传输
  • 流管理: 每个请求-响应对使用独立的 QUIC 流
  • 服务器推送: 支持服务器主动推送资源
  • 错误处理: 定义了完整的流错误和连接错误处理机制
  • 扩展性: 提供灵活的扩展机制


1. 简介 (Introduction)​

HTTP 语义 ([HTTP]) 在互联网上被广泛用于各种服务.这些语义最常与 HTTP/1.1 和 HTTP/2 一起使用.HTTP/1.1 已在各种传输层和会话层上使用, 而 HTTP/2 主要与基于 TCP 的 TLS 一起使用.HTTP/3 在新的传输协议 QUIC 上支持相同的语义.

1.1. HTTP 的早期版本 (Prior Versions of HTTP)​

HTTP/1.1 ([HTTP/1.1]) 使用空格分隔的文本字段来传输 HTTP 消息.虽然这些交换是人类可读的, 但使用空格进行消息格式化会导致解析复杂性和对变体行为的过度容忍.

由于 HTTP/1.1 不包含多路复用层, 通常使用多个 TCP 连接来并行处理请求.然而, 这对拥塞控制和网络效率有负面影响, 因为 TCP 不会在多个连接之间共享拥塞控制.

HTTP/2 ([HTTP/2]) 引入了二进制帧和多路复用层, 以在不修改传输层的情况下改善延迟.然而, 由于 HTTP/2 多路复用的并行特性对 TCP 的丢失恢复机制不可见, 丢失或重新排序的数据包会导致所有活动事务停滞, 无论该事务是否直接受到丢失数据包的影响.

1.2. 委托给 QUIC (Delegation to QUIC)​

QUIC 传输协议结合了流多路复用和每流流量控制, 类似于 HTTP/2 帧层提供的功能.通过在流级别提供可靠性和在整个连接上进行拥塞控制, QUIC 能够相比 TCP 映射改善 HTTP 的性能.QUIC 还在传输层集成了 TLS 1.3 ([TLS]), 提供与在 TCP 上运行 TLS 相当的机密性和完整性, 同时具有 TCP Fast Open ([TFO]) 改进的连接建立延迟.

本文档定义了 HTTP/3: 一种在 QUIC 传输协议上映射 HTTP 语义的方式, 在很大程度上借鉴了 HTTP/2 的设计.HTTP/3 依赖 QUIC 提供数据的机密性和完整性保护、对等身份验证以及可靠的按序每流传输.在将流生命周期和流量控制问题委托给 QUIC 的同时, 在每个流上使用类似于 HTTP/2 帧的二进制帧.一些 HTTP/2 功能被 QUIC 包含, 而其他功能则在 QUIC 之上实现.

QUIC 在 [QUIC-TRANSPORT] 中描述.有关 HTTP/2 的完整描述, 请参见 [HTTP/2].



2. HTTP/3 协议概览 (HTTP/3 Protocol Overview)​

HTTP/3 使用 QUIC 传输协议和类似于 HTTP/2 的内部帧层为 HTTP 语义提供传输.

一旦客户端知道某个端点存在 HTTP/3 服务器, 它就会打开一个 QUIC 连接.QUIC 提供协议协商、基于流的多路复用和流量控制.HTTP/3 端点的发现在第 3.1 节中描述.

在每个流中, HTTP/3 通信的基本单元是帧 (frame, 第 7.2 节).每种帧类型服务于不同的目的.例如, HEADERS 和 DATA 帧构成 HTTP 请求和响应的基础 (第 4.1 节).适用于整个连接的帧在专用的控制流上传送.

请求的多路复用使用 QUIC 流抽象执行, 在 [QUIC-TRANSPORT] 的第 2 节中描述.每个请求-响应对占用一个 QUIC 流.流之间相互独立, 因此一个被阻塞或遭受数据包丢失的流不会阻止其他流的进展.

服务器推送 (Server push) 是 HTTP/2 ([HTTP/2]) 中引入的一种交互模式, 允许服务器在客户端发出指定请求之前主动向客户端推送请求-响应交换.这以网络使用量换取潜在的延迟收益.几个 HTTP/3 帧用于管理服务器推送, 如 PUSH_PROMISE, MAX_PUSH_ID 和 CANCEL_PUSH.

与 HTTP/2 一样, 请求和响应字段在传输时被压缩.由于 HPACK ([HPACK]) 依赖于压缩字段段的有序传输 (QUIC 不提供这种保证), HTTP/3 用 QPACK ([QPACK]) 替换了 HPACK.QPACK 使用单独的单向流来修改和跟踪字段表状态, 而编码的字段段引用表的状态而不修改它.

2.1. 文档组织 (Document Organization)​

以下各节提供了 HTTP/3 连接生命周期的详细概述:

  • "连接建立与管理" (Connection Setup and Management, 第 3 节) 涵盖如何发现 HTTP/3 端点以及如何建立 HTTP/3 连接.

  • "在 HTTP/3 中表达 HTTP 语义" (Expressing HTTP Semantics in HTTP/3, 第 4 节) 描述如何使用帧来表达 HTTP 语义.

  • "连接关闭" (Connection Closure, 第 5 节) 描述 HTTP/3 连接如何终止, 无论是优雅终止还是突然终止.

线路协议 (wire protocol) 的细节以及与传输层的交互在后续各节中描述:

  • "流映射与使用" (Stream Mapping and Usage, 第 6 节) 描述 QUIC 流的使用方式.

  • "HTTP 帧层" (HTTP Framing Layer, 第 7 节) 描述大多数流上使用的帧.

  • "错误处理" (Error Handling, 第 8 节) 描述如何处理和表达错误条件, 无论是在特定流上还是针对整个连接.

最后各节提供了额外的资源:

  • "HTTP/3 的扩展" (Extensions to HTTP/3, 第 9 节) 描述如何在未来文档中添加新功能.

  • 附录 A 中可以找到 HTTP/2 和 HTTP/3 之间更详细的比较.

2.2. 约定和术语 (Conventions and Terminology)​

本文档中的关键词 "MUST" (必须), "MUST NOT" (禁止), "REQUIRED" (必需), "SHALL" (应), "SHALL NOT" (不应), "SHOULD" (应当), "SHOULD NOT" (不应当), "RECOMMENDED" (推荐), "NOT RECOMMENDED" (不推荐), "MAY" (可以) 和 "OPTIONAL" (可选) 应按照 BCP 14 [RFC2119] [RFC8174] 中的描述进行解释, 当且仅当它们以全大写形式出现时, 如此处所示.

本文档使用 [QUIC-TRANSPORT] 中的可变长度整数编码.

使用以下术语:

abort (中止): 流或连接的突然终止, 可能由于错误条件.

client (客户端): 发起 HTTP/3 连接的端点.客户端发送 HTTP 请求并接收 HTTP 响应.

connection (连接): 使用 QUIC 作为传输协议的两个端点之间的传输层连接.

connection error (连接错误): 影响整个 HTTP/3 连接的错误.

endpoint (端点): 连接的客户端或服务器.

frame (帧): HTTP/3 中流上通信的最小单元, 由标头和根据帧类型构造的可变长度字节序列组成.

本文档和 [QUIC-TRANSPORT] 中都存在称为 "帧" 的协议元素.当引用来自 [QUIC-TRANSPORT] 的帧时, 帧名称将以 "QUIC" 为前缀.例如, "QUIC CONNECTION_CLOSE 帧".没有此前缀的引用指的是第 7.2 节中定义的帧.

HTTP/3 connection (HTTP/3 连接): 协商的应用层协议为 HTTP/3 的 QUIC 连接.

peer (对等方): 一个端点.在讨论特定端点时, "peer" 指的是讨论主体远程的端点.

receiver (接收方): 正在接收帧的端点.

sender (发送方): 正在传输帧的端点.

server (服务器): 接受 HTTP/3 连接的端点.服务器接收 HTTP 请求并发送 HTTP 响应.

stream (流): QUIC 传输提供的双向或单向字节流.HTTP/3 连接中的所有流都可以被视为 "HTTP/3 流", 但在 HTTP/3 中定义了多种流类型.

stream error (流错误): 单个流上的应用层错误.

术语 "content (内容)" 在 [HTTP] 的第 6.4 节中定义.

最后, 术语 "resource (资源)", "message (消息)", "user agent (用户代理)", "origin server (源服务器)", "gateway (网关)", "intermediary (中间方)", "proxy (代理)" 和 "tunnel (隧道)" 在 [HTTP] 的第 3 节中定义.

本文档中的数据包图使用 [QUIC-TRANSPORT] 第 1.3 节中定义的格式来说明字段的顺序和大小.



3. 连接建立与管理 (Connection Setup and Management)​

3.1. 发现 HTTP/3 端点 (Discovering an HTTP/3 Endpoint)​

HTTP 依赖于权威响应 (authoritative response) 的概念: 在响应消息产生时, 根据目标资源的状态, 由目标 URI 中标识的源服务器 (或在其指导下) 确定为该请求最合适的响应.[HTTP] 的第 4.3 节讨论了如何定位 HTTP URI 的权威服务器.

"https" 方案将权威与拥有客户端认为对 URI 的权威组件标识的主机可信的证书相关联.在 TLS 握手中收到服务器证书后, 客户端必须使用 [HTTP] 第 4.3.4 节中描述的过程验证该证书是否与 URI 的源服务器可接受匹配.如果证书无法针对 URI 的源服务器进行验证, 客户端禁止将该服务器视为该源的权威服务器.

客户端可以尝试通过将主机标识符解析为 IP 地址、在指定端口上与该地址建立 QUIC 连接 (包括如上所述验证服务器证书), 以及通过该安全连接向服务器发送针对 URI 的 HTTP/3 请求消息来访问具有 "https" URI 的资源.除非使用其他机制来选择 HTTP/3, 否则在 TLS 握手期间在应用层协议协商 (ALPN; 参见 [RFC7301]) 扩展中使用令牌 "h3".

连接问题 (例如, 阻止 UDP) 可能导致无法建立 QUIC 连接; 在这种情况下, 客户端应当尝试使用基于 TCP 的 HTTP 版本.

服务器可以在任何 UDP 端口上提供 HTTP/3 服务; 替代服务广告总是包含显式端口, 并且 URI 包含显式端口或与方案关联的默认端口.

3.1.1. HTTP 替代服务 (HTTP Alternative Services)​

HTTP 源可以通过 Alt-Svc HTTP 响应头字段或 HTTP/2 ALTSVC 帧 ([ALTSVC]) 使用 "h3" ALPN 令牌来广告等效的 HTTP/3 端点的可用性.

例如, 源可以在 HTTP 响应中通过包含以下头字段来指示 HTTP/3 在同一主机名的 UDP 端口 50781 上可用:

Alt-Svc: h3=":50781"

在收到指示 HTTP/3 支持的 Alt-Svc 记录后, 客户端可以尝试与指定的主机和端口建立 QUIC 连接; 如果此连接成功, 客户端可以使用本文档中描述的映射发送 HTTP 请求.

3.1.2. 其他方案 (Other Schemes)​

尽管 HTTP 独立于传输协议, 但 "http" 方案将权威与在权威组件中标识的任何主机的指定端口上接收 TCP 连接的能力相关联.由于 HTTP/3 不使用 TCP, 因此 HTTP/3 不能用于直接访问由 "http" URI 标识的资源的权威服务器.但是, 诸如 [ALTSVC] 之类的协议扩展允许权威服务器识别也是权威的其他服务, 这些服务可能可以通过 HTTP/3 访问.

在对方案不是 "https" 的源发出请求之前, 客户端必须确保服务器愿意为该方案提供服务.对于方案为 "http" 的源, [RFC8164] 中描述了一种实验性方法来完成此操作.未来可能会为各种方案定义其他机制.

3.2. 连接建立 (Connection Establishment)​

HTTP/3 依赖于 QUIC 版本 1 作为底层传输.未来的规范可能会定义将其他 QUIC 传输版本与 HTTP/3 一起使用.

QUIC 版本 1 使用 TLS 版本 1.3 或更高版本作为其握手协议.HTTP/3 客户端必须支持一种机制, 以便在 TLS 握手期间向服务器指示目标主机.如果服务器由域名 ([DNS-TERMS]) 标识, 则客户端必须发送服务器名称指示 (SNI; [RFC6066]) TLS 扩展, 除非使用指示目标主机的替代机制.

QUIC 连接按照 [QUIC-TRANSPORT] 中的描述建立.在连接建立期间, 通过在 TLS 握手中选择 ALPN 令牌 "h3" 来指示 HTTP/3 支持.可以在同一握手中提供对其他应用层协议的支持.

虽然与核心 QUIC 协议相关的连接级选项在初始加密握手中设置, 但特定于 HTTP/3 的设置在 SETTINGS 帧中传送.在 QUIC 连接建立后, 每个端点必须在其各自的 HTTP 控制流的初始帧上发送 SETTINGS 帧.

3.3. 连接重用 (Connection Reuse)​

HTTP/3 连接在多个请求之间是持久的.为了获得最佳性能, 预计客户端在确定不再需要与服务器进行进一步通信之前 (例如, 当用户离开特定网页时) 或直到服务器关闭连接之前, 不会关闭连接.

一旦存在到服务器端点的连接, 此连接可以被重用于具有多个不同 URI 权威组件的请求.要将现有连接用于新源, 客户端必须使用 [HTTP] 第 4.3.4 节中描述的过程验证服务器为新源服务器提供的证书.这意味着客户端需要保留服务器证书以及验证该证书所需的任何其他信息; 不这样做的客户端将无法为其他源重用连接.

如果证书因任何原因对于新源不可接受, 则禁止重用该连接, 并且应当为新源建立新连接.如果证书无法验证的原因可能适用于已与该连接关联的其他源, 则客户端应当重新验证这些源的服务器证书.例如, 如果证书验证失败是因为证书已过期或被撤销, 这可能用于使该证书用于建立权威的所有其他源失效.

客户端不应当向给定的 IP 地址和 UDP 端口打开多个 HTTP/3 连接, 其中 IP 地址和端口可能来自 URI, 选定的替代服务 ([ALTSVC]), 配置的代理, 或其中任何一个的名称解析.客户端可以使用不同的传输或 TLS 配置向同一 IP 地址和 UDP 端口打开多个 HTTP/3 连接, 但应当避免创建具有相同配置的多个连接.

鼓励服务器尽可能长时间地保持打开的 HTTP/3 连接, 但如有必要, 允许终止空闲连接.当任一端点选择关闭 HTTP/3 连接时, 终止端点应当首先发送 GOAWAY 帧 (第 5.2 节), 以便两个端点都可以可靠地确定先前发送的帧是否已被处理, 并优雅地完成或终止任何必要的剩余任务.

不希望客户端重用特定源的 HTTP/3 连接的服务器可以通过在响应请求时发送 421 (Misdirected Request) 状态码来指示它对请求不具有权威性; 参见 [HTTP] 的第 7.4 节.



4. 在 HTTP/3 中表达 HTTP 语义 (Expressing HTTP Semantics in HTTP/3)​

4.1. HTTP 消息帧 (HTTP Message Framing)​

客户端在请求流 (request stream) 上发送 HTTP 请求, 该流是客户端发起的双向 QUIC 流; 参见第 6.1 节.客户端必须在给定流上仅发送单个请求.服务器在与请求相同的流上发送零个或多个中间 HTTP 响应, 然后是单个最终 HTTP 响应, 详见下文.有关中间和最终 HTTP 响应的描述, 请参见 [HTTP] 的第 15 节.

推送的响应在服务器发起的单向 QUIC 流上发送; 参见第 6.2.2 节.服务器以与标准响应相同的方式发送零个或多个中间 HTTP 响应, 然后是单个最终 HTTP 响应.推送在第 4.6 节中有更详细的描述.

在给定流上, 接收到多个请求或在最终 HTTP 响应之后接收到额外的 HTTP 响应必须被视为格式错误 (malformed).

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

  1. 头部段 (header section), 包括消息控制数据, 作为单个 HEADERS 帧发送,

  2. 可选地, 内容 (content), 如果存在, 作为一系列 DATA 帧发送, 以及

  3. 可选地, 尾部段 (trailer section), 如果存在, 作为单个 HEADERS 帧发送.

头部和尾部段在 [HTTP] 的第 6.3 节和第 6.5 节中描述; 内容在 [HTTP] 的第 6.4 节中描述.

接收到无效的帧序列必须被视为类型为 H3_FRAME_UNEXPECTED 的连接错误.特别是, 在任何 HEADERS 帧之前的 DATA 帧, 或尾部 HEADERS 帧之后的 HEADERS 或 DATA 帧, 被视为无效.其他帧类型, 特别是未知的帧类型, 可能会受到它们自己的规则约束; 参见第 9 节.

服务器可以在响应消息的帧之前, 之后或与之交错发送一个或多个 PUSH_PROMISE 帧.这些 PUSH_PROMISE 帧不是响应的一部分; 有关更多详细信息, 请参见第 4.6 节.推送流上不允许 PUSH_PROMISE 帧; 包含 PUSH_PROMISE 帧的推送响应必须被视为类型为 H3_FRAME_UNEXPECTED 的连接错误.

未知类型的帧 (第 9 节), 包括保留帧 (第 7.2.8 节), 可以在本节描述的其他帧之前, 之后或与之交错发送到请求或推送流上.

HEADERS 和 PUSH_PROMISE 帧可能引用对 QPACK 动态表的更新.虽然这些更新不直接是消息交换的一部分, 但它们必须在消息被消费之前接收和处理.有关更多详细信息, 请参见第 4.2 节.

HTTP/3 不定义传输编码 (transfer codings, 参见 [HTTP/1.1] 的第 7 节); Transfer-Encoding 头字段禁止使用.

当且仅当一个或多个中间响应 (1xx; 参见 [HTTP] 的第 15.2 节) 在对同一请求的最终响应之前时, 响应可以由多个消息组成.中间响应不包含内容或尾部段.

HTTP 请求/响应交换完全消耗一个客户端发起的双向 QUIC 流.发送请求后, 客户端必须关闭流以进行发送.除非使用 CONNECT 方法 (参见第 4.4 节), 否则客户端禁止使流关闭依赖于接收对其请求的响应.发送最终响应后, 服务器必须关闭流以进行发送.此时, QUIC 流完全关闭.

当流关闭时, 这表示最终 HTTP 消息的结束.由于某些消息很大或无界, 端点应当在收到足够的消息以取得进展后立即开始处理部分 HTTP 消息.如果客户端发起的流在没有足够的 HTTP 消息来提供完整响应的情况下终止, 服务器应当使用错误码 H3_REQUEST_INCOMPLETE 中止其响应流.

如果响应不依赖于尚未发送和接收的请求的任何部分, 服务器可以在客户端发送整个请求之前发送完整响应.当服务器不需要接收请求的其余部分时, 它可以中止读取请求流, 发送完整响应, 并干净地关闭流的发送部分.在请求客户端停止在请求流上发送时, 应当使用错误码 H3_NO_ERROR.客户端禁止因其请求被突然终止而丢弃完整响应, 尽管客户端出于其他原因始终可以自行决定丢弃响应.如果服务器发送部分或完整响应但不中止读取请求, 客户端应当继续发送请求内容并正常关闭流.

4.1.1. 请求取消和拒绝 (Request Cancellation and Rejection)​

一旦打开了请求流, 任一端点都可以取消请求.如果响应不再引起兴趣, 客户端取消请求; 如果服务器无法或选择不响应, 服务器取消请求.在可能的情况下, 推荐服务器发送带有适当状态码的 HTTP 响应, 而不是取消已开始处理的请求.

实现应当通过突然终止流中仍然打开的任何方向来取消请求.为此, 实现重置流的发送部分并中止读取流的接收部分; 参见 [QUIC-TRANSPORT] 的第 2.4 节.

当服务器在不执行任何应用处理的情况下取消请求时, 该请求被视为 "拒绝 (rejected)".服务器应当使用错误码 H3_REQUEST_REJECTED 中止其响应流.在此上下文中, "处理" 意味着流中的某些数据被传递到某些可能因此采取某些操作的更高层软件.客户端可以将服务器拒绝的请求视为从未发送过, 从而允许稍后重试.

服务器禁止对部分或完全处理的请求使用 H3_REQUEST_REJECTED 错误码.当服务器在部分处理后放弃响应时, 它应当使用错误码 H3_REQUEST_CANCELLED 中止其响应流.

客户端应当使用错误码 H3_REQUEST_CANCELLED 取消请求.在收到此错误码后, 如果没有执行处理, 服务器可以使用错误码 H3_REQUEST_REJECTED 突然终止响应.客户端禁止使用 H3_REQUEST_REJECTED 错误码, 除非服务器已请求使用此错误码关闭请求流.

如果流在接收完整响应后被取消, 客户端可以忽略取消并使用响应.但是, 如果流在接收部分响应后被取消, 则不应当使用该响应.只有幂等操作 (idempotent actions) 如 GET, PUT 或 DELETE 可以安全重试; 客户端不应当自动重试具有非幂等方法的请求, 除非它有某种方式知道请求语义独立于方法是幂等的, 或有某种方式检测原始请求从未应用.有关更多详细信息, 请参见 [HTTP] 的第 9.2.2 节.

4.1.2. 格式错误的请求和响应 (Malformed Requests and Responses)​

格式错误的请求或响应是一个原本有效的帧序列, 但由于以下原因而无效:

  • 存在禁止的字段或伪头字段,

  • 缺少强制伪头字段,

  • 伪头字段的值无效,

  • 字段后出现伪头字段,

  • HTTP 消息的序列无效,

  • 包含大写字段名称, 或

  • 字段名称或值中包含无效字符.

当包含 Content-Length 头字段 ([HTTP] 的第 8.6 节) 时定义为具有内容的请求或响应, 如果 Content-Length 头字段的值不等于接收到的 DATA 帧长度之和, 则格式错误.定义为永远没有内容的响应, 即使存在 Content-Length, 也可以具有非零 Content-Length 头字段, 即使 DATA 帧中不包含内容.

处理 HTTP 请求或响应的中间方 (即, 任何不作为隧道的中间方) 禁止转发格式错误的请求或响应.检测到的格式错误的请求或响应必须被视为类型为 H3_MESSAGE_ERROR 的流错误.

对于格式错误的请求, 服务器可以在关闭或重置流之前发送指示错误的 HTTP 响应.客户端禁止接受格式错误的响应.请注意, 这些要求旨在防止针对 HTTP 的几种类型的常见攻击; 它们是故意严格的, 因为宽容可能会将实现暴露于这些漏洞.

4.2. HTTP 字段 (HTTP Fields)​

HTTP 消息将元数据作为一系列键值对 (称为 "HTTP 字段") 携带; 参见 [HTTP] 的第 6.3 节和第 6.5 节.有关已注册的 HTTP 字段列表, 请参见在 https://www.iana.org/assignments/http-fields/ 维护的 "超文本传输协议 (HTTP) 字段名称注册表".与 HTTP/2 一样, HTTP/3 对字段名称中字符的使用, Connection 头字段和伪头字段有额外的考虑.

字段名称是包含 ASCII 字符子集的字符串.[HTTP] 的第 5.1 节更详细地讨论了 HTTP 字段名称和值的属性.字段名称中的字符必须在编码之前转换为小写.包含字段名称中大写字符的请求或响应必须被视为格式错误.

HTTP/3 不使用 Connection 头字段来指示特定于连接的字段; 在此协议中, 特定于连接的元数据由其他方式传送.端点禁止生成包含特定于连接的字段的 HTTP/3 字段段; 任何包含特定于连接的字段的消息必须被视为格式错误.

唯一的例外是 TE 头字段, 它可以出现在 HTTP/3 请求头中; 当它出现时, 它禁止包含除 "trailers" 之外的任何值.

将 HTTP/1.x 消息转换为 HTTP/3 的中间方必须删除特定于连接的头字段, 如 [HTTP] 的第 7.6.1 节中所讨论的, 否则其消息将被其他 HTTP/3 端点视为格式错误.

4.2.1. 字段压缩 (Field Compression)​

[QPACK] 描述了 HPACK 的一个变体, 它使编码器能够控制压缩可能导致多少队头阻塞 (head-of-line blocking).这允许编码器平衡压缩效率与延迟.HTTP/3 使用 QPACK 压缩头部和尾部段, 包括头部段中存在的控制数据.

为了实现更好的压缩效率, Cookie 头字段 ([COOKIES]) 可以在压缩之前拆分为单独的字段行, 每个字段行包含一个或多个 cookie 对 (cookie-pairs).如果解压缩的字段段包含多个 cookie 字段行, 则在传递到 HTTP/2 或 HTTP/3 之外的上下文 (例如 HTTP/1.1 连接或通用 HTTP 服务器应用) 之前, 必须使用两字节分隔符 "; " (ASCII 0x3b, 0x20) 将它们连接成单个字节串.

4.2.2. 头部大小约束 (Header Size Constraints)​

HTTP/3 实现可以对它将在单个 HTTP 消息上接受的消息头的最大大小施加限制.接收到大于其愿意处理的头部段的服务器可以发送 HTTP 431 (Request Header Fields Too Large) 状态码 ([RFC6585]).客户端可以丢弃无法处理的响应.字段列表的大小是根据字段的未压缩大小计算的, 包括名称和值的长度 (以字节为单位), 每个字段增加 32 字节的开销.

如果实现希望将此限制通知其对等方, 它可以作为 SETTINGS_MAX_FIELD_SECTION_SIZE 参数中的字节数传送.收到此参数的实现不应当发送超过指示大小的 HTTP 消息头, 因为对等方可能会拒绝处理它.但是, HTTP 消息可以在到达源服务器之前经过一个或多个中间方; 参见 [HTTP] 的第 3.7 节.由于此限制由处理消息的每个实现单独应用, 因此不能保证低于此限制的消息会被接受.

4.3. HTTP 控制数据 (HTTP Control Data)​

与 HTTP/2 一样, HTTP/3 使用一系列伪头字段, 其中字段名称以 : 字符 (ASCII 0x3a) 开头.这些伪头字段传送消息控制数据; 参见 [HTTP] 的第 6.2 节.

伪头字段不是 HTTP 字段.端点禁止生成本文档中定义以外的伪头字段.但是, 扩展可以协商修改此限制; 参见第 9 节.

伪头字段仅在定义它们的上下文中有效.为请求定义的伪头字段禁止出现在响应中; 为响应定义的伪头字段禁止出现在请求中.伪头字段禁止出现在尾部段中.端点必须将包含未定义或无效伪头字段的请求或响应视为格式错误.

所有伪头字段必须出现在头部段中常规头字段之前.任何在常规头字段之后的头部段中包含伪头字段的请求或响应必须被视为格式错误.

4.3.1. 请求伪头字段 (Request Pseudo-Header Fields)​

为请求定义了以下伪头字段:

":method": 包含 HTTP 方法 ([HTTP] 的第 9 节)

":scheme": 包含目标 URI 的方案部分 ([URI] 的第 3.1 节).

:scheme 伪头字段不限于方案为 "http" 和 "https" 的 URI.代理或网关可以转换非 HTTP 方案的请求, 从而能够使用 HTTP 与非 HTTP 服务交互.

有关使用 "https" 以外的方案的指导, 请参见第 3.1.2 节.

":authority": 包含目标 URI 的权威部分 ([URI] 的第 3.2 节).对于方案为 "http" 或 "https" 的 URI, 权威禁止包含已弃用的 userinfo 子组件.

为确保可以准确地再现 HTTP/1.1 请求行, 当从具有方法特定形式的请求目标的 HTTP/1.1 请求进行转换时, 必须省略此伪头字段; 参见 [HTTP] 的第 7.1 节.直接生成 HTTP/3 请求的客户端应当使用 :authority 伪头字段而不是 Host 头字段.将 HTTP/3 请求转换为 HTTP/1.1 的中间方必须通过复制 :authority 伪头字段的值来创建 Host 字段 (如果请求中不存在 Host 字段).

":path": 包含目标 URI 的路径和查询部分 ("path-absolute" 产生式以及可选的 ? 字符 (ASCII 0x3f) 后跟 "query" 产生式; 参见 [URI] 的第 3.3 节和第 3.4 节.

对于 "http" 或 "https" URI, 此伪头字段禁止为空; 不包含路径组件的 "http" 或 "https" URI 必须包含值 / (ASCII 0x2f).不包含路径组件的 OPTIONS 请求对 :path 伪头字段包含值 * (ASCII 0x2a); 参见 [HTTP] 的第 7.1 节.

所有 HTTP/3 请求必须恰好包含 :method, :scheme 和 :path 伪头字段的一个值, 除非请求是 CONNECT 请求; 参见第 4.4 节.

如果 :scheme 伪头字段标识具有强制权威组件的方案 (包括 "http" 和 "https"), 则请求必须包含 :authority 伪头字段或 Host 头字段.如果这些字段存在, 它们禁止为空.如果两个字段都存在, 它们必须包含相同的值.如果方案没有强制权威组件并且在请求目标中未提供, 则请求禁止包含 :authority 伪头字段或 Host 头字段.

省略强制伪头字段或包含这些伪头字段的无效值的 HTTP 请求格式错误.

HTTP/3 未定义携带 HTTP/1.1 请求行中包含的版本标识符的方式.HTTP/3 请求隐式具有协议版本 "3.0".

4.3.2. 响应伪头字段 (Response Pseudo-Header Fields)​

对于响应, 定义了单个 ":status" 伪头字段, 它携带 HTTP 状态码; 参见 [HTTP] 的第 15 节.此伪头字段必须包含在所有响应中; 否则, 响应格式错误 (参见第 4.1.2 节).

HTTP/3 未定义携带 HTTP/1.1 状态行中包含的版本或原因短语的方式.HTTP/3 响应隐式具有协议版本 "3.0".

4.4. CONNECT 方法 (The CONNECT Method)​

CONNECT 方法请求接收方建立到由请求目标标识的目标源服务器的隧道; 参见 [HTTP] 的第 9.3.6 节.它主要与 HTTP 代理一起使用, 以便与源服务器建立 TLS 会话, 以便与 "https" 资源交互.

在 HTTP/1.x 中, CONNECT 用于将整个 HTTP 连接转换为到远程主机的隧道.在 HTTP/2 和 HTTP/3 中, CONNECT 方法用于在单个流上建立隧道.

CONNECT 请求必须按如下方式构造:

  • :method 伪头字段设置为 "CONNECT"

  • :scheme 和 :path 伪头字段被省略

  • :authority 伪头字段包含要连接的主机和端口 (等效于 CONNECT 请求的请求目标的 authority-form; 参见 [HTTP] 的第 7.1 节).

请求流在请求结束时保持打开状态以携带要传输的数据.不符合这些限制的 CONNECT 请求格式错误.

支持 CONNECT 的代理与在 :authority 伪头字段中标识的服务器建立 TCP 连接 ([RFC0793]).一旦成功建立此连接, 代理就会向客户端发送包含 2xx 系列状态码的 HEADERS 帧, 如 [HTTP] 的第 15.3 节中所定义.

流上的所有 DATA 帧对应于在 TCP 连接上发送或接收的数据.客户端发送的任何 DATA 帧的有效负载由代理传输到 TCP 服务器; 从 TCP 服务器接收的数据由代理打包成 DATA 帧.请注意, 不能保证 TCP 段的大小和数量可预测地映射到 HTTP DATA 或 QUIC STREAM 帧的大小和数量.

一旦 CONNECT 方法完成, 流上仅允许发送 DATA 帧.如果扩展定义明确允许, 可以使用扩展帧.接收到任何其他已知帧类型必须被视为类型为 H3_FRAME_UNEXPECTED 的连接错误.

TCP 连接可以由任一对等方关闭.当客户端结束请求流时 (即, 代理处的接收流进入 "Data Recvd" 状态), 代理将在其到 TCP 服务器的连接上设置 FIN 位.当代理接收到设置了 FIN 位的数据包时, 它将关闭它发送给客户端的发送流.在单个方向上保持半关闭的 TCP 连接不是无效的, 但通常被服务器处理得很差, 因此客户端不应当在仍期望从 CONNECT 目标接收数据时关闭流以进行发送.

TCP 连接错误通过突然终止流来发出信号.代理将 TCP 连接中的任何错误 (包括接收设置了 RST 位的 TCP 段) 视为类型为 H3_CONNECT_ERROR 的流错误.

相应地, 如果代理检测到流或 QUIC 连接有错误, 它必须关闭 TCP 连接.如果代理检测到客户端已重置流或中止从流读取, 它必须关闭 TCP 连接.如果流被客户端重置或中止读取, 代理应当在另一个方向上执行相同的操作, 以确保流的两个方向都被取消.在所有这些情况下, 如果底层 TCP 实现允许, 代理应当发送设置了 RST 位的 TCP 段.

由于 CONNECT 创建到任意服务器的隧道, 支持 CONNECT 的代理应当将其使用限制在一组已知端口或安全请求目标列表中; 有关更多详细信息, 请参见 [HTTP] 的第 9.3.6 节.

4.5. HTTP 升级 (HTTP Upgrade)​

HTTP/3 不支持 HTTP 升级机制 ([HTTP] 的第 7.8 节) 或 101 (Switching Protocols) 信息状态码 ([HTTP] 的第 15.2.2 节).

4.6. 服务器推送 (Server Push)​

服务器推送是一种交互模式, 允许服务器在客户端发出请求之前主动向客户端推送请求-响应交换.客户端可以通过在 SETTINGS 帧中将 SETTINGS_ENABLE_PUSH 设置为 0 来禁用服务器推送.服务器禁止向已将 SETTINGS_ENABLE_PUSH 设置为 0 的客户端发送推送; 违反此规定的服务器行为必须被视为类型为 H3_SETTINGS_ERROR 的连接错误.

与 HTTP/2 一样, 服务器通过在客户端发起的请求流上发送 PUSH_PROMISE 帧 (第 7.2.5 节) 来发起推送.推送 ID 用于标识服务器推送 (参见第 4.6.1 节).推送 ID 在 PUSH_PROMISE 帧中携带, 该帧还包括归属于服务器生成的请求的请求头段, 如 [HTTP] 的第 15 节中所述.

服务器从它发起的推送流 (第 6.2.2 节) 发送推送的响应.推送响应的传递与对常规请求的响应相同.推送响应的响应头段在 HEADERS 帧中携带, 如第 7.2.4 节所述.服务器可以通过在推送流上使用推送 ID 发送 CANCEL_PUSH 帧来取消承诺的推送.

客户端使用 MAX_PUSH_ID 帧 (第 7.2.7 节) 控制服务器可以承诺的推送数量.服务器禁止发送推送 ID 大于客户端为连接提供的最大推送 ID 的 PUSH_PROMISE 帧或 CANCEL_PUSH 帧.客户端必须将尝试这样做视为类型为 H3_ID_ERROR 的连接错误.

一旦推送流被 PUSH_PROMISE 帧打开或保留, 只要客户端未取消推送, 就可以使用推送流.一旦客户端从控制流接收到 CANCEL_PUSH 帧或从推送流接收到流终止, 推送就被取消.如果推送流在没有 CANCEL_PUSH 的情况下终止, 推送仍被视为成功完成.

客户端可以通过发送 CANCEL_PUSH 帧来中止推送.在服务器收到它之后, 如果推送尚未完成, 服务器必须中止发送推送.客户端还可以通过重置推送流来中止推送.在这两种情况下, 接收方可以安全地丢弃已接收的任何推送响应状态.

一旦请求流关闭, 实现可以选择仅缓冲对推送响应的引用或完全删除对推送响应的引用.如果在关联的请求流关闭的情况下接收到推送响应, 这并不表示推送失败.

推送流始终由推送 ID 引用.PUSH_PROMISE 帧的接收方将推送 ID 与客户端发起的流相关联, 而在推送流上接收 HEADERS 帧的客户端将推送 ID 与接收到的推送相匹配.

4.6.1. 推送 ID (Push IDs)​

推送 ID 是 62 位无符号整数 (参见 [QUIC-TRANSPORT] 的第 16 节), 用于标识服务器推送.推送 ID 在连接的生命周期内是唯一的.

推送 ID 空间从零开始, 是整数空间的子集; 因此, 推送 ID 不能出现在需要流 ID 或请求 ID 的上下文中.特别是, 推送 ID 不允许出现在 GOAWAY 帧中 (参见第 5.2 节).

推送 ID 在单个 PUSH_PROMISE 帧 (参见第 7.2.5 节) 和单个推送流 (参见第 4.6 节和第 6.2.2 节) 中使用.这些使用必须引用服务器在连接生命周期内做出的相同承诺推送.

在推送流上发送推送响应后, 推送 ID 无法重用.如果客户端从不同流接收到同一推送 ID 上的另一个推送流头或另一个 PUSH_PROMISE, 则必须将其视为类型为 H3_ID_ERROR 的连接错误.


5. 连接关闭 (Connection Closure)​

一旦建立, HTTP/3 连接可以在关闭之前用于许多请求和响应.连接关闭可以以几种不同的方式发生.

5.1. 空闲连接 (Idle Connections)​

每个 QUIC 端点在握手期间声明一个空闲超时.如果 QUIC 连接保持空闲 (未接收数据包) 的时间超过此持续时间, 对等方将假定连接已关闭.HTTP/3 实现需要为新请求打开新的 HTTP/3 连接, 如果现有连接已空闲时间超过 QUIC 握手期间协商的空闲超时, 并且如果接近空闲超时则应当这样做; 参见 [QUIC-TRANSPORT] 的第 10.1 节.

预期 HTTP 客户端会请求传输在有请求或服务器推送的响应未完成时保持连接打开, 如 [QUIC-TRANSPORT] 的第 10.1.2 节中所述.如果客户端不期望来自服务器的响应, 允许空闲连接超时比花费精力维护可能不需要的连接更可取.网关可以维护连接以预期需求, 而不是承担与服务器建立连接的延迟成本.服务器不应当主动保持连接打开.

5.2. 连接关闭 (Connection Shutdown)​

即使连接不空闲, 任一端点都可以决定停止使用连接并发起优雅的连接关闭.端点通过发送 GOAWAY 帧来发起 HTTP/3 连接的优雅关闭.GOAWAY 帧包含一个标识符, 该标识符向接收方指示在此连接中已处理或可能已处理的请求或推送的范围.服务器发送客户端发起的双向流 ID; 客户端发送推送 ID.具有指示标识符或更大标识符的请求或推送被 GOAWAY 的发送方拒绝 (第 4.1.1 节).如果未处理请求或推送, 此标识符可以为零.

GOAWAY 帧中的信息使客户端和服务器能够就在 HTTP/3 连接关闭之前接受了哪些请求或推送达成一致.在发送 GOAWAY 帧后, 端点应当明确取消 (参见第 4.1.1 节和第 7.2.3 节) 具有大于或等于指示标识符的任何请求或推送, 以便清理受影响流的传输状态.随着更多请求或推送到达, 端点应当继续这样做.

端点禁止在从对等方收到 GOAWAY 帧后发起新请求或承诺新推送.客户端可以建立新连接以发送其他请求.

某些请求或推送可能已经在传输中:

  • 在收到 GOAWAY 帧后, 如果客户端已经发送了流 ID 大于或等于 GOAWAY 帧中包含的标识符的请求, 则不会处理这些请求.客户端可以在不同的 HTTP 连接上安全地重试未处理的请求.无法重试请求的客户端会在服务器关闭连接时丢失所有正在进行的请求.

    流 ID 小于服务器的 GOAWAY 帧中的流 ID 的请求可能已被处理; 在收到响应, 单独重置流, 收到另一个流 ID 低于所讨论请求的 GOAWAY, 或连接终止之前, 无法知道它们的状态.

    如果这些请求未被处理, 服务器可以拒绝指示 ID 以下流上的个别请求.

  • 如果服务器在承诺推送 ID 大于或等于 GOAWAY 帧中包含的标识符后接收到 GOAWAY 帧, 则不会接受这些推送.

当提前知道连接关闭时, 服务器应当发送 GOAWAY 帧, 即使提前通知很小, 以便远程对等方可以知道请求是否已被部分处理.例如, 如果 HTTP 客户端在服务器关闭 QUIC 连接的同时发送 POST, 如果服务器不发送 GOAWAY 帧来指示它可能作用于哪些流, 则客户端无法知道服务器是否开始处理该 POST 请求.

端点可以发送指示不同标识符的多个 GOAWAY 帧, 但每个帧中的标识符禁止大于任何先前帧中的标识符, 因为客户端可能已经在另一个 HTTP 连接上重试了未处理的请求.接收包含比先前接收的更大标识符的 GOAWAY 必须被视为类型为 H3_ID_ERROR 的连接错误.

尝试优雅关闭连接的端点可以发送值设置为最大可能值 (对于服务器为 2^62-4, 对于客户端为 2^62-1) 的 GOAWAY 帧.这确保对等方停止创建新请求或推送.在允许任何正在进行的请求或推送到达的时间之后, 端点可以发送另一个 GOAWAY 帧, 指示它可能在连接结束之前接受哪些请求或推送.这确保可以干净地关闭连接而不会丢失请求.

客户端在它发送的 GOAWAY 中为推送 ID 字段选择的值具有更大的灵活性.值 2^62-1 表示服务器可以继续履行已承诺的推送.较小的值表示客户端将拒绝推送 ID 大于或等于此值的推送.与服务器一样, 客户端可以发送后续的 GOAWAY 帧, 只要指定的推送 ID 不大于任何先前发送的值.

即使 GOAWAY 指示在收到后不会处理或接受给定的请求或推送, 底层传输资源仍然存在.发起这些请求的端点可以取消它们以清理传输状态.

一旦所有已接受的请求和推送都已处理, 端点可以允许连接变为空闲, 或者可以发起连接的立即关闭.完成优雅关闭的端点应当在关闭连接时使用 H3_NO_ERROR 错误码.

如果客户端已使用所有可用的双向流 ID 用于请求, 则服务器无需发送 GOAWAY 帧, 因为客户端无法发出进一步的请求.

5.3. 立即应用关闭 (Immediate Application Closure)​

HTTP/3 实现可以随时立即关闭 QUIC 连接.这会导致向对等方发送 QUIC CONNECTION_CLOSE 帧, 指示应用层已终止连接.此帧中的应用错误码向对等方指示连接关闭的原因.有关关闭 HTTP/3 连接时可以使用的错误码, 请参见第 8 节.

在关闭连接之前, 可以发送 GOAWAY 帧以允许客户端重试某些请求.将 GOAWAY 帧包含在与 QUIC CONNECTION_CLOSE 帧相同的数据包中可提高客户端接收该帧的可能性.

如果有未明确关闭的打开流, 则在连接关闭时它们会被隐式关闭; 参见 [QUIC-TRANSPORT] 的第 10.2 节.

5.4. 传输关闭 (Transport Closure)​

由于各种原因, QUIC 传输可能向应用层指示连接已终止.这可能是由于对等方的显式关闭、传输级错误或中断连接的网络拓扑变化.

如果连接在没有 GOAWAY 帧的情况下终止, 客户端必须假定发送的任何请求 (无论是全部还是部分) 可能已被处理.


6. 流映射与使用 (Stream Mapping and Usage)​

QUIC 流提供可靠的按序字节传输, 但不保证与其他流上的字节相关的传输顺序.在 QUIC 版本 1 中, 包含 HTTP 帧的流数据由 QUIC STREAM 帧携带, 但此帧对 HTTP 帧层不可见.传输层缓冲并排序接收到的流数据, 向应用暴露可靠的字节流.尽管 QUIC 允许流内的无序传输, 但 HTTP/3 不使用此功能.

QUIC 流可以是单向的 (仅从发起方到接收方携带数据) 或双向的 (双向携带数据).流可以由客户端或服务器发起.有关 QUIC 流的更多详细信息, 请参见 [QUIC-TRANSPORT] 的第 2 节.

当通过 QUIC 发送 HTTP 字段和数据时, QUIC 层处理大部分流管理.使用 QUIC 时 HTTP 不需要进行任何单独的多路复用: 通过 QUIC 流发送的数据始终映射到特定的 HTTP 事务或整个 HTTP/3 连接上下文.

6.1. 双向流 (Bidirectional Streams)​

所有客户端发起的双向流用于 HTTP 请求和响应.双向流确保响应可以轻松与请求关联.这些流称为请求流.

这意味着客户端的第一个请求发生在 QUIC 流 0 上, 后续请求在流 4、8 等上.为了允许这些流打开, HTTP/3 服务器应当为允许的流数量和初始流流量控制窗口配置非零最小值.为了不不必要地限制并行性, 应当同时允许至少 100 个请求流.

HTTP/3 不使用服务器发起的双向流, 尽管扩展可以定义这些流的用途.客户端必须将收到服务器发起的双向流视为类型为 H3_STREAM_CREATION_ERROR 的连接错误, 除非已协商此类扩展.

6.2. 单向流 (Unidirectional Streams)​

单向流 (无论哪个方向) 用于多种目的.目的由流类型指示, 流类型在流开始时作为可变长度整数发送.此整数后面的数据格式和结构由流类型确定.

Unidirectional Stream Header {
Stream Type (i),
}

图 1: 单向流头部

本文档定义了两种流类型: 控制流 (第 6.2.1 节) 和推送流 (第 6.2.2 节).[QPACK] 定义了两种额外的流类型.HTTP/3 的扩展可以定义其他流类型; 有关更多详细信息, 请参见第 9 节.某些流类型是保留的 (第 6.2.3 节).

HTTP/3 连接在其生命周期早期阶段的性能对单向流上数据的创建和交换很敏感.过度限制流数量或这些流的流量控制窗口的端点将增加远程对等方提前达到限制并被阻塞的可能性.特别是, 实现应当考虑远程对等方可能希望对它们被允许使用的某些单向流行使保留流行为 (第 6.2.3 节).

每个端点需要为 HTTP 控制流创建至少一个单向流.QPACK 需要两个额外的单向流, 其他扩展可能需要更多流.因此, 客户端和服务器发送的传输参数必须允许对等方创建至少三个单向流.这些传输参数还应当为每个单向流提供至少 1,024 字节的流量控制信用.

请注意, 如果对等方在创建关键单向流之前耗尽所有初始信用, 则端点不需要授予额外的信用来创建更多单向流.端点应当首先创建 HTTP 控制流以及强制扩展 (如 QPACK 编码器和解码器流) 所需的单向流, 然后根据其对等方的允许创建额外的流.

如果流头部指示接收方不支持的流类型, 则无法消费流的其余部分, 因为语义未知.未知流类型的接收方必须中止读取流或丢弃传入数据而不进一步处理.如果中止读取, 接收方应当使用 H3_STREAM_CREATION_ERROR 错误码或保留错误码 (第 8.1 节).接收方禁止将未知流类型视为任何类型的连接错误.

由于某些流类型可能影响连接状态, 接收方不应当在读取流类型之前丢弃来自传入单向流的数据.

实现可以在知道对等方是否支持之前发送流类型.但是, 可能修改现有协议组件 (包括 QPACK 或其他扩展) 的状态或语义的流类型, 禁止在已知对等方支持之前发送.

除非另有规定, 发送方可以关闭或重置单向流.接收方必须容忍在接收单向流头部之前关闭或重置单向流.

6.2.1. 控制流 (Control Streams)​

控制流由流类型 0x00 指示.此流上的数据由 HTTP/3 帧组成, 如第 7.2 节所定义.

每一方必须在连接开始时发起单个控制流, 并将其 SETTINGS 帧作为此流上的第一帧发送.如果控制流的第一帧是任何其他帧类型, 则必须将其视为类型为 H3_MISSING_SETTINGS 的连接错误.每个对等方仅允许一个控制流; 收到声称是控制流的第二个流必须被视为类型为 H3_STREAM_CREATION_ERROR 的连接错误.发送方禁止关闭控制流, 接收方禁止请求发送方关闭控制流.如果任一控制流在任何时候关闭, 则必须将其视为类型为 H3_CLOSED_CRITICAL_STREAM 的连接错误.第 8 节描述了连接错误.

由于控制流的内容用于管理其他流的行为, 端点应当提供足够的流量控制信用以防止对等方的控制流被阻塞.

使用一对单向流而不是单个双向流.这允许任一对等方在能够发送数据时立即发送数据.根据 QUIC 连接上是否有 0-RTT 可用, 客户端或服务器可能能够首先发送流数据.

6.2.2. 推送流 (Push Streams)​

服务器推送是 HTTP/2 中引入的可选功能, 允许服务器在发出请求之前发起响应.有关更多详细信息, 请参见第 4.6 节.

推送流由流类型 0x01 指示, 后跟它履行的承诺的推送 ID, 编码为可变长度整数.此流上的剩余数据由 HTTP/3 帧组成, 如第 7.2 节所定义, 并通过零个或多个中间 HTTP 响应后跟单个最终 HTTP 响应来履行承诺的服务器推送, 如第 4.1 节所定义.第 4.6 节描述了服务器推送和推送 ID.

只有服务器可以推送; 如果服务器接收到客户端发起的推送流, 则必须将其视为类型为 H3_STREAM_CREATION_ERROR 的连接错误.

Push Stream Header {
Stream Type (i) = 0x01,
Push ID (i),
}

图 2: 推送流头部

客户端不应当在读取推送流头部之前中止读取推送流, 因为这可能导致客户端和服务器之间对哪些推送 ID 已被消费存在分歧.

每个推送 ID 必须仅在推送流头部中使用一次.如果客户端检测到推送流头部包含在另一个推送流头部中使用的推送 ID, 则客户端必须将其视为类型为 H3_ID_ERROR 的连接错误.

6.2.3. 保留流类型 (Reserved Stream Types)​

格式为 0x1f * N + 0x21 (对于非负整数值 N) 的流类型被保留, 以行使忽略未知类型的要求.这些流没有语义, 并且可以在需要应用层填充时发送.它们也可以在当前未传输数据的连接上发送.端点禁止认为这些流在收到时具有任何含义.

流的有效负载和长度以发送实现选择的任何方式选择.发送保留流类型时, 实现可以干净地终止流或重置它.重置流时, 应当使用 H3_NO_ERROR 错误码或保留错误码 (第 8.1 节).


7. HTTP 帧层 (HTTP Framing Layer)​

HTTP 帧在 QUIC 流上携带, 如第 6 节所述.HTTP/3 定义了三种流类型: 控制流、请求流和推送流.本节描述 HTTP/3 帧格式及其允许的流类型; 参见表 1 的概述.

表 1: HTTP/3 帧和流类型概述

帧类型 (Frame)控制流 (Control Stream)请求流 (Request Stream)推送流 (Push Stream)章节 (Section)
DATANoYesYes7.2.1
HEADERSNoYesYes7.2.2
CANCEL_PUSHYesNoNo7.2.3
SETTINGSYes (1)NoNo7.2.4
PUSH_PROMISENoYesNo7.2.5
GOAWAYYesNoNo7.2.6
MAX_PUSH_IDYesNoNo7.2.7
ReservedYesYesYes7.2.8

SETTINGS 帧只能作为控制流的第一帧出现; 这在表 1 中用 (1) 标记.

请注意, 与 QUIC 帧不同, HTTP/3 帧可以跨越多个数据包.

7.1. 帧布局 (Frame Layout)​

所有帧具有以下格式:

HTTP/3 Frame Format {
Type (i),
Length (i),
Frame Payload (..),
}

帧包括以下字段:

  • Type: 标识帧类型的可变长度整数
  • Length: 描述帧有效负载长度 (以字节为单位) 的可变长度整数
  • Frame Payload: 有效负载, 其语义由 Type 字段确定

每个帧的有效负载必须恰好包含其描述中标识的字段.包含标识字段之后的额外字节或在标识字段结束之前终止的帧有效负载必须被视为类型为 H3_FRAME_ERROR 的连接错误.

7.2. 帧定义 (Frame Definitions)​

7.2.1. DATA​

DATA 帧 (type=0x00) 传送与 HTTP 请求或响应内容关联的任意可变长度字节序列.

DATA 帧必须与 HTTP 请求或响应关联.如果在控制流上接收到 DATA 帧, 接收方必须以类型为 H3_FRAME_UNEXPECTED 的连接错误响应.

7.2.2. HEADERS​

HEADERS 帧 (type=0x01) 用于携带使用 QPACK 编码的 HTTP 字段段.

HEADERS 帧只能在请求流或推送流上发送.如果在控制流上接收到 HEADERS 帧, 接收方必须以类型为 H3_FRAME_UNEXPECTED 的连接错误响应.

7.2.3. CANCEL_PUSH​

CANCEL_PUSH 帧 (type=0x03) 用于在接收推送流之前请求取消服务器推送.

当客户端发送 CANCEL_PUSH 帧时, 它表示不希望接收承诺的资源.服务器应当中止发送资源.

CANCEL_PUSH 帧在控制流上发送.在请求流或推送流上接收 CANCEL_PUSH 帧必须被视为类型为 H3_FRAME_UNEXPECTED 的连接错误.

7.2.4. SETTINGS​

SETTINGS 帧 (type=0x04) 传送影响端点通信方式的配置参数.

SETTINGS 帧必须作为每个控制流的第一帧发送.SETTINGS 帧禁止在任何其他流上发送.

定义的设置标识符包括:

  • SETTINGS_MAX_FIELD_SECTION_SIZE (0x06): 最大字段段大小
  • SETTINGS_QPACK_MAX_TABLE_CAPACITY (0x01): QPACK 相关设置
  • SETTINGS_QPACK_BLOCKED_STREAMS (0x07): QPACK 相关设置

7.2.5. PUSH_PROMISE​

PUSH_PROMISE 帧 (type=0x05) 用于在请求流上从服务器向客户端携带承诺的请求头字段段.

PUSH_PROMISE 帧只能在请求流上发送.在控制流或推送流上接收 PUSH_PROMISE 帧必须被视为类型为 H3_FRAME_UNEXPECTED 的连接错误.

7.2.6. GOAWAY​

GOAWAY 帧 (type=0x07) 用于发起连接的优雅关闭.

GOAWAY 帧始终在控制流上发送.在请求流或推送流上接收 GOAWAY 帧必须被视为类型为 H3_FRAME_UNEXPECTED 的连接错误.

7.2.7. MAX_PUSH_ID​

MAX_PUSH_ID 帧 (type=0x0d) 由客户端用于控制服务器可以发起的服务器推送数量.

MAX_PUSH_ID 帧始终在控制流上发送.服务器禁止发送 MAX_PUSH_ID 帧.

7.2.8. 保留帧类型 (Reserved Frame Types)​

格式为 0x1f * N + 0x21 (对于非负整数值 N) 的帧类型被保留, 以行使忽略未知类型的要求.这些帧没有语义, 并且可以在任何流上发送.


8. 错误处理 (Error Handling)​

当流无法成功完成时, QUIC 允许应用突然终止 (重置) 该流并传达原因; 参见 [QUIC-TRANSPORT] 的第 2.4 节.这称为 "流错误 (stream error)".HTTP/3 实现可以决定关闭 QUIC 流并传达错误类型.第 8.1 节定义了错误码的线路编码.流错误不同于指示错误条件的 HTTP 状态码.流错误表示发送方未传输或消费完整的请求或响应, 而 HTTP 状态码表示成功接收的请求的结果.

如果需要终止整个连接, QUIC 同样提供了传达原因的机制; 参见 [QUIC-TRANSPORT] 的第 5.3 节.这称为 "连接错误 (connection error)".与流错误类似, HTTP/3 实现可以终止 QUIC 连接并使用第 8.1 节中的错误码传达原因.

尽管关闭流和连接的原因称为 "错误", 但这些操作不一定表示连接或任一实现存在问题.例如, 如果不再需要请求的资源, 可以重置流.

端点可以在某些情况下选择将流错误视为连接错误, 以响应单个流上的条件关闭整个连接.实现在做出此选择之前需要考虑对未完成请求的影响.

由于可以在不进行协商的情况下定义新的错误码 (参见第 9 节), 因此在意外上下文中使用错误码或接收未知错误码必须被视为等同于 H3_NO_ERROR.但是, 无论错误码如何, 关闭流都可能产生其他影响; 例如, 参见第 4.1 节.

8.1. HTTP/3 错误码 (HTTP/3 Error Codes)​

定义了以下错误码用于突然终止流、中止读取流或立即关闭 HTTP/3 连接.

H3_NO_ERROR (0x0100)
无错误.当需要关闭连接或流但没有错误要发出信号时使用.

H3_GENERAL_PROTOCOL_ERROR (0x0101)
对等方违反了协议要求, 其方式不匹配更具体的错误码, 或者端点拒绝使用更具体的错误码.

H3_INTERNAL_ERROR (0x0102)
HTTP 栈中发生了内部错误.

H3_STREAM_CREATION_ERROR (0x0103)
端点检测到其对等方创建了它不会接受的流.

H3_CLOSED_CRITICAL_STREAM (0x0104)
HTTP/3 连接所需的流被关闭或重置.

H3_FRAME_UNEXPECTED (0x0105)
接收到在当前状态或当前流上不允许的帧.

H3_FRAME_ERROR (0x0106)
接收到无法满足布局要求或大小无效的帧.

H3_EXCESSIVE_LOAD (0x0107)
端点检测到其对等方表现出可能产生过度负载的行为.

H3_ID_ERROR (0x0108)
流 ID 或推送 ID 使用不正确, 例如超过限制、减少限制或重用.

H3_SETTINGS_ERROR (0x0109)
端点在 SETTINGS 帧的有效负载中检测到错误.

H3_MISSING_SETTINGS (0x010a)
在控制流的开头未接收到 SETTINGS 帧.

H3_REQUEST_REJECTED (0x010b)
服务器在不执行任何应用处理的情况下拒绝了请求.

H3_REQUEST_CANCELLED (0x010c)
请求或其响应 (包括推送的响应) 被取消.

H3_REQUEST_INCOMPLETE (0x010d)
客户端的流在不包含完整形式的请求的情况下终止.

H3_MESSAGE_ERROR (0x010e)
HTTP 消息格式错误, 无法处理.

H3_CONNECT_ERROR (0x010f)
响应 CONNECT 请求建立的 TCP 连接被重置或异常关闭.

H3_VERSION_FALLBACK (0x0110)
请求的操作无法通过 HTTP/3 提供服务.对等方应当通过 HTTP/1.1 重试.

格式为 0x1f * N + 0x21 (对于非负整数值 N) 的错误码被保留, 以行使将未知错误码视为等同于 H3_NO_ERROR 的要求 (第 9 节).实现应当在本应发送 H3_NO_ERROR 时以某种概率从此空间中选择错误码.


9. HTTP/3 的扩展 (Extensions to HTTP/3)​

HTTP/3 允许扩展协议.在本节描述的限制内, 协议扩展可用于提供额外的服务或改变协议的任何方面.扩展仅在单个 HTTP/3 连接的范围内有效.

这适用于本文档中定义的协议元素.这不影响扩展 HTTP 的现有选项, 例如定义新方法、状态码或字段.

扩展允许使用新的帧类型 (第 7.2 节)、新的设置 (第 7.2.4.1 节)、新的错误码 (第 8 节) 或新的单向流类型 (第 6.2 节).建立了注册表来管理这些扩展点: 帧类型 (第 11.2.1 节)、设置 (第 11.2.2 节)、错误码 (第 11.2.3 节) 和流类型 (第 11.2.4 节).

实现必须忽略所有可扩展协议元素中的未知或不支持的值.实现必须丢弃数据或中止读取具有未知或不支持类型的单向流.这意味着任何这些扩展点都可以在没有事先安排或协商的情况下被扩展安全使用.但是, 当已知帧类型需要位于特定位置时, 例如 SETTINGS 帧作为控制流的第一帧 (参见第 6.2.1 节), 未知帧类型不满足该要求, 并且应当被视为错误.

可能改变现有协议组件语义的扩展必须在使用之前进行协商.例如, 改变 HEADERS 帧布局的扩展在对等方发出可接受的肯定信号之前不能使用.协调何时生效这样修改的布局可能会很复杂.因此, 为现有协议元素的新定义分配新标识符可能更有效.

本文档没有规定协商扩展使用的特定方法, 但它指出可以使用设置 (第 7.2.4.1 节) 来实现该目的.如果两个对等方都设置了表示愿意使用扩展的值, 则可以使用该扩展.如果将设置用于扩展协商, 则必须以这样的方式定义默认值: 如果省略设置, 则禁用扩展.


10. 安全考虑 (Security Considerations)​

HTTP/3 的安全考虑应与带有 TLS 的 HTTP/2 相当.然而, [HTTP/2] 第 10 节中的许多考虑适用于 [QUIC-TRANSPORT], 并在该文档中讨论.

10.1. 服务器权威 (Server Authority)​

HTTP/3 依赖于 HTTP 的权威定义.建立权威的安全考虑在 [HTTP] 的第 17.1 节中讨论.

10.2. 跨协议攻击 (Cross-Protocol Attacks)​

在 TLS 和 QUIC 握手中使用 ALPN 在处理应用层字节之前建立目标应用协议.这确保端点对对等方正在使用相同的协议有强有力的保证.

这并不能保证防止所有跨协议攻击.[QUIC-TRANSPORT] 的第 21.5 节描述了 QUIC 数据包明文可用于对不使用认证传输的端点执行请求伪造的一些方式.

10.3. 中间方封装攻击 (Intermediary-Encapsulation Attacks)​

HTTP/3 字段编码允许表达在 HTTP 使用的语法中无效的字段名称 (参见 [HTTP] 的第 5.1 节).包含无效字段名称的请求或响应必须被视为格式错误.

类似地, HTTP/3 可以传输无效的字段值.虽然大多数可以编码的值不会改变字段解析, 但回车符 (ASCII 0x0d)、换行符 (ASCII 0x0a) 和空字符 (ASCII 0x00) 如果逐字转换, 可能会被攻击者利用.

10.4. 推送响应的可缓存性 (Cacheability of Pushed Responses)​

推送的响应没有来自客户端的显式请求; 请求由服务器在 PUSH_PROMISE 帧中提供.

当多个租户在同一服务器上共享空间时, 该服务器必须确保租户无法推送他们没有权限的资源的表示.

10.5. 拒绝服务考虑 (Denial-of-Service Considerations)​

HTTP/3 连接相比 HTTP/1.1 或 HTTP/2 连接可能需要更大的资源承诺来运行.

发送对等方需要忽略的未定义协议元素的能力可能被滥用, 导致对等方花费额外的处理时间.

不监控此类行为的端点会将自己暴露于拒绝服务攻击的风险中.实现应当跟踪这些功能的使用并对其使用设置限制.

10.5.1. 字段段大小限制 (Limits on Field Section Size)​

大型字段段 (第 4.1 节) 可能导致实现提交大量状态.端点可以使用 SETTINGS_MAX_FIELD_SECTION_SIZE (第 4.2.2 节) 设置来通知对等方可能应用于字段段大小的限制.

10.5.2. CONNECT 问题 (CONNECT Issues)​

CONNECT 方法可用于在代理上创建不成比例的负载, 因为与创建和维护 TCP 连接相比, 流创建相对便宜.

10.6. 压缩的使用 (Use of Compression)​

当压缩在与攻击者控制下的数据相同的上下文中压缩机密数据时, 压缩可以允许攻击者恢复机密数据.HTTP/3 启用字段压缩 (第 4.2 节).

在安全通道上通信的实现禁止压缩包含机密和攻击者控制数据的内容, 除非为每个数据源使用单独的压缩上下文.

10.7. 填充和流量分析 (Padding and Traffic Analysis)​

填充可用于模糊帧内容的确切大小, 并用于缓解 HTTP 内的特定攻击.

10.8. 帧解析 (Frame Parsing)​

几个协议元素包含嵌套的长度元素.实现必须确保帧的长度与它包含的字段的长度完全匹配.

10.9. 早期数据 (Early Data)​

使用带有 HTTP/3 的 0-RTT 会产生重放攻击的风险.[HTTP-REPLAY] 中的反重放缓解措施必须在使用带有 0-RTT 的 HTTP/3 时应用.

10.10. 迁移 (Migration)​

某些 HTTP 实现使用客户端地址进行日志记录或访问控制.由于 QUIC 客户端的地址可能在连接期间更改, 因此此类实现需要主动检索客户端的当前地址或明确接受原始地址可能更改.

10.11. 隐私考虑 (Privacy Considerations)​

HTTP/3 的几个特性为观察者提供了随时间关联单个客户端或服务器的操作的机会.这些包括设置的值、对刺激的反应时间以及对由设置控制的任何功能的处理.

HTTP/3 偏好使用单个 QUIC 连接允许关联用户在站点上的活动.


11. IANA 考虑 (IANA Considerations)​

本文档注册了一个新的 ALPN 协议 ID (第 11.1 节), 并创建了管理 HTTP/3 中代码点分配的新注册表.

11.1. HTTP/3 识别字符串的注册 (Registration of HTTP/3 Identification String)​

本文档在 [RFC7301] 中建立的 "TLS 应用层协议协商 (ALPN) 协议 ID" 注册表中为 HTTP/3 的识别创建了新的注册.

"h3" 字符串标识 HTTP/3:

  • 协议 (Protocol): HTTP/3
  • 识别序列 (Identification Sequence): 0x68 0x33 ("h3")
  • 规范 (Specification): 本文档

11.2. 新注册表 (New Registries)​

本文档中创建的新注册表在 [QUIC-TRANSPORT] 第 22.1 节中记录的 QUIC 注册策略下运行.这些注册表都包括 [QUIC-TRANSPORT] 第 22.1.1 节中列出的通用字段集.这些注册表收集在 "超文本传输协议版本 3 (HTTP/3)" 标题下.

这些注册表中的初始分配都被指定为永久状态, 并列出 IETF 作为更改控制者和 HTTP 工作组 ([email protected]) 作为联系人.

11.2.1. 帧类型 (Frame Types)​

本文档为 HTTP/3 帧类型代码建立了注册表."HTTP/3 帧类型" 注册表管理 62 位空间.

表 2: 初始 HTTP/3 帧类型

帧类型 (Frame Type)值 (Value)规范 (Specification)
DATA0x00第 7.2.1 节
HEADERS0x01第 7.2.2 节
Reserved0x02本文档
CANCEL_PUSH0x03第 7.2.3 节
SETTINGS0x04第 7.2.4 节
PUSH_PROMISE0x05第 7.2.5 节
Reserved0x06本文档
GOAWAY0x07第 7.2.6 节
MAX_PUSH_ID0x0d第 7.2.7 节

11.2.2. 设置参数 (Settings Parameters)​

本文档为 HTTP/3 设置建立了注册表."HTTP/3 设置" 注册表管理 62 位空间.

表 3: 初始 HTTP/3 设置

设置名称 (Setting Name)值 (Value)规范 (Specification)默认值 (Default)
MAX_FIELD_SECTION_SIZE0x06第 4.2.2 节无限制 (Unlimited)

11.2.3. 错误码 (Error Codes)​

本文档为 HTTP/3 错误码建立了注册表."HTTP/3 错误码" 注册表管理 62 位空间.

本文档注册的条目显示在第 8.1 节中.

11.2.4. 流类型 (Stream Types)​

本文档为 HTTP/3 单向流类型建立了注册表."HTTP/3 流类型" 注册表管理 62 位空间.

表 5: 初始 HTTP/3 流类型

流类型 (Stream Type)值 (Value)规范 (Specification)发送方 (Sender)
控制流 (Control Stream)0x00第 6.2.1 节双方 (Both)
推送流 (Push Stream)0x01第 4.6 节服务器 (Server)

附录 A. 从 HTTP/2 过渡的考虑 (Considerations for Transitioning from HTTP/2)​

HTTP/3 基于 HTTP/2 设计并共享核心语义.本附录总结了 HTTP/2 和 HTTP/3 之间的主要差异, 以帮助实现者理解这两个协议之间的关系.

A.1. 流 (Streams)​

HTTP/3 使用 QUIC 流, 而 HTTP/2 在 TCP 上使用流抽象.主要差异:

  • 流标识符 (Stream Identifiers): HTTP/3 中的流 ID 由 QUIC 分配, 而不是由 HTTP/3 分配
  • 流优先级 (Stream Priority): HTTP/3 不包括 HTTP/2 的流优先级方案
  • 流量控制 (Flow Control): HTTP/3 使用 QUIC 的流量控制机制

A.2. HTTP 帧类型 (HTTP Frame Types)​

许多 HTTP/2 帧类型在 HTTP/3 中被保留或修改:

保留的帧类型 (Preserved Frame Types):

  • DATA (0x00) - 类似功能
  • HEADERS (0x01) - 类似功能
  • SETTINGS (0x04) - 类似但仅在控制流上
  • PUSH_PROMISE (0x05) - 类似功能
  • GOAWAY (0x07) - 类似功能

删除或替换的 HTTP/2 帧类型 (Removed or Replaced HTTP/2 Frame Types):

  • PRIORITY (0x02) - 在 HTTP/3 中删除
  • RST_STREAM (0x03) - 被 QUIC 的 RESET_STREAM 替换
  • PING (0x06) - 被 QUIC 的 PING 帧替换
  • WINDOW_UPDATE (0x08) - 被 QUIC 的流量控制替换
  • CONTINUATION (0x09) - 在 HTTP/3 中不需要

HTTP/3 中的新帧类型 (New Frame Types in HTTP/3):

  • CANCEL_PUSH (0x03) - 取消服务器推送
  • MAX_PUSH_ID (0x0d) - 控制推送 ID 空间

A.3. HTTP/2 SETTINGS 参数 (HTTP/2 SETTINGS Parameters)​

HTTP/3 中的设置与 HTTP/2 不同:

删除的设置 (Removed Settings):

  • SETTINGS_HEADER_TABLE_SIZE - 被 QPACK 设置替换
  • SETTINGS_ENABLE_PUSH - 通过 MAX_PUSH_ID 控制
  • SETTINGS_MAX_CONCURRENT_STREAMS - 由 QUIC 传输参数控制
  • SETTINGS_INITIAL_WINDOW_SIZE - 被 QUIC 流量控制替换
  • SETTINGS_MAX_FRAME_SIZE - 在 HTTP/3 中不需要
  • SETTINGS_MAX_HEADER_LIST_SIZE - 被 SETTINGS_MAX_FIELD_SECTION_SIZE 替换

保留的设置 (Preserved Settings):

  • SETTINGS_MAX_FIELD_SECTION_SIZE (0x06) - 类似于 HTTP/2 的 SETTINGS_MAX_HEADER_LIST_SIZE

A.4. HTTP/2 错误码 (HTTP/2 Error Codes)​

HTTP/3 定义了自己的错误码集, 与 HTTP/2 的错误码不同.实现者应当注意映射关系, 但不应假设存在直接的一对一对应关系.

A.5. 其他差异 (Other Differences)​

连接管理 (Connection Management):

  • HTTP/3 使用 QUIC 的连接管理, 包括连接迁移和多路径支持
  • 不需要 HTTP/2 的连接序言

服务器推送 (Server Push):

  • HTTP/3 中的服务器推送机制与 HTTP/2 类似, 但使用不同的帧和流类型
  • HTTP/3 中推送 ID 是显式的

字段压缩 (Field Compression):

  • HTTP/3 使用 QPACK 而不是 HPACK
  • QPACK 旨在处理无序传输

可扩展性 (Extensibility):

  • HTTP/3 提供更灵活的扩展机制
  • 新帧类型、设置和流类型可以在不协商的情况下使用