Skip to main content

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

4.1 HTTP Message Framing (HTTP消息帧化)

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

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

在给定流上,接收多个请求或在最终HTTP响应之后接收附加HTTP响应必须 (MUST) 被视为格式错误 (malformed)。

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

  1. 头部段 (header section),包括消息控制数据,作为单个HEADERS帧发送
  2. 可选的内容 (content),如果存在,作为一系列DATA帧发送
  3. 可选的尾部段 (trailer section),如果存在,作为单个HEADERS帧发送

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

接收到无效的帧序列必须 (MUST) 被视为类型为 H3_FRAME_UNEXPECTED 的连接错误。特别是,任何HEADERS帧之前的DATA帧,或尾部HEADERS帧之后的HEADERS或DATA帧,都被视为无效。其他帧类型,特别是未知的帧类型,可能根据其自身的规则被允许;参见第9节。

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

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

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

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

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

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

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

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

4.2 HTTP Fields (HTTP字段)

HTTP字段名称和值是octets的序列,编码规则和语义在 [HTTP] 的第5节中指定。字段名称和值可以包含除 0x00 之外的任何octet。然而,HTTP/3不支持 obs-fold 生成(参见 [HTTP/1.1] 的第5.2节)。

4.2.1 Field Compression (字段压缩)

[QPACK] 描述了一种压缩格式和操作,用于有效地表示HTTP字段名称和值。为了允许在并行流上独立地解码字段段,QPACK使用三种类型的流:编码器流、解码器流和消息流(请求和推送流)。

QPACK编码器和解码器流是客户端和服务器之间的单向流。编码器流由QPACK编码器发起,并由QPACK解码器读取。解码器流由QPACK解码器发起,并由QPACK编码器读取。对等方必须 (MUST) 在连接开始时创建对等方发起的单向流,这些流的 stream type 为 0x02(编码器流)和 0x03(解码器流)。如果缺少任一流类型,则必须 (MUST) 将其视为类型为 H3_MISSING_SETTINGS 的连接错误。

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

HTTP实现可能对 header section 施加实现限制。头部大小受限于连接上所有流的累计大小以及单个流上的大小。SETTINGS参数可用于向对等方传达这些限制,这些限制由 [HTTP] 的第5.2节定义。

HTTP/3使用与HTTP/2相同的头部大小限制机制:

  • SETTINGS_MAX_FIELD_SECTION_SIZE:表示服务器愿意接收的头部段的最大大小(以octets为单位)

服务器可以使用 431 (Request Header Fields Too Large) 状态码(参见 [HTTP] 的第15.5.20节)拒绝头部过大的请求。

4.3 HTTP Control Data (HTTP控制数据)

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

伪头字段不是HTTP字段。端点禁止 (MUST NOT) 生成 [HTTP] 或本文档中定义的伪头字段以外的伪头字段。然而,扩展字段可以被协商以允许使用额外的伪头字段;参见第9节。

伪头字段仅在头部段中有效。请求和响应中的所有伪头字段必须 (MUST) 出现在头部段的开始处。包含伪头字段作为字段段的一部分在尾部段中或在字段段的其他部分之后的请求或响应必须 (MUST) 被视为格式错误。

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

HTTP/3中定义了以下伪头字段用于HTTP请求:

  • :method:包含HTTP方法(参见 [HTTP] 的第9节)
  • :scheme:包含目标URI的scheme部分(参见 [HTTP] 的第4.2.1节)
  • :authority:包含目标URI的authority部分(参见 [HTTP] 的第4.2.2节)。在客户端请求中禁止 (MUST NOT) 包含废弃的 userinfo 子组件
  • :path:包含目标URI的path和query部分(参见 [HTTP] 的第4.2.3节和第4.2.4节)

所有HTTP/3请求必须 (MUST) 恰好包含每个 :method:scheme:path 伪头字段各一个,除非它是CONNECT请求(参见第4.4节)。如果 :scheme 伪头字段标识的scheme具有强制性的authority组件(包括"http"和"https"),请求必须 (MUST) 恰好包含一个 :authority 伪头字段的值;参见 [HTTP] 的第4.2.2节。如果scheme不要求authority组件,请求禁止 (MUST NOT) 包含 :authority 伪头字段。

HTTP/3不定义携带目标URI片段部分的方法(参见 [HTTP] 的第4.2.5节)。

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

HTTP/3中为响应定义了单个 :status 伪头字段。该伪头字段携带HTTP状态码;参见 [HTTP] 的第15节。所有响应必须 (MUST) 恰好包含一个 :status 伪头字段。

4.4 The CONNECT Method (CONNECT方法)

CONNECT方法 ([HTTP] 第9.3.6节) 主要与HTTP代理一起使用,以建立到源服务器的TLS会话,目的是与https资源交互。在HTTP/1.x中,CONNECT用于将整个HTTP连接转换为到远程主机的隧道。在HTTP/2和HTTP/3中,CONNECT方法用于建立通过单个流到远程主机的隧道。

使用CONNECT方法的HTTP/3请求必须 (MUST) 采用以下形式:

  • :method 伪头字段设置为 CONNECT
  • :scheme:path 伪头字段省略
  • :authority 伪头字段包含要连接的主机和端口

对CONNECT请求的响应不受其他类型的响应的一些限制。成功的响应(任何2xx状态码)表示代理已建立到 :authority 伪头字段中标识的服务器的TCP连接。

CONNECT请求的任何2xx响应表示代理承诺对数据流进行双向隧道传输:客户端发送的数据由代理发送到TCP服务器,TCP服务器发送的数据由代理发送到客户端。

CONNECT创建的隧道仅限于单个流。只有DATA帧使用与隧道对应的流发送。到代理的QUIC连接的生命周期不受隧道的约束。

4.5 HTTP Upgrade (HTTP升级)

HTTP/3不支持HTTP Upgrade机制(参见 [HTTP] 的第7.8节)。使用扩展CONNECT协议(参见第4.4节和 [EXT-CONNECT2])来协商使用其他协议。

4.6 Server Push (服务器推送)

服务器推送是一种可选功能,允许服务器发起对客户端的响应。服务器推送类似于请求/响应,不同之处在于服务器发起交换。

推送响应始终与来自客户端的显式请求相关联。服务器在请求流上发送PUSH_PROMISE帧以指示它正在推送响应。PUSH_PROMISE帧包括代表服务器发起的请求的头部段,并使用push ID标识推送响应。

服务器然后在服务器发起的单向流上发送推送响应。流标识符是使用在PUSH_PROMISE帧中建立的push ID创建的;参见第6.2.2节。

4.6.1 Push Requests (推送请求)

服务器推送在语义上等同于服务器响应客户端请求;然而,在这种情况下,该请求也由服务器提供,作为PUSH_PROMISE帧。

PUSH_PROMISE帧包括描述服务器归因于请求的请求的头部段。无法包含请求内容。推送的响应始终与客户端发起的请求相关联,客户端在对等方发起的双向流上接收该请求。

推送请求必须 (MUST) 是可缓存的(参见 [HTTP-CACHING] 的第3节),必须 (MUST) 是安全的(参见 [HTTP] 的第9.2.1节),并且禁止 (MUST NOT) 包括请求内容。因此,接收到不可缓存、不安全或包含请求内容的推送请求必须 (MUST) 被视为类型为 H3_GENERAL_PROTOCOL_ERROR 的流错误。

客户端在发送包含所有这些元素的请求时应该 (SHOULD) 验证服务器是否被授权(参见 [HTTP] 的第4.3.4节)为提供的 :authority。例如,连接到 server.example.com 的服务器只允许推送对源 https://server.example.com/ 的响应。

4.6.2 Push Responses (推送响应)

在发送引用推送流的PUSH_PROMISE帧后,服务器可以开始在推送流上发送推送响应。推送响应必须 (MUST) 包括 :status 伪头字段,并可以包括尾部段。

响应由零个或多个临时响应组成,每个临时响应由HEADERS帧传达(参见 [HTTP] 的第15.2节),可选的最终响应由HEADERS帧传达,零个或多个DATA帧,可选地一个包含尾部段的HEADERS帧。

推送的响应不能包括控制数据,除了用于携带推送响应内容的HEADERS和DATA帧之外。任何其他帧类型的接收必须 (MUST) 被视为类型为 H3_FRAME_UNEXPECTED 的流错误。

服务器可以在推送流的任何点停止发送推送响应。这可能是由于检测到推送不再需要,错误,或客户端对推送流的取消。

客户端可以通过中止读取推送流来取消服务器推送。这向服务器发出信号,表明客户端不再需要推送响应。然后客户端应该 (SHOULD) 使用错误代码 H3_REQUEST_CANCELLED 中止读取推送流。

4.6.3 Push Control (推送控制)

如果客户端不想接收推送响应,或者如果它希望限制它接收的推送响应的数量,它可以发送MAX_PUSH_ID帧以限制服务器可以使用的push ID的数量。MAX_PUSH_ID帧定义了服务器可以发送PUSH_PROMISE帧的push ID的最大值。服务器禁止 (MUST NOT) 发送带有大于MAX_PUSH_ID帧中指定的最大push ID的push ID的PUSH_PROMISE帧。

客户端在接收到对服务器发起的双向流的PUSH_PROMISE或在推送流上的响应之前,应该 (SHOULD) 发送MAX_PUSH_ID帧。这允许服务器在开始工作之前了解客户端的推送能力。


重要提示

  • HTTP/3采用QPACK进行头部压缩,避免HTTP/2 HPACK的队头阻塞问题
  • 服务器推送在HTTP/3中得到保留,但使用方式与HTTP/2类似
  • CONNECT方法在HTTP/3中采用特殊的伪头字段格式
  • HTTP Upgrade机制在HTTP/3中不受支持