@(StuRep)
来自百度FEX前端团队
介绍
HTTP/1.0只允许在一个连接上建立一个当前未完成的请求。HTTP/1.1管道只部分处理了请求并发和报头阻塞的问题。因此客户端需要发起多次请求通过数次连接服务器来减少延迟。
此外,HTTP/1.1的报头字段经常重复和冗长。在产生更多或更大的网络数据包时,可能导致小的初始TCP堵塞窗口被快速填充。这可能在多个请求建立在一个新的TCP连接时导致过度的延迟。
本协议通过定义一个优化的基础连接的HTTP语义映射来解决这些问题。 具体地,它允许在同一连接上交错地建立请求和响应消息,并使用高效率编码的HTTP报头字段。 它还允许请求的优先级,让更多的重要的请求更快速的完成,进一步提升了性能。
最终协议设计为对网络更友好,因为它相对HTTP/1.x减少了TCP连接。 这意味着与其他流更少的竞争以及更长时间的连接,从而更有效地利用可用的网络容量。
最后,这种封装也通过使用二进制消息帧使信息处理更具扩展性。
_
HTTP / 2协议概述
HTTP/2中基本的协议单位是帧。每个帧都有不同的类型和用途。例如,报头(HEADERS)和数据(DATA)帧组成了基本的HTTP 请求和响应;其他帧例如 设置(SETTINGS)、窗口更新(WINDOW_UPDATE)和推送承诺(PUSH_PROMISE)是用来实现HTTP/2的其他功能。
HTTP/2添加了一种新的交互模式,即服务器能推送消息给客户端。服务器推送允许服务端预测客户端需要来发送数据给客户端,交换网络使用以阻止潜在的延迟增长。服务器通过复用一个以PUSH_PROMISE帧发送的请求来实现推送,然后服务端可以在一个单独的流里面发送响应给这个合成的请求。
帧包含的HTTP报头字段是压缩的。HTTP请求有可能是高度冗余的,因此压缩能显著减少请求和响应的大小。
_
启动HTTP/2
一个HTTP/2连接是运行在TCP连接上的应用层协议。客户端是TCP连接的发起者。
HTTP/2使用与HTTP/1.1相同的"http"和"https" 资源标识符(URI)。使用相同的默认端口:"http" 的80端口及“https”的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中描述。
针对"http"启动HTTP/2
客户端无法预知服务端是否支持HTTP/2.0 的情况下使用HTTP升级机制发起“http” URI请求([RFC7230] 章节6.7)。客户端发起一个http1.1请求,其中包含识别HTTP/2的升级报头字段与h2c token。HTTP/1.1必须包含一个确切的HTTP2-Settings中的报头字段。
针对“https”启动HTTP/2
客户端在不了解服务端是否支持HTTP/2的时候,会使用TSL [TLS12] 于其应用层协议协商扩展 [TLSALPN]。
使用TLS的HTTP/2 使用"h2"程序token。“h2c”token绝对不能由客户端或者选定的服务端发送。
TSL协商一旦完成,客户端和服务端都可以发送连接序言。
HTTP/2连接序言
在建立TCP连接并且检测到HTTP/2会被各个对等端使用后,每个端点必须发送一个连接序言最终确认并作为建立HTTP/2连接的初始设置参数。
如果任何一个端点没有以一个有效的连接序言开头,客户端和服务端必须终止TCP连接。如果端点并没有使用HTTP/2此时可以省略超时(GOAWAY)帧(章节6.8)。
_
HTTP帧
TTP/2连接一旦建立,端点之间可以马上交换数据帧。
帧格式
帧报头字段定义是:
- R: 保留的2位字段。这些字节的语义是未定义的,并且在发送的时候必须保持未设置(0),接收的时候必须被忽略此字段。
- Length: 14位无符号整数的帧主体长度。8字节长度的帧报头信息不计算在此内。
- Type: 帧的8位类型。帧类型定义了剩余的帧报头和帧主体将如何被解释。具体实现必须在收到未知帧类型(任何未在文档中定义的帧)时作为连接错误中的类型协议错误(PROTOCOL_ERROR)处理。
- Flags: 为帧类型保留的8字节字段有具体的布尔标识。标识针对确定的帧类型赋予特定的语义。确定帧类型定义语义以外的标示必须被忽略,并且必须在发送的时候保留未设置(0)。
- R: 1位的保留字段。这个字段的语义未设置并且必须在发送的时候保持未设置(0),在接受的时候必须被忽略。
- Stream Identifier: 31字节的流标识符(见StreamIdentifiers)。0是保留的,标明帧是与连接相关作为一个整体而不是一个单独的流。
报头压缩和解压缩
HTTP/2报文报头字段是包含一个或多个相关的键值对。他们在HTTP请求响应消息及服务器推送操作(见章节8.2)中使用。
报头集合是0个或多个报头字段的集合。当他通过连接传输的时候,报头集合将使用HTTP报头压缩序列化到报文报头块中。序列化的报头块被分割成一个或多个的字节序列,称为报头分区,并在报头(章节6.2)、推送承诺(章节6.6)及延续帧(章节6.10)的载体中传送。
HTTP报文头压缩并不保留报头字段的相关顺序。具有多个值的报头字段使用特定的分割器被编码分割到一个单独的报头区域(见 章节8.1.2.3 HeaderOrdering),这保留了该报头字段中各种值的对应顺序。
_
流和多路复用
流是一个独立的,客户端和服务端在HTTP/2连接下交换帧的双向序列。流有一下几个重要特点:
- 一个单独的HTTP/2连接能够保持多个同时打开的流,各个端点间从多个流中交换帧。
- 流可以被被客户端或者服务端单方面建立使用或分享。
- 流可以被任何一个连接终端关闭。
- 在流内发送帧的顺序很重要。它们将按被接收的顺序处理。特别是报头及数据帧的顺序语义上是有意义的。
- 流以一个整数标识。标识符有启动流的终端分配。
流标识
流由31位字节的无符号整数标识。客户端发起的流必须以奇数标示;服务器发起的流必须使用偶数来标示。0(0x0)用来标识连接控制信息流,且绝对不能用来建立一个新流。
HTTP/1.1升级到HTTP/2的请求将收到一个1(0x1)标识的流的响应。升级完成后,0x1流将对客户端处于“半封闭(本地)”状态。因此,0x1流不能被从HTTP/1.1升级的客户端用来作为一个新的流的标识符。
流标识符不能被重复使用。生存期长的连接可能导致流标识符可用范围耗尽。客户端不能新建流标识符时可以针对新流建立一个新的连接。服务端不能新建流标识符时可以发送一个超时帧(GOAWAY)强制客户端对新的流使用新的连接。
流并发
对等端可以使用设置帧里面的SETTINGS_MAX_CONCURRENT_STREAMS参数来限制流的并发量。最大并发流设置(章节6.5.2)仅适用于终端并且只对接收到此设置的对等端有效。也就是说:客户端可以指定服务端能启动的流最大并发量,而且服务端能指定客户端能启动的流最大并发量。终端绝对不能超过对等端设置的限制。
终端绝对不能超过对等端设定的设置。终端接收到报头帧导致他们广播的并发流超过限制的必须将这作为流错误(章节5.4.2)处理。终端希望将SETTINGS_MAX_CONCURRENT_STREAMS的值减少到比当前打开的流更小时可以关闭超过新的设置值的流或者允许流结束。
Flow Control 流量控制
使用复用流介绍了针对TCP连接的资源争夺导致的流阻塞。流量控制方案等确保同一连接上的流相互之间不会造成破坏性的干扰。流量控制在单个流及整个连接过程中使用。
HTTP/2 通过使用WINDOW_UPDATE帧类型来提供流量控制(章节6.9)。
流量控制规则
HTTP/2流流量控制目标在于允许不需要协议改动的情况下改进流量控制算法。HTTP/2中的流量控制有以下特点:
- 流量控制是逐跳的,而不是头尾连接的。
- 流量控制是基于窗口更新帧的。接收端广播自己准备在流及整个连接过程中接收的字节大小。这是一个信用为基础的方案。
- 流量控制是有方向性的,由接收端全权掌握。接收端可以选择针对流及整个连接设置任意的窗口大小。发送端必须遵守接收端的流量控制限制。客户端、服务端及中端代理作为接收者时都独立的向外广播他们各自的流量控制窗口,作为发送者时遵守接收端的限制。
- 每个新的流及整个连接的流量控制窗口初始值是65,535字节。
- 帧类型决定了是否适用流量控制规则。本文档定义的帧中,只有DATA帧受流量控制;所有其他的帧不受广播的流量控制窗口影响。
- 流量控制不能被禁用。
正确使用流量控制
流量控制的定义是用来保护端点在资源约束条件下的操作。例如,一个代理需要在很多连接之间共享内存,也有可能有缓慢的上游连接和快速的下游连接。流量控制解决的情况是接收端在一个流上处理数据的同时同样想继续处理同个连接上的其他流。
流优先级
新建流的终端可以在报头帧(章节6.2)中包含优先级信息来对流标记优先级。对于已存在的流,优先级帧(章节6.3)可以用来改变流优先级。
优先级的目的是允许终端表达它如何让对等端管理并发流时分配资源。更重要的是,在发送容量有限时优先级能用来选择流来传输帧。
流的优先级明确设置将输入到优先级处理过程中。它并不能保证能相当其他相关流有特殊的处理或者传输顺序。终端并不能使用优先级强制要求对等端按照特定顺序处理并发流。因此优先级的表达仅仅是一个建议。
优先级信息可以像它们被创建一样使用报头帧或者使用优先级帧来明确指定或者改变。提供优先级信息是可选的,没有明确指定时使用默认值(章节5.3.5)。
_
错误码
错误码是32位字段,用在RST_STREAM和超时帧中用来标识流或者链接错误的原因。
定义了以下错误码:
- NO_ERROR (0): 相关的条件并不是错误的结果。例如超时帧可以携带此错误码指示连接的平滑关闭。
- PROTOCOL_ERROR (1): 终端检测到一个不确定的协议错误。这个错误用在一个更具体的错误码不可用的时候。
- INTERNAL_ERROR (2): 终端遇到意外的内部错误。
- FLOW_CONTROL_ERROR (3): 终端检测到对等端违反了流量控制协议。
- SETTINGS_TIMEOUT (4): 终端发送了设置帧,但是没有及时收到响应。见Settings Synchronization。
- STREAM_CLOSED (5): 终端在流半封闭的时候收到帧。
- FRAME_SIZE_ERROR (6): 终端收到大小超过最大尺寸的帧。
- REFUSED_STREAM (7): 终端拒绝流在它执行任何应用处理之前,详见Reliability(章节 8.1.4)
- CANCEL (8): 终端使用这个标示某个流不再需要。
- COMPRESSION_ERROR (9): 终端无法维持报头压缩上下文的连接
- CONNECT_ERROR (10): 响应某个连接请求建立的连接被服为异常关闭。
- ENHANCE_YOUR_CALM (11): 终端检测出对等端在表现出可能会产生过大负荷的行为。
- INADEQUATE_SECURITY (12): 基础传输包含属性不满足文档或者终端申明的最小要求。
_
服务端推送
HTTP/2允许服务端针对客户端一个单独的请求,主动的发送(或推送)一个或者多个相关的响应。这种特定在服务端知道客户端需要这些响应来完整的处理最初的请求的时候特别有用。
_
额外HTTP要求/考虑
这段概况了HTTP协议的属性,包括提高互操作性、减少暴露已知的安全漏洞,或者减少执行变动的可能。
连接管理
HTTP/2连接是永久性的。为了最佳的性能,它期待直到确定与服务端的进一步沟通不再必要的时候,客户端才会关闭连接(例如,当用户导航到其他特定的网页),或者直到服务端关闭连接。
_
安全性考虑
服务端认证
客户端只有经过权限验证才能获取HTTP/2响应的资源。这在服务器推送中(章节8.2)尤为重要,客户端在接收响应前验证PUSH_PROMISE帧。
HTTP/2依据HTTP/1.1权限定义来检测服务端是否有权限提供给定的响应,见RFC2818 章节3.这依赖于本地“HTTP”URI方案的域名解析,以及服务端提供的“https”方案验证。
客户端绝对不能以任何形式使用服务端提供的客户端没有权限的资源。
跨协议攻击
在跨协议攻击中,攻击者使客户端在一种协议中向解析另一种协议的服务器启动一个交易。攻击者可能能够使交易看起来在第二种协议中是合法的。结合对web上下文的利用,这个可以针对保护不力的服务器在秘密网络下进行互动。
中介端封装攻击
HTTP/2报头名称和值编码成带有长度前缀的字节序列。这使得HTTP/2能够携带任何字符串的字节作为报头字段的名称或值。中介端直接转换HTTP/2请求或响应到HTTP/1.1时可以允许HTTP/1.1消息创建的损坏。攻击者可以利用这个行为让中介端创建HTTP/1.1消息时带有非法报头字段、额外报头字段,甚至是完全伪造的新消息。
推送响应的缓存
推送响应并没有一个来自客户端的明确请求;请求是服务端从PUSH_PROMISE帧中提供的。
缓存响应推送可能是基于原始服务器的缓存控制报头字段的指导。然而,如果服务端主机包含多个用户可能会导致问题。例如,服务端可能为多个用户每个提供小部分的URI空间。