过去,浏览器使用多个 TCP 连接来发出并行请求。但是,这是有局限性的。如果使用的连接过多,则将适得其反(TCP 拥塞控制将被无效化,导致的用塞事件将会损害性能和网络),并且从根本上讲是不公平的(因为浏览器会占用许多本不该属于它的资源)。同时,大量请求意味着“在线”上有大量重复数据。
这两个因素都意味着 HTTP/1.1 请求有很多与之相关的开销。如果请求过多,则会影响性能。
这使得业界误解了“最佳实践”,进行诸如 spriting 图片合并,data: inlining 内联数据,Domain Sharding 域名分片和 Concatenation 文件合并之类的事情。这些 hack 行为表明协议本身存在潜在问题,在使用的时候会出现很多问题。
HTTP/2 是由 IETF 的 HTTP 工作组开发的,该工作组维护 HTTP 协议。它由许多 HTTP 实现者,用户,网络运营商和 HTTP 专家组成。
请注意,虽然我们的邮件列表托管在 W3C 网站上,但这并不是 W3C 的努力。但是,Tim Berners-Lee 和 W3C TAG 与 WG 的工作进度保持同步。
大量的人为这项工作做出了贡献,最活跃的参与者包括来自诸如 Firefox,Chrome,Twitter,Microsoft 的 HTTP stack,Curl 和 Akamai 等“大型”项目的工程师,以及许多诸如 Python、Ruby 和 NodeJS 之类的 HTTP 实现者。
要了解有关 IETF 的更多信息,请参见Tao of the IETF。您还可以在 Github 的贡献者图中了解谁为规范做出了贡献,以及谁在我们的实现列表中参与该项目。
在征求提案和进行选择过程之后,选择 SPDY/2 作为 HTTP/2 的基础。此后,根据工作组的讨论和实现者的反馈,进行了许多更改。在整个过程中,SPDY 的核心开发人员都参与了 HTTP/2 的开发,包括 Mike Belshe 和 Roberto Peon。2015 年 2 月,Google 宣布了其计划删除对 SPDY 的支持,转而支持 HTTP/2。
是二进制的,而不是文本的 完全多路复用,而不是有序和阻塞 因此可以使用一个连接进行并行处理 使用头压缩来减少开销 允许服务器主动将响应"推送"到客户端缓存中
与诸如 HTTP/1.x 之类的文本协议相比,二进制协议解析起来更高效,更“紧凑”,并且最重要的是,它们比二进制协议更不容易出错,因为它们对空格处理,大写,行尾,空白行等的处理很有帮助。例如,HTTP/1.1 定义了四种不同的解析消息的方式。在 HTTP/2 中,只有一个代码路径。
HTTP/2 在 telnet 中不可用,但是我们已经有了一些工具支持,例如 Wireshark 插件。
HTTP/1.1 试图通过管道修复此问题,但是并不能完全解决问题(较大或较慢的响应仍会阻止其他问题)。此外,由于许多中间件和服务器未正确处理管线化,因此很难部署它。
这迫使客户使用多种试探法(通常是猜测法)来决定通过哪些连接提交哪些请求;由于页面加载的数据量通常是可用连接数的 10 倍(或更多),因此会严重影响性能,通常会导致被阻止的请求“泛滥”。
多路复用通过允许同时发送多个请求和响应消息来解决这些问题。甚至有可能将一条消息的一部分与另一条消息混合在一起。所以在这种情况下,客户端只需要一个连接就能加载一个页面。
一个应用程序打开如此多的连接,已经远远超出了当初设计 TCP 时的预想。由于每个连接都会响应大量的数据,这会造成中间网络中的缓冲区溢出的风险,从而导致网络拥塞事件并重新传输。
此外,使用这么多连接还会强占许多网络资源。这些资源都是从那些“遵纪守法”的应用那“偷”的(VoIP 就是个很好的例子)。
服务器推送可以通过“推送”它认为客户端需要的响应到其缓存中,来避免服务器的这种往返延迟。
但是,“推送”响应不是“神奇的”——如果使用不正确,可能会损害性能。正确使用 Server Push 是正在进行的实验和研究领域。
假设一个页面包含大约 80 个资源需要加载(在当今的 Web 中是保守的),并且每个请求具有 1400 字节的消息头(这并不罕见,这要归功于 Cookie,Referer 等),至少要 7 到 8 个来回去“在线”获得这些消息头。这还不包括响应时间——那只是从客户端那里获取到它们所花的时间而已。
这是因为 TCP 的慢启动机制造成的,根据已确认的数据包数量,从而对新连接上发送数据的进行限制——有效地限制了最初的几次来回可以发送的数据包数量。
相比之下,即使对报头进行轻微的压缩,这些请求也可以在一次往返(甚至一个数据包)内搞定。
这种额外开销是相当大的,尤其是考虑到对移动客户端的影响时,即使在网络状况良好的条件下,移动客户端的往返延迟通常也要几百毫秒。
从那时起,一个重要的攻击方式 CRIME 诞生了,这种方式可以攻击加密文件内部的所使用的压缩流(如 GZIP)。
使用 CRIME,攻击者有能力将数据注入加密流中,并可以“探测”明文并恢复它。由于这是 Web,因此 JavaScript 使这成为可能,而且已经有了通过对受到 TLS 保护的 HTTP 资源的使用CRIME来还原出 cookies 和认证令牌(Toekn)的案例。
结果,我们无法使用 GZIP 压缩。没有找到适合该用例并且可以安全使用的其他算法,我们创建了一种新的,专门针对报头的压缩方案,该方案以粗粒度压缩模式运行;由于 HTTP 标头通常在消息之间不改变,因此仍然可以提供合理的压缩效率,并且更加安全。
这是因为 HTTP 被广泛使用。如果我们使用此版本的 HTTP 引入一种新的状态机制(例如之前讨论过的例子)或更改了核心方法(值得庆幸的是,尚未提出该方法),则意味着新协议与现有 Web 不兼容。
特别是,我们希望能够在不损失任何信息的情况下从 HTTP/1 转换为 HTTP/2。如果我们开始“清理”报头(并且大多数人会同意,因为 HTTP 报头很乱),将会出现很多与现有 Web 互操作性的问题。
这样做只会对新协议的普及造成麻烦。
综上所述,HTTP 工作组负责所有 HTTP,而不仅仅是 HTTP/2。这样,我们可以研究与版本无关的新机制,只要它们与现有 Web 向后兼容即可。
先前收到过 HTTP “APIs” 在 HTTP/2 中具有良好性能等特点这样的反馈,那是因为 API 不需要在设计中考虑诸如请求开销之类的问题。
话虽如此,我们正在考虑的改进的主要焦点是典型的浏览用例,因为这是该协议的核心用例。
我们的章程对此表示:
The resulting specification(s) are expected to meet these goals for common existing deployments of HTTP; in particular, Web browsing (desktop and mobile), non-browsers ("HTTP APIs"), Web serving (at a variety of scales), and intermediation (by proxies, corporate firewalls, "reverse" proxies and Content Delivery Networks). Likewise, current and future semantic extensions to HTTP/1.x (e.g., headers, methods, status codes, cache directives) should be supported in the new protocol.
正在制定的规范需要满足现在已经普遍部署了的 HTTP 的功能要求;具体来说主要包括,Web 浏览(桌面端和移动端),非浏览器(“HTTP APIs” 形式的),Web 服务(大范围的),还有各种网络中介(借助代理,企业防火墙,反向代理以及内容分发网络实现的)。同样的,对 HTTP/1.x 当前和未来的语义扩展 (例如,消息头,方法,状态码,缓存指令) 都应该在新的协议中支持。
Note that this does not include uses of HTTP where non-specified behaviours are relied upon (e.g., connection state such as timeouts or client affinity,and "interception" proxies); these uses may or may not be enabled by the final product.
值得注意的是,这里没有包括将 HTTP 用于非特定行为所依赖的场景中(例如超时,连接状态以及拦截代理)。这些可能并不会被最终的产品启用。
但是,一些实现已声明它们仅在通过加密连接使用 HTTP/2 时才支持 HTTP/2,并且当前没有浏览器支持未加密的 HTTP/2。
有关详细信息,请参见规范。
还讨论了其他机制,例如对 HTTP:// URL 使用 TLS(所谓的“机会主义加密”);参见 RFC 8164。
还有几种可用的服务器(包括 Akamai,Google 和 Twitter 的主要站点提供的 beta 支持),以及许多可以部署和测试的开源实现。
有关更多详细信息,请参见实现列表。
最初的提议里有流分组的概念,它可以共享上下文,流量控制等。虽然这将使代理受益(以及代理用户的体验),但这样做却增加了相当多的复杂性。所以我们就决定先以一个简单的东西开始,看看它会有多糟糕的问题,并且在未来的协议版本中解决这些问题(如果有的话)。
如果单独考虑霍夫曼解码,那么任何比所需填充长的符号都可以工作;但是,HPACK 的设计允许按字节比较霍夫曼编码的字符串。通过要求将 EOS 符号的位用于填充,我们确保用户可以对霍夫曼编码的字符串进行字节比较,以确定是否相等。反过来,这意味着许多 headers 可以在不需要霍夫曼解码的情况下被解析。
对于 TLS(h2)上的 HTTP/2 ,如果您未实现 http1.1 ALPN 标识符,则无需支持任何 HTTP/1.1 功能。
对于基于 TCP(h2c)的 HTTP/2 ,您需要实现初始 Upgrade 升级请求。
只支持 h2c 的客户端需要生成一个针对 OPTIONS 的请求,因为 “*” 或者一个针对 “/” 的 HEAD 请求,它们相当安全且易于构造。希望仅实现 HTTP/2 的客户端将需要将没有 101 状态码的 HTTP/1.1 响应视为错误。
只支持 h2c 的服务器可以使用一个固定的 101 响应来接收一个包含升级(Upgrade)消息头字段的请求。没有 h2c 升级令牌的请求可以通过包含 Upgrade 头字段的 505(不支持 HTTP 版本)状态码拒绝。不希望处理 HTTP/1.1 响应的服务器应在发送连接序言后,应该立即用 REFUSED_STREAM 错误码拒绝 stream 1,以鼓励客户端通过 upgraded 的 HTTP/2 连接重试请求。
例如,如果这样一个数据包告诉对等端有更多可用的窗口来发送数据,那么将其发送延迟数毫秒(或更长时间)会对高速连接造成严重影响。
推送基于请求内容而变化的资源可能是不明智的。目前,浏览器只会推送请求,如果他们不这样做,就会提出匹配的请求(请参阅 RFC 7234 的第 4 节)。
某些缓存不考虑所有请求头字段中的变化,即使它们在 Vary 头字段中。为了使推送资源被接收的可能性最大化,内容协商是最好的选择。基于 accept-encoding 报头字段的内容协商受到缓存的广泛尊重,但是可能无法很好地支持其他头字段。