本规格书对HTTP协议(被称作HTTP/2)的语义进行了描述. HTTP/2 可以对网络资源进行更有效的使用,通过引入标头压缩来降低对于延时的感知,允许在同一个连接中并行进行信息交换。本文也介绍了从server到client的未经允许的请求的表现形式。
本文提供对 HTTP/1.1 消息语法的替代选择,而不是绝对的替换。HTTP现存的语义并未改变
HTTP 是一种非常成功的协议。然而,HTTP/1.1所使用的基本传输方式对当今的应用性能有负面影响。
特别地,在给定的TCP连接的某一时刻,HTTP/1.0只允许一个请求出现在链路上. HTTP/1.1增加了request pipeline,但只是处理了请求部分的并发性,没有解决队头阻塞问题。因此,HTTP/1.0和HTTP/1.1客户端需要建立多个连接到服务器,在每个连接上建立请求,已达到并发并降低延迟。
此外,HTTP 头部字段经常是重复冗余的,在产生更多或更大的网络数据包时,可能导致小的初始TCP堵塞窗口被快速填充。这可能在多个请求建立在一个新的TCP连接时导致过度的延迟
HTTP/2 通过定义一个优化的HTTP的语义到基本连接间的映射来解决以上问题。具体地,它允许在同一个连接上交错的建立请求和应答信息,并使用更高效率编码的HTTP报头字段。它还允许定义请求的优先级,让更多重要的请求更快的传输,进一步提高性能。
与HTTP/1.x 协议相比,HTTP/2 协议对网络更友好,因为在网络上的TCP连接数量会更少。这意味着与其他的流更少的竞争及更长时间的连接,从而更有效的利用现有网络容量。
最后,通过使用二进制消息帧,HTTP/2 也能对消息进行更有效的处理。
HTTP/2 提供了HTTP语义的传输优化。HTTP/2支持HTTP/1.1的所有核心特性,并在不同的方面做的更有效率。
在HTTP/2中,基本的协议单位是帧frame
。每种帧类型用于不同的目的。例如,HEADERS 和 DATA frame 构成了 基本的HTTP 请求和响应;其他的帧类型像 SETTING、WINDOW_UPDATE、PUSH_PROMISE 被用于实现HTTP/2的其他特性。
请求的多路复用是通过在一个流上分配多个HTTP请求应答交换来实现的。在很大程度上,流是相互独立的,因此一个请求的阻塞或者终止不会影响其他流的处理。
流量控制和优先级能确保合理使用多路复用流。流量控制有助于确保只有接收者需要的数据被传输。优先级能确保有限的资源能优先被重要的请求使用。
HTTP/2增加了一宗新的交互模式,即服务器也能推送消息到客户端。服务器推送允许服务器预测客户端需要的数据,并将数据发送给客户端,交换网络的使用来阻止潜在的延迟增长。服务器通过复用一个以PUSH_PROMISE帧发送的请求来实现推送,然后服务端可以在一个单独的流里面发送响应给这个合成的请求。
帧包含的HTTP报头字段是压缩的。HTTP请求有可能是高度冗余的,因此压缩能显著减少请求和响应的大小
HTTP/2 规格文档被分为4部分:
一些帧和流层的概念是与HTTP隔离的,本协议不是为了定义一个通用的帧层。这些帧和流层是为了HTTP协议和服务端推送的需求定制的。
文中使用以下术语:
客户端:初始化HTTP/2连接的端点。客户端发送HTTP请求,接收HTTP应答。
连接:在两个端点间的传输层连接。
连接错误:整个HTTP/2连接期间发生错误。
端点:连接的客户端或者服务端。
帧: HTTP/2连接上的最小通信单位,包含一个报文头和变长的序列。
对等端:一个端点。当讨论特定的端点时,peer指远程端点。
接收者:接收frame的端点。
发送者: 发送frame的端点。
服务端:接收HTTP/2连接的端点。服务端能接收HTTP请求,发送HTTP应答。
流: 在HTTP/2连接上的一个双向帧的流。
流错误码:HTTP/2流中的一个错误。
一个HTTP/2连接是运行在一个TCP连接上的应用层协议。客户端是协议的初始化者。
HTTP/2视同与HTTP/1.1相同的“http”和“https”URI,使用相同的端口“80”或者“443”。因此,因此,实现对例如http://example.org/foo或https://example.com/bar目标资源的URI请求处理需要首先确定上游服务端(当前客户端希望建立连接的对等端)是否支持HTTP/2。
这意味着检测“http” 及“https” 的URIs是否支持HTTP/2的方法是不一样的。检测"http"URIs在章节3.2中描述。检测"https"URIs 在章节3.3中描述。
本协议定义了两种标识符
用到"h2" 或者 “h2c” 表明使用文档中定义的传输、安全、帧及语义化消息。
客户端无法预知服务端是否支持HTTP/2.0 的情况下使用HTTP升级机制发起“http” URI请求([RFC7230] 章节6.7)。客户端发起一个http1.1请求,其中包含识别HTTP/2的升级报头字段与h2c token。HTTP/1.1必须包含一个确切的HTTP2-Settings中的报头字段。
例如:
GET / HTTP/1.1
Host: server.example.com
Connection: Upgrade, HTTP2-Settings
Upgrade: h2c
HTTP2-Settings:
包含负载内容的请求必须在客户端能发送HTTP/2帧之前被发送。这意味着一个较大的请求能阻塞连接的使用直到该请求被完整发送。
如果一个请求的并发后续请求是重要的,那么可以使用一个小的请求来执行升级到HTTP/2的操作,这样仅消耗一个额外的往返成本。
不支持HTTP/2的服务端响应该请求,通过在报文头中不包含 Upgrade 字段:
HTTP/1.1 200 OK
Content-Length: 243
Content-Type: text/html
...
服务端必须忽略升级报头字段中的“h2” token。“h2” token基于TLS实现的HTTP/2,协商方法在章节3.3中定义。
支持HTTP/2的服务端可以返回一个101(转换协议)响应来接受升级请求。在101空内容响应终止后,服务端可以开始发送HTTP/2帧。这些帧必须包含一个发起升级的请求的响应。
HTTP/1.1 101 Switching Protocols
Connection: Upgrade
Upgrade: h2c
[ HTTP/2 connection ...
第一个被服务端发送的HTTP/2帧是一个设置(SETTINGS)帧。在收到101响应后,客户端发送一个包含设置(SETTINGS)帧的连接序言里。
之前用来发送升级的请求被分配到流1(1是流标识符)中,流1有最高优先级。流1对与客户端到服务端方向是隐式半关闭的,应为作为HTTP/1.1的请求已经完成。在开始HTTP/2连接后,流1被用于应答。
从HTTP/1.1升级到HTTP/2的请求必须包含一个确切的HTTP2-Settings报头字段.HTTP/2-Setting Header Field 是一个特定连接的报文首部字段,该字段包含管理HTTP/2连接的参数,这是从对于服务端接受升级请求的预测中所获取的。
HTTP2-Settings = token68
服务端未检测到此报头字段必须拒绝客户端的升级尝试。服务端绝对不能发送此报头字段。
HTTP/2-Setting Header Field的内容是 SETTING帧的负载,该负载使用base64url编码,任何的’='后的字符都被忽略。ABNF[RFC5234]产品中对token68的定义在[RFC7235] 章节2.1中。
服务端就像对任何其他设置(SETTINGS)帧一样对这些值进行解码和解释。因为101响应的隐式声明,对这些设置参数的确认不是必须的。这些升级请求中的值使得协议不需要上述设置参数的默认值,同时使客户端有机会在从服务端接受任何帧之前提供其他参数。
先验是指预先知道。
客户端能通过其他方式知道服务端是否支持HTTP/2。
客户端可以对支持HTTP/2的服务器在建立连接后发送链接序言,支护可以立即发送HTTP/2帧。服务端可以通过连接序言中的“PRI”方法来区分这种连接。这只对基于明文TCP的HTTP/2连接建立有影响;支持HTTP/2的服务端对“https”URI需要支持TLS中的协商扩展。
对HTTP/2之前的支持并不表明一个给定的服务器会在以后的连接中一定支持HTTP/2。服务器配置有可能改变或者集群中不同服务器配置有差异。拦截代理(又叫“透明”代理)是另一个可能得原因
在HTTP/2中,每个端点被要求发送一个连接序言作为一个最终的使用协议确认,以及链接HTTP/2连接的初始化设置。客户端和服务端发送不同的连接序言。
客户端连接序言以24个字节的需里开始,以十六进制表示:
0x505249202a20485454502f322e300d0a0d0a534d0d0a0d0a
上述的24个字节字符内容是PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n
。该序列后跟着一个SETTING 帧,可以为空帧。客户端在收到101转换协议响应(升级成功指示)后马上发送客户端连接序言,或者作为TLS连接的第一个应用数据字节。如果在预先知道服务器支持HTTP/2的情况下启动HTTP/2连接,客户端在连接建立后发送连接序言。
客户端连接序言是用来让大部分的HTTP/1.1或者HTTP/1.0服务端以及中介端不试图进一步处理帧。注意这并不能处理【讨论】中提到的问题。
服务端连接序言包含一个有可能是空的设置(SETTING)帧(章节6.5),它必须在HTTP/2连接中首个发送。
在发送连接序言后,从对等端发来的SETTING帧是作为链接序言的一部分存在的,必须被应答。
为了避免不必要的延迟,允许客户端在发送客户端连接序言之后立即发送其他额外的帧,不需要等待收到服务端连接序言。不过需要注意的是,服务端连接序言设置(SETTINGS)帧可能包含一些关于期望客户端如何与服务端通信的所必须修改的参数。在收到这些设置(SETTINGS)帧之后,客户端应当遵守所有设置的参数。
客户端和服务端必须把无效的连接序言当作一个连接错误对待,该连接错误类型是 PROTOCOL_ERROR
。在这种情况下,可以忽略GOAWAY帧,因为一个无效的序言表明对等端没有使用HTTP/2
一旦 HTTP/2被建立,端点间可以开始交换帧。
所有的帧以9字节的报头开始并且跟着可变长度的负载。
如下图所示: 8 ∗ 9 = 72 8*9=72 8∗9=72 bit,分为5部分
24 | 8 | 8 | 1 | 31 |
---|---|---|---|---|
Length(24) | Type(8) | Flag(8) | R | Stream Identifier(31) |
Length: 24bit 无符号整数作为帧的负载长度,超过 2 14 = 16384 2^{14} = 16384 214=16384 的内容禁止发送,除非接收端已经通过SETTINGS_MAX_FRAME_SIZE设置了更大的值。
9字节长度的帧报头信息长度不包含在该值中,即帧头长度是固定的9字节,负载的长度用Length来额外表示。
Type: 帧的8位类型。帧类型定义了剩余的帧报头和帧主体将如何被解释。具体实现必须在收到未知帧类型(任何未在文档中定义的帧)时作为连接错误中的类型协议错误(PROTOCOL_ERROR)处理。
Flags: 为帧类型保留的8字节字段有具体的布尔标识。 标识针对确定的帧类型赋予特定的语义。确定帧类型定义语义以外的标示必须被忽略,并且必须在发送的时候保留未设置(0)。
R : 1位的保留字段。这个字段的语义未设置并且必须在发送的时候保持未设置(0),在接受的时候必须被忽略。
Stream Identifier : 31字节的流标识符(见StreamIdentifiers)。0是保留的,标明帧是与连接相关作为一个整体而不是一个单独的流。
帧主体的结构和内容完全取决于帧类型。
帧负载大小被限制在SETTINGS_MAX_FRAME_SIZE以内。该设置的取值范围是 2 14 − 1 2^{14}-1 214−1 (16,383)字节和 2 24 − 1 2^{24}-1 224−1(16,777,215) 字节。
所有的实现必须具备接收和处理此最大长度帧的能力。帧头的9字节长度不包含在帧大小内。
如果一个帧大小超过设定的限制,或者太小无法包含必须的基础帧数据,这个端点必须发送一个帧大小错误(FRAME_SIZE_ERROR)。如果帧大小错误可能修改整个连接状态,必须作为一个连接错误(章节5.4.1)处理;这包括与0流一起的携带报头区块(即报文头(HEADERS),推送承诺(PUSH_PROMISE)和延续(CONTINUATION)帧)、设置(SETTINGS)以及任何窗口更新(WINDOW_UPDATE)帧。
与HTTP/1相似,HTTP/2中的报头字段也是一个name与一个或者多个值相联系。报头字段被用于请求和应答消息和一些推送操作中。
报头集合是零或者多个报文头部字段的集合。当在连接上传输时,报头集合将使用HTTP报头压缩序列化到报文报头块中。序列化的报头块被分割成一个或多个的字节序列,称为报头分区,并在报头HEADER、推送承诺PUSH_PROMISE及延续帧CONTINUATION的载体中传送。
报文头Cookie字段被通过HTTP映射特殊处理
接收端点连接报头区块重新组装,并且解压缩区块后重建报头集合。
一个完整的报头区块包含:
报头压缩是有状态的并且在整个连接过程中使用同个压缩环境。在报头块中的解码错误必须作为连接错误(COMPRESSION_ERROR)来对待。
每个报头区块作为离散的单元处理。报头区块必须作为一个连续的帧序列传输,没有任何类型或任何其他流的交错帧。一个报头HEADERS或者延续CONTINUATION帧序列的最后一帧必须有报头终止标记设置。推送承诺PUSH_PROMISE或者延续CONTINUATION帧序列的最后一帧必须具有报头终止标记设置。
报头区块必须被报头HEADERS、推送承诺PUSH_PROMISE或延续CONTINUATION的有效载体发送,因为这些帧中携带了能被接收端修改的压缩上下文数据。端点在接收报头HEADERS、推送承诺PUSH_PROMISE或延续CONTINUATION帧时必须重新组装报头区块并且执行解压缩,即便这些帧将被废弃。如何不能重建报头区间,接收端必须终止连接并报类型为解压缩错误的连接错误(章节5.4.1)。
流是一个独立的,客户端和服务端在HTTP/2连接下交换帧的双向序列。流有一下几个重要特点:
请注意该图仅展示了流状态的转换和帧对这些转换的影响。在这方面,延续帧不会影响流状态的转换,但是对后面跟随的报头或者推送承诺是有影响的。
当流在传输的时候,各个端点对在传送中的流状态的主观认识可能不同。终端并不协调流的创建;它们是被任意终端单方面创建的。不匹配的状态导致的消极结果是在发送RST_STREAM流之后它们的“关闭”是受限制的,因为可能在关闭之后帧才被接收。
流有以下状态:
idle: 所有流以“空闲”状态开始。在这种状态下,没有任何帧的交换。
下列传输在这种状态下是有效的
reserved (local): 在“报留(本地)”状态的是已经被承诺发送推送承诺PUSH_PROMISE帧的流。一个推送承诺PUSH_PROMISE帧通过使一个流与一个由远端对等端初始化的打开的流相关联来保留一个空闲流。
在这种状态下,只有下列传输是可能的:
在这种状态下一个端绝对不能发送报头HEADERS帧、RST_STREAM、PRIORITY以外的帧。
在这种状态下一个优先级PRIORITY帧可能被接收。接收到任何报头HEADERS帧、RST_STREAM帧或者优先级PRIORITY帧以外的帧都将被认为是类型为协议错误PROTOCOL_ERROR的连接错误(章节5.4.1)。
reserved (remote):在“保留(远程)”状态下的流说明已经被远程对等端所保留。
在这种状态下,只有下列传输是可能的:
这种状态下任意终端可以发送一个优先级PRIORITY帧来变更保留流的优先级顺序。终端绝对不能发送任何RST_STREAM 、WINDOW_UPDATE和优先级PRIORITY以外的帧。
接收任何RST_STREAM、RST_STREAM和优先级PRIORITY以外的帧必须作为类型为 协议错误PROTOCOL_ERROR的连接错误(章节5.4.1)来处理。
open: 处于“打开”状态的流可以被两个对等端来发送任何类型的帧。在这种状态下,发送数据的对等端检查被广播端FlowControl流量控制限制(章节5.2)。
在这种状态下每个终端可以发送一个带有END_STREAM结束流标记的帧来使流转换到其中一种“半关闭”状态:一个终端发送一个结束流END_STREAM标记使流变成“半封闭”状态;一个终端接收一个结束流END_STREAM标记使流变成“半封闭(远程)”状态。
这种状态下各个终端可以发送一个RST_STREAM帧来使流转换到"关闭"状态。
half-closed (local):“半封闭(本地)”状态下的流不能发送帧。只有窗口更新(WINDOW_UPDATE)、优先级(PRIORITY)和终止流(RST_STREAM)帧能在这种状态下发送。
这种状态下,当流接收到包含END_STREAM标记的帧或者某个终端发送了RST_STREAM帧,流转换到“关闭”状态。
half-closed (remote):"半封闭(远程)"状态下的流不再被对等端用来发送帧。这种状态下,执行流量控制的终端不再承担接收留空控制窗口的工作。
如果终端接收到处于这种状态下的流发送的额外的帧,除非是延续CONTINUATION帧,否则必须返回类型为流关闭STREAM_CLOSED的流错误(章节5.4.2)。
这种状态下,当流发送一个带有终止流END_STREAM标记的帧或者某个终端发送了一个RST_STREAM帧,流将转换到“关闭”状态。
closed :“关闭”状态是终止状态。
终端绝对不能通过关闭的流发送除PRORITY之前的其他帧。
终端在收到RST_STREAM后接收的任何帧必须作为类型为流关闭STREAM_CLOSED的StreamErrorHandler流错误stream error(章节5.4.2)处理。相似的,终端接收到带有END_STREAM标记设置的数据DATA帧之后的任何帧,或在带有END_STREAM终止流标记且后面没有延续CONTINUATION帧的报头HEADERS帧之后收到任何帧都必须作为类型为流关闭STREAM_CLOSED的连接错误(章节5.4.1)处理。
在这种情况下,在带有END_STREAM标记的DATA或HEADERS帧发送之后一小段时间内可以接收WINDOW_UPDATE或者RST_STREAM帧。在远端对等端接收并处理带有END_STREAM标记的帧之前,可以发送任意这几种帧。在这种状态下终端必须忽略接收到的WINDOW_UPDATE,PRIORITY, 或 RST_STREAM帧,但终端也可以当作类型为PROTOCOL_ERROR的连接错误(章节5.4.1)处理。
关闭的流上可以发送优先级帧用来对依赖当前关闭流的流进行优先级重排序。终端应该处理优先级帧,但当该流已经从依赖树(章节5.3.4)中移除时可以忽略。
如果流在发送RST_STREAM帧后转换到这种状态,接收到RST_STREAM的对等端可能已经发送或者队列中准备发送无法取消的帧。终端必须忽略从已经发送RST_STREAM帧的流接收到的帧。终端可以选择设置忽略帧的超时时间并在超过限制后作为错误处理。
在发送RST_STREAM之后收到的流量受限帧(如数据DATA帧)转向流量控制窗口连接处理。尽管这些帧可以被忽略,因为他们是在发送端接收到RST_STREAM之前发送的,但发送端会认为这些帧与流量控制窗口不符。
终端可能在发送RST_STREAM之后接收PUSH_PROMISE帧。即便相关的流已经被重置,推送承诺帧也能使流变成“保留”状态。因此,需要RST_STREAM来关闭一个不想要的被承诺流。
本文档中没有明确说明的地方,具体实现时接收描述状态中没有明确许可的信息都应作为类型为协议错误(PROTOCOL_ERROR)的连接错误(章节5.4.1)来处理。
流由31位字节的无符号整数标识。客户端发起的流必须以奇数标示;服务器发起的流必须使用偶数来标示。0(0x0)用来标识连接控制信息流,且绝对不能用来建立一个新流。
HTTP/1.1升级到HTTP/2的请求将收到一个1(0x1)标识的流的响应。升级完成后,0x1流将对客户端处于“半封闭(本地)”状态。因此,0x1流不能被从HTTP/1.1升级的客户端用来作为一个新的流的标识符。
一个新建立的流标识符必须数值大于任何终端已经打开或者保留的流标识符。规则适用于使用报头帧打开的流以及使用推送承诺帧保留的流。终端收到不规范的流标识符必须响应一个类型为协议错误(PROTOCOL_ERROR)的连接错误。
新的流标识符第一次被使用时将隐式关闭所有处于“空闲”状态下可能已经被对等端初始化而且流标识符数字小于新标识符的流。例如,一个客户端发送一个流7的报头帧,那么在流7发送或者接收帧后从没有发送帧的流5将转换为“关闭”状态。
流标识符不能被重复使用。生存期长的连接可能导致流标识符可用范围耗尽。客户端不能新建流标识符时可以针对新流建立一个新的连接。服务端不能新建流标识符时可以发送一个超时帧(GOAWAY)强制客户端对新的流使用新的连接。
对等端可以使用设置帧里面的SETTINGS_MAX_CONCURRENT_STREAMS参数来限制流的并发量。最大并发流设置(章节6.5.2)仅适用于终端并且只对接收到此设置的对等端有效。也就是说:客户端可以指定服务端能启动的流最大并发量,而且服务端能指定客户端能启动的流最大并发量。终端绝对不能超过对等端设置的限制。
处于“打开”或者任意一种“半封闭”状态的流均计入终端被允许启动的流次数中。处于任意这三种状态下的流都将计入SETTINGS_MAX_CONCURRENT_STREAMS设置次数中。处于任意一种“保留”状态下的流不计入打开次数中。
终端绝对不能超过对等端设定的设置。终端接收到报头帧导致他们广播的并发流超过限制的必须将这作为流错误(章节5.4.2)处理。终端希望将SETTINGS_MAX_CONCURRENT_STREAMS的值减少到比当前打开的流更小时可以关闭超过新的设置值的流或者允许流结束。
使用复用流介绍了针对TCP连接的资源争夺导致的流阻塞。流量控制方案等确保同一连接上的流相互之间不会造成破坏性的干扰。流量控制在单个流及整个连接过程中使用。
HTTP/2 通过使用WINDOW_UPDATE帧类型来提供流量控制(章节6.9)。
HTTP/2流流量控制目标在于允许不需要协议改动的情况下改进流量控制算法。HTTP/2中的流量控制有以下特点:
本规范定义了一系列的帧类型,每种类型由独特的8位类型代码标记。不管是在连接管理或单独的流中,每种帧都为了特定的目的而服务。
特定帧类型的传输可以修改连接的状态。如果终端不能保持同步的连接状态,连接中的通信将失败。因此,终端对于如何使用给定帧修改状态达成共识很重要。
数据帧(类型=0x0)表示随意,由伴随流的可变长度序列组成。例如,一个或多个数据帧被用来携带HTTP请求或者响应的载体。
数据帧也可以包含任意填充物。填充物可以添加到数据帧中来隐藏消息的大小。Padding是一种安全特性。
数据帧定义了以下标记:
数据帧绝对需要与流相关联。如果接收到流标记字段是0x0的数据帧,必须响应一个类型为PROTOCOL_ERROR的连接错误(章节5.4.1)。
数据帧遵从流量控制,并且只有在流是打开或者半封闭(远端)状态下才能够被发送。填充同样包含在流量控制中。如果数据帧在相关流不是在打开和半封闭(本地)状态下被接收,接收端必须响应一个类型为流关闭的流错误(章节5.4.2)。
填充字节的总数取决于Pad Length 的值。如果填充物的大小大于帧有效载荷的大小,接收端必须作为类型为PROTOCOL_ERROR的连接错误(章节5.4.1)处理。
报头帧(类型=0x1)用于打开一个流,另外可以携带报头块数据。报头帧在流空闲、本地流保流、打开和半关闭(远程)的状态下发送。
报头帧主体有以下字段:
报头帧定义了以下标记:
报头帧的主体包含一个报头区块。报头区块大于一个报头帧的将在延续帧中(章节6.10)继续传送。
报头帧必须与一个流相关联。如果一个接收到一个流标示识0x0得报头帧,接收端必须响应一个类型为协议错误的连接错误(章节5.4.1)。
报头帧改变连接状态在章节4.3此中表述。
报头帧包含可选的填充段。填充字段和标记同数据帧中描述的相同(章节6.1)。
优先级帧(type=0x2)明确了发送者建议的流的优先级(章节5.3)。它可以在任何流状态下发送,包括已闭合的流。
优先级不定义任何标记。
优先级帧与存在的流相关联。如果接收端收到流标识为0的优先级帧,必须响应一个类型为协议错误的连接错误(章节5.4.1)。
优先级帧可以在流的任何状态下发送,但它不能在由单个报头区块组成的连续帧之间发送。需要注意的是这个帧可能在处理或者帧发送已经完成之后才到达,这可能导致没有效果。对于处于“半封闭(远程)”状态下的流,优先级帧只能影响流的处理而不是传输。
优先级帧是关闭的流上唯一允许发送的帧。这允许通过修改可能已关闭的父节点流的优先级来重新设置一组有依赖的流的优先级。然而,关闭流上发送的优先级帧可能存在被对等端忽略的风险,因为对等端可能已经丢弃了这个流的优先级状态信息。
RST_STREAM帧(type=0x3)允许立即终止一个流。发送RST_STREAM是为了取消一个流或者指示一个错误条件已经产生。
RST_STREAM 帧由一个无符号的32位整数标记错误码。错误码指明流被终止的原因。
RST_STREAM 帧未定义任何标记。
RST_STREAM 帧完全终止相关的流并使其转入关闭状态。在接收到流的RST_STREAM帧后,接收端绝对不能在流上发送额外的帧。然而,在发送RST_STREAM帧后,发送端必须要准备接收并处理流上的其他帧,因为对等端有可能在收到RST_STREAM帧前就已经发送这些帧。
RST_STREAM 帧必须与流相关联。如果接收端收到流标示符为0x0的RST_STREAM 帧,必须作为类型为协议错误的连接错误(章节5.4.1)处理。
RST_STREAM帧绝对不能在流处于“空闲”状态下发送。如果接收端收到流状态为空闲的RST_STREAM帧,必须作为类型为协议错误的连接错误(章节5.4.1)处理。
设置帧(type=0x4)包含影响如何与终端通信的设置参数(例如偏好设置以及对等端的行为约束),并且用来确认这些参数的接收。单个的设置参数也可以被认为是“设置”。
设置参数不是通过协商确定的;它们描述发送端的特点,并被接收端使用。相同的参数对不同的对等端设置可能不同。例如,一个客户端可能设置一个较高的流量控制窗口,而服务器为了保存资源可能设置一个较低的值。
设置帧必须由两个终端在连接开始的时候发送,并且可以由各个终端在连接生存期的任意时间发送。具体实现必须支持本规范定义的所有参数。
设置帧的所有参数将替换参数中现有值。参数由他们出现的顺序来处理,而且接收设置帧并不需要保存当前值以外的任何状态。因此,设置参数的值是接收端接收到的最后一个值。
设置参数是被接收端公认的。为了实现这个,设置帧定义了以下标记:
设置帧总是应用于连接,而不是一个单独的流。流的设置帧标识必须为0.如果终端接收到流设置帧标识不是0的设置帧,必须响应一个类型为协议错误的连接错误(章节5.4.1)。
设置帧影响连接状态。格式错误或者未完成的设置帧必须作为类型为协议错误的连接错误处理。
设置帧载体包含0个或多个参数,每个包含一个无符号的16位标识以及一个无符号的32位值
大部分设置值收益于或者需要了解对等端接收到并且改变了通信过的参数的值的时机。为了提供这样一种同步的时间点,接收到没有设置ACK标记的设置帧必须尽快将更新过的参数适用于接收端上。
设置帧的值必须按照它们出现的顺序被使用,在处理值中间不能处理其他帧。一旦所有的值被应用,接收端必须马上发送一个带有ACK标记的设置帧。在接收到带有ACK标记的设置帧后,修改参数的发送端可以认为修改已生效。
设置帧的值必须按照它们出现的顺序被使用,在处理值中间不能处理其他帧。一旦所有的值被应用,接收端必须马上发送一个带有ACK标记的设置帧。在接收到带有ACK标记的设置帧后,修改参数的发送端可以认为修改已生效。
PUSH_PROMISE frame (type=0x5)用于在发送者准备初始化流之前通知对等端点。推送承诺帧包含了终端准备创建的长流的31位无符号标记以及提供附加上下文的报头的集合。
推送承诺帧定义了以下标记:
推送承诺帧必须与现有的由对等端初始化的流相关联。如果流标识字段为0x0,接收端必须响应一个类型为协议错误的连接错误
被承诺的流并不需要以被承诺的顺序使用。推送承诺只保留接下来会使用的流的标识符。
如果对等端的SETTINGS_ENABLE_PUSH设置是0那么推送承诺绝对不能发送。终端已经设置了禁止推送承诺并且收到确认的必须将接收到推送承诺帧作为类型为协议错误的连接错误处理(章节5.4.1)。
推送承诺的接收端可以选择给推送承诺的发送端返回一个与被承诺的流标识符相关的RST_STREAM标记来拒绝接收承诺流
PUSH_PROMISE通过两种方式修改连接状态。这包括一个报头区块(章节4.3)可能修改压缩状态。PUSH_PROMISE同样保留流后续使用,导致被推送的流进入到“保留”状态。发送端绝对不能在流上发送PUSH_PROMISE除非流是“打开”或者“半封闭(远程)”状态;发送端绝对要保证被承诺的流对于新的流标示(章节5.1.1)来说是一个有效的选择(就是说,被承诺的流必须进入"空闲"状态)。
由于PUSH_PROMISE保留了一个流、忽略一个PUSH_PROMISE 帧都会导致流状态变得不确定。接收端接收到流状态不是“打开”或者“半封闭(本地)”的流的推送承诺帧必须作为类型为协议错误的连接错误(章节5.4.1)处理。相似的,接收端必须对在一个非法标示(章节5.1.1)的流(即流的标识当前不在空闲状态)上建立的推送承诺作为类型为协议错误的连接错误(章节5.4.1)处理。
PING帧(type=0x6)是一种从发送端测量最小的RTT时间的机制,同样也是一种检测连接是否可用的方法。PING帧可以被任何终端发送。
除了帧报头之外,PING帧必须在载体中包含一个8字节长度的数据。发送端可以选择使用任何指并在任何时候使用。
接收到不包含ACK标记的PING帧必须发送一个带有ACK比标记的PING帧响应,以及一个相同的载荷。PING响应应当设置比其他帧更高的优先级。
PING帧定义了以下标记:
PING帧不必与任何独立的流相关联。如果收到流标示字段不是0x0的PING帧,接收端必须响应一个类型为协议错误的连接错误。
超时帧(type=0x7)通知远端对等端不要在这个连接上建立新流。超时帧可以由客户端或者服务端发送。一旦发送,发动端将忽略当前连接上新的和标示符大于上一个流的帧的发送。接收端接收到超时帧后绝对不能在这个连接上打开新的流,但是可以针对新的流创建一个新的连接。
这个帧的目的是允许终端优雅的停止接收新的流,但仍可以继续完成之前已经建立的流的处理。这使得管理员行为可用,如服务器维护。
在终端启动新的流及远端发送超时帧之间有一个内在的竞争条件。为了处理这种情况,超时帧带有当前连接中发送终端处理的最后一个流的标识。如果超时帧的接收端使用了比指定的流更新的流,它们将不会被发送端处理,而且接收端可以认为这些流根本没有被创建(因此接收端可以稍后在新的连接上重新创建这些流)。
终端在关闭一个连接之前总是应当发送一个超时帧,这样远端就能知道一个流是否已被部分处理。例如,如果一个HTTP客户端在服务端关闭连接的时候发送了一个POST请求,如果服务端不发送一个指示它在哪里停止工作的超时帧,客户端将不知道这个POST请求是否已开始被处理。对于不规范的对等端,终端可以选择不发送超时帧的情况下关闭连接。
对于行为不规范的对等端,终端可以选择不发送超时帧直接关闭连接。
超时帧没有定义任何标记。
超时帧适用于连接而不是特定的流。终端接收到流标识符不是0x0的超时帧必须作为类型为协议错误的连接错误(章节5.4.1)处理。
超时帧中最后一个流的标识包含了接收端接收到并可能已经进行某些处理的流的标识的最大值。所有小于或等于此指定标识符的流都可能通过某种方式被处理。如果没有流被处理,最后流的标识符设置为0。
如果连接在没有超时帧的情况下终止,这个值有效的是最大的流标识符。
小于或等于最后流标识符上的流的活动可能仍然能成功完成。超时帧的发送端可能通过发送超时帧优雅地关闭了连接,保持连接在打开状态直到正在处理的流全部处理完成。
如果环境改变终端可能发送多个超时帧。例如,终端发送带有NO_ERROR标记的超时帧来优雅关闭时随后可能遇到的情况需要理解终止连接。从最后一个超时帧接收到的最后的流标识符标识表示这些流可能已经被处理了。终端绝对不能增加他们最后发送的流标识的值,因为对等端可能已经有在其他连接上未处理的重试请求。
服务端关闭连接时,终端无法重试请求的将丢失所有正在发送的请求。尤其是针对中介端无法使用HTTP/2服务客户端的时候。无负担尝试优雅关闭连接时应当发送一个携带2^{31} -1 大小的流标识符和一个错误码的初始超时帧。这个信号对客户端来说意味着即将关闭连接并且不能建立更多请求了。在等待至少一个RTT时间之后,服务端能发送另外一个带有更新后的最终流标识符超时帧。这个确保了连接能彻底的关闭而不用丢失请求。
在发送超时帧后,发送端能丢弃流标识符大于最终流标识的流的帧。然而,任何修改流状态的帧不能被全部忽略。例如,报头帧、推送承诺帧和延续帧必须被最低限度的处理来保证维持的报头压缩是连续的(章节4.3);类似的数据帧必须被计入连接流程控制窗口中。这些处理失败可能导致流量控制或者报头压缩状态不同步。
超时帧同样包含一个32位的错误码(章节7),里面包含了关闭连接的原因。
终端可以在超时帧载体上附加不透明数据。额外的调试数据仅用来诊断没有语义值。调试信息可以包含安全或者隐私敏感的数据。登录或者其他持续存储的数据必须有足够的保障措施,以防止未经授权的访问。
WINDOW_UPDATE帧(type=0x8)用来实现流量控制;概述见章节5.2。
流量控制在两种层面上操作:每个单独的流或者整个连接。
所有类型的流量控制都是逐跳的;就是说,只在两个终端之间作用。中介端不在依赖的连接上转接WINDOW_UPDATE帧。接收端对数据的显示可以直接导致流量控制信息的传播转到原始发送端。
流量控制只适用于确定受流量控制影响的帧。文档中定义的帧类型中,只包括数据帧。不受流量控制的帧必须被接收和处理,除非接收端无法为帧分配资源。接收端如果无法接收帧,可以响应一个流错误(章节5.4.2)或者类型为流量控制错误的连接错误(章节5.4.1)。
INDOW_UPDATE帧的载体是一个保bit,加上一个无符号31bit为整数表明发送端除了现有的流量控制窗口可以发送的字节数。留空控制窗口有效的增量范围是 1 至 2 31 − 1 1 至 2^{31}-1 1至231−1(0x7fffffff) 字节。
WINDOW_UPDATE帧没有定义任何标记。
WINDOW_UPDATE可以专指某个流或者整个连接。在前者的情况下,帧的流标识符指的是被影响的流;在后者情况下,值"0"表示整个连接都受这个帧的影响.
REAM标记的帧的对等端来发送。这意味着接收端可以在“半封闭(远程)”或者“关闭”的流上接收WINDOW_UPDATE帧。接收端绝对不能作为错误处理,见章节5.1
接收端收到受流量控制的帧必须总是计算流量对整个连接流量控制的影响量,除非接收端将这作为连接错误处理。即使帧出错这也是必须的。因为发送端将这个帧计入了流量控制窗口,如果接收端没有这样做,发送端和接收端的流量控制会不相同。
HTTP/2中流量控制是通过每个发送端在每个流上携带一个窗口来实现的。流量控制窗口是一个简单的整数值,指示发送端被允许传输的字节数;因此,它的大小是接收端的缓存能力的衡量。
流量控制窗口对流和连接的流量控制窗口都适用。发送端绝对不能发送超出接收端广播的流量控制窗口大小的可用空间长度的受流量控制影响的帧。在各个流量控制窗口中没有可用空间时,可以发送带有END_STREAM标记的长度为0的帧(例如,空数据帧)。
流量控制计算中,8字节的帧报头不被计入。
在发送一个流量控制帧后,发送端在各个窗口中可用空间中减去发送的帧长度。
接收端发送一个WINDOW_UPDATE帧当它接受信息并释放了流量控制窗口的空间。单独的WINDOW_UPDATE帧用于流及连接层面的流量控制窗口中。
发送端收到WINDOW_UPDATE后按帧中指定的大小更新到正确的窗口。
发送端绝对不允许流量控制窗口超过 2 31 − 1 2^{31}-1 231−1字节。如果发送端接收到WINDOW_UPDATE使得流量控制窗口超过这个最大值,它必须适当地终止这个流或者这个连接。对于流,发送端发送一个带有流量控制错误的错误码的ST_STREAM帧;对于连接,发送一个带有流量控制错误码的超时帧。
发送端发送的受流量控制的帧以及接收端收到的WINDOW_UPDATE帧是完全相互异步的。这种属性让接收端积极的更新发送端携带的窗口大小来防止流停转。
HTTP/2连接初次建立时,新的流创建的初始化流量控制大小是65,535字节。连接的流量控制大小是65,535字节。两个终端都能通过在组成连接序言的设置帧中携带一个SETTINGS_INITIAL_WINDOW_SIZE设置调整新流的初始化窗口大小。连接的流量控制窗口只能被WINDOWS_UPDATE帧修改。
在收到设置帧指定SETTINGS_INITIAL_WINDOW_SIZE前,终端只能只有流量控制的默认窗口值。类似的,连接的流量控制窗口初始化时也是默认值知道收到WINDOW_UPDATE帧。
设置帧可以针对所有当前的流修改流量控制初始化大小。当SETTINGS_INITIAL_WINDOW_SIZE值改变时,接收端必须将根据新旧值调整其保留的所有流的窗口大小设置帧不能修改连接的流量控制窗口。
SETTINGS_INITIAL_WINDOW_SIZE的修改可能导致流量控制窗口的可用空间为负数。发送端必须跟踪负数的流量控制窗口,并且绝对不能发送新的受流量控制的帧直到接收到窗口更新帧使得流量控制窗口变成正数。
例如,如果客户端在建立的连接上立即发送60KB的数据,而终端将初始的窗口大小设置成16KB,客户端将重新计算流量控制窗口的可用空间为-44KB。终端将保持一个负数的流量控制窗口直到窗口更新帧恢复窗口到正数,这个时候客户端才能恢复数据发送。
设置帧不能修改链接流量控制窗口。
终端必须将SETTINGS_INITIAL_WINDOW_SIZE的修改导致流量控制窗口超过最大值的情况作为类型为流量控制错误的连接错误(章节 5.4.1)处理。
接收端希望使用比当前大小更小的流量控制窗口可以发送一个新的设置帧。然而,接收端必须准备好接收超过窗口大小的数据,因为发送端在处理设置帧之前发送了低于最低下限的数据。
在发送减小初始化流量控制窗口大小的设置帧后,接收端有两种选择处理流超过流量限制的情况:
延续帧(type=0x9)用来延续一个报头区块(章节4.3)碎片序列。在现有流上可以发送到一个已存在延续帧,只要相同流上的前一阵是报头帧、推送承诺帧或者不带有END_HEADERS标记的延续帧。
延续帧实体包含一个报头区块碎片(章节4.3)。
延续帧载体有以下字段:
延续帧改变连接状态如章节4.3中定义。
延续帧必须与流相关联。如果延续帧的相关流表示字段是0x0,终端必须响应一个类型为协议错误的连接错误。
延续帧必须跟在不带有END_HEADERS设置的报头帧、推送承诺帧或延续帧后面。终端接收到不符合此规则的必须响应一个类型为协议错误的连接错误(章节5.4.1)。
错误码共享一个功能的代码空间。一些错误代码只适用于特定的条件,在某些帧类型没有定义的语义。
HTTP/2的目的是尽可能的兼容目前使用的HTTP。这意味着,从服务端或者客户端应用的角度来看,该协议的特点是不变的。为了实现这点,所有响应与请求的语义都将保留,尽管包含这些语义的语法已经改变。
因此,HTTP/1.1语义与内容、有条件的请求、范围请求、缓存与验证定义的规范与要求同样适用于HTTP/2。HTTP/1.1消息语法与路由选定的内容,例如HTTP与HTTPS URI方案,也同样适用于HTTP/2,但是表达这些协议的语义在下面的章节定义。
在HTTP/1.1中已经默认使用来持久连接,可以做到多个请求复用在同一个tpc连接上,同时利用pipeline机制,可以让请求同时在一个tcp上发送,但是http本质上还是一个请求/响应模型,服务端仍然需要按照请求的顺序依次恢复,不能乱序回复。这样要是前面的回应特别慢,后面会有许多请求排队等着。这种情况称之为队头阻塞(head-of-line blocking)
RFC7540