HTTP多版本对比
上个世纪90年代初期,蒂姆·伯纳斯-李(Tim Berners-Lee)及其 CERN的团队共同努力,制定了互联网的基础,定义了互联网的四个构建模块:
- 超文本文档格式(HTML)
- 数据传输协议(HTTP)
- 用于查看超文本的网络浏览器(第一个浏览器,WorldWideWeb)
- 用于传输数据的服务器(httpd的早期版本)
HTTP重用了现有的 TCP/IP 协议来进行数据传输,其中 HTTP 消息字节位于应用层,如下图中的浅蓝色所示。
1、HTTP/0.9
这是第一个 HTTP草案。它只有一个 GET方法,也不支持头部或状态代码;唯一可用的数据格式是 HTML。就像 HTTP/1.0和 HTTP/1.1一样,HTTP消息以 ASCII文本结构呈现。
HTTP/0.9请求的示例:
GET /mypage.html
响应示例:
<html>A verysimple HTML page</html>
2、HTTP/1.0
这个版本定义了现在所用的 HTTP结构,类似于一份备忘录,同时引入了新的方法(HEAD和POST)、MIME类型、状态码和协议版本。
HTTP/1.0请求的示例:
GET /mypage.html HTTP/1.0User-Agent: NCSA_Mosaic/2.0 (Windows 3.1)
响应示例:
200 OK
Date: Tue, 15 Nov 1994 08:12:31 GMT
Server: CERN/3.0 libwww/2.17
Content-Type: text/html
<HTML>A page with an image<IMG SRC="/myimage.gif"/></HTML>
3、HTTP/1.1
这个版本于1997年初推出,距离其前身只有几个月。主要变化包括:
持久的TCP连接(keep-alive),可以节省机器和网络资源。在之前的版本中,每个请求都要打开一个新的TCP连接,并在响应后关闭。
Host头部,支持在同一个IP下有多个服务器。
有关编码、缓存、语言和MIME类型的头部约定。
HTTP/1.1请求的示例:
GET /api/fruit/orange HTTP/1.1
Host: www.fruityvice.com
Accept-Encoding: gzip, deflate, br
响应示例:
HTTP/1.1 200 OK
Server: nginx/1.16.1
Date: Sun, 10 Mar 2024 20:44:25 GMT
Transfer-Encoding: chunked
Connection: keep-alive
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-store, must-revalidate, no-cache,max-age=0
Pragma: no-cache
X-Frame-Options: DENY
Content-Type: application/json
Expires: 0
{"name":"Orange","id":2,"family":"Rutaceae","order":"Sapindales","genus":"Citrus","nutritions":{"calories":43,"fat":0.2,"sugar":8.2,"carbohydrates":8.3,"protein":1.0}}
4、HTTP/2
2015年,在对互联网性能进行多年观察和研究后,人们基于谷歌的 SPDY协议,提出并建立了 HTTP/2。
它与 HTTP/1.1有很多不同,包括将多个消息复用到单个 TCP数据包中、消息的二进制格式以及用于头部的 HPACK压缩。
在 HTTP/1.1中,两个请求不能共享同一个 TCP连接,必须等待第一个请求结束后才能开始后续请求。这被称为头部阻塞。在下面的图表中,由于只使用了一个 TCP连接,请求2必须等到响应1到达之后才能发送。
HTTP/2通过流(stream)解决了这个问题,每个流对应一个消息。许多流可以交错在单个 TCP数据包中。如果某个流由于某种原因无法发送其数据,其他流可以在 TCP数据包中顶替它。
HTTP/2的流被分成帧(frame),每个帧包含:帧类型、它所属的流以及长度(以字节为单位)。在下面的图表中,彩色矩形是一个 TCP数据包,✉ 代表其中的一个 HTTP/2帧。第一个和第三个 TCP数据包携带了不同流的帧。
下面的图像展示了帧如何放置在 TCP数据包中。流1携带了一个 JavaScript文件的 HTTP响应,而流2携带了一个 CSS文件的 HTTP响应。
5、HTTP/3
HTTP/3诞生于一个名为 QUIC的新传输协议,该协议由谷歌于2012年创建。QUIC被封装在 UDP内,与 TCP相比,它有以下优点:
- 建立连接和TLS身份验证的往返包数量更少;
- 在处理数据包丢失方面具有更强的连接韧性;
- 解决了TCP和TLS中存在的头部阻塞问题。
HTTP/2解决了 HTTP头部阻塞问题,但是,这个问题在 TCP和 TLS中也存在。TCP认为它需要发送的数据是一系列连续的数据包,如果有任何数据包丢失,必须重新发送,以保持信息的完整性。在 TCP中,直到丢失的数据包成功重新发送到目的地之前,后续的数据包都不能被发送。
下面的图示直观地解释了在 HTTP/2中这种情况是如何发生的。第二个数据包只包含了响应1的帧,但是它的丢失延迟了响应1和响应2,这意味着在这种情况下没有并行处理。
为了解决 TCP的头部阻塞问题,QUIC决定使用 UDP作为传输协议,因为UDP不保证到达。在TCP中作为传输层的数据完整性责任被移至QUIC的应用层,消息的帧可以无序到达,而不会阻塞不相关的流。
与 TCP相关的 TLS(SSL)中的头部阻塞是因为加密通常应用于整个消息内容,这意味着需要接收所有数据之后才能进行解密。使用 QUIC时,加密是针对每个 QUIC数据包的,到达时就可以进行解密,而无需预先接收所有数据包。
带有 TLS的 TCP:
- 输入:A+B+C
- 加密:crypt(A+B+C) = D+E+F
- 数据包:D, E, F
- 接收:decrypt(D+E+F)
- A+B+C
带有TLS的QUIC:
- 输入:A+B+C
- 加密:crypt(A) = X, crypt(B) = Y, crypt(C) = Z
- 包:X, Y, Z
- 接收:decrypt(X) + decrypt(Y) + decrypt(Z)
- A+B+C
6、比较表
* TLS 1.2需要2次往返进行加密握手,而 TLS 1.3只需要1次,还有 0-RTT(零往返时间恢复)选项,其中不需要先前的握手。然而,0-RTT会导致重放攻击,因此是不安全的。
** QUIC的连接 ID可能被用于指纹识别,一项研究指出,这存在对用户隐私的风险。
7、哪个版本最好?
目前最好的两个版本是 HTTP/2和 HTTP/3。
HTTP/3是为不稳定的连接设计的,例如手机和卫星网络。为了应对网络不稳定性,QUIC在数据流之间具有很高的独立性,并且在丢包时具有很好的弹性。然而,HTTP/3也存在性能损失,主要是因为1)由于 UDP的使用率较低,路由器和操作系统在过去几十年里没有对 UDP协议进行优化,使其相对于 TCP而言速度较慢;2)QUIC使用逐个数据包的加密需要更多的数学运算,效率低于 TCP中使用的整个消息加密。此外,UDP协议在某些网络中受到限制,以防止诸如 UDP洪水攻击和 DNS放大攻击等攻击。
在可靠和稳定的连接上,HTTP/2通常比 HTTP/3提供更好的性能。
一般来说,建议进行兼容性和性能测试,以确定哪个版本最合适。此外,服务器可以接受 HTTP/2和 HTTP/3连接,让客户端决定使用哪个版本。