HTTP-3
最后更新于
HTTP/3 弃用 TCP 协议, 改为使用基于 UDP 协议的 QUIC 协议和类似于 HTTP/2 的内部成帧层为 HTTP 语义提供传输。
HTTP/2 引入了二进制成帧和复用层,以在不修改传输层的情况下改善延迟。 然而,由于 HTTP/2 多路复用的并行特性对于 TCP 的丢失恢复机制来说是不可见的,因此丢失或重新排序的数据包会导致所有活动事务遇到停顿,无论该事务是否直接受到丢失数据包的影响。
QUIC 是基于 UDP 的多路复用安全传输
QUIC 传输协议结合了流复用和每流流量控制,类似于 HTTP/2 帧层提供的功能。通过提供流级别的可靠性和整个连接的拥塞控制,与 TCP 映射相比,QUIC 能够提高 HTTP 的性能。 QUIC 还在传输层整合了 TLS 1.3 的连接设置延迟。
HTTP/3 基于 QUIC 定义了三种流类型:控制流,请求流,推送流。
QUIC 流提供可靠的按顺序传输字节,但不保证其他流上字节的传输顺序。
传输层缓冲并排序接收到的流数据,向应用程序公开可靠的字节流。 尽管 QUIC 允许流中的无序传送,但 HTTP/3 并未使用此功能。
当 HTTP 字段和数据通过 QUIC 发送时,QUIC 层处理大部分流管理。
所有客户端发起的双向流都用于 HTTP 请求和响应。 双向流确保响应可以轻松地与请求相关联。 这些流称为请求流(Request Stream)。
这意味着客户端的第一个请求发生在 QUIC 流 0 上,后续请求发生在流 4、8 ...。
HTTP/3 服务器应该为允许的流数量和初始流流量控制窗口配置非零最小值。 为了避免不必要地限制并行性,一次至少应该允许 100 个请求流。
HTTP/3 不使用服务器启动的双向流。 客户端将收到服务器发起的双向流视为类型为 H3_STREAM_CREATION_ERROR
的连接错误。
任一方向的单向流可用于多种目的。 可以通过 HTTP/3 的扩展来定义。
客户端和服务器发送的传输参数必须允许对方创建至少三个单向流:
控制流
QPACK 编码器流
QPACK 解码器流
每个单向流提供至少 1024 字节的可用流量。
建关键单向流之前消耗了所有初始信用,端点应该创建 HTTP 控制流首先创建强制扩展所需的单向流。
除非另有说明,发送方可以关闭或重置单向流。 接收方必须允许单向流在接收到单向流标头之前被关闭或重置。
控制流(Control Streams)
控制流由流类型 0x00
指示。 该流上的数据由 HTTP/3 帧组成。
每一方必须在连接开始时启动单个控制流并发送其 SETTING
帧作为该流上的第一帧。 每个对等端点仅允许一个控制流。
控制流的内容用于管理其他流的行为,所以端点应该提供足够的流量以防止对等方的控制流被阻塞。
控制流使用一对单向流而不是单个双向流。这允许任一对等方尽快发送数据。
推送流(Push Streams)
服务器推送是 HTTP/2 中引入的一项可选功能,允许服务器在收到请求之前发起响应。
只有服务器可以推送,如果服务器接收到客户端发起的推送流,则必须将其视为类型为 H3_STREAM_CREATION_ERROR
的连接错误
该流上的其余数据由 HTTP/3 帧组成
每个 push_id
只能在推送流头中使用一次。 如果客户端检测到推流头中包含推送 ID 在另一个推送流标头中使用的,客户端必须将其视为类型为 H3_ID_ERROR
的连接错误。
客户端不应在读取推送流标头之前中止对推送流的读取,因为这可能会导致客户端和已使用 push_id
的服务器之间出现分歧。
与 HTTP/2 相比,HTTP/3 允许使用更多数量的流 (2 62 -1)。 HTTP/3 中的流并发由 QUIC 管理。
在 HTTP/2 中,只有请求和响应主体受到流量控制。 所有 HTTP/3 帧都在 QUIC 流上发送,因此所有流上的所有帧都在 HTTP/3 中进行流量控制。
由于其他单向流类型的存在,HTTP/3 并不完全依赖并发单向流的数量来控制并发进行中推送的数量。 相反,HTTP/3 客户端使用 MAX_PUSH_ID
帧来控制从 HTTP/3 服务器接收的推送数量。
帧(frame) 是 HTTP/3 中流上的最小通信单元,由标头和根据帧类型构造的可变长度字节序列组成。 每种帧类型都有不同的用途。
HTTP 帧在 QUIC 流上承载,HTTP/3 帧和流类型概述:
Frame | Control Stream | Request Stream | Push Stream |
---|---|---|---|
| No | Yes | Yes |
| No | Yes | Yes |
| Yes | No | No |
| Yes (1) | No | No |
| No | Yes | No |
| Yes | No | No |
| Yes | No | No |
所有帧都具有以下结构:
DATA
帧传送与 HTTP 请求或响应内容相关的任意、可变长度的字节序列。
HEADERS
帧用于携带使用 QPACK 编码的 HTTP 字段部分。
CANCEL_PUSH
帧用于取消服务器推送,在推送流被接受到之前。
SETTINGS
帧传达影响端点通信方式的配置参数。
设置帧必须作为每个控制流的第一帧发送,由每个对等方发送,并且不得随后发送。
设置帧不得在控制流以外的任何流上发送。
PUSH_PROMISE
帧用于在请求流中从服务器向客户端传输承诺的请求标头部分。
当客户端发送一个请求给服务器时,服务器可以选择性地使用 PUSH_PROMISE
来主动推送额外的资源给客户端。
GOAWAY
帧(用于通过任一端点启动 HTTP/3 连接的正常关闭。 GOAWAY
允许端点停止接受新的请求或推送,同时仍完成对先前收到的请求和推送的处理。
客户端使用 MAX_PUSH_ID
帧来控制服务器可以发起的服务器推送数量。
HTTP/2 中的许多帧概念可以在 QUIC 上省略。 包括 PRIORITY
、RST_STREAM
、END_STREAM
、PING
、WINDOW_UPDATE
、CONTINUATION
HTTP/2 在所有流中的帧之间提供绝对排序,而 QUIC 仅在每个流上提供这种保证。 因此,如果帧类型假设仍会按照发送的顺序接收来自不同流的帧,则 HTTP/3 将破坏它们。
类似于 HTTP/2,HTTP/3 中的 HTTP 消息(请求或响应)包含:
标头部分,包括消息控制数据,单个 HEADER
帧;
内容,由一系列 DATA
帧组成;
预告部分,单个 HEADER
帧。
客户端在请求流上发送 HTTP 请求,这是客户端发起的双向 QUIC 流。 一个 HTTP 请求对应一个请求流,在给定的流上,收到多个请求视为格式错误。
服务器在与请求流上发送零个或多个临时 HTTP 响应,然后发送一个最终 HTTP 响应。 收到最终 HTTP 响应之后的附加 HTTP 响应视为格式错误。
HTTP 请求/响应交换完全消耗客户端发起的双向 QUIC 流。 发送请求后,客户端必须关闭“发送流”。 除非使用 CONNECT
方法,否则客户端不得依赖于接收对其请求的响应来关闭流。 发送最终响应后,服务器必须关闭“发送流”。 至此,QUIC 流完全关闭。
由于某些消息很大或不受限制,一旦收到足够的消息以取得进展,端点就应该开始处理部分 HTTP 消息。 如果响应不依赖于请求中尚未发送和接收的任何部分,则服务器可以在客户端发送整个请求之前发送完整的响应。
请求取消和拒绝
当服务器不需要接收请求的其余部分时,它可以中止读取请求流,发送完整的响应,并干净地关闭流的发送部分
一旦请求流已经打开,请求可以被任一端点取消。 如果客户不再对响应感兴趣,则取消请求; 如果服务器无法响应或选择不响应,则会取消请求。 如果可能,建议服务器发送带有适当状态代码的 HTTP 响应,而不是取消已经开始处理的请求。
如果流在收到完整响应后被取消,客户端可以忽略取消并使用响应。
与 HTTP/2 一样,HTTP/3 对于字段名称、连接标头字段和伪标头字段中的字符使用也有其他注意事项。 将 HTTP/1.x 消息转换为 HTTP/3 的中介必须删除连接特定的标头字段
HTTP/3 不使用 Connection
标头字段来指示特定于连接的字段;在此协议中,特定于连接的元数据通过其他方式传送。
HTTP 字段压缩
与 HTTP/2 一样,HTTP/3 使用使用一系列伪标头字段,压缩消息的标头和尾部部分,包括标头部分中存在的控制数据。
由于 HPACK 依赖于压缩字段部分的按序传输(QUIC 未提供保证), 因此 HTTP/3 用 QPACK 替换 HPACK。 QPACK 使用单独的单向流来修改和跟踪字段表状态,而编码字段部分引用表的状态而不修改它。
HTTP/3 不支持 HTTP 升级机制
在 HTTP/3 中,推送流的机制有所不同。服务器在首次接收到客户端的请求时,可以直接发送 PUSH_PROMISE
帧来创建一个推送流。 这个 PUSH_PROMISE
帧会指示客户端将要推送的资源和关联的流 ID。 客户端在接收到 PUSH_PROMISE
帧后可以决定是否接受这个推送,并建立相应的推送流来获取资源。
服务器可能在一个响应消息之前、中间、之后,发送一个或多个 PUSH_PROMISE
帧,这些 PUSH_PROMISE
帧不是响应的一部分。
服务器以与标准响应相同的方式发送零个或多个临时 HTTP 响应,然后发送单个最终 HTTP 响应。 收到最终 HTTP 响应之后的附加 HTTP 响应视为格式错误。
HTTP 源如何通告等效 HTTP/3 端点:
使用 ALPN 令牌 h3
通过 HTTP 响应标头字段 Alt-Svc
HTTP/2 ALTSVC
帧。
例如,源可以在 HTTP 响应中通过包含以下标头字段来指示 HTTP/3 在同一主机名的 UDP 端口 50781 上可用:
收到指示 HTTP/3 支持的 Alt-Svc 记录后,客户端可以尝试建立到指定主机和端口的 QUIC 连接;如果此连接成功,客户端可以使用本文档中描述的映射发送 HTTP 请求。
HTTP/3 依赖 QUIC v1 作为底层传输。 QUIC v1 使用 TLS v1.3 或更高版本作为其握手协议
如果服务器由域名标识,则客户端必须发送服务器名称指示(SNI)TLS 扩展, 除非使用替代机制来指示目标主机。
在连接建立期间,通过在 TLS 握手中选择 ALPN 令牌 h3
来表明支持 HTTP/3
除非使用其他机制来选择 HTTP/3,否则
h3
将在 TLS 握手期间用于应用层协议协商
虽然与核心 QUIC 协议相关的连接级选项是在初始加密握手中设置的。 特定于 HTTP/3 的设置是在 SETTINGS
帧中, 建立链接成功后,SETTINGS
必须由每个端点作为其各自 HTTP 控制流的初始帧发送。
连接问题(例如,阻止 UDP)可能会导致无法建立 QUIC 连接;在这种情况下,客户端应该尝试使用基于 TCP 的 HTTP 版本。
HTTP/3 连接在多个请求中是持久的。 为了获得最佳性能,预计客户端不会关闭连接,直到确定不需要与服务器进行进一步通信(例如,当用户离开特定网页时)或直到服务器关闭连接。
链接建立成功后,多个不同 URI 权限组件的请求可以在同一链接里。
要将现有连接用于新源(New Origin),必须验证服务器为新源服务器提供的证书。 如果由于任何原因该证书对于新源不可接受,则不得重用该连接,并且应该为新源建立新连接。
客户端不应打开多个到给定 IP 地址和 UDP 端口的 HTTP/3 连接。 其中 IP 地址和端口可能源自 URI、选定的替代服务、配置的代理或名称解析。
客户端可以使用不同的传输或 TLS 配置打开到相同 IP 地址和 UDP 端口的多个 HTTP/3 连接, 但应避免使用相同的配置创建多个连接。
鼓励服务器尽可能长时间地保持开放的 HTTP/3 连接,但允许在必要时终止空闲连接。
一旦建立,HTTP/3 连接可用于随着时间的推移进行许多请求和响应,直到连接关闭。
空闲连接
每个 QUIC 端点在握手期间声明空闲超时。如果 QUIC 连接保持空闲状态(未收到数据包)的时间超过此持续时间,则对等方将假定连接已关闭。
如果现有连接的空闲时间超过了 QUIC 握手期间协商的空闲超时,则 HTTP/3 实现将需要为新请求打开一个新的 HTTP/3 连接
当请求或服务器推送有未完成的响应时,HTTP 客户端应请求传输保持连接打开。
如果客户端不期望服务器响应,那么允许空闲连接超时比花费精力维护可能不需要的连接更好。 网关可以在预期需要时维持连接,而不是产生与服务器建立连接的延迟成本。 服务器不应该主动保持连接打开。
连接关闭
即使连接不空闲,任一端点也可以决定停止使用该连接并发起正常的连接关闭。
当任一端点选择关闭 HTTP/3 连接时,终止端点应该首先发送 GOAWAY
帧,以便两个端点都可以可靠地确定先前发送的帧是否已被处理,并优雅地完成或终止任何必要的剩余任务。