互联网工程任务组(IETF) M. Thomson,编辑
请求评论:9113 Mozilla
废止: 7540, 8740 C. Benfield,编辑
类别:标准轨道 Apple Inc.
ISSN:2070-1721 2022年6月

HTTP/2


摘要

本规范描述了超文本传输协议(HTTP)语义的一种优化表达,称为 HTTP 版本 2(HTTP/2)。HTTP/2 通过引入字段压缩并允许在同一连接上进行多个并发交换,实现了更有效的网络资源利用并降低了延迟。

本文件废止了 RFC 7540 和 8740。

本备忘录的状态

这是一个互联网标准轨道文档。

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

有关本文件当前状态、任何勘误以及如何就其提供反馈的信息,请访问 https://www.rfc-editor.org/info/rfc9113

Copyright Notice

Copyright (c) 2022 IETF Trust and the persons identified as the document authors. All rights reserved.

This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License.


1. 介绍

使用超文本传输协议(HTTP,[HTTP])的应用的性能与每个 HTTP 版本如何使用底层传输以及传输运行的条件有关。

发出多个并发请求可以降低延迟并提高应用性能。HTTP/1.0 仅允许在给定的 TCP [TCP] 连接上同时存在一个未完成的请求。HTTP/1.1 [HTTP/1.1] 增加了请求流水线,但这只是部分解决了请求并发问题,仍然存在应用层的队首阻塞。因此,HTTP/1.0 和 HTTP/1.1 客户端使用与服务器的多个连接来发出并发请求。

此外,HTTP 字段通常重复且冗长,导致不必要的网络流量,并使初始 TCP 拥塞窗口快速填满。当在新的 TCP 连接上发出多个请求时,这可能导致过高的延迟。

HTTP/2 通过定义 HTTP 语义到底层连接的优化映射来解决这些问题。具体来说,它允许在同一连接上交错消息,并为 HTTP 字段使用高效编码。它还允许对请求进行优先级排序,使更重要的请求更快完成,从而进一步提高性能。

由此产生的协议对网络更加友好,因为与 HTTP/1.x 相比可以使用更少的 TCP 连接。这意味着与其他流的竞争更少且连接寿命更长,从而更好地利用可用的网络容量。但请注意,本协议并未解决 TCP 的队首阻塞问题。

最后,HTTP/2 还通过使用二进制消息分帧来实现更高效的消息处理。

本文件废止 RFCs 7540 和 8740。附录 B 列出了显著的变更。


2. HTTP/2 协议概述

HTTP/2 为 HTTP 语义提供了一个优化的传输。HTTP/2 支持所有核心的 HTTP 功能,但目标是比 HTTP/1.1 更高效。

HTTP/2 是一种面向连接的应用层协议,运行在 TCP 连接之上([TCP])。客户端为 TCP 连接的发起端。

HTTP/2 中的基本协议单元是帧(第 4.1 节)。每种帧类型具有不同的用途。例如,HEADERSDATA 帧构成了 HTTP 请求和响应的基础(第 8.1 节);其他帧类型如 SETTINGSWINDOW_UPDATEPUSH_PROMISE 则用于支持其他 HTTP/2 特性。

通过为每个 HTTP 请求/响应交换关联各自的流(第 5 节)来实现请求的复用。流在很大程度上互相独立,因此被阻塞或停滞的请求或响应不会阻止其他流的进展。

有效使用复用依赖于流量控制与优先级。流量控制(第 5.2 节)通过限制传输的数据量到接收方能够处理的范围,确保可以高效地使用复用流。优先级(第 5.3 节)确保有限资源得到最有效的使用。本次对 HTTP/2 的修订弃用了来自 [RFC7540] 的优先级信令方案。

由于连接中使用的 HTTP 字段可能包含大量冗余数据,携带这些字段的帧会被压缩(第 4.3 节)。这对常见情况下的请求大小尤其有利,允许将多个请求压缩为一个数据包。

最后,HTTP/2 增加了一种新的可选交互模式,服务器可以将响应推送给客户端(第 8.4 节)。这旨在允许服务器推测性地向客户端发送服务器预期客户端需要的数据,以牺牲部分网络使用换取潜在的延迟收益。服务器通过合成一个请求并将其作为 PUSH_PROMISE 帧发送来实现此目的,然后可以在单独的流上向该合成请求发送响应。

2.1. 文档组织

HTTP/2 规范分为四部分:

  • 启动 HTTP/2(第 3 节)涵盖了如何启动 HTTP/2 连接。
  • 帧层(第 4 节)和流层(第 5 节)描述了 HTTP/2 帧的结构以及如何形成复用流。
  • 帧(第 6 节)和错误(第 7 节)定义包含了 HTTP/2 中使用的帧和错误类型的详尽信息。
  • HTTP 映射(第 8 节)和附加要求(第 9 节)描述了如何使用帧和流来表达 HTTP 语义。

虽然某些帧和流层的概念与 HTTP 是分离的,但本规范并不定义一个完全通用的帧层。帧和流层是为满足 HTTP 的需求而定制的。

2.2. 约定与术语

在本文档中,关键字 "MUST"、"MUST NOT"、"REQUIRED"、"SHALL"、"SHALL NOT"、"SHOULD"、"SHOULD NOT"、"RECOMMENDED"、"NOT RECOMMENDED"、"MAY" 和 "OPTIONAL" 的解释应当按照 BCP 14 中的定义来理解,且仅当它们以全大写形式出现时,如此处所示。[RFC2119] [RFC8174]

所有数值均采用网络字节序。除非另有说明,值为无符号。字面值以十进制或十六进制表示,视情况而定。十六进制字面值以 "0x" 为前缀以区别于十进制字面值。

本规范使用 第 1.3 节 中描述的约定来描述二进制格式(见 RFC 9000 [QUIC])。注意该格式使用网络字节序,并且高位比特在前,低位比特在后。

下列术语将被使用:

client:
发起 HTTP/2 连接的端点。客户端发送 HTTP 请求并接收 HTTP 响应。
connection:
两个端点之间的传输层连接。
connection error:
影响整个 HTTP/2 连接的错误。
endpoint:
连接的一端,指客户端或服务器中的任意一方。
frame:
HTTP/2 连接内的最小通信单元,由一个头部和根据帧类型结构化的可变长度八位组序列组成。
peer:
一个端点。当讨论特定端点时,“peer” 指的是相对于主要讨论对象的远端端点。
receiver:
接收帧的端点。
sender:
发送帧的端点。
server:
接受 HTTP/2 连接的端点。服务器接收 HTTP 请求并发送 HTTP 响应。
stream:
HTTP/2 连接内的双向帧流。
stream error:
单个 HTTP/2 流上的错误。

最后,术语 "gateway"、"intermediary"、"proxy" 和 "tunnel" 在 RFC 9110 第 3.7 节 中定义(见 [HTTP])。中间件在不同时候既充当客户端又充当服务器的角色。

消息主体中“内容”一词的含义在 RFC 9110 第 6.4 节 中定义(见 [HTTP])。


3. 启动 HTTP/2

生成 HTTP 请求的实现需要发现服务器是否支持 HTTP/2。

HTTP/2 使用在 RFC 9110 第 4.2 节 中定义的 "http" 和 "https" URI 方案,与 HTTP/1.1 相同的默认端口号(见 [HTTP][HTTP/1.1])。这些 URI 并不包含关于上游服务器(客户端希望建立连接的直接对端)支持哪些 HTTP 版本的任何指示。

确定对 "http" 和 "https" URI 的 HTTP/2 支持的方式是不同的。针对 "https" URI 的发现描述见 第 3.2 节。对于 "http" URI 的 HTTP/2 支持只能通过带外手段发现,并且需要如 第 3.3 节 所述的先验知识。

3.1. HTTP/2 版本标识

本文档定义的协议有两个标识符。基于任一标识符创建连接都意味着使用本文档中描述的传输、分帧和消息语义。

  • 字符串 "h2" 标识在使用传输层安全(TLS)的情况下的协议;参见 第 9.2 节。该标识符用于 TLS 的 ALPN 扩展 字段,并在任何标识通过 TLS 的 HTTP/2 的地方使用。

    字符串 "h2" 被序列化为 ALPN 协议标识符时为两个八位组序列:0x68, 0x32。

  • 字符串 "h2c" 以前用于 HTTP Upgrade 机制的 Upgrade 首部字段中的令牌(参见 RFC 9110 第 7.8 节)。该用法从未被广泛部署,并在本文档中被弃用。与之相关的 HTTP2-Settings 首部字段在用于升级到 "h2c" 时也同样被弃用。

3.2. 为 "https" URI 启动 HTTP/2

向 "https" URI 发出请求的客户端使用 TLS [TLS13],并使用 ALPN 扩展 [TLS-ALPN]

在 TLS 上的 HTTP/2 使用 "h2" 协议标识符。客户端不得发送或服务器不得选择 "h2c" 协议标识符;"h2c" 描述的是不使用 TLS 的协议。

一旦 TLS 协商完成,客户端和服务器都 MUST 发送连接前言(第 3.4 节)。

3.3. 通过先验知识启动 HTTP/2

客户端可以通过其他方式获知特定服务器支持 HTTP/2。例如,客户端可以被配置为知道某个服务器支持 HTTP/2。

知道服务器支持 HTTP/2 的客户端可以建立 TCP 连接并发送连接前言(第 3.4 节)随后发送 HTTP/2 帧。服务器可以通过连接前言的存在来识别这些连接。这仅影响通过明文 TCP 建立的 HTTP/2 连接;通过 TLS 的 HTTP/2 连接 MUST 使用 TLS 中的协议协商 [TLS-ALPN]

同样,服务器 MUST 发送连接前言(第 3.4 节)。

在没有额外信息的情况下,先前支持 HTTP/2 并不能强烈表明给定服务器将来仍会支持 HTTP/2。例如,服务器配置可能发生变化,集群中不同实例之间的配置可能不同,或网络条件可能发生变化。

3.4. HTTP/2 连接前言

在 HTTP/2 中,每个端点都必须发送连接前言,以最终确认所使用的协议并为 HTTP/2 连接建立初始设置。客户端和服务器各自发送不同的连接前言。

客户端连接前言以 24 个八位组的序列开始,用十六进制表示为:

  0x505249202a20485454502f322e300d0a0d0a534d0d0a0d0a

即,连接前言以字符串 "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" 开始。此序列 MUST 后接一个 SETTINGS 帧(第 6.5 节),该帧 MAY 为空。客户端将客户端连接前言作为连接的第一个应用数据八位组发送。

服务器连接前言由一个可能为空的 SETTINGS 帧组成(第 6.5 节),该帧 MUST 是服务器在 HTTP/2 连接中发送的第一个帧。

作为连接前言一部分从对端收到的 SETTINGSMUST 在发送连接前言后被确认(参见 第 6.5.3 节)。

为避免不必要的延迟,客户端允许在发送客户端连接前言后立即向服务器发送额外的帧,而无需等待接收服务器连接前言。但需要注意的是,服务器连接前言的 SETTINGS 帧可能包含必须更改客户端与服务器通信方式的设置。收到 SETTINGS 帧后,客户端应遵守所建立的任何设置。在某些配置中,服务器可能在客户端发送额外帧之前先发送 SETTINGS,从而避免该问题。

客户端和服务器 MUST 将无效的连接前言视为一种连接错误(第 5.4.1 节),错误类型为 PROTOCOL_ERROR。在这种情况下可以省略 GOAWAY 帧(第 6.8 节),因为无效的前言表明对端未使用 HTTP/2。


4. HTTP 帧

一旦建立 HTTP/2 连接,端点就可以开始交换帧。

4.1. 帧 格式

所有帧以固定的 9 八位组头部开始,随后是可变长度的帧负载。

HTTP Frame {
  Length (24),
  Type (8),

  Flags (8),

  Reserved (1),
  Stream Identifier (31),

  Frame Payload (..),
}

图 1:帧 布局

帧头的各字段定义如下:

Length:
帧负载的长度,以无符号 24 位整数表示,单位为八位组。除非接收方为 SETTINGS_MAX_FRAME_SIZE 设置了更大的值,否则不得发送大于 214(16,384)的值。
帧头的 9 个八位组不计入该值。
Type:
帧的 8 位类型。帧类型决定帧的格式和语义。本文档中定义的帧列在 第 6 节 中。实现 MUST 忽略并丢弃未知类型的帧。
Flags:
一个 8 位字段,保留给与帧类型相关的布尔标志。
标志的语义由指定的帧类型定义。未使用的标志指在特定帧类型下没有定义语义的标志。未使用的标志在接收时 MUST 被忽略,发送时 MUST 保持未置位(0x00)。
Reserved:
一个保留的 1 位字段。该位的语义未定义,发送时 MUST 保持未置位(0x00),接收时 MUST 被忽略。
Stream Identifier:
流标识符(参见 第 5.1.1 节)以无符号 31 位整数表示。值 0x00 保留给与整个连接相关而非某个单独流相关的帧。

帧负载的结构和内容完全取决于帧类型。

4.2. 帧 大小

帧负载的大小受接收方在 SETTINGS_MAX_FRAME_SIZE 设置中通告的最大值限制。该设置可以取值在 214(16,384)到 224-1(16,777,215)八位组之间的任意值,含端点。

所有实现 MUST 能够接收并最小限度处理长度达 214 八位组的帧,外加 9 八位组的帧头(第 4.1 节)。描述帧大小时不包括帧头的大小。

如果帧超出 SETTINGS_MAX_FRAME_SIZE 中定义的大小、超出为该帧类型定义的任何限制,或太小以致无法包含强制性帧数据,则端点 MUST 发送错误代码 FRAME_SIZE_ERROR。在可能改变整个连接状态的帧中出现的帧大小错误 MUST 被视为连接错误(第 5.4.1 节);这包括任何携带字段块(第 4.3 节)(即 HEADERSPUSH_PROMISECONTINUATION)、SETTINGS 帧,以及任何流标识为 0 的帧。

端点没有义务使用帧中所有可用空间。使用小于允许最大值的帧可以提高响应性。发送大的帧可能会导致发送时间敏感的帧(例如 RST_STREAMWINDOW_UPDATEPRIORITY)的延迟,如果这些时间敏感帧被较大的帧阻塞,可能会影响性能。

4.3. 字段段压缩与解压缩

字段段压缩是将一组字段行(参见 RFC 9110 第 5.2 节)压缩以形成字段块的过程。字段段解压缩是将字段块解码回字段行集合的过程。HTTP/2 字段段压缩与解压缩的细节在 [COMPRESSION] 中定义,该文档出于历史原因将这些过程称为头部压缩与解压缩。

每个字段块承载单个字段段的所有压缩字段行。头部段还包括以伪首部字段形式存在的与消息相关的控制数据(见 第 8.3 节),其格式与字段行相同。

字段块承载请求、响应、承诺请求和推送响应的控制数据和头部段(参见 第 8.4 节)。除临时响应和包含在 PUSH_PROMISE(第 6.6 节)帧中的请求外,所有这些消息都可以选择性地包含承载尾部段的字段块。

字段段是字段行的集合。字段块中的每个字段行承载单个值。序列化后的字段块随后被划分为一个或多个八位组序列,称为字段块片段。第一个字段块片段在 HEADERS(第 6.2 节)或 PUSH_PROMISE(第 6.6 节)的帧负载中传输,后续字段块片段可以跟随 CONTINUATION(第 6.10 节)帧来承载。

Cookie header field 在 HTTP 映射中被特殊处理(见 第 8.2.3 节)。

接收端点通过将片段串联重组字段块,然后解压缩该块以重建字段段。

完整的字段段由以下任一项组成:

每个字段块作为一个离散单元进行处理。字段块 MUST 作为一连串连续的帧传输,不得与任何其他类型的帧或来自任何其他流的帧交错。HEADERSCONTINUATION 帧序列中的最后一帧具有 END_HEADERS 标志。PUSH_PROMISECONTINUATION 帧序列中的最后一帧也具有 END_HEADERS 标志。这使得字段块在逻辑上等价于单个帧。

字段块片段只能作为 HEADERSPUSH_PROMISECONTINUATION 帧的帧负载发送,因为这些帧携带可能修改接收方维护的压缩上下文的数据。接收 HEADERSPUSH_PROMISECONTINUATION 帧的端点需要重组字段块并执行解压缩,即使这些帧将被丢弃。接收方 MUST 在不能对字段块进行解压缩时以连接错误(第 5.4.1 节)的类型 COMPRESSION_ERROR 终止连接。

字段块解码错误 MUST 被视为连接错误(第 5.4.1 节)且类型为 COMPRESSION_ERROR

4.3.1. 压缩状态

字段压缩是有状态的。每个端点都有一个 HPACK 编码器上下文和一个 HPACK 解码器上下文,用于对连接上的所有字段块进行编码和解码。RFC 7541 第 4 节 定义了动态表,这是每个上下文的主要状态。

动态表有一个由 HPACK 解码器设置的最大尺寸。端点通过 SETTINGS_HEADER_TABLE_SIZE 设置通告其 HPACK 解码器上下文所选择的大小;见 第 6.5.2 节。当连接建立时,双方 HPACK 解码器和编码器的动态表大小从 4,096 字节开始,这是 SETTINGS_HEADER_TABLE_SIZE 设置的初始值。

对 SETTINGS_HEADER_TABLE_SIZE 所设置最大值的任何更改在端点确认设置(第 6.5.3 节)后生效。该端点的 HPACK 编码器可以将动态表设置为不超过解码器所设置的最大值的任意大小。HPACK 编码器通过动态表大小更新指令声明动态表的大小(参见 RFC 7541 第 6.3 节)。

一旦端点确认了将 SETTINGS_HEADER_TABLE_SIZE 的值降低到低于动态表当前大小的更改,其 HPACK 编码器 MUST 在下一个字段块以一个动态表大小更新指令开始,该指令将动态表设置为小于或等于降低后的最大值;参见 RFC 7541 第 4.2 节。如果在确认将动态表最大值降低后随后的字段块未以符合要求的动态表大小更新指令开始,端点 MUST 将该字段块视为连接错误(第 5.4.1 节),错误类型为 COMPRESSION_ERROR


5. Streams and Multiplexing

“流”是在 HTTP/2 连接内客户端和服务器之间交换的独立双向帧序列。流具有若干重要特性:

  • 单个 HTTP/2 连接可以包含多个并发打开的流,任一端点可以交错发送来自多个流的帧。
  • 流可以由任一端点单方面建立并使用,或由任一端点共享。
  • 流可以由任一端点关闭。
  • 发送帧的顺序具有重要意义。接收方按接收顺序处理帧。特别地,HEADERSDATA 帧的顺序在语义上是重要的。
  • 流由整数标识。流标识符由发起该流的端点分配。

5.1. Stream States

流的生命周期如图 2所示。

send PP recv PP idle send H / reserved recv H reserved (local) (remote) recv ES send ES send H open recv H half- half- closed send R / closed (remote) recv R (local) send ES / recv ES / send R / send R / recv R recv R send R / send R / recv R closed recv R

图 2:流状态

send:
端点发送该帧
recv:
端点接收该帧
H:
HEADERS 帧(含隐含的 CONTINUATION 帧)
ES:
END_STREAM 标志
R:
RST_STREAM
PP:
PUSH_PROMISE 帧(含隐含的 CONTINUATION 帧);状态转换针对被承诺的流

注意此图仅显示流状态转换以及影响这些转换的帧和标志。在这方面,CONTINUATION 帧不会导致状态转换;它们实际上是所跟随的 HEADERSPUSH_PROMISE 的一部分。就状态转换而言,END_STREAM 标志被作为与承载它的帧分离的单独事件来处理;带有 END_STREAM 标志的 HEADERS 帧可能会导致两个状态转换。

双方对流状态有主观视图,当有帧在传输中时双方的视图可能不同。端点不会协调流的创建;流由任一端点单方面创建。状态不匹配的负面影响仅限于发送 RST_STREAM 后的“closed”状态,在该状态下可能在一段时间内仍然接收到帧。

流具有以下状态:

idle:
所有流以“idle”(空闲)状态开始。
从该状态的有效转换如下:
  • 作为客户端发送 HEADERS 帧,或作为服务器接收 HEADERS 帧,会使流变为“open”(打开)。流标识符按 第 5.1.1 节 所述选择。相同的 HEADERS 帧也可能使流立即变为“half-closed”。
  • 在另一个流上发送 PUSH_PROMISE 帧会为之后使用保留一个空闲流。该被保留的流状态转换为“reserved (local)”。只有服务器可以发送 PUSH_PROMISE 帧。
  • 在另一个流上接收 PUSH_PROMISE 帧会为之后使用保留一个空闲流。该被保留的流状态转换为“reserved (remote)”。只有客户端可以接收 PUSH_PROMISE 帧。
  • 注意 PUSH_PROMISE 帧不是在被保留的空闲流上发送的,而是在 Promised Stream ID 字段中引用新保留的流。
  • 使用较大编号的流标识符打开流会导致该流立即转换为“closed”;注意该转换未画在图中。
在此状态上接收除 HEADERSPRIORITY 以外的任何帧,MUST 将其视为类型为 PROTOCOL_ERROR 的连接错误(见 第 5.4.1 节)。如果该流是由服务器发起(见 第 5.1.1 节),则接收 HEADERSMUST 也被视为类型为 PROTOCOL_ERROR 的连接错误(见 第 5.4.1 节)。
reserved (local):
“reserved (local)” 状态的流是通过发送 PUSH_PROMISE 帧而被承诺的流。PUSH_PROMISE 帧通过将该流与由远端对端发起的打开流相关联来保留一个空闲流(见 第 8.4 节)。
在此状态下,仅允许以下转换:
  • 端点可以发送 HEADERS 帧。这会使流以“half-closed (remote)” 状态打开。
  • 任一端点可以发送 RST_STREAM 帧使该流变为“closed”,从而释放该流的保留。
端点在该状态下MUST NOT发送除 HEADERSRST_STREAMPRIORITY 以外的任何类型的帧。
在此状态下可以接收 PRIORITYWINDOW_UPDATE 帧。接收除 RST_STREAMPRIORITYWINDOW_UPDATE 以外的任何帧,MUST 被视为类型为 PROTOCOL_ERROR 的连接错误(见 第 5.4.1 节)。
reserved (remote):
“reserved (remote)” 状态的流已被远端对等方保留。
在此状态下,仅允许以下转换:
  • 接收 HEADERS 帧会使流转换为“half-closed (local)”。
  • 任一端点可以发送 RST_STREAM 帧使该流变为“closed”,从而释放该流的保留。
端点在该状态下MUST NOT发送除 RST_STREAMWINDOW_UPDATEPRIORITY 以外的任何类型的帧。
在此状态上接收除 HEADERSRST_STREAMPRIORITY 以外的任何帧,MUST 被视为类型为 PROTOCOL_ERROR 的连接错误(见 第 5.4.1 节)。
open:
处于“open”状态的流可由双方用于发送任意类型的帧。在此状态中,发送方须遵守已通告的流级别流量控制限制(见 第 5.2 节)。
在该状态下,任一端点可以发送带有 END_STREAM 标志的帧,这会使流转换到某一“half-closed”状态。发送 END_STREAM 标志的端点会使流变为“half-closed (local)”;接收 END_STREAM 标志的端点会使流变为“half-closed (remote)”。
任一端点可以从该状态发送 RST_STREAM 帧,使其立即转换为“closed”。
half-closed (local):
处于“half-closed (local)” 的流不能用于发送除 WINDOW_UPDATEPRIORITYRST_STREAM 以外的帧。
当接收到带有 END_STREAM 标志的帧或任一对等方发送 RST_STREAM 帧时,该流会从此状态转换为“closed”。
在此状态下可以接收任何类型的帧。为继续接收受流控的帧,需要使用 WINDOW_UPDATE 帧提供流量控制额度。在发送带有 END_STREAM 标志的帧后的一段短时间内,接收方可以忽略到达的 WINDOW_UPDATE 帧。
此状态下可以接收 PRIORITY 帧。
half-closed (remote):
处于“half-closed (remote)” 的流不再被对端用于发送帧。在此状态下,端点不再有义务维护接收方的流量控制窗口。
如果端点在该状态下接收到除 WINDOW_UPDATEPRIORITYRST_STREAM 以外的其他帧,MUST 使用类型为 STREAM_CLOSED 的流错误进行响应(见 第 5.4.2 节)。
“half-closed (remote)” 状态的流仍可由端点用来发送任意类型的帧。在此状态中,端点继续遵守已通告的流级别流量控制限制(见 第 5.2 节)。
该流可通过发送带有 END_STREAM 标志的帧或任一对等方发送 RST_STREAM 帧而转换为“closed”。
closed:
“closed” 状态为终止状态。
当端点既发送又接收了带有 END_STREAM 标志的帧后,流进入“closed”状态。端点发送或接收 RST_STREAM 帧后,流也会进入“closed”状态。
端点MUST NOT在已关闭的流上发送除 PRIORITY 以外的帧。端点MAY将接收已关闭流上任何其他类型的帧视为类型为 STREAM_CLOSED 的连接错误(见 第 5.4.1 节),但有如下说明。
在对等方接收并处理使流进入“closed”状态的帧之前,发送了带有 END_STREAM 标志或 RST_STREAM 帧的端点可能会收到来自其对等方的 WINDOW_UPDATERST_STREAM 帧。
在“open”或“half-closed (local)” 状态的流上发送 RST_STREAM 帧的端点可能会收到任意类型的帧。对等方可能在处理 RST_STREAM 帧之前已发送或排队发送了这些帧。端点MUST 对在该状态收到的帧执行最小处理然后丢弃。这意味着要更新 HEADERS 和 PUSH_PROMISE 帧的头部压缩状态。接收 PUSH_PROMISE 帧也会导致被承诺的流变为“reserved (remote)”,即使该 PUSH_PROMISE 帧是收到在已关闭的流上。同时,DATA 帧的内容计入连接的流量控制窗口。
端点可以对所有处于“closed”状态的流执行此最小处理。端点MAY使用其他信号来检测对等方已接收导致流进入“closed”状态的帧,并将除 PRIORITY 以外的任何帧的接收视为类型为 PROTOCOL_ERROR 的连接错误(见 第 5.4.1 节)。端点可以使用指示对等方已接收关闭信号的帧来驱动此行为。端点SHOULD NOT 使用定时器来完成此目的。例如,在关闭流后发送 SETTINGS 帧的端点,在接收到该设置的确认后,可以安全地将随后在该流上接收的 DATA 帧视为错误。其他可用的信号可能包括 PING 帧、在关闭流后创建的流上接收数据,或对关闭流后创建的请求的响应。

在没有更具体规则的情况下,实现者SHOULD将接收在某状态描述中未明确允许的帧视为类型为 PROTOCOL_ERROR 的连接错误(见 第 5.4.1 节)。注意 PRIORITY 可以在任何流状态中发送和接收。

本节中的规则仅适用于本文档定义的帧。对于其语义未知的帧的接收不能被视为错误,因为这些帧的发送和接收条件也未知;参见 第 5.5 节

关于 HTTP 请求/响应交换的状态转换示例可见于 第 8.8 节。服务器推送的状态转换示例可见于 第 8.4.1 节第 8.4.2 节

5.1.1. Stream Identifiers

流由无符号 31 位整数标识。由客户端发起的流MUST使用奇数编号的流标识符;由服务器发起的流MUST使用偶数编号的流标识符。流标识符零(0x00)用于连接控制消息;流标识符零不能用于建立新流。

新建立流的标识符MUST在数值上大于发起端之前已打开或保留的所有流。这适用于通过 HEADERS 帧打开的流以及使用 PUSH_PROMISE 保留的流。接收端点对意外的流标识符MUST以类型为 PROTOCOL_ERROR 的连接错误进行响应(见 第 5.4.1 节)。

一个 HEADERS 帧会将帧头中流标识符指定的客户端发起的流从“idle”转换为“open”。一个 PUSH_PROMISE 帧会将帧负载中 Promised Stream ID 字段指定的服务器发起的流从“idle”转换为“reserved (local)”或“reserved (remote)”。当流从“idle”状态转换时,所有可能已被对等方使用较低编号打开的“idle”状态流会立即转换为“closed”。即,端点可以跳过某个流标识符,其效果是该被跳过的流立即被关闭。

流标识符不能重用。长连接可能导致端点耗尽可用的流标识符范围。无法建立新流标识符的客户端可以为新流建立新连接。无法建立新流标识符的服务器可以发送 GOAWAY 帧,从而迫使客户端为新流打开新连接。

5.1.2. Stream Concurrency

对等方可以使用 SETTINGS_MAX_CONCURRENT_STREAMS 参数(参见 第 6.5.2 节)在 SETTINGS 帧中限制并发活动流的数量。最大并发流设置针对每个端点是特定的,仅适用于接收该设置的对等方。也就是说,客户端指定服务器可发起的最大并发流数,服务器指定客户端可发起的最大并发流数。

处于“open”状态或任一“half-closed”状态的流计入端点被允许打开的最大流数。处于这三种状态中的流计入 SETTINGS_MAX_CONCURRENT_STREAMS 设置所通告的限制。处于任一“reserved”状态的流不计入流限制。

端点MUST NOT 超过其对等方设置的限制。接收导致其已通告并发流限制被超出的 HEADERS 帧的端点MUST将其视为类型为 PROTOCOL_ERRORREFUSED_STREAM 的流错误(见 第 5.4.2 节)。错误码的选择决定了端点是否希望启用自动重试(有关详细信息,请参见 第 8.7 节)。

希望将 SETTINGS_MAX_CONCURRENT_STREAMS 值降低到低于当前打开流数的端点,可以通过关闭超出新值的流或允许流完成来实现。

5.2. Flow Control

在流上进行复用会引入对同一 TCP 连接使用的争用,从而导致流被阻塞。流量控制机制确保同一连接上的各流不会相互破坏性地干扰。流量控制既用于单个流,也用于整个连接。

HTTP/2 通过使用 WINDOW_UPDATE 帧(见 第 6.9 节)提供流量控制。

5.2.1. Flow-Control Principles

HTTP/2 的流量控制旨在允许使用各种流量控制算法,而无需更改协议。HTTP/2 中的流量控制具有以下特征:

  1. 流量控制是特定于单个连接的。HTTP/2 流量控制在单跳的端点之间运行,而不是覆盖整个端到端路径。
  2. 流量控制基于 WINDOW_UPDATE 帧。接收方通告它在某个流和整个连接上准备接收的八位组数量。这是基于额度的方案。
  3. 流量控制是有方向性的,总体控制由接收方提供。接收方MAY为每个流和整个连接选择任意窗口大小。发送方MUST尊重接收方施加的流量控制限制。客户端、服务器和中间件都作为接收方独立通告其流量控制窗口,并在发送时遵守对等方设置的流量控制限制。
  4. 初始的流量控制窗口值对于新流和整个连接均为 65,535 八位组。
  5. 帧类型决定流量控制是否适用于该帧。在本文档指定的帧中,仅 DATA 帧受流量控制;所有其他帧类型不占用已通告的流量控制窗口空间。这确保控制帧不会被流量控制阻塞。
  6. 端点可以选择禁用自己的流量控制,但端点不能忽略对等方发出的流量控制信号。
  7. HTTP/2 仅定义 WINDOW_UPDATE 帧的格式和语义(见 第 6.9 节)。本文档不规定接收方何时发送该帧或发送何值,也不指定发送方如何选择发送数据包。实现可以选择适合其需求的任何算法。

实现者还负责对请求和响应的发送进行优先级安排,选择如何避免请求的队首阻塞,以及管理新流的创建。这些算法选择可能会与任何流量控制算法相互作用。

5.2.2. Appropriate Use of Flow Control

流量控制的定义是为了保护在资源受限下运行的端点。例如,代理需要在多个连接之间共享内存,并且可能存在上游连接慢、下游连接快的情况。流量控制用于当接收方无法处理某个流上的数据但又希望继续处理同一连接上的其他流时的场景。

不需要该能力的部署可以通告最大大小(231-1)的流量控制窗口,并在收到任何数据时通过发送 WINDOW_UPDATE 帧来维护该窗口。这实际上对该接收方禁用了流量控制。相反,发送方始终受接收方通告的流量控制窗口约束。

资源受限的部署(例如内存受限)可以通过流量控制限制对等方可消耗的内存量。但请注意,如果在不了解带宽*时延积的情况下启用流量控制,可能导致对可用网络资源的次优使用(参见 [RFC7323])。

即便完全了解当前的带宽*时延积,实施流量控制也可能很困难。端点MUST在数据可用时尽快从 TCP 接收缓冲区读取并处理 HTTP/2 帧。未能及时读取可能在关键帧(如 WINDOW_UPDATE)未被读取和处理时导致死锁。及时读取帧并不会使端点暴露于资源耗尽攻击,因为 HTTP/2 的流量控制限制了资源承诺。

5.2.3. Flow-Control Performance

如果端点无法确保其对等方在此连接上始终拥有大于对等方带宽*时延积的可用流量控制窗口空间,则其接收吞吐量将受限于 HTTP/2 流量控制,从而导致性能下降。

及时发送 WINDOW_UPDATE 帧可以改善性能。端点需要在提高接收吞吐量与管理资源耗尽风险之间取得平衡,并在定义其窗口大小管理策略时仔细注意 第 10.5 节

5.3. Prioritization

在像 HTTP/2 这样的复用协议中,为流分配带宽和计算资源的优先级安排对获得良好性能至关重要。糟糕的优先级方案可能导致 HTTP/2 性能变差。在 TCP 层没有并行性的情况下,性能甚至可能显著低于 HTTP/1.1。

良好的优先级方案受益于上下文相关知识的应用,例如资源内容、资源之间的相互关系以及这些资源将如何被对等方使用。特别是,客户端可以掌握有关请求优先级的知识,这对于服务器的优先级安排是有用的。在这些情况下,让客户端提供优先级信息可以提高性能。

5.3.1. Background on Priority in RFC 7540

RFC 7540 定义了用于请求优先级信令的复杂系统。然而,该系统被证明过于复杂,且未能被一致实现。

该灵活方案使得客户端可以以非常不同的方式表达优先级,采用的做法缺乏一致性。对服务器而言,实现对该方案的通用支持很复杂。客户端和服务器中优先级的实现都不均衡。许多服务器部署在优先处理请求时忽略客户端信号。

简言之,RFC 7540 中的优先级信令未取得成功。

5.3.2. Priority Signaling in This Document

此次对 HTTP/2 的更新弃用了在 RFC 7540 中定义的优先级信令。与优先级信号相关的大部分文本未包含在本文档中。为确保与使用 RFC 7540 中描述的优先级信令的实现互操作,保留了有关帧字段的说明和部分强制处理要求。

关于 RFC 7540 优先级方案的详尽描述仍可在 RFC 7540 第 5.3 节 中找到。

在许多情况下,信令优先级信息对于获得良好性能是必要的。在优先级信令很重要的场景中,鼓励端点使用替代方案,例如在 [HTTP-PRIORITY] 中描述的方案。

尽管 RFC 7540 的优先级信令未被广泛采用,但其提供的信息在缺乏更好信息时仍可能有用。接收 HEADERSPRIORITY 帧中优先级信号的端点可以通过应用这些信息受益。尤其是,消费这些信号的实现不应在没有替代方案的情况下丢弃这些优先级信号。

服务器在缺乏任何优先级信号时SHOULD使用其他上下文信息来确定请求的优先级。服务器MAY将完全缺乏信号解释为客户端未实现该功能。RFC 7540 第 5.3.5 节 中描述的默认行为在大多数条件下已知性能较差,因此其使用不太可能是有意的。

5.4. Error Handling

HTTP/2 分帧机制允许两类错误:

  • 使整个连接不可用的错误条件称为连接错误。
  • 发生在单个流上的错误称为流错误。

错误代码列表见 第 7 节

端点在处理过程中可能会遇到会导致多个错误的帧。实现者MAY在处理时发现多个错误,但SHOULD最多报告一个流错误和一个连接错误。

对给定流报告的第一个流错误会阻止该流上报告任何其他错误。相比较,协议允许发送多个 GOAWAY 帧,但端点SHOULD仅报告一种连接错误,除非在优雅关闭期间遇到错误。如果发生这种情况,端点MAY在已有包含 NO_ERROR 的 GOAWAY 后,发送一个带有新错误码的额外 GOAWAY。

如果端点检测到多个不同错误,它MAY选择报告其中任意一个。如果某帧导致连接错误,则必须报告该错误。此外,端点MAY在检测到错误条件时使用任何适用的错误码;可以始终使用通用错误码(如 PROTOCOL_ERRORINTERNAL_ERROR)代替更具体的错误码。

5.4.1. Connection Error Handling

连接错误是任何阻止帧层进一步处理或破坏任何连接状态的错误。

遇到连接错误的端点SHOULD首先发送一个 GOAWAY 帧(见 第 6.8 节),并在其中包含其最后成功从对等方接收的流标识符。GOAWAY 帧包含表示连接终止原因的错误码(见 第 7 节)。在为错误情况发送 GOAWAY 帧后,端点MUST 关闭 TCP 连接。

GOAWAY 可能不会被可靠地被接收端接收。在发生连接错误时,GOAWAY 仅提供一种尽力而为的尝试,以向对等方说明为什么连接被终止。

端点可以随时结束连接。特别地,端点MAY选择将流错误视为连接错误。若条件允许,端点SHOULD在结束连接时发送 GOAWAY 帧。

5.4.2. Stream Error Handling

流错误是与特定流相关的错误,不影响其他流的处理。

检测到流错误的端点应发送一个 RST_STREAM 帧(见 第 6.4 节),其中包含发生错误的流的流标识符。RST_STREAM 帧包含表示错误类型的错误码。

RST_STREAM 是端点可在流上发送的最后一帧。发送 RST_STREAM 帧的一方MUST准备接收对端已发送或已排队待发送的任何帧。这些帧可以被忽略,除非它们修改了连接状态(例如字段段压缩(见 第 4.3 节)或流量控制所维护的状态)。

通常,端点SHOULD NOT为同一流发送多个 RST_STREAM 帧。但如果在超过一个往返时间后仍在已关闭的流上接收到帧,端点MAY发送额外的 RST_STREAM 帧。允许此行为是为处理不良实现所设计的。

为避免循环,端点MUST NOTRST_STREAM 帧作出 RST_STREAM 的响应。

5.4.3. Connection Termination

如果在流仍处于“open”或“half-closed”状态时 TCP 连接被关闭或重置,则受影响的流无法被自动重试(有关详情见 第 8.7 节)。

5.5. Extending HTTP/2

HTTP/2 允许对协议进行扩展。在本节所述的限制范围内,协议扩展可用于提供附加服务或更改协议的任何方面。扩展仅在单个 HTTP/2 连接的范围内生效。

这适用于本文档中定义的协议元素。这不影响扩展 HTTP 的现有选项,例如定义新的方法、状态码或字段(参见 RFC 9110 第 16 节)。

扩展允许使用新的帧类型(见 第 4.1 节)、新的设置(见 第 6.5 节)或新的错误码(见 第 7 节)。用于管理这些扩展点的登记表在 RFC 7540 第 11 节 中定义。

实现者MUST忽略可扩展协议元素中未知或不支持的值。实现者MUST丢弃具有未知或不支持类型的帧。这意味着任何这些扩展点都可以在无需事先安排或协商的情况下由扩展安全使用。然而,出现在字段块中间的扩展帧(见 第 4.3 节)是不允许的;这些帧MUST 被视为类型为 PROTOCOL_ERROR 的连接错误(见 第 5.4.1 节)。

扩展SHOULD避免更改本文档中定义的协议元素或未定义扩展机制的元素。这包括更改帧布局、对将帧组成 HTTP 消息的方式的添加或更改(见 第 8.1 节)、伪首部字段的定义,或更改任何合规端点可能将其视为连接错误的协议元素(见 第 5.4.1 节)。

更改现有协议元素或状态的扩展MUST在使用前进行协商。例如,改变 HEADERS 帧布局的扩展在对端发出接受该改变的积极信号之前不得使用。在这种情况下,可能还需要协调何时使修订后的布局生效。例如,将除 DATA 帧以外的帧视为受流量控制需要双方都理解的语义改变,因此只能通过协商来完成。

本文档不强制规定用于协商扩展的具体方法,但注意到可以使用一个设置(见 第 6.5.2 节)来实现该目的。如果双方都设置了表明愿意使用该扩展的值,则可以使用该扩展。如果使用设置进行扩展协商,则必须以使扩展初始处于禁用状态的方式定义该设置的初始值。


6. 帧 定义

本规范定义了若干帧类型,每种由唯一的 8 位类型代码标识。每种帧类型在建立和管理整个连接或各个流时发挥不同的作用。

特定帧类型的传输可以改变连接状态。如果端点未能保持对连接状态的同步视图,则连接内的成功通信将不再可能。因此,端点需要对任意给定帧如何影响状态具有一致的理解。

6.1. DATA

DATA 帧(type=0x00)传输与流相关的任意可变长度八位组序列。例如,一个或多个 DATA 帧用于承载 HTTP 请求或响应的消息内容。

DATA 帧MAY也可以包含填充(padding)。可以向 DATA 帧添加填充以掩盖消息的实际长度。填充是一种安全特性;参见 第 10.7 节

DATA Frame {
  Length (24),
  Type (8) = 0x00,

  Unused Flags (4),
  PADDED Flag (1),
  Unused Flags (2),
  END_STREAM Flag (1),

  Reserved (1),
  Stream Identifier (31),

  [Pad Length (8)],
  Data (..),
  Padding (..2040),
}

图 3:DATA 帧格式

Length、Type、Unused Flag(s)、Reserved 和 Stream Identifier 字段在 第 4 节 中描述。DATA 帧还包含下列附加字段:

Pad Length:
一个 8 位字段,包含帧填充的长度(以八位组为单位)。仅当 PADDED 标志被置位时才存在此字段。
Data:
应用数据。数据的长度为在减去存在的其他字段长度后帧负载中剩余的部分。
Padding:
不含应用语义的填充八位组。发送时填充八位组MUST被设置为零。接收方没有义务验证填充,但可以将非零填充视为类型为 PROTOCOL_ERROR 的连接错误(参见 第 5.4.1 节)。

DATA 帧定义了以下标志:

PADDED (0x08):
置位时表示存在 Pad Length 字段及其描述的任何填充。
END_STREAM (0x01):
置位时表示该帧是端点为标识流发送的最后一个帧。设置该标志会使流进入某个“half-closed”状态或“closed”状态(参见 第 5.1 节)。

DATA 帧MUST关联到某个流。如果接收到 Stream Identifier 字段为 0x00 的 DATA 帧,接收方MUST以类型为 PROTOCOL_ERROR 的连接错误进行响应(参见 第 5.4.1 节)。

DATA 帧受流量控制约束,只能在流处于“open”或“half-closed (remote)”状态时发送。整个 DATA 帧负载都计入流量控制,包括 Pad Length 和 Padding 字段(如存在)。如果接收到的 DATA 帧所属流不处于“open”或“half-closed (local)”状态,接收方MUST以类型为 STREAM_CLOSED 的流错误进行响应(参见 第 5.4.2 节)。

填充八位组的总数由 Pad Length 字段的值决定。如果填充长度等于或大于帧负载长度,接收方MUST将其视为类型为 PROTOCOL_ERROR 的连接错误(参见 第 5.4.1 节)。

6.2. HEADERS

HEADERS 帧(type=0x01)用于打开流(参见 第 5.1 节),并携带字段块片段。尽管名称为 HEADERS,HEADERS 帧既可以承载头部段也可以承载尾部段。HEADERS 帧可以在流处于“idle”、“reserved (local)”、“open”或“half-closed (remote)”状态时发送。

HEADERS Frame {
  Length (24),
  Type (8) = 0x01,

  Unused Flags (2),
  PRIORITY Flag (1),
  Unused Flag (1),
  PADDED Flag (1),
  END_HEADERS Flag (1),
  Unused Flag (1),
  END_STREAM Flag (1),

  Reserved (1),
  Stream Identifier (31),

  [Pad Length (8)],
  [Exclusive (1)],
  [Stream Dependency (31)],
  [Weight (8)],
  Field Block Fragment (..),
  Padding (..2040),
}

图 4:HEADERS 帧格式

Length、Type、Unused Flag(s)、Reserved 和 Stream Identifier 字段在 第 4 节 中描述。HEADERS 帧负载还具有下列附加字段:

Pad Length:
一个 8 位字段,包含帧填充的长度(以八位组为单位)。仅当 PADDED 标志被置位时才存在此字段。
Exclusive:
一个单比特标志。仅当 PRIORITY 标志被置位时才存在此字段。HEADERS 帧中的优先级信号已被弃用;参见 第 5.3.2 节
Stream Dependency:
一个 31 位流标识符。仅当 PRIORITY 标志被置位时才存在此字段。
Weight:
一个无符号 8 位整数。仅当 PRIORITY 标志被置位时才存在此字段。
Field Block Fragment:
字段块片段(参见 第 4.3 节)。
Padding:
不含应用语义的填充八位组。发送时填充八位组MUST被设置为零。接收方没有义务验证填充,但可以将非零填充视为类型为 PROTOCOL_ERROR 的连接错误(参见 第 5.4.1 节)。

HEADERS 帧定义了以下标志:

PRIORITY (0x20):
置位时表示 Exclusive、Stream Dependency 和 Weight 字段存在。
PADDED (0x08):
置位时表示 Pad Length 字段及其描述的任何填充存在。
END_HEADERS (0x04):
置位时表示该帧包含完整的字段块(参见 第 4.3 节),并且后面不再跟随任何 CONTINUATION 帧。
若 HEADERS 帧未置 END_HEADERS 标志,则MUST由同一流的 CONTINUATION 帧继续。接收方MUST将任何其他类型的帧或来自不同流的帧视为类型为 PROTOCOL_ERROR 的连接错误(参见 第 5.4.1 节)。
END_STREAM (0x01):
置位时表示该字段块(参见 第 4.3 节)是端点为标识流发送的最后一个字段块。
带有 END_STREAM 标志的 HEADERS 帧表示流的结束。但是,带有 END_STREAM 标志的 HEADERS 帧之后可以跟随同一流的 CONTINUATION 帧。从逻辑上讲,这些 CONTINUATION 帧是 HEADERS 帧的一部分。

HEADERS 帧的帧负载包含字段块片段(参见 第 4.3 节)。不能完全放入 HEADERS 帧的字段块将在 CONTINUATION 帧中继续(参见 第 6.10 节)。

HEADERS 帧MUST关联到某个流。如果接收到 Stream Identifier 字段为 0x00 的 HEADERS 帧,接收方MUST以类型为 PROTOCOL_ERROR 的连接错误进行响应(参见 第 5.4.1 节)。

HEADERS 帧按照 第 4.3 节 描述的方式改变连接状态。

填充八位组的总数由 Pad Length 字段的值决定。如果填充长度等于或大于帧负载长度,接收方MUST将其视为类型为 PROTOCOL_ERROR 的连接错误(参见 第 5.4.1 节)。

6.3. PRIORITY

PRIORITY 帧(type=0x02)已被弃用;参见 第 5.3.2 节。PRIORITY 帧可以在任何流状态发送,包括 idle 或 closed 流。

PRIORITY Frame {
  Length (24) = 0x05,
  Type (8) = 0x02,

  Unused Flags (8),

  Reserved (1),
  Stream Identifier (31),

  Exclusive (1),
  Stream Dependency (31),
  Weight (8),
}

图 5:PRIORITY 帧格式

Length、Type、Unused Flag(s)、Reserved 和 Stream Identifier 字段在 第 4 节 中描述。PRIORITY 帧的帧负载包含下列附加字段:

Exclusive:
一个单比特标志。
Stream Dependency:
一个 31 位流标识符。
Weight:
一个无符号 8 位整数。

PRIORITY 帧不定义任何标志。

PRIORITY 帧总是标识一个流。如果接收到的 PRIORITY 帧的流标识符为 0x00,接收方MUST以类型为 PROTOCOL_ERROR 的连接错误进行响应(参见 第 5.4.1 节)。

发送或接收 PRIORITY 帧不会影响任何流的状态(参见 第 5.1 节)。PRIORITY 帧可以在任何状态的流上发送,包括 "idle" 或 "closed"。PRIORITY 帧不得发送在构成单个字段块的连续帧之间(参见 第 4.3 节)。

长度不是 5 个八位组的 PRIORITY 帧MUST被视为类型为 FRAME_SIZE_ERROR 的流错误(参见 第 5.4.2 节)。

6.4. RST_STREAM

RST_STREAM 帧(type=0x03)允许立即终止流。发送 RST_STREAM 用于请求取消流或指示已发生错误情况。

RST_STREAM Frame {
  Length (24) = 0x04,
  Type (8) = 0x03,

  Unused Flags (8),

  Reserved (1),
  Stream Identifier (31),

  Error Code (32),
}

图 6:RST_STREAM 帧格式

Length、Type、Unused Flag(s)、Reserved 和 Stream Identifier 字段在 第 4 节 中描述。此外,RST_STREAM 帧包含一个无符号 32 位整数,标识错误码(参见 第 7 节)。该错误码指示流被终止的原因。

RST_STREAM 帧不定义任何标志。

RST_STREAM 帧会完全终止被引用的流并使其进入“closed”状态。接收到针对某流的 RST_STREAM 后,接收方MUST NOT为该流发送额外的帧(PRIORITY 除外)。然而,在发送 RST_STREAM 之后,发送端MUST准备接收并处理可能在对端处理到 RST_STREAM 之前已发送或已排队的额外帧。

RST_STREAM 帧MUST关联到某个流。如果接收到 Stream Identifier 字段为 0x00 的 RST_STREAM 帧,接收方MUST将其视为类型为 PROTOCOL_ERROR 的连接错误(参见 第 5.4.1 节)。

不得对处于“idle”状态的流发送 RST_STREAM 帧。如果接收到标识空闲流的 RST_STREAM 帧,接收方MUST将其视为类型为 PROTOCOL_ERROR 的连接错误(参见 第 5.4.1 节)。

长度不是 4 个八位组的 RST_STREAM 帧MUST被视为类型为 FRAME_SIZE_ERROR 的连接错误(参见 第 5.4.1 节)。

6.5. SETTINGS

SETTINGS 帧(type=0x04)传递影响端点通信方式的配置参数,例如对等方行为的偏好和约束。SETTINGS 帧也用于确认接收到的这些设置。SETTINGS 帧中的单个配置参数称为“setting”。

设置不是协商的;它们描述发送方的特性,由接收方使用。各对等方可以为同一设置通告不同的值。例如,客户端可能设置较大的初始流量控制窗口,而服务器可能设置较小的值以节约资源。

在连接开始时,双方端点MUST发送 SETTINGS 帧,并且在连接的整个生命周期内任一端点MAY在任何时候发送 SETTINGS 帧。实现MUST支持本规范定义的所有设置。

SETTINGS 帧中的每个参数会替换该参数的任何现有值。设置按出现顺序处理,接收 SETTINGS 帧的一方不需要维护除每个设置当前值之外的任何状态。因此,SETTINGS 参数的值为接收方最后看到的值。

SETTINGS 帧由接收端点进行确认。为支持此点,SETTINGS 帧定义了 ACK 标志:

ACK (0x01):
置位时表示该帧确认已接收并应用了对等方的 SETTINGS 帧。置位该位时,SETTINGS 帧的帧负载MUST为空。接收带有 ACK 标志且长度字段不为 0 的 SETTINGS 帧MUST被视为类型为 FRAME_SIZE_ERROR 的连接错误(参见 第 5.4.1 节)。更多信息见 第 6.5.3 节(“Settings Synchronization”)。

SETTINGS 帧始终适用于连接,而不是单个流。SETTINGS 帧的流标识符MUST为 0x00。如果接收到的 SETTINGS 帧的 Stream Identifier 字段不是 0x00,接收方MUST以类型为 PROTOCOL_ERROR 的连接错误进行响应(参见 第 5.4.1 节)。

SETTINGS 帧影响连接状态。格式错误或不完整的 SETTINGS 帧MUST被视为类型为 PROTOCOL_ERROR 的连接错误(参见 第 5.4.1 节)。

长度不是 6 的倍数的 SETTINGS 帧MUST被视为类型为 FRAME_SIZE_ERROR 的连接错误(参见 第 5.4.1 节)。

6.5.1. SETTINGS 格式

SETTINGS 帧的帧负载由零个或多个 setting 组成,每个 setting 包含一个无符号 16 位的设置标识符和一个无符号 32 位的值。

SETTINGS Frame {
  Length (24),
  Type (8) = 0x04,

  Unused Flags (7),
  ACK Flag (1),

  Reserved (1),
  Stream Identifier (31) = 0,

  Setting (48) ...,
}

Setting {
  Identifier (16),
  Value (32),
}

图 7:SETTINGS 帧格式

Length、Type、Unused Flag(s)、Reserved 和 Stream Identifier 字段在 第 4 节 中描述。SETTINGS 帧的帧负载包含任意数量的 Setting 字段,每个 Setting 包含:

Identifier:
一个 16 位的设置标识符;见 第 6.5.2 节
Value:
该设置的 32 位值。

6.5.2. 已定义的设置

定义如下设置:

SETTINGS_HEADER_TABLE_SIZE (0x01):
该设置允许发送方告知远端端点用于解码字段块的压缩表的最大大小(以八位组为单位)。编码器可以通过字段块内部特定的压缩格式信令选择任何小于或等于该值的大小(参见 [COMPRESSION])。初始值为 4,096 八位组。
SETTINGS_ENABLE_PUSH (0x02):
该设置可用于启用或禁用服务器推送。如果服务器接收到此参数被设置为 0,则服务器MUST NOT发送 PUSH_PROMISE 帧;参见 第 8.4 节。一个客户端在将该参数设为 0 并被确认后,若接收到 PUSH_PROMISE 帧,MUST将其视为类型为 PROTOCOL_ERROR 的连接错误(参见 第 5.4.1 节)。
SETTINGS_ENABLE_PUSH 的初始值为 1。对于客户端,该值表示它愿意接收 PUSH_PROMISE 帧。对于服务器,该初始值无效且等同于 0。任何非 0 或 1 的值MUST被视为类型为 PROTOCOL_ERROR 的连接错误(参见 第 5.4.1 节)。
服务器MUST NOT显式将此值设为 1。服务器可以在发送 SETTINGS 帧时选择省略该设置,但若包含该设置,其值MUST为 0。客户端MUST将接收带有 SETTINGS_ENABLE_PUSH 为 1 的 SETTINGS 帧视为类型为 PROTOCOL_ERROR 的连接错误(参见 第 5.4.1 节)。
SETTINGS_MAX_CONCURRENT_STREAMS (0x03):
该设置指示发送方允许的最大并发流数。该限制具有方向性:它适用于发送方允许接收方创建的流数量。最初,该值无限。建议该值不少于 100,以免不必要地限制并行度。
对于 SETTINGS_MAX_CONCURRENT_STREAMS 的值为 0,端点SHOULD NOT将其视为特殊情况。零值会阻止新流的创建;但任何耗尽活动流的限制都会产生相同效果。服务器SHOULD仅在短时间内设置零值;如果服务器不希望接收请求,关闭连接更为合适。
SETTINGS_INITIAL_WINDOW_SIZE (0x04):
该设置指示发送方用于流级别流量控制的初始窗口大小(以八位组为单位)。初始值为 216-1(65,535)八位组。
该设置影响所有流的窗口大小(见 第 6.9.2 节)。
超过最大流量控制窗口 231-1 的值MUST被视为类型为 FLOW_CONTROL_ERROR 的连接错误(参见 第 5.4.1 节)。
SETTINGS_MAX_FRAME_SIZE (0x05):
该设置指示发送方愿意接收的最大帧负载大小(以八位组为单位)。
初始值为 214(16,384)八位组。端点通告的值MUST位于该初始值与允许的最大帧大小(224-1 或 16,777,215 八位组)之间(含端点)。超出此范围的值MUST被视为类型为 PROTOCOL_ERROR 的连接错误(参见 第 5.4.1 节)。
SETTINGS_MAX_HEADER_LIST_SIZE (0x06):
该建议性设置告知对等方发送方可接受的最大字段段大小(以八位组为单位)。该值基于未压缩的字段行大小,包括名和值的长度(以八位组为单位)加上每个字段行 32 八位组的开销。
对于任意给定请求,可以强制执行比通告值更低的限制。该设置的初始值为无限制。

接收带有任何未知或不支持标识符的 SETTINGS 帧的端点MUST忽略该设置。

6.5.3. 设置同步

大多数 SETTINGS 值受益于或需要了解对等方何时已接收并应用了更改的参数值。为提供此类同步时点,接收未置 ACK 的 SETTINGS 帧的端点MUST在收到后尽快应用更新的设置。SETTINGS 帧按接收顺序被确认。

SETTINGS 帧中的值MUST按出现顺序处理,中间不得有其他帧处理。Unsupported settings MUST be ignored. Once all values have been processed, the recipient MUST immediately emit a SETTINGS frame with the ACK flag set. Upon receiving a SETTINGS frame with the ACK flag set, the sender of the altered settings can rely on the values from the oldest unacknowledged SETTINGS frame having been applied.

如果 SETTINGS 帧的发送方在合理时间内未收到确认,则MAY发起类型为 SETTINGS_TIMEOUT 的连接错误(参见 第 5.4.1 节)。在设置超时时需要为对端的处理延迟留出余量;仅基于端点间的往返时间来设置超时可能导致误报错误。

6.6. PUSH_PROMISE

PUSH_PROMISE 帧(type=0x05)用于提前通知对等端点发送方打算发起的流。PUSH_PROMISE 帧包含发送方计划创建的流的无符号 31 位标识符,以及提供该流上下文的字段段。第 8.4 节 对 PUSH_PROMISE 的使用进行了详尽描述。

PUSH_PROMISE Frame {
  Length (24),
  Type (8) = 0x05,

  Unused Flags (4),
  PADDED Flag (1),
  END_HEADERS Flag (1),
  Unused Flags (2),

  Reserved (1),
  Stream Identifier (31),

  [Pad Length (8)],
  Reserved (1),
  Promised Stream ID (31),
  Field Block Fragment (..),
  Padding (..2040),
}

图 8:PUSH_PROMISE 帧格式

Length、Type、Unused Flag(s)、Reserved 和 Stream Identifier 字段在 第 4 节 中描述。PUSH_PROMISE 帧负载具有下列附加字段:

Pad Length:
一个 8 位字段,包含帧填充的长度(以八位组为单位)。仅当 PADDED 标志被置位时才存在此字段。
Promised Stream ID:
一个无符号 31 位整数,标识被 PUSH_PROMISE 保留的流。被承诺的流标识符MUST为发送方下一次发送流时的合法选择(参见 第 5.1.1 节 中的“new stream identifier”)。
Field Block Fragment:
字段块片段(参见 第 4.3 节),包含请求控制数据和头部段。
Padding:
不含应用语义的填充八位组。发送时填充八位组MUST被设置为零。接收方没有义务验证填充,但可以将非零填充视为类型为 PROTOCOL_ERROR 的连接错误(参见 第 5.4.1 节)。

PUSH_PROMISE 帧定义了以下标志:

PADDED (0x08):
置位时表示存在 Pad Length 字段及其描述的任何填充。
END_HEADERS (0x04):
置位时表示该帧包含完整的字段块(参见 第 4.3 节),并且后面不再跟随任何 CONTINUATION 帧。
若 PUSH_PROMISE 帧未置 END_HEADERS 标志,则MUST由同一流的 CONTINUATION 帧继续。接收方MUST将任何其他类型的帧或来自不同流的帧视为类型为 PROTOCOL_ERROR 的连接错误(参见 第 5.4.1 节)。

PUSH_PROMISE 帧MUST仅发送在对等方发起并处于“open”或“half-closed (remote)”状态的流上。PUSH_PROMISE 的 Stream Identifier 字段指示它所关联的流。如果 Stream Identifier 字段为 0x00,接收方MUST以类型为 PROTOCOL_ERROR 的连接错误进行响应(参见 第 5.4.1 节)。

被承诺的流不必按承诺顺序使用。PUSH_PROMISE 仅为稍后使用保留流标识符。

如果对等端点的 SETTINGS_ENABLE_PUSH 设置为 0,则不得发送 PUSH_PROMISE。将该设置设为 0 并收到确认的端点MUST将接收 PUSH_PROMISE 视为类型为 PROTOCOL_ERROR 的连接错误(参见 第 5.4.1 节)。

PUSH_PROMISE 的接收方可以通过对被承诺的流返回引用该被承诺流标识符的 RST_STREAM 来选择拒绝被承诺的流。

PUSH_PROMISE 帧以两种方式修改连接状态。首先,包含字段块(参见 第 4.3 节)可能修改接收方为字段段压缩维护的状态。其次,PUSH_PROMISE 还为稍后使用保留一个流,使被承诺流进入“reserved (local)”或“reserved (remote)”状态。发送方MUST NOT在流既非“open”也非“half-closed (remote)”的情况下发送 PUSH_PROMISE;发送方MUST确保被承诺的流对于新的流标识符是有效的选择(参见 第 5.1.1 节)(即,被承诺的流MUST处于“idle”状态)。

由于 PUSH_PROMISE 会保留流,忽略 PUSH_PROMISE 将导致该流状态变得不确定。接收方MUST将对既不处于“open”也不处于“half-closed (local)”状态的流上接收 PUSH_PROMISE 视为类型为 PROTOCOL_ERROR 的连接错误(参见 第 5.4.1 节)。但是,在关联流上已发送 RST_STREAM 的端点MUST处理可能在接收并处理 RST_STREAM 之前创建的 PUSH_PROMISE 帧。

接收方MUST将承诺非法流标识符的 PUSH_PROMISE(参见 第 5.1.1 节)视为类型为 PROTOCOL_ERROR 的连接错误(参见 第 5.4.1 节)。注意,非法流标识符指的是不是当前处于“idle”状态的流的标识符。

填充八位组的总数由 Pad Length 字段的值决定。如果填充长度等于或大于帧负载长度,接收方MUST将其视为类型为 PROTOCOL_ERROR 的连接错误(参见 第 5.4.1 节)。

6.7. PING

PING 帧(type=0x06)是一种测量发送方最小往返时间并确定空闲连接是否仍然可用的机制。PING 帧可以由任一端点发送。

PING Frame {
  Length (24) = 0x08,
  Type (8) = 0x06,

  Unused Flags (7),
  ACK Flag (1),

  Reserved (1),
  Stream Identifier (31) = 0,

  Opaque Data (64),
}

图 9:PING 帧格式

Length、Type、Unused Flag(s)、Reserved 和 Stream Identifier 字段在 第 4 节 中描述。

除了帧头外,PING 帧MUST在帧负载中包含 8 个字节的不透明数据。发送方可以选择任意值并以任意方式使用这些八位组。

接收未包含 ACK 标志的 PING 帧的接收方MUST发送一个带有 ACK 标志的 PING 帧作为响应,且帧负载应与原始帧相同。PING 响应SHOULD被赋予比其他任何帧更高的优先级。

PING 帧定义了以下标志:

ACK (0x01):
置位时表示该 PING 帧为 PING 响应。接收方MUST在 PING 响应中置位该标志。含该标志的 PING 帧MUST NOT再被响应。

PING 帧不关联到任何单独的流。如果接收到 Stream Identifier 字段值不是 0x00 的 PING 帧,接收方MUST以类型为 PROTOCOL_ERROR 的连接错误进行响应(参见 第 5.4.1 节)。

长度字段值不是 8 的 PING 帧的接收应被视为类型为 FRAME_SIZE_ERROR 的连接错误(参见 第 5.4.1 节)。

6.8. GOAWAY

GOAWAY 帧(type=0x07)用于启动连接关闭或指示严重错误条件。GOAWAY 允许端点优雅地停止接受新流,同时继续完成对先前已建立流的处理。这使得诸如服务器维护等管理操作成为可能。

在端点启动新流与远端发送 GOAWAY 帧之间存在固有的竞争条件。为处理这种情况,GOAWAY 包含发送端可能已处理或可能处理的最后一个由对端发起的流的流标识符。例如,如果服务器发送 GOAWAY,则该标识符为客户端所发起的编号最高的流。

一旦发送 GOAWAY,发送方将忽略对端在具有大于所包含最后流标识符的标识符上发送的帧。GOAWAY 的接收方MUST NOT在该连接上打开更多流,尽管可以为新流建立新连接。

如果 GOAWAY 的接收方已在标识符比 GOAWAY 指示值更高的流上发送数据,则这些流未被或将不会被处理。GOAWAY 的接收方可以将这些流视为从未被创建,从而允许这些流在新连接上稍后重试。

端点SHOULD在关闭连接之前始终发送 GOAWAY,以便远端可以知道某个流是否已被部分处理。例如,如果 HTTP 客户端在服务器关闭连接的同时发送了 POST,请求的客户端无法知道服务器是否开始处理该 POST,除非服务器发送 GOAWAY 来指示它可能已作用于哪些流。

端点可能选择在对方行为不当时关闭连接而不发送 GOAWAY。

GOAWAY 帧不一定紧跟在关闭连接之前;若接收方不再需要连接,仍应在终止连接之前发送 GOAWAY 帧。

GOAWAY Frame {
  Length (24),
  Type (8) = 0x07,

  Unused Flags (8),

  Reserved (1),
  Stream Identifier (31) = 0,

  Reserved (1),
  Last-Stream-ID (31),
  Error Code (32),
  Additional Debug Data (..),
}

图 10:GOAWAY 帧格式

Length、Type、Unused Flag(s)、Reserved 和 Stream Identifier 字段在 第 4 节 中描述。

GOAWAY 帧不定义任何标志。

GOAWAY 帧适用于连接,而非特定流。若接收到带有非 0x00 流标识符的 GOAWAY,端点MUST将其视为类型为 PROTOCOL_ERROR 的连接错误(参见 第 5.4.1 节)。

GOAWAY 中的最后流标识符包含发送 GOAWAY 的一方可能已对其采取或可能采取动作的编号最高的流标识符。所有编号小于或等于该标识符的流都可能以某种方式被处理。若未处理任何流,则最后流标识符可设置为 0。

如果连接在没有 GOAWAY 帧的情况下终止,则最后流标识符实际相当于可能的最高流标识符。

对于编号小于或等于最后流标识符且在连接关闭前未完全关闭的流,无法重新尝试请求、事务或任何协议活动,惟可对幂等操作(如 HTTP GET、PUT 或 DELETE)进行重试。任何使用更高编号流的协议活动都可以安全地在新连接上重试。

编号小于或等于最后流标识符的流上的活动仍可能成功完成。GOAWAY 的发送者可以通过发送 GOAWAY 并在所有进行中的流完成之前保持连接“open”状态来优雅地关闭连接。

端点MAY在情况变化时发送多个 GOAWAY。例如,在优雅关闭期间发送带有 NO_ERROR 的 GOAWAY 后,端点可能遇到需要立即终止连接的情况。最后收到的 GOAWAY 中的最后流标识符表示哪些流可能已被作用。端点MUST NOT增加其在最后流标识符中发送的值,因为对等方可能已在另一连接上重试未处理的请求。

无法重试请求的客户端会在服务器关闭连接时失去所有在飞请求。这对于可能不使用 HTTP/2 与客户端通信的中间件尤为如此。试图优雅关闭连接的服务器SHOULD先发送一个最后流标识符设置为 231-1 且错误码为 NO_ERROR 的初始 GOAWAY,向客户端表示即将关闭且禁止发起更多请求。在允许至少一个往返时间以便处理任何进行中的流创建后,服务器MAY发送另一个带有更新最后流标识符的 GOAWAY,从而确保连接可以在不丢失请求的情况下被干净地关闭。

在发送 GOAWAY 后,发送方可以丢弃由接收方发起且编号高于已指示最后流标识符的流的帧。然而,任何改变连接状态的帧不能完全被忽略。例如,HEADERSPUSH_PROMISECONTINUATIONMUST被最小处理,以确保字段段压缩维护的状态一致(参见 第 4.3 节);类似地,DATA 帧MUST计入连接的流量控制窗口。不处理这些帧会导致流量控制或字段段压缩状态不同步。

GOAWAY 帧还包含一个 32 位错误码(参见 第 7 节),指出关闭连接的原因。

端点MAY在任何 GOAWAY 帧的帧负载中附加不透明数据。附加调试数据仅用于诊断目的且不承载语义值。调试信息可能包含安全或隐私敏感数据。对日志或以其他方式持久保存的调试数据MUST采取足够的防护以防止未授权访问。

6.9. WINDOW_UPDATE

WINDOW_UPDATE 帧(type=0x08)用于实现流量控制;概述见 第 5.2 节

流量控制在两个层面上运行:每个单独流以及整个连接。

这两类流量控制都是逐跳(hop-by-hop)的,即仅在两个端点之间。中间件不会在依赖的连接间转发 WINDOW_UPDATE 帧。然而,任何接收方对数据传输的节流都可能间接导致流量控制信息向原始发送方传播。

流量控制仅适用于被标识为受流控的帧。在本文档定义的帧类型中,仅包括 DATA 帧。免于流量控制的帧MUST被接受并处理,除非接收方无法分配资源来处理该帧。若接收方无法接受帧,接收方MAY以类型为 FLOW_CONTROL_ERROR 的流错误(参见 第 5.4.2 节)或连接错误(参见 第 5.4.1 节)作出响应。

WINDOW_UPDATE Frame {
  Length (24) = 0x04,
  Type (8) = 0x08,

  Unused Flags (8),

  Reserved (1),
  Stream Identifier (31),

  Reserved (1),
  Window Size Increment (31),
}

图 11:WINDOW_UPDATE 帧格式

Length、Type、Unused Flag(s)、Reserved 和 Stream Identifier 字段在 第 4 节 中描述。WINDOW_UPDATE 帧的帧负载由一位保留位加上一个无符号 31 位整数组成,该整数指示发送方可在现有流量控制窗口之外额外发送的八位组数。窗口增量的合法范围为 1 到 231-1(2,147,483,647)八位组。

WINDOW_UPDATE 帧不定义任何标志。

WINDOW_UPDATE 帧可以特定于某个流,也可以针对整个连接。在前一种情况下,帧的流标识符指示受影响的流;在后一种情况下,值 "0" 表示整个连接为该帧的主体。

接收方MUST将窗口增量为 0 的 WINDOW_UPDATE 视为类型为 PROTOCOL_ERROR 的流错误(参见 第 5.4.2 节);对连接流量控制窗口的错误MUST视为连接错误(参见 第 5.4.1 节)。

发送过带有 END_STREAM 标志的帧的端点也可以发送 WINDOW_UPDATE。这意味着接收方可能在“half-closed (remote)”或“closed”状态的流上收到 WINDOW_UPDATE。接收方MUST NOT将此视为错误(参见 第 5.1 节)。

接收方在接收受流控帧时MUST始终将其计入连接流量控制窗口,除非接收方将此视为连接错误(参见 第 5.4.1 节)。即便该帧存在错误,也必须这样做。发送方将该帧计入流量控制窗口,但若接收方不计入,则发送方与接收方的流量控制窗口可能不同步。

长度不是 4 个八位组的 WINDOW_UPDATE 帧MUST被视为类型为 FRAME_SIZE_ERROR 的连接错误(参见 第 5.4.1 节)。

6.9.1. 流量控制窗口

HTTP/2 的流量控制通过每个发送方在每个流上维护的窗口实现。流量控制窗口是一个简单的整数值,指示发送方被允许传输的八位组数;因此其大小衡量了接收方的缓冲能力。

适用两类流量控制窗口:流级别窗口和连接级别窗口。发送方MUST NOT发送长度超过接收方通告的任一流量控制窗口可用空间的受流控帧。具有 END_STREAM 标志且长度为零的帧(即空的 DATA 帧)MAY在两种流量控制窗口均无可用空间时发送。

在流量控制计算中,不计入 9 八位组的帧头。

发送受流控帧后,发送方将两种窗口中可用的空间均按已发送帧的长度减少。

帧的接收方在消费数据并释放窗口空间时发送 WINDOW_UPDATE 帧。流级别和连接级别窗口分别发送各自的 WINDOW_UPDATE。建议接收方避免发送非常小增量的 WINDOW_UPDATE;参见 RFC 1122 第 4.2.3.3 节

接收 WINDOW_UPDATE 的发送方按帧中指定的数量更新相应的窗口。

发送方MUST NOT允许流量控制窗口超过 231-1 八位组。如果发送方接收到使窗口超过该最大值的 WINDOW_UPDATE,则应适当终止流或连接:对流发送带有 FLOW_CONTROL_ERROR 错误码的 RST_STREAM;对连接发送带有 FLOW_CONTROL_ERROR 错误码的 GOAWAY

发送方的受流控帧与接收方的 WINDOW_UPDATE 帧完全异步。这一属性允许接收方积极地更新发送方维护的窗口大小以防止流停滞。

6.9.2. 初始流量控制窗口大小

在 HTTP/2 连接首次建立时,新流的初始流量控制窗口大小为 65,535 八位组。连接级别的流量控制窗口也为 65,535 八位组。双方端点可以通过在 SETTINGS 帧中包含 SETTINGS_INITIAL_WINDOW_SIZE 的值来调整新流的初始窗口大小。连接级别窗口只能通过 WINDOW_UPDATE 帧更改。

在收到设置 SETTINGS_INITIAL_WINDOW_SIZE 值的 SETTINGS 帧之前,端点在发送受流控帧时只能使用默认的初始窗口大小。同样,连接流量控制窗口在收到 WINDOW_UPDATE 之前基于默认初始窗口大小设置。

除了改变尚未活动的流的流控窗口外,SETTINGS 帧还可以更改具有活动流控窗口(即处于“open”或“half-closed (remote)”状态的流)的初始窗口大小。当 SETTINGS_INITIAL_WINDOW_SIZE 的值发生变化时,接收方MUST按新值与旧值之差调整其维护的所有流的流控窗口大小。

SETTINGS_INITIAL_WINDOW_SIZE 的更改可能导致某个流的可用窗口空间变为负值。发送方MUST跟踪负的流量控制窗口,并在收到使窗口变为正的 WINDOW_UPDATE 之前MUST NOT发送新的受流控帧。

例如,如果客户端在连接建立时立即发送 60 KB,而服务器将初始窗口大小设置为 16 KB,则客户端在接收到该 SETTINGS 帧后会重新计算可用流控窗口为 -44 KB。客户端在 WINDOW_UPDATE 帧将窗口恢复为正值之前,保持负窗口并等待恢复后才可继续发送。

SETTINGS 帧不能更改连接级别的流量控制窗口。

端点MUST将导致任一流量控制窗口超出最大大小的 SETTINGS_INITIAL_WINDOW_SIZE 更改视为类型为 FLOW_CONTROL_ERROR 的连接错误(参见 第 5.4.1 节)。

6.9.3. 减少流窗口大小

希望使用比当前大小更小的流量控制窗口的接收方可以发送新的 SETTINGS 帧。然而,接收方MUST准备接收超过该窗口大小的数据,因为发送方可能在处理 SETTINGS 帧之前发送超过更低限制的数据。

在发送降低初始流量控制窗口大小的 SETTINGS 帧之后,接收方MAY继续处理超过流控限制的流。允许这些流继续并不意味着接收方可以立即减少其为流量控制窗口保留的空间。由于需要 WINDOW_UPDATE 帧以允许发送方恢复发送,这些流的进展也可能停滞。接收方MAY改为对受影响的流发送带有 FLOW_CONTROL_ERROR 错误码的 RST_STREAM

6.10. CONTINUATION

CONTINUATION 帧(type=0x09)用于继续字段块片段的序列(参见 第 4.3 节)。只要前面的帧位于同一流并且是未置 END_HEADERS 标志的 HEADERSPUSH_PROMISE 或 CONTINUATION 帧,就可以发送任意数量的 CONTINUATION 帧。

CONTINUATION Frame {
  Length (24),
  Type (8) = 0x09,

  Unused Flags (5),
  END_HEADERS Flag (1),
  Unused Flags (2),

  Reserved (1),
  Stream Identifier (31),

  Field Block Fragment (..),
}

图 12:CONTINUATION 帧格式

Length、Type、Unused Flag(s)、Reserved 和 Stream Identifier 字段在 第 4 节 中描述。CONTINUATION 帧负载包含字段块片段(参见 第 4.3 节)。

CONTINUATION 帧定义了以下标志:

END_HEADERS (0x04):
置位时表示该帧结束一个字段块(参见 第 4.3 节)。
如果未置 END_HEADERS 标志,则此帧MUST后续另一个 CONTINUATION 帧。接收方MUST将任何其他类型的帧或来自不同流的帧视为类型为 PROTOCOL_ERROR 的连接错误(参见 第 5.4.1 节)。

CONTINUATION 帧按 第 4.3 节 定义的方式改变连接状态。

CONTINUATION 帧MUST关联到某个流。如果接收到 Stream Identifier 字段为 0x00 的 CONTINUATION 帧,接收方MUST以类型为 PROTOCOL_ERROR 的连接错误进行响应(参见 第 5.4.1 节)。

CONTINUATION 帧MUST由未置 END_HEADERS 标志的 HEADERSPUSH_PROMISE 或 CONTINUATION 帧先行。若接收方观察到违反此规则的情况,接收方MUST以类型为 PROTOCOL_ERROR 的连接错误进行响应(参见 第 5.4.1 节)。


7. 错误代码

错误代码是 32 位字段,用于在 RST_STREAMGOAWAY 帧中传达流或连接错误的原因。

错误代码共享通用代码空间。某些错误代码仅适用于流或整个连接,在另一种上下文中没有定义语义。

定义了以下错误代码:

NO_ERROR (0x00):
相关情况并非由错误导致。例如,GOAWAY 可能包含此代码以指示连接的优雅关闭。
PROTOCOL_ERROR (0x01):
端点检测到不具体的协议错误。当没有更具体的错误代码可用时使用此错误。
INTERNAL_ERROR (0x02):
端点遇到意外的内部错误。
FLOW_CONTROL_ERROR (0x03):
端点检测到其对等方违反了流量控制协议。
SETTINGS_TIMEOUT (0x04):
端点发送了一个 SETTINGS 帧但未在及时的时间内收到响应。见 第 6.5.3 节(“设置同步”)。
STREAM_CLOSED (0x05):
在流半关闭后,端点收到了一个帧。
FRAME_SIZE_ERROR (0x06):
端点收到了大小无效的帧。
REFUSED_STREAM (0x07):
在进行任何应用层处理之前,端点拒绝了该流(详情见 第 8.7 节)。
CANCEL (0x08):
端点使用此错误代码表明该流不再需要。
COMPRESSION_ERROR (0x09):
端点无法维护连接的字段段压缩上下文。
CONNECT_ERROR (0x0a):
对 CONNECT 请求(第 8.5 节)建立的连接已被重置或异常关闭。
ENHANCE_YOUR_CALM (0x0b):
端点检测到其对等方表现出可能导致过高负载的行为。
INADEQUATE_SECURITY (0x0c):
底层传输具有不满足最低安全要求的属性(见 第 9.2 节)。
HTTP_1_1_REQUIRED (0x0d):
端点要求使用 HTTP/1.1 而不是 HTTP/2。

未知或不被支持的错误代码MUST NOT触发任何特殊行为。实现MAY将它们等同于 INTERNAL_ERROR 来处理。


8. 在 HTTP/2 中表达 HTTP 语义

HTTP/2 是 HTTP 消息抽象的一个实现(见 RFC 9110 第 6 节)。

8.1. HTTP 消息分帧

客户端在新流上发送 HTTP 请求,使用先前未使用的流标识符(见 第 5.1.1 节)。服务器在与请求相同的流上发送 HTTP 响应。

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

  1. 一个 HEADERS 帧(后接零个或多个 CONTINUATION 帧),包含头部段(参见 RFC 9110 第 6.3 节),
  2. 零个或多个包含消息内容的 DATA 帧(参见 RFC 9110 第 6.4 节),以及
  3. 可选地,一个 HEADERS 帧(后接零个或多个 CONTINUATION 帧)包含尾部段(如存在,参见 RFC 9110 第 6.5 节)。

仅对于响应,服务器MAY在包含最终响应的 HEADERS 帧之前发送任意数量的临时响应。临时响应由一个 HEADERS 帧(可能后接零个或多个 CONTINUATION 帧)组成,包含信息性(1xx)HTTP 响应的控制数据和头部段。携带信息性状态码的、带有 END_STREAM 标志的 HEADERS 帧是格式错误的(见 第 8.1.1 节)。

序列中的最后一帧带有 END_STREAM 标志,注意带有 END_STREAM 标志的 HEADERS 帧后仍可跟随承载字段块剩余片段的 CONTINUATION 帧。

HEADERS 帧与可能随后出现的任何 CONTINUATION 帧之间,不得出现来自任何流的其他帧。

HTTP/2 使用 DATA 帧承载消息内容。在 HTTP/2 中不能使用在 HTTP/1.1 中定义的 chunked 传输编码;参见 第 8.2.2 节

尾部字段由也终止流的字段块承载。即,尾部字段由一个 HEADERS 帧开始,后接零个或多个 CONTINUATION 帧,其中该 HEADERS 帧带有 END_STREAM 标志。尾部MUST NOT包含伪首部字段(见 第 8.3 节)。接收方若在尾部中收到伪首部字段,MUST将该请求或响应视为格式错误(见 第 8.1.1 节)。

接收在打开请求的 HEADERS 帧之后且未置 END_STREAM 的另一个 HEADERS 帧,或者在接收到包含最终(非信息性)状态码的 HEADERS 帧之后接收额外的 HEADERS,接收方MUST将对应的请求或响应视为格式错误(见 第 8.1.1 节)。

一次 HTTP 请求/响应交换完全消耗单个流。请求以将流置为“open”的 HEADERS 帧开始。请求以带有 END_STREAM 标志的帧结束,这会使客户端的流变为“half-closed (local)”,服务器的流变为“half-closed (remote)”。响应流以零个或多个信息性响应(HEADERS 帧)开始,随后是包含最终状态码的 HEADERS 帧。

在服务器发送——或客户端接收——带有 END_STREAM 标志的帧(包括完成字段块所需的任何 CONTINUATION 帧)后,HTTP 响应即完成。如果响应不依赖于尚未发送和接收的请求部分,服务器可以在客户端发送完整请求之前发送完整响应。若成立,服务器MAY在发送完整响应后通过发送带有 RST_STREAM 且错误码为 NO_ERROR 的帧来请求客户端无错误地中止请求传输。客户端MUST NOT因接收到此类 RST_STREAM 而丢弃响应,尽管客户端可出于其他原因随时丢弃响应。

8.1.1. 格式错误的消息

格式错误的请求或响应是指帧序列在其它方面有效但因存在多余帧、禁止的字段或伪首部字段、缺少强制伪首部字段、包含大写字段名或在某些情况下字段名/值无效等原因而无效(见 第 8.2 节)。

包含消息内容的请求或响应可以包含 content-length 头字段。如果 content-length 头字段的值不等于形成内容的 DATA 帧负载长度之和(除非该消息被定义为无内容),则该请求或响应也是格式错误的。例如,204 或 304 响应无内容,HEAD 请求的响应亦无内容。被定义为无内容的响应MAY包含非零的 content-length 头字段,尽管 DATA 帧中未包含内容。

处理 HTTP 请求或响应的中间件(即任何不充当隧道的中间件)MUST NOT转发格式错误的请求或响应。被检测到的格式错误请求或响应MUST被视为类型为 PROTOCOL_ERROR 的流错误(见 第 5.4.2 节)。

对于格式错误的请求,服务器MAY在关闭或重置流之前发送 HTTP 响应。客户端MUST NOT接受格式错误的响应。

逐步处理消息的端点可能在识别请求或响应为格式错误之前已执行了一些处理。例如,在未接收完整请求的情况下,可能生成信息性或 404 状态码。同样,中间件在检测到错误之前可能已转发不完整的消息。当响应不依赖于请求其余部分时,服务器MAY在接收完整请求前生成最终响应。

这些要求旨在防范若干针对 HTTP 的常见攻击;它们被刻意制定得严格,因为宽松的行为可能使实现暴露于这些漏洞之中。

8.2. HTTP 字段

HTTP 字段(见 RFC 9110 第 5 节)通过 HTTP/2 的 HEADERS、CONTINUATION 和 PUSH_PROMISE 帧传输,并使用 HPACK 进行压缩。

构造 HTTP/2 消息时,字段名MUST转换为小写。

8.2.1. 字段有效性

HTTP 中对字段名和值的定义禁止某些字符,而 HPACK 可能能够传递这些字符。HTTP/2 实现SHOULD根据 RFC 9110 第 5.1 和 5.5 节的定义验证字段名和值,并将包含禁止字符的消息视为格式错误(见 第 8.1.1 节)。

未能验证字段可被用于请求走私攻击。特别是,当消息通过 HTTP/1.1 转发时,未验证的字段可能使攻击成为可能,因为像回车(CR)、换行(LF)和冒号(COLON)等字符用作分隔符。实现MUST执行以下对字段名和值的最小验证:

  • 字段名MUST NOT包含范围 0x00-0x20、0x41-0x5a、或 0x7f-0xff(包含端点)的字符。这明确排除了所有不可见的 ASCII 字符、ASCII 空格(0x20)以及大写字母('A' 到 'Z',ASCII 0x41 到 0x5a)。
  • 除伪首部字段外(其名称以单个冒号开头),字段名MUST NOT包含冒号(ASCII COLON,0x3a)。
  • 字段值MUST NOT在任何位置包含零值(ASCII NUL,0x00)、换行(ASCII LF,0x0a)或回车(ASCII CR,0x0d)。
  • 字段值MUST NOT以 ASCII 空白字符(ASCII SP 或 HTAB,0x20 或 0x09)开始或结束。

包含违反上述任一条件字段的请求或响应MUST被视为格式错误(见 第 8.1.1 节)。特别是,不在转发消息时处理字段的中间件MUST NOT转发包含上述任何禁止值的字段。

当请求消息违反这些要求时,实现SHOULD返回 400(Bad Request)状态码,除非有更合适的状态码或无法发送(例如错误发生在尾部字段中)。

8.2.2. 连接特定的首部字段

HTTP/2 不使用 Connection 头字段来指示连接特定的首部字段;在此协议中,连接特定的元数据通过其他方式传递。端点MUST NOT生成包含连接特定首部字段的 HTTP/2 消息。这包括 Connection 头字段以及在 RFC 9110 第 7.6.1 节中列为具有连接特定语义的字段(即 Proxy-ConnectionKeep-AliveTransfer-EncodingUpgrade)。任何包含连接特定首部字段的消息MUST被视为格式错误(见 第 8.1.1 节)。

唯一的例外是 TE 头字段,它MAY出现在 HTTP/2 请求中;当出现时,其值MUST NOT包含除 "trailers" 以外的任何值。

将 HTTP/1.x 消息转换为 HTTP/2 的中间件MUST移除连接特定的首部字段,否则它们的消息将被其他 HTTP/2 端点视为格式错误(见 RFC 9110 第 7.6.1 节)。

8.2.3. 压缩 Cookie 头字段

Cookie header field 使用分号 (";") 分隔 cookie-pair(或“crumbs”)。该头字段包含多个值,但不使用逗号 (",") 作为分隔符,因此无法将 cookie-pair 放在多个字段行上发送。这会显著降低压缩效率,因为对单个 cookie-pair 的更新会使存储在 HPACK 表中的字段行失效。

为提高压缩效率,Cookie 头字段MAY被拆分为多个首部字段,每个字段包含一个或多个 cookie-pair。如果在解压后存在多个 Cookie 头字段,这些字段在传递到非 HTTP/2 上下文(例如 HTTP/1.1 连接或通用 HTTP 服务器应用)之前,MUST使用两个八位组分隔符 0x3b, 0x20(ASCII 字符串 "; ")将它们连接成单个八位串。

因此,下面两组 Cookie 头字段在语义上等价。

cookie: a=b; c=d; e=f

cookie: a=b
cookie: c=d
cookie: e=f

8.3. HTTP 控制数据

HTTP/2 使用以 ':' 字符(ASCII 0x3a)开头的特殊伪首部字段来传达消息控制数据(见 RFC 9110 第 6.2 节)。

伪首部字段不是 HTTP 首部字段。端点MUST NOT生成除本文档定义之外的伪首部字段。注意扩展可以协商使用额外的伪首部字段;参见 第 5.5 节

伪首部字段仅在其定义的上下文中有效。为请求定义的伪首部字段MUST NOT出现在响应中;为响应定义的伪首部字段MUST NOT出现在请求中。伪首部字段MUST NOT出现在尾部段中。端点在请求或响应中包含未定义或无效的伪首部字段时,MUST将其视为格式错误(见 第 8.1.1 节)。

所有伪首部字段MUST在字段块中出现在所有常规字段行之前。任何包含在字段块中出现在常规字段行之后的伪首部字段的请求或响应MUST被视为格式错误(见 第 8.1.1 节)。

相同的伪首部字段名MUST NOT在字段块中出现多次。包含重复伪首部字段名的请求或响应字段块MUST被视为格式错误(见 第 8.1.1 节)。

8.3.1. 请求伪首部字段

为 HTTP/2 请求定义了以下伪首部字段:

  • :method” 伪首部字段包含 HTTP 方法(见 RFC 9110 第 9 节)。

  • :scheme” 伪首部字段包含请求目标中 scheme 部分。方案来自目标 URI 的 scheme,或者来自已转换请求的 scheme(例如见 RFC 9112 第 3.3 节)。对于 CONNECT 请求省略 scheme。

    :scheme” 不仅限于 "http" 和 "https"。代理或网关可以翻译对非 HTTP 方案的请求,使得使用 HTTP 与非 HTTP 服务交互成为可能。

  • :authority” 伪首部字段传达目标 URI 的 authority 部分。接收 HTTP/2 请求的一方MUST NOT在存在 “:authority” 时使用 Host 头字段来确定目标 URI。

    直接生成 HTTP/2 请求的客户端MUST使用 “:authority” 伪首部字段传递 authority 信息,除非没有要传递的 authority 信息(在这种情况下不得生成 “:authority”)。

    客户端不得生成与 “:authority” 伪首部字段不同的 Host 头字段。服务器SHOULD将包含与 “:authority” 不同实体的 Host 字段视为格式错误。比较字段值时需要规范化(见 RFC 3986 第 6.2 节)。源服务器可应用任意规范化方法,而其他服务器MUST执行基于 scheme 的规范化。

    转发请求到 HTTP/2 的中间件MUST使用原始请求控制数据的 authority 信息构造 “:authority” 伪首部字段,除非原始请求目标 URI 不包含 authority 信息(在此情况下不得生成 “:authority”)。注意 Host 头字段并非该信息的唯一来源。

    需要生成 Host 头字段的中间件(例如为构造 HTTP/1.1 请求)MUST使用 “:authority” 的值作为 Host 字段的值,除非中间件也改变了请求目标。这样可以替换任何现有的 Host 字段以避免 HTTP 路由中的潜在漏洞。

    转发请求到 HTTP/2 的中间件MAY保留任何 Host 头字段。

    注意 CONNECT 或星号形式的 OPTIONS 请求的请求目标从不包含 authority 信息。

    对于 "http" 或 "https" scheme 的 URI,“:authorityMUST NOT包含已弃用的 userinfo 子组件。

  • :path” 伪首部字段包含目标 URI 的路径和查询部分(即 absolute-path 的产生式,且可选地在其后有 '?' 和 query)。对于星号形式的 OPTIONS 请求,":path" 的值为 '*'.

    对于 "http" 或 "https" URI,该伪首部字段MUST NOT为空;若这些 URI 不包含路径组件,则MUST使用 '/' 作为值。此规则的例外包括:

    • 对不包含路径组件的 "http" 或 "https" URI 的 OPTIONS 请求;这些MUST使用值 '*' 的 ":path"(见 RFC 9110 第 7.1 节)。
    • CONNECT 请求(见第 8.5 节),其中 ":path" 伪首部字段被省略。

所有 HTTP/2 请求MUST包含恰好一个有效的 ":method"、":scheme" 和 ":path" 伪首部字段,除非它们是 CONNECT 请求。省略强制伪首部字段的 HTTP 请求是格式错误的(见 第 8.1.1 节)。

单个 HTTP/2 请求不携带显式的协议版本指示。所有 HTTP/2 请求隐含的协议版本为 "2.0"。

8.3.2. 响应伪首部字段

对于 HTTP/2 响应,定义了单个 ":status" 伪首部字段,用于承载 HTTP 状态码字段。该伪首部字段MUST包含在所有响应中,包括临时响应;否则响应为格式错误(见 第 8.1.1 节)。

HTTP/2 响应隐含的协议版本为 "2.0"。

8.4. 服务器推送

HTTP/2 允许服务器在与先前客户端发起的请求相关联时,预先向客户端发送(或“推送”)响应(以及相应的“被承诺”请求)。

服务器推送的设计目标是通过预测客户端随后会发出的请求来改善客户端感知的性能,从而为这些请求节省一次往返。例如,请求 HTML 常常会随后请求该页面引用的样式表和脚本。若这些请求被推送,客户端就不需要等待在 HTML 中接收到它们的引用再发出单独请求。

实际上,服务器推送难以有效使用,因为它要求服务器正确预测客户端会发出的附加请求,同时考虑缓存、内容协商和用户行为等因素。预测错误可能导致性能下降,因为额外的数据占用了带宽,可能与更重要的响应产生竞争。尤其是,推送大量数据可能会与更重要的响应发生争用。

客户端可以请求禁用服务器推送,但这是逐跳协商的。可以将 SETTINGS_ENABLE_PUSH 设置为 0 以表示禁用服务器推送。

被承诺的请求MUST是安全的并且可缓存(参见 RFC 9110 中相应部分)。被承诺的请求不得包含任何内容或尾部段。客户端在接收到不具备可缓存性、或未知为安全、或表明存在请求内容的被承诺请求时,MUST使用类型为 PROTOCOL_ERROR 的流错误重置被承诺的流。

如果客户端实现了 HTTP 缓存,且被推送的响应可缓存,则客户端可以存储它们。被推送的响应在被承诺流标识符标识的流仍然打开时,被视为已在源服务器上成功验证(例如当响应具有 "no-cache" 指令时)。

不可缓存的被推送响应MUST NOT被任何 HTTP 缓存存储,但它们MAY单独提供给应用程序使用。

服务器MUST在 ":authority" 伪首部字段中包含服务器有权代表之值。客户端在接收到由服务器并非权威的 PUSH_PROMISE 时,MUST将其视为类型为 PROTOCOL_ERROR 的流错误。

中间件可以从服务器接收推送并选择不将其转发给客户端。换言之,如何使用推送信息由中间件决定。同样,中间件也可能选择在没有服务器动作的情况下向客户端发起额外的推送。

客户端不能推送。因此,服务器MUST将接收客户端的 PUSH_PROMISE 帧视为类型为 PROTOCOL_ERROR 的连接错误。服务器不得将 SETTINGS_ENABLE_PUSH 设置为非 0 的值(见第 6.5.2 节)。

8.4.1. 推送请求

服务器推送在语义上等同于服务器对请求的响应;但在此情况下,该请求由服务器作为 PUSH_PROMISE 帧发送。

PUSH_PROMISE 帧包含字段块,内含服务器归属该请求的控制数据和完整的请求头字段集合。无法对包含消息内容的请求进行推送响应。

被承诺的请求总是与客户端的显式请求关联。服务器发送的 PUSH_PROMISE 帧在该显式请求的流上发送。PUSH_PROMISE 帧还包含一个由服务器从可用流标识符中选择的被承诺流标识符(见第 5.1.1 节)。

PUSH_PROMISE 和随后任何 CONTINUATION 帧中的头字段MUST构成一组有效且完整的请求头字段集合(见第 8.3.1 节)。服务器MUST在 ":method" 伪首部字段中包含一个安全且可缓存的方法。如果客户端收到不完整或无效头字段集合的 PUSH_PROMISE,或 ":method" 标识的方法非安全,则客户端MUST在被承诺流上使用类型为 PROTOCOL_ERROR 的流错误响应。

服务器SHOULD在发送任何引用被承诺响应的帧之前发送 PUSH_PROMISE 帧,以避免客户端在接收到任何 PUSH_PROMISE 帧之前发起相应请求的竞争。

例如,若服务器接收到对包含多个图像文件嵌入链接的文档的请求并选择将这些图像推送给客户端,则在包含图像链接的 DATA 帧之前发送 PUSH_PROMISE 帧可以确保客户端在发现嵌入链接之前就知道资源将被推送。

PUSH_PROMISEMUST NOT由客户端发送。

服务器可以在任何客户端发起的流上发送 PUSH_PROMISE 帧,但该流相对于服务器必须处于“open”或“half-closed (remote)” 状态。PUSH_PROMISE 帧可与构成响应的帧交错,但不得与构成单个字段块的 HEADERSCONTINUATION 帧交错。

发送 PUSH_PROMISE 帧会创建一个新流,并将该流置为服务器的 “reserved (local)” 状态和客户端的 “reserved (remote)” 状态。

8.4.2. 推送响应

在发送 PUSH_PROMISE 帧之后,服务器可以在使用被承诺流标识符的服务器发起流上开始传送被推送的响应,服务器在该流上以与第 8.1 节定义相同的帧序列传输 HTTP 响应。该流在发送初始 HEADERS 帧后变为对服务器的 “half-closed (remote)” 状态(对客户端为 “half-closed (local)”)。

一旦客户端接收到 PUSH_PROMISE 并选择接受被推送的响应,客户端SHOULD NOT在被承诺流关闭之前为该被承诺响应发起任何请求。

如果客户端出于任何原因决定不希望从服务器接收被推送的响应,或服务器在开始发送被承诺响应方面过慢,客户端可以发送 RST_STREAM 帧,使用 CANCELREFUSED_STREAM 错误码并引用被推送流的标识符。

客户端可以使用 SETTINGS_MAX_CONCURRENT_STREAMS 设置限制服务器可并发推送的响应数量。将该设置通告为零会阻止服务器打开推送响应所需的流;但这并不阻止服务器使用 PUSH_PROMISE 保留流,因为保留的流不计入并发流限制。若客户端不希望接收被推送资源,需要重置任何不需要的保留流或将 SETTINGS_ENABLE_PUSH 设为 0。

接收被推送响应的客户端MUST验证服务器是否为权威(见第 10.1 节)或提供被推送响应的代理是否已为对应请求配置。例如,仅为 example.com DNS-ID 提供证书的服务器不允许为 <https://www.example.org/doc> 推送响应。

PUSH_PROMISE 流的响应以 HEADERS 帧开始,这会立即将流置为服务器的 “half-closed (remote)” 状态和客户端的 “half-closed (local)” 状态,并以带有 END_STREAM 标志的帧结束,该帧将流置为 “closed”。

8.5. CONNECT 方法

CONNECT 方法用于将 HTTP 连接转换为到远程主机的隧道。CONNECT 主要与 HTTP 代理一起使用,以建立到源服务器的 TLS 会话,从而访问 "https" 资源。

在 HTTP/2 中,CONNECT 方法在单个 HTTP/2 流上建立到远程主机的隧道,而不是将整个连接转换为隧道。CONNECT 头部段按照第 8.3.1 节定义来构造,但有若干区别。具体而言:

  • :method” 伪首部字段被设为 CONNECT
  • :scheme” 和 “:path” 伪首部字段MUST被省略。
  • :authority” 伪首部字段包含要连接的主机和端口(等价于 CONNECT 请求目标的 authority 形式)。

不符合这些限制的 CONNECT 请求为格式错误(见 第 8.1.1 节)。

支持 CONNECT 的代理会为 ":authority" 伪首部字段中标识的主机和端口建立一个 TCP 连接。一旦该连接成功建立,代理向客户端发送包含 2xx 系列状态码的 HEADERS 帧。

在每一端首次发送的 HEADERS 帧之后,所有后续的 DATA 帧对应于在该 TCP 连接上发送的数据。代理将客户端发送的任何 DATA 帧的帧负载转发到 TCP 服务器;从 TCP 服务器接收的数据由代理组装为 DATA 帧。除 DATA 或流管理帧(如 RST_STREAMWINDOW_UPDATEPRIORITY)外的帧类型不得在已连接的流上发送,若接收则MUST视为流错误。

TCP 连接可以由任一端关闭。带有 END_STREAM 标志的 DATA 帧被视为与 TCP FIN 位等价。客户端在接收到带有 END_STREAM 标志的帧后,预期发送一个带有 END_STREAM 标志的 DATA 帧。代理在接收到带有 END_STREAM 标志的 DATA 帧时,会在最后一个 TCP 段上以 FIN 位发送附带数据;代理在接收到带有 FIN 位的 TCP 段时,会发送带有 END_STREAM 标志的 DATA 帧。注意最终的 TCP 段或 DATA 帧可能为空。

TCP 连接错误以 RST_STREAM 方式报告。代理将 TCP 连接的任何错误(包括接收到带有 RST 位的 TCP 段)视为类型为 CONNECT_ERROR 的流错误。相应地,代理在检测到流或 HTTP/2 连接错误时MUST发送带有 RST 位的 TCP 段。

8.6. Upgrade 首部字段

HTTP/2 不支持 101(Switching Protocols)信息性状态码。

101(Switching Protocols)的语义不适用于多路复用协议。类似功能可通过使用扩展 CONNECT(RFC 8441)实现,其他协议也能使用 HTTP/2 用于协商的相同机制。

8.7. 请求可靠性

通常,当发生错误时,HTTP 客户端无法重试非幂等请求,因为无法确定错误的性质。在错误发生之前可能已进行了一些服务器处理,重新尝试请求可能导致不良后果。

HTTP/2 提供两种机制以向客户端保证请求未被处理:

  • GOAWAY 帧指示可能被处理的最高流编号。编号更高的流上的请求因此可保证安全地重试。
  • REFUSED_STREAM 错误码可以包含在 RST_STREAM 帧中,以指示在任何处理发生之前流被关闭。发送在该重置流上的任何请求都可安全重试。

未被处理的请求尚未失败;客户端MAY自动重试它们,即使这些请求使用非幂等方法。

服务器MUST NOT声明某流未被处理,除非它能保证此事实。如果某流上的帧已被传递给应用层,则MUST NOT对该流使用 REFUSED_STREAM,并且 GOAWAY 帧必须包含大于或等于该流标识符的值。

此外,PING 帧为客户端提供了一种简单测试连接的方法。闲置的连接可能会被某些中间盒静默丢弃绑定。PING 帧允许客户端在不发送请求的情况下安全地测试连接是否仍然活动。

8.8. 示例

本节展示 HTTP/1.1 的请求和响应,并说明等效的 HTTP/2 请求和响应示例。

8.8.1. 简单请求

一个 HTTP GET 请求包含控制数据和无消息内容的请求头,因此可作为单个 HEADERS 帧传输,后接零或多个包含序列化请求头字段块的 CONTINUATION 帧。下面的 HEADERS 帧同时置有 END_HEADERS 和 END_STREAM 标志;不发送任何 CONTINUATION 帧。

  GET /resource HTTP/1.1           HEADERS
  Host: example.org          ==>     + END_STREAM
  Accept: image/jpeg                 + END_HEADERS
                                       :method = GET
                                       :scheme = https
                                       :authority = example.org
                                       :path = /resource
                                       host = example.org
                                       accept = image/jpeg

8.8.2. 简单响应

类似地,仅包含控制数据和响应头的响应作为一个 HEADERS 帧(后接零或多个 CONTINUATION)传输,包含序列化的响应头字段块。

  HTTP/1.1 304 Not Modified        HEADERS
  ETag: "xyzzy"              ==>     + END_STREAM
  Expires: Thu, 23 Jan ...           + END_HEADERS
                                       :status = 304
                                       etag = "xyzzy"
                                       expires = Thu, 23 Jan ...

8.8.3. 复杂请求

包含控制数据、请求头及消息内容的 HTTP POST 请求被传输为一个 HEADERS 帧(后接零或多个 CONTINUATION),随后是一个或多个 DATA 帧,最后的 CONTINUATION(或 HEADERS)帧置有 END_HEADERS,最后的 DATA 帧置有 END_STREAM。

  POST /resource HTTP/1.1          HEADERS
  Host: example.org          ==>     - END_STREAM
  Content-Type: image/jpeg           - END_HEADERS
  Content-Length: 123                  :method = POST
                                       :authority = example.org
                                       :path = /resource
  {binary data}                        :scheme = https

                                   CONTINUATION
                                     + END_HEADERS
                                       content-type = image/jpeg
                                       host = example.org
                                       content-length = 123

                                   DATA
                                     + END_STREAM
                                   {binary data}

注意,构成任何给定字段行的数据可以分布在字段块片段之间。示例中字段行分配到帧的方式仅为说明性。

8.8.4. 带主体的响应

包含控制数据、响应头及消息内容的响应作为一个 HEADERS 帧(后接零或多个 CONTINUATION),随后是一个或多个 DATA 帧传输,序列中的最后一个 DATA 帧置有 END_STREAM 标志:

  HTTP/1.1 200 OK                  HEADERS
  Content-Type: image/jpeg   ==>     - END_STREAM
  Content-Length: 123                + END_HEADERS
                                       :status = 200
  {binary data}                        content-type = image/jpeg
                                       content-length = 123

                                   DATA
                                     + END_STREAM
                                   {binary data}

8.8.5. 信息性响应

使用除 101 外的 1xx 状态码的信息性响应作为一个 HEADERS 帧传输,后接零或多个 CONTINUATION 帧。

尾部段作为字段块在请求或响应字段块和所有 DATA 帧发送完之后发送。组成尾部段的字段块所起始的 HEADERS 帧带有 END_STREAM 标志。

下面的示例包含一个 100 (Continue) 状态码(作为对包含 Expect: 100-continue 的请求的响应)以及一个尾部段:

  HTTP/1.1 100 Continue            HEADERS
  Extension-Field: bar       ==>     - END_STREAM
                                     + END_HEADERS
                                       :status = 100
                                       extension-field = bar

  HTTP/1.1 200 OK                  HEADERS
  Content-Type: image/jpeg   ==>     - END_STREAM
  Transfer-Encoding: chunked         + END_HEADERS
  Trailer: Foo                         :status = 200
                                       content-type = image/jpeg
  123                                  trailer = Foo
  {binary data}
  0                                DATA
  Foo: bar                           - END_STREAM
                                   {binary data}

                                   HEADERS
                                     + END_STREAM
                                     + END_HEADERS
                                       foo = bar

9. HTTP/2 连接

本节概述了若干 HTTP 属性,这些属性可改善互操作性、降低已知安全漏洞的暴露,或减少实现差异的可能性。

9.1. 连接管理

HTTP/2 连接是持久的。为获得最佳性能,期望客户端在确定不再需要与服务器进行更多通信(例如用户导航离开某个网页)之前,不关闭连接,或直到服务器关闭连接为止。

客户端不应对给定的主机和端口对打开多个 HTTP/2 连接(该主机由 URI、选定的alternative service 或已配置的代理推导得出)。

客户端可以创建附加连接作为替换,用以替换接近耗尽可用流标识符空间的连接(见 第 5.1.1 节)、刷新 TLS 连接的密钥材料,或替换已遇到错误的连接(见 第 5.4.1 节)。

客户端可以使用不同的Server Name Indication 值或使用不同的 TLS 客户端证书对相同 IP 地址和 TCP 端口打开多个连接,但避免创建配置相同的多个连接。

鼓励服务器尽可能长时间保持连接打开,但在必要时允许终止空闲连接。当任一端点选择关闭传输层 TCP 连接时,终止端点先发送一个 GOAWAY(见 第 6.8 节)帧,以便双方能可靠地确定此前发送的帧是否已被处理,并优雅地完成或终止任何必要的剩余任务。

9.1.1. 连接重用

对源服务器建立的连接,无论是直接建立还是通过使用 CONNECT 方法(见 第 8.5 节)创建的隧道,都可以被重用于具有多个不同 URI authority 分量的请求。只要源服务器对该 URI 具有权威性(见 第 10.1 节),连接就可以重用。对于没有 TLS 的 TCP 连接,这取决于主机解析到相同的 IP 地址。

对于 "https" 资源,连接重用还取决于是否具有对该 URI 主机有效的证书。服务器呈现的证书必须满足客户端在为该 URI 主机建立新的 TLS 连接时会执行的任何检查。单个证书可用于为多个源建立权限。RFC 9110 第 4.3 节 描述了客户端如何确定服务器是否对某个 URI 具有权威性。

在某些部署中,将一个连接重用于多个来源可能导致请求被定向到错误的源服务器。例如,可能存在在中间盒上执行 TLS 终止的情况,该中间盒使用 TLS 的 Server Name Indication 扩展来选择源服务器。这意味着客户端可能向并非请求意图目标的服务器发送请求,即使该服务器在其他方面具有权威性。

不希望客户端重用连接的服务器可以通过在响应请求时返回 421(Misdirected Request)状态码来表明其对该请求并非权威(见 RFC 9110 第 15.5.20 节)。

配置为通过 HTTP/2 使用代理的客户端通过单个连接将请求定向到该代理。也就是说,通过代理发送的所有请求都重用与该代理的连接。

9.2. TLS 特性使用

对通过 TLS 的 HTTP/2 实现,必须使用 TLS 1.2 或更高版本。应遵循 通用的 TLS 使用指导,同时还需遵守对 HTTP/2 特有的一些额外限制。

TLS 实现必须支持 TLS 的 Server Name Indication (SNI) 扩展。如果通过域名识别服务器,客户端必须发送 server_name TLS 扩展,除非使用了其他方式指示目标主机。

有关协商 TLS 1.3 的 HTTP/2 部署要求见 第 9.2.3 节。TLS 1.2 部署需遵守 第 9.2.1 节第 9.2.2 节 的要求。鼓励实现提供符合这些要求的默认值,但承认最终由部署方负责合规性。

9.2.1. TLS 1.2 特性

本节描述了可与 HTTP/2 一起使用的 TLS 1.2 特性集的限制。由于部署限制,若不满足这些限制可能无法导致 TLS 协商失败。端点可以对不满足这些 TLS 要求的 HTTP/2 连接立即以类型为 INADEQUATE_SECURITY 的连接错误(见 第 5.4.1 节)终止连接。

在 TLS 1.2 上部署的 HTTP/2必须禁用压缩。TLS 压缩可能导致暴露原本不会被泄露的信息。通用压缩并不必要,因为 HTTP/2 提供了更具上下文意识的压缩功能,通常在性能和安全方面更合适。

在 TLS 1.2 上部署的 HTTP/2必须禁用重新协商。端点必须将 TLS 重新协商视为类型为 PROTOCOL_ERROR 的连接错误(见 第 5.4.1 节)。注意,禁用重新协商可能会导致由于底层密码套件可加密消息数的限制而使长连接变得不可用。

端点可以使用重新协商为握手中提供的客户端凭证提供机密保护,但任何重新协商必须在发送连接前言(connection preface)之前发生。服务器在建立连接后立刻看到重新协商请求时,请求客户端证书。

这实际上阻止了在对特定受保护资源的请求之后使用重新协商。未来的规范可能提供支持此用例的方法。或者,服务器可能使用类型为 HTTP_1_1_REQUIRED 的错误来请求客户端使用支持重新协商的协议。

实现必须为使用临时有限域 Diffie-Hellman (DHE) 的密码套件支持至少 2048 位的短暂密钥交换大小,为使用临时椭圆曲线 Diffie-Hellman (ECDHE) 的密码套件支持至少 224 位。客户端必须接受最高到 4096 位的 DHE 大小。端点将协商到比最小限制更小的密钥大小视为类型为 INADEQUATE_SECURITY 的连接错误(见 第 5.4.1 节)。

9.2.2. TLS 1.2 密码套件

在 TLS 1.2 上部署的 HTTP/2不应使用附录 A(禁止的 TLS 1.2 密码套件)中列出的任何被禁止的密码套件。

端点可以在协商出被禁止的密码套件时选择生成类型为 INADEQUATE_SECURITY 的连接错误。若部署选择使用被禁止的密码套件,则有触发连接错误的风险,除非已知潜在对等方接受该密码套件。

实现不得对协商到并非被禁止的密码套件生成此错误。因此,当客户端提供一个非被禁止的密码套件时,它们必须准备在 HTTP/2 中使用该密码套件。

被禁止的密码套件列表包含 TLS 1.2 所强制要求的密码套件,这意味着 TLS 1.2 部署可能会出现许可密码套件集合不相交的问题。为避免这种导致 TLS 握手失败的问题,使用 TLS 1.2 的 HTTP/2 部署必须支持 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,并使用 P-256 椭圆曲线。

注意,客户端可能会声明支持被禁止的密码套件以便允许与不支持 HTTP/2 的服务器建立连接。这允许服务器在 HTTP/1.1 上选择那些在 HTTP/2 中被禁止的密码套件。然而,如果应用层协议和密码套件被独立选择,这可能导致使用被禁止的密码套件协商出 HTTP/2。

9.2.3. TLS 1.3 特性

TLS 1.3 包含许多早期版本中不可用的特性。本节讨论这些特性的使用。

HTTP/2 服务器不得发送 TLS 1.3 握手后证书请求(post-handshake CertificateRequest)消息。HTTP/2 客户端必须将 TLS 握手后证书请求消息视为类型为 PROTOCOL_ERROR 的连接错误(见 第 5.4.1 节)。

对握手后认证的禁止适用于即便客户端提供了 "post_handshake_auth" TLS 扩展的情况。握手后认证支持可能独立于 ALPN 被通告。客户端可能在其他协议中提供该能力,但包含该扩展并不意味着在 HTTP/2 中支持。

TLS 1.3 定义了其他握手后消息,如 NewSessionTicket 和 KeyUpdate,因其与 HTTP/2 无直接交互,可照常使用。除非某种新类型的 TLS 消息的使用依赖于与应用层协议的交互,否则该 TLS 消息可在握手完成后发送。

TLS 早期数据(early data)可以用于发送请求,前提是遵循 RFC 8470 中的指导。客户端在早期数据中发送请求时假设服务器设置为初始值。


10. 安全注意事项

TLS 的使用对于提供本协议的许多安全属性是必要的。除非按照 第 9.2 节 中所述使用 TLS,本节中的许多声明并不成立。

10.1. 服务器权威性

HTTP/2 依赖于 HTTP 对 authority 的定义来确定服务器在提供特定响应时是否具有权威性(参见 RFC 9110 第 4.3 节)。这依赖于对于 "http" URI 方案的本地名称解析以及对于 "https" 方案的经过认证的服务器身份。

10.2. 跨协议攻击

在跨协议攻击中,攻击者促使客户端在一个协议中向理解另一种协议的服务器发起事务。攻击者可能能使该事务在第二种协议中看起来是有效事务。结合 Web 环境的能力,这可以被用来与私有网络中保护不佳的服务器交互。

完成带有 HTTP/2 ALPN 标识符的 TLS 握手可被视为对跨协议攻击的充分防护。ALPN 提供了服务器愿意继续使用 HTTP/2 的积极指示,从而防止对其他基于 TLS 的协议的攻击。

TLS 中的加密使得攻击者难以控制可用于对明文协议进行跨协议攻击的数据。

HTTP/2 的明文版本对跨协议攻击的保护最小。连接前言(见 第 3.4 节)包含一个旨在使 HTTP/1.1 服务器困惑的字符串,但对其他协议并未提供特殊保护。

10.3. 中间件封装攻击

HPACK 允许对字段名和值进行编码,这些字段在其他 HTTP 版本中可能被视为定界符。将 HTTP/2 请求或响应转换为其他 HTTP 版本的中间件必须在转换消息之前根据 第 8.2 节 的规则验证字段。翻译包含无效定界符的字段可能导致接收方错误地解释消息,这可能被攻击者利用。

第 8.2 节 未包含伪首部字段的具体验证规则。如果这些字段的值被使用,则需要额外的验证。这在将 ":scheme"、":authority" 和 ":path" 组合成单一 URI 字符串时尤为重要。类似问题也可能在将该 URI 或仅 ":path" 与 ":method" 组合以构造请求行时出现。除非输入值被完全验证,否则简单的串联是不安全的。

中间件可拒绝包含无效字段名或字段值的字段——尤其是那些不符合 RFC 9110 第 5 节 ABNF 语法的字段。不仅限于此,未执行第 8.2 节 最低验证要求的中间件可能会转发包含无效字段名或值的消息。

接收需要在转发前移除的字段的中间件(参见 RFC 9110 第 7.6.1 节)必须在转发消息时移除或替换这些首部字段。此外,中间件在转发包含 Content-Length 字段的消息时应谨慎,以确保消息格式正确(见 第 8.1.1 节)。这可确保当消息被转换为 HTTP/1.1 时,分帧将是正确的。

10.4. 被推送响应的可缓存性

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

是否缓存被推送的响应可依据源服务器在 Cache-Control 头字段中提供的指导。但如果单个服务器承载多个租户,这可能引发问题。例如,服务器可能为多个用户提供其 URI 空间的不同小部分。

当多个租户共享同一服务器空间时,该服务器必须确保租户不能推送其无权拥有的资源的表示。未能强制执行此点将允许某租户提供一个表示并使其从缓存中被提供,从而覆盖权威租户所提供的实际表示。

对源服务器并非权威的被推送响应(见 第 10.1 节不得被使用或缓存。

10.5. 拒绝服务(DoS)考量

与 HTTP/1.1 连接相比,HTTP/2 连接可能需要更多的资源来运行。字段段压缩和流量控制依赖于更多状态的承诺。这些特性的设置确保了对此类功能的内存承诺是严格有界的。

PUSH_PROMISE 帧的数量并不以相同方式受限。接受服务器推送的客户端限制允许处于 “reserved (remote)” 状态的流数量。过多的服务器推送流可被视为类型为 ENHANCE_YOUR_CALM 的流错误(见 第 5.4.2 节)。

已发现若干 HTTP/2 实现易受拒绝服务攻击的影响。以下列出实现可能受到拒绝服务攻击的已知方式:

  • 对未完成的出站帧进行低效跟踪可能导致过载,若对手能促使大量帧排队发送。对手可以使用多种技术导致大量帧被生成:

    • WINDOW_UPDATE 帧中提供极小的流量控制增量,会促使发送方生成大量 DATA 帧。
    • 端点必须响应 PING 帧。
    • 每个 SETTINGS 帧都需要确认。
    • 无效的请求(或服务器推送)可能导致对端发送 RST_STREAM 帧作为响应。
  • 攻击者可以在 HTTP/2 层提供大量流量控制额度,但在 TCP 层扣留这些额度,阻止帧被发送。如果端点在不考虑 TCP 限制的情况下构建并记忆要发送的帧,可能会遭受资源耗尽。
  • 大量小帧或空帧可以被滥用以迫使对端花费时间处理帧头。对此需谨慎,因为某些使用小帧的场景是合法的,例如在流结束时发送空的 DATACONTINUATION 帧。
  • SETTINGS 帧也可能被滥用以消耗对端的额外处理时间,例如通过无意义地更改设置、发送多个未定义设置或在同一帧中重复更改相同设置。
  • 使用 PRIORITY 帧处理重新优先级可能需要大量处理时间,如果发送大量 PRIORITY 帧,会导致过载。
  • 字段段压缩也为攻击者浪费处理资源提供了机会;参见 HPACK 第 7 节,了解潜在滥用的更多细节。
  • SETTINGS 中的限制无法立即降低,这使端点暴露于可能超过新限制的对等方行为之下。尤其是在建立连接之后,客户端在知晓服务器设置之前可能会超出限制而并非明显的协议违规。

大多数可能被滥用以实施拒绝服务的特性(如 SETTINGS 更改、小帧、字段段压缩)都有合法用途。仅在不必要或过度使用这些特性时,它们才成为负担。

不监控这些特性使用情况的端点将面临拒绝服务风险。实现跟踪这些特性的使用并对其设置限制。端点可以将可疑活动视为类型为 ENHANCE_YOUR_CALM 的连接错误(见 第 5.4.1 节)。

10.5.1. 字段块大小限制

大型字段块(见 第 4.3 节)会导致实现承诺大量状态。用于路由的字段行可能出现在字段块的末尾,这阻止了将字段流式传输到其最终目的地。由于该顺序和其它原因(例如确保缓存正确性),端点可能需要缓冲整个字段块。因为字段块大小没有硬性上限,一些端点可能被迫为字段块承诺大量可用内存。

端点可以使用 SETTINGS_MAX_HEADER_LIST_SIZE 向对等方建议未压缩字段块大小可能适用的限制。该设置仅为建议,端点可以选择发送超过此限制的字段块并承担请求或响应被视为格式错误的风险。该设置是特定于连接的,因此任何请求或响应都可能遇到具有更低、未知限制的跃点。中间件可以尝试通过传递不同对等方提供的值来避免该问题,但它们没有义务这么做。

服务器若接收到其不愿处理的大字段块,可以返回 HTTP 431(Request Header Fields Too Large)状态码。客户端可以丢弃其无法处理的响应。必须对字段块进行处理以确保一致的连接状态,除非连接被关闭。

10.5.2. CONNECT 相关问题

CONNECT 方法可能会对代理造成不成比例的负载,因为与创建和维护 TCP 连接相比,创建流的开销相对较小。代理在关闭承载 CONNECT 请求的流之后可能仍为该 TCP 连接保留一些资源,因为外发的 TCP 连接可能仍处于 TIME_WAIT 状态。因此,代理不能仅依赖 SETTINGS_MAX_CONCURRENT_STREAMS 来限制 CONNECT 请求消耗的资源。

10.6. 压缩使用

当秘密数据与攻击者可控数据在相同上下文中被压缩时,压缩可能允许攻击者恢复秘密数据。HTTP/2 支持字段行的压缩(见 第 4.3 节);下列关注点同样适用于 HTTP 压缩内容编码的使用(见 RFC 9110 第 8.4.1 节)。

已有针对压缩的可行攻击利用了 Web 的特性(例如 BREACH)。攻击者诱导包含不同明文的多次请求,观察每次得到的密文长度,当对秘密的猜测正确时,所得到的密文长度会更短,从而泄露信息。

在安全信道上通信的实现不得压缩同时包含机密数据和攻击者可控数据的内容,除非对每个数据源使用了独立的压缩字典。如果无法可靠确定数据来源,则不得使用压缩。通用流压缩(例如 TLS 提供的)不得与 HTTP/2 一起使用(见 第 9.2 节)。

关于头字段压缩的更多注意事项见 HPACK

10.7. 填充的使用

HTTP/2 中的填充并非用于替代通用填充(例如 TLS 提供的填充)。冗余填充甚至可能适得其反。正确使用填充可能依赖于对被填充数据的具体了解。

为缓解依赖于压缩的攻击,禁用或限制压缩可能比使用填充作为对策更可取。

填充可用于模糊帧内容的精确大小,并用于缓解 HTTP 中的特定攻击——例如当压缩内容同时包含攻击者可控明文与秘密数据时(如 BREACH)。

使用填充可能产生低于直观预期的保护效果。充其量,填充只是通过增加攻击者需要观察的帧数来使其更难推断长度信息。不正确实现的填充方案容易被击破。尤其是,具有可预测分布的随机填充提供的保护非常有限;同样,将帧负载填充到固定大小会在帧负载大小跨越固定边界时泄露信息,如果攻击者可以控制明文,这种情况是可能的。

中间件保留 DATA 帧的填充,但对于 HEADERSPUSH_PROMISE 帧中间件可以去除填充。中间件改变帧填充量的一个合理原因是为了增强填充所提供的保护。

10.8. 隐私考量

HTTP/2 的若干特性使观察者有机会随时间关联单个客户端或服务器的行为。这些包括设置的值、流量控制窗口的管理方式、为流分配优先级的方式、对刺激的反应时序,以及由设置控制的任何特性的处理方式。

由于这些在行为上创建了可观察的差异,它们可能被用于对特定客户端进行指纹识别,如 RFC 6973 第 3.2 节 所定义。

HTTP/2 倾向于使用单个 TCP 连接,这允许关联用户在站点上的活动。为不同来源重用连接允许跨这些来源进行跟踪。

由于 PING 和 SETTINGS 帧会引发即时响应,它们可被端点用于测量到对等方的延迟。在某些场景中,这可能具有隐私影响。

10.9. 远程计时攻击

远程计时攻击通过观察服务器在处理使用秘密的数据的请求时所需时间的变化来提取秘密。HTTP/2 支持并发请求的创建和处理,这可以使攻击者更好地控制请求处理何时开始。多个 HTTP/2 请求可以包含在同一 IP 分组或 TLS 记录中。因此,HTTP/2 通过消除请求传递的可变性,仅留下请求顺序和响应的交付作为计时可变性的来源,从而使远程计时攻击更高效。

确保处理时间不依赖于秘密值是防御任何形式计时攻击的最佳方法。


11. IANA 注意事项

此修订版的 HTTP/2 将 HTTP2-Settings 头字段和 h2c 升级标记(两者均在 [RFC7540] 中定义)标记为已过时。

Section 11 of [RFC7540] 登记了 h2h2c 的 ALPN 标识符以及 PRI HTTP 方法。RFC 7540 还为帧类型、设置和错误代码建立了登记表。这些注册和注册表适用于 HTTP/2,但在本文档中未重新定义。

IANA 已在下列注册表中将对 RFC 7540 的引用更新为指向本文档:“TLS Application-Layer Protocol Negotiation (ALPN) Protocol IDs”、“HTTP/2 Frame Type”、“HTTP/2 Settings”、“HTTP/2 Error Code” 和 “HTTP Method Registry”。对 PRI 方法的注册已更新为参照 Section 3.4;所有其他节号保持不变。

IANA 已更改 RFC 7540 中为试验性使用保留的 “HTTP/2 Frame Type” 和 “HTTP/2 Settings” 注册表部分的策略。注册表的这些部分将采用与各注册表其余部分相同的策略运行。

11.1. HTTP2-Settings 头字段注册

本节将由 Section 11.5 of [RFC7540] 注册的 HTTP2-Settings 头字段在 “Hypertext Transfer Protocol (HTTP) Field Name Registry” 中标注为已过时。该功能已被移除:参见 Section 3.1。根据 Section 18.4 of [HTTP] 的要求,注册信息已更新以包括必要的细节:

Field Name:
HTTP2-Settings
Status:
obsoleted
Reference:
Section 3.2.1 of [RFC7540] 
Comments:
已过时;参见本文件的 第 11.1 节

11.2. h2c 升级标记

本节记录了由 Section 11.8 of [RFC7540] 在 “Hypertext Transfer Protocol (HTTP) Upgrade Token Registry” 中注册的 h2c 升级标记为已过时。该功能已被移除:参见 Section 3.1。注册信息更新如下:

Value:
h2c
Description:
(OBSOLETE) Hypertext Transfer Protocol version 2 (HTTP/2)
Expected Version Tokens:
None
Reference:
Section 3.1 of this document

12. 参考文献

12.1. 规范性参考文献

[CACHING]
Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke, Ed., “HTTP Caching”, STD 98, RFC 9111, DOI 10.17487/RFC9111, June 2022, <https://www.rfc-editor.org/info/rfc9111>.
[COMPRESSION]
Peon, R. and H. Ruellan, “HPACK: Header Compression for HTTP/2”, RFC 7541, DOI 10.17487/RFC7541, May 2015, <https://www.rfc-editor.org/info/rfc7541>.
[COOKIE]
Barth, A., “HTTP State Management Mechanism”, RFC 6265, DOI 10.17487/RFC6265, April 2011, <https://www.rfc-editor.org/info/rfc6265>.
[HTTP]
Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke, Ed., “HTTP Semantics”, STD 97, RFC 9110, DOI 10.17487/RFC9110, June 2022, <https://www.rfc-editor.org/info/rfc9110>.
[QUIC]
Iyengar, J., Ed. and M. Thomson, Ed., “QUIC: A UDP-Based Multiplexed and Secure Transport”, RFC 9000, DOI 10.17487/RFC9000, May 2021, <https://www.rfc-editor.org/info/rfc9000>.
[RFC2119]
Bradner, S., “Key words for use in RFCs to Indicate Requirement Levels”, BCP 14, RFC 2119, DOI 10.17487/RFC2119, March 1997, <https://www.rfc-editor.org/info/rfc2119>.
[RFC3986]
Berners-Lee, T., Fielding, R., and L. Masinter, “Uniform Resource Identifier (URI): Generic Syntax”, STD 66, RFC 3986, DOI 10.17487/RFC3986, January 2005, <https://www.rfc-editor.org/info/rfc3986>.
[RFC8174]
Leiba, B., “Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words”, BCP 14, RFC 8174, DOI 10.17487/RFC8174, May 2017, <https://www.rfc-editor.org/info/rfc8174>.
[RFC8422]
Nir, Y., Josefsson, S., and M. Pegourie-Gonnard, “Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS) Versions 1.2 and Earlier”, RFC 8422, DOI 10.17487/RFC8422, August 2018, <https://www.rfc-editor.org/info/rfc8422>.
[RFC8470]
Thomson, M., Nottingham, M., and W. Tarreau, “Using Early Data in HTTP”, RFC 8470, DOI 10.17487/RFC8470, September 2018, <https://www.rfc-editor.org/info/rfc8470>.
[TCP]
Postel, J., “Transmission Control Protocol”, STD 7, RFC 793, DOI 10.17487/RFC0793, September 1981, <https://www.rfc-editor.org/info/rfc793>.
[TLS-ALPN]
Friedl, S., Popov, A., Langley, A., and E. Stephan, “Transport Layer Security (TLS) Application-Layer Protocol Negotiation Extension”, RFC 7301, DOI 10.17487/RFC7301, July 2014, <https://www.rfc-editor.org/info/rfc7301>.
[TLS-ECDHE]
Rescorla, E., “TLS Elliptic Curve Cipher Suites with SHA-256/384 and AES Galois Counter Mode (GCM)”, RFC 5289, DOI 10.17487/RFC5289, August 2008, <https://www.rfc-editor.org/info/rfc5289>.
[TLS-EXT]
Eastlake 3rd, D., “Transport Layer Security (TLS) Extensions: Extension Definitions”, RFC 6066, DOI 10.17487/RFC6066, January 2011, <https://www.rfc-editor.org/info/rfc6066>.
[TLS12]
Dierks, T. and E. Rescorla, “The Transport Layer Security (TLS) Protocol Version 1.2”, RFC 5246, DOI 10.17487/RFC5246, August 2008, <https://www.rfc-editor.org/info/rfc5246>.
[TLS13]
Rescorla, E., “The Transport Layer Security (TLS) Protocol Version 1.3”, RFC 8446, DOI 10.17487/RFC8446, August 2018, <https://www.rfc-editor.org/info/rfc8446>.
[TLSBCP]
Sheffer, Y., Holz, R., and P. Saint-Andre, “Recommendations for Secure Use of Transport Layer Security (TLS) and Datagram Transport Layer Security (DTLS)”, BCP 195, RFC 7525, DOI 10.17487/RFC7525, May 2015, <https://www.rfc-editor.org/info/rfc7525>.

12.2. 参考资料(补充)

[ALT-SVC]
Nottingham, M., McManus, P., and J. Reschke, “HTTP Alternative Services”, RFC 7838, DOI 10.17487/RFC7838, April 2016, <https://www.rfc-editor.org/info/rfc7838>.
[BREACH]
Gluck, Y., Harris, N., and A. Prado, “BREACH: Reviving the CRIME Attack”, July 2013, <https://breachattack.com/resources/BREACH%20-%20SSL,%20gone%20in%2030%20seconds.pdf>.
[DNS-TERMS]
Hoffman, P., Sullivan, A., and K. Fujiwara, “DNS Terminology”, BCP 219, RFC 8499, DOI 10.17487/RFC8499, January 2019, <https://www.rfc-editor.org/info/rfc8499>.
[HTTP-PRIORITY]
Oku, K. and L. Pardue, “Extensible Prioritization Scheme for HTTP”, RFC 9218, DOI 10.17487/RFC9218, June 2022, <https://www.rfc-editor.org/info/rfc9218>.
[HTTP/1.1]
Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke, Ed., “HTTP/1.1”, STD 99, RFC 9112, DOI 10.17487/RFC9112, June 2022, <https://www.rfc-editor.org/info/rfc9112>.
[NFLX-2019-002]
Netflix, “HTTP/2 Denial of Service Advisory”, August 2019, <https://github.com/Netflix/security-bulletins/blob/master/advisories/third-party/2019-002.md>.
[PRIVACY]
Cooper, A., Tschofenig, H., Aboba, B., Peterson, J., Morris, J., Hansen, M., and R. Smith, “Privacy Considerations for Internet Protocols”, RFC 6973, DOI 10.17487/RFC6973, July 2013, <https://www.rfc-editor.org/info/rfc6973>.
[RFC1122]
Braden, R., Ed., “Requirements for Internet Hosts - Communication Layers”, STD 3, RFC 1122, DOI 10.17487/RFC1122, October 1989, <https://www.rfc-editor.org/info/rfc1122>.
[RFC3749]
Hollenbeck, S., “Transport Layer Security Protocol Compression Methods”, RFC 3749, DOI 10.17487/RFC3749, May 2004, <https://www.rfc-editor.org/info/rfc3749>.
[RFC6125]
Saint-Andre, P. and J. Hodges, “Representation and Verification of Domain-Based Application Service Identity within Internet Public Key Infrastructure Using X.509 (PKIX) Certificates in the Context of Transport Layer Security (TLS)”, RFC 6125, DOI 10.17487/RFC6125, March 2011, <https://www.rfc-editor.org/info/rfc6125>.
[RFC6585]
Nottingham, M. and R. Fielding, “Additional HTTP Status Codes”, RFC 6585, DOI 10.17487/RFC6585, April 2012, <https://www.rfc-editor.org/info/rfc6585>.
[RFC7323]
Borman, D., Braden, B., Jacobson, V., and R. Scheffenegger, Ed., “TCP Extensions for High Performance”, RFC 7323, DOI 10.17487/RFC7323, September 2014, <https://www.rfc-editor.org/info/rfc7323>.
[RFC7540]
Belshe, M., Peon, R., and M. Thomson, Ed., “Hypertext Transfer Protocol Version 2 (HTTP/2)”, RFC 7540, DOI 10.17487/RFC7540, May 2015, <https://www.rfc-editor.org/info/rfc7540>.
[RFC8441]
McManus, P., “Bootstrapping WebSockets with HTTP/2”, RFC 8441, DOI 10.17487/RFC8441, September 2018, <https://www.rfc-editor.org/info/rfc8441>.
[RFC8740]
Benjamin, D., “Using TLS 1.3 with HTTP/2”, RFC 8740, DOI 10.17487/RFC8740, February 2020, <https://www.rfc-editor.org/info/rfc8740>.
[TALKING]
Huang, L., Chen, E., Barth, A., Rescorla, E., and C. Jackson, “Talking to Yourself for Fun and Profit”, 2011, <https://www.adambarth.com/papers/2011/huang-chen-barth-rescorla-jackson.pdf>.

附录 A. 禁止的 TLS 1.2 密码套件

HTTP/2 实现可以将使用 TLS 1.2 协商下列任一密码套件视为类型为 INADEQUATE_SECURITY 的连接错误(参见 第 5.4.1 节):

  • TLS_NULL_WITH_NULL_NULL
  • TLS_RSA_WITH_NULL_MD5
  • TLS_RSA_WITH_NULL_SHA
  • TLS_RSA_EXPORT_WITH_RC4_40_MD5
  • TLS_RSA_WITH_RC4_128_MD5
  • TLS_RSA_WITH_RC4_128_SHA
  • TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5
  • TLS_RSA_WITH_IDEA_CBC_SHA
  • TLS_RSA_EXPORT_WITH_DES40_CBC_SHA
  • TLS_RSA_WITH_DES_CBC_SHA
  • TLS_RSA_WITH_3DES_EDE_CBC_SHA
  • TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA
  • TLS_DH_DSS_WITH_DES_CBC_SHA
  • TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA
  • TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA
  • TLS_DH_RSA_WITH_DES_CBC_SHA
  • TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA
  • TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA
  • TLS_DHE_DSS_WITH_DES_CBC_SHA
  • TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA
  • TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA
  • TLS_DHE_RSA_WITH_DES_CBC_SHA
  • TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA
  • TLS_DH_anon_EXPORT_WITH_RC4_40_MD5
  • TLS_DH_anon_WITH_RC4_128_MD5
  • TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA
  • TLS_DH_anon_WITH_DES_CBC_SHA
  • TLS_DH_anon_WITH_3DES_EDE_CBC_SHA
  • TLS_KRB5_WITH_DES_CBC_SHA
  • TLS_KRB5_WITH_3DES_EDE_CBC_SHA
  • TLS_KRB5_WITH_RC4_128_SHA
  • TLS_KRB5_WITH_IDEA_CBC_SHA
  • TLS_KRB5_WITH_DES_CBC_MD5
  • TLS_KRB5_WITH_3DES_EDE_CBC_MD5
  • TLS_KRB5_WITH_RC4_128_MD5
  • TLS_KRB5_WITH_IDEA_CBC_MD5
  • TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA
  • TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA
  • TLS_KRB5_EXPORT_WITH_RC4_40_SHA
  • TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5
  • TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5
  • TLS_KRB5_EXPORT_WITH_RC4_40_MD5
  • TLS_PSK_WITH_NULL_SHA
  • TLS_DHE_PSK_WITH_NULL_SHA
  • TLS_RSA_PSK_WITH_NULL_SHA
  • TLS_RSA_WITH_AES_128_CBC_SHA
  • TLS_DH_DSS_WITH_AES_128_CBC_SHA
  • TLS_DH_RSA_WITH_AES_128_CBC_SHA
  • TLS_DHE_DSS_WITH_AES_128_CBC_SHA
  • TLS_DHE_RSA_WITH_AES_128_CBC_SHA
  • TLS_DH_anon_WITH_AES_128_CBC_SHA
  • TLS_RSA_WITH_AES_256_CBC_SHA
  • TLS_DH_DSS_WITH_AES_256_CBC_SHA
  • TLS_DH_RSA_WITH_AES_256_CBC_SHA
  • TLS_DHE_DSS_WITH_AES_256_CBC_SHA
  • TLS_DHE_RSA_WITH_AES_256_CBC_SHA
  • TLS_DH_anon_WITH_AES_256_CBC_SHA
  • TLS_RSA_WITH_NULL_SHA256
  • TLS_RSA_WITH_AES_128_CBC_SHA256
  • TLS_RSA_WITH_AES_256_CBC_SHA256
  • TLS_DH_DSS_WITH_AES_128_CBC_SHA256
  • TLS_DH_RSA_WITH_AES_128_CBC_SHA256
  • TLS_DHE_DSS_WITH_AES_128_CBC_SHA256
  • TLS_RSA_WITH_CAMELLIA_128_CBC_SHA
  • TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA
  • TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA
  • TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA
  • TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA
  • TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA
  • TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
  • TLS_DH_DSS_WITH_AES_256_CBC_SHA256
  • TLS_DH_RSA_WITH_AES_256_CBC_SHA256
  • TLS_DHE_DSS_WITH_AES_256_CBC_SHA256
  • TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
  • TLS_DH_anon_WITH_AES_128_CBC_SHA256
  • TLS_DH_anon_WITH_AES_256_CBC_SHA256
  • TLS_RSA_WITH_CAMELLIA_256_CBC_SHA
  • TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA
  • TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA
  • TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA
  • TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA
  • TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA
  • TLS_PSK_WITH_RC4_128_SHA
  • TLS_PSK_WITH_3DES_EDE_CBC_SHA
  • TLS_PSK_WITH_AES_128_CBC_SHA
  • TLS_PSK_WITH_AES_256_CBC_SHA
  • TLS_DHE_PSK_WITH_RC4_128_SHA
  • TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA
  • TLS_DHE_PSK_WITH_AES_128_CBC_SHA
  • TLS_DHE_PSK_WITH_AES_256_CBC_SHA
  • TLS_RSA_PSK_WITH_RC4_128_SHA
  • TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA
  • TLS_RSA_PSK_WITH_AES_128_CBC_SHA
  • TLS_RSA_PSK_WITH_AES_256_CBC_SHA
  • TLS_RSA_WITH_SEED_CBC_SHA
  • TLS_DH_DSS_WITH_SEED_CBC_SHA
  • TLS_DH_RSA_WITH_SEED_CBC_SHA
  • TLS_DHE_DSS_WITH_SEED_CBC_SHA
  • TLS_DHE_RSA_WITH_SEED_CBC_SHA
  • TLS_DH_anon_WITH_SEED_CBC_SHA
  • TLS_RSA_WITH_AES_128_GCM_SHA256
  • TLS_RSA_WITH_AES_256_GCM_SHA384
  • TLS_DH_RSA_WITH_AES_128_GCM_SHA256
  • TLS_DH_RSA_WITH_AES_256_GCM_SHA384
  • TLS_DH_DSS_WITH_AES_128_GCM_SHA256
  • TLS_DH_DSS_WITH_AES_256_GCM_SHA384
  • TLS_DH_anon_WITH_AES_128_GCM_SHA256
  • TLS_DH_anon_WITH_AES_256_GCM_SHA384
  • TLS_PSK_WITH_AES_128_GCM_SHA256
  • TLS_PSK_WITH_AES_256_GCM_SHA384
  • TLS_RSA_PSK_WITH_AES_128_GCM_SHA256
  • TLS_RSA_PSK_WITH_AES_256_GCM_SHA384
  • TLS_PSK_WITH_AES_128_CBC_SHA256
  • TLS_PSK_WITH_AES_256_CBC_SHA384
  • TLS_PSK_WITH_NULL_SHA256
  • TLS_PSK_WITH_NULL_SHA384
  • TLS_DHE_PSK_WITH_AES_128_CBC_SHA256
  • TLS_DHE_PSK_WITH_AES_256_CBC_SHA384
  • TLS_DHE_PSK_WITH_NULL_SHA256
  • TLS_DHE_PSK_WITH_NULL_SHA384
  • TLS_RSA_PSK_WITH_AES_128_CBC_SHA256
  • TLS_RSA_PSK_WITH_AES_256_CBC_SHA384
  • TLS_RSA_PSK_WITH_NULL_SHA256
  • TLS_RSA_PSK_WITH_NULL_SHA384
  • TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256
  • TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256
  • TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256
  • TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256
  • TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256
  • TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256
  • TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256
  • TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256
  • TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256
  • TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256
  • TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256
  • TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256
  • TLS_RSA_WITH_ARIA_128_CBC_SHA256
  • TLS_RSA_WITH_ARIA_256_CBC_SHA384
  • TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256
  • TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384
  • TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256
  • TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384
  • TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256
  • TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384
  • TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256
  • TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384
  • TLS_DH_anon_WITH_ARIA_128_CBC_SHA256
  • TLS_DH_anon_WITH_ARIA_256_CBC_SHA384
  • TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256
  • TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384
  • TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256
  • TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384
  • TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256
  • TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384
  • TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256
  • TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384
  • TLS_RSA_WITH_ARIA_128_GCM_SHA256
  • TLS_RSA_WITH_ARIA_256_GCM_SHA384
  • TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256
  • TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384
  • TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256
  • TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384
  • TLS_DH_anon_WITH_ARIA_128_GCM_SHA256
  • TLS_DH_anon_WITH_ARIA_256_GCM_SHA384
  • TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256
  • TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384
  • TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256
  • TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384
  • TLS_PSK_WITH_ARIA_128_CBC_SHA256
  • TLS_PSK_WITH_ARIA_256_CBC_SHA384
  • TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256
  • TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384
  • TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256
  • TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384
  • TLS_PSK_WITH_ARIA_128_GCM_SHA256
  • TLS_PSK_WITH_ARIA_256_GCM_SHA384
  • TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256
  • TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384
  • TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256
  • TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384
  • TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256
  • TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384
  • TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256
  • TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384
  • TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256
  • TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384
  • TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256
  • TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384
  • TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256
  • TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384
  • TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256
  • TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384
  • TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256
  • TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384
  • TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256
  • TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384
  • TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256
  • TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384
  • TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256
  • TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384
  • TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256
  • TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384
  • TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256
  • TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384
  • TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256
  • TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384
  • TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256
  • TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384
  • TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256
  • TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384
  • TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256
  • TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384
  • TLS_RSA_WITH_AES_128_CCM
  • TLS_RSA_WITH_AES_256_CCM
  • TLS_RSA_WITH_AES_128_CCM_8
  • TLS_RSA_WITH_AES_256_CCM_8
  • TLS_PSK_WITH_AES_128_CCM
  • TLS_PSK_WITH_AES_256_CCM
  • TLS_PSK_WITH_AES_128_CCM_8
  • TLS_PSK_WITH_AES_256_CCM_8

欲了解更多细节,请参阅 第 9.2.2 节


附录 B. 相对于 RFC 7540 的变更

本修订包含下列实质性更改:

  • 基于 [RFC8740] 定义了对 TLS 1.3 的使用,且本文件已取代该文档。
  • RFC 7540 中定义的优先级方案已弃用。保留了 PRIORITY 帧的格式定义以及 HEADERS 帧中优先级字段的定义,并保留了何时可以发送和接收 PRIORITY 帧的规则,但这些字段的语义仅在 RFC 7540 中描述。RFC 7540 的优先级信令方案并不成功。推荐使用在 [HTTP-PRIORITY] 中描述的更简单信令。
  • HTTP/1.1 的 Upgrade 机制已弃用,并且不再在本文档中指定。该机制从未得到广泛部署,明文 HTTP/2 用户选择了 prior-knowledge 实现。
  • 字段名和值的验证范围已收窄。明确规定了中间件必须执行的强制性验证,并修改了请求的错误报告以鼓励返回 400 系列状态码。
  • 之前为试验性使用保留的设置和帧类型码点范围现已可供通用使用。
  • 明确且更全面地标识了被禁止的连接特定首部字段。
  • Host 与 ":authority" 不再允许不一致。
  • 第 4.3.1 节 中澄清了在设置更改后发送动态表大小更新指令的规则。

还包含了编辑性更改。特别是,术语和文档结构的更改是响应对 核心 HTTP 语义 的更新所做出的调整。那些文档现在包含一些最初在 RFC 7540 中定义的概念,例如 421 状态码或连接合并(connection coalescing)。


致谢

本文档的非平凡贡献应归功于多年来为 HTTP 工作组作出贡献的大量人员。[RFC7540] 列出了更多应当被感谢的人员名单。


贡献者

Mike Belshe 和 Roberto Peon 撰写了本文档所基于的文本。


作者联系方式

Martin Thomson (editor)
Mozilla
Australia
EMail: mt@lowentropy.net
Cory Benfield (editor)
Apple Inc.
EMail: cbenfield@apple.com