Skip to main content

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 节)。