一个HTTP请求流程
在地址栏输入网址后:
- 构建请求:浏览器构建请求行信息如
GET /index.html HTTP1.1
,准备发起网络请求 - 查找缓存:发起网络请求前,浏览器先在缓存中查询是否有要请求的文件,如有,则拦截请求,返回资源副本,并结束请求,如无则继续
- 准备IP地址和端口:查找DNS缓存,如无则进行DNS查询,最终拿到请求域名对应的IP地址,HTTP协议端口默认8080
- 等待TCP队列:浏览器中同一个域名最多只能建立6个TCP连接,多出来的需要排队等待
- 建立TCP连接:浏览器通过TCP三次握手与服务器建立连接
- 发送HTTP请求
- 服务器处理HTTP请求并响应
- 断开TCP连接:四次挥手
HTTP/0.9
1991年提出,最初的目的只是为了传输体积很小的HTML文件,因此称为超文本传输协议。特点:
- 只有一个请求行,以唯一支持的GET开头,后跟资源路径,如
GET /index.html
,没有请求头和请求体。 - 服务器只返回文件内容,没有返回头信息,因此只能传输HTML文件。
- 返回文件因为都是HTML格式,所以以ASCII字符流来传输。
HTTP/1.0
1996年发布。 1994年出现了拨号上网,同年网景推出了浏览器,万维网进入了高速发展的阶段。为支持多种类型的文件下载,HTTP/1.0引入了请求头和响应头。引入了状态码、提供了Cache机制来缓存下载过的资源、加入了用户代理字段。
例如HTTP请求头告诉服务器自己希望服务器返回的文件类型、压缩方法、文件的编码方式、语种:
accept: text/html
accept-encoding: gzip, deflate, br
accept-Charset: ISO-8859-1,utf-8
accept-language: zh-CN,zh
服务器返回自己最终选择的方式,如压缩方法、文件类型:
content-encoding: br
content-type: text/html; charset=UTF-8
HTTP/1.1
仅在几个月后,1997年发布了HTTP/1.1,也是HTTP的第一个标准化版本。
持久连接
HTTP/1.0中每一对请求响应都需要单独的TCP连接,HTTP/1.1增加了持久连接,在一个TCP连接上进行多次请求和响应。
默认是Connection: keep-alive
,即开启,设置Connection: close
手动关闭。
配合Keep-Alive: timeout=5, max=1000
来设定连接时长。其中timeout指定一个空闲连接需要保持打开状态的最小时长(单位:秒)。max指定此次连接的最大请求数。
目前浏览器对于同一域名允许最多同时建立6个TCP持久连接。
不成熟的管线化
管线化指将多个HTTP请求批量提交给服务器,服务器依然根据请求顺序来回复浏览器的请求。由于种种原因,这个技术的尝试失败了。
支持虚拟主机
HTTP/1.0中一个IP地址只能绑定一个域名,因此服务器只能支持一个域名。随着虚拟主机技术的发展,需要实现一台物理主机上绑定多个各自拥有域名的虚拟主机。HTTP/1.1在请求头中增加了Host字段,用来表示当前域名,供服务器区分。
支持动态生成的内容
HTTP/1.0中需要在响应头中设置完整的数据大小如Content-Length: 901
,以便浏览器根据数据大小准确接收数据。但对于动态生成的内容,传输前不知道最终大小,导致浏览器无法正确接收所有数据。
HTTP/1.1引入了Chunk transfer机制,服务器将数据分割成若干大小的数据块,每个数据块发送时都会附上数据块的长度,最后使用一个零长度的块来结束。这样就可以支持动态内容了。
客户端Cookie、安全机制
客户端Cookie机制
用户登录时,服务器验证用户登录信息正确后,会生成一段表示用户身份的字符串,并写入响应头Set-Cookie字段里,然后发送给浏览器,如Set-Cookie: UID=3431uad;
。
浏览器将Set-Cookie字段中的值保存到本地,当用户再次访问服务器时,浏览器会读取之前保存的Cookie数据并写入请求头的Cookie字段中,如Cookie: UID=3431uad;
。
服务器根据Cookie字段中的值查找该用户的信息,判断是否已登录,然后生成包含该用户信息的页面数据,返回给浏览器。
安全机制
HttpOnly:Set-Cookie: id=a3fWa; HttpOnly
。禁止JavaScript通过document.cookie访问cookie,以阻止XSS攻击。
SameSite: Set-Cookie: id=a3fWa; SameSite=Strict
。SameSite有三个值:
- None: 浏览器会在同站请求、跨站请求下继续发送cookies,不区分大小写。
- Strict:浏览器只在访问相同站点时发送cookie。
- Lax:新版浏览器的默认选项,在跨站点的情况下,只允许从第三方站点通过链接打开或get请求携带Cookie,通过post或img、iframe等标签加载的url不会携带cookie。
问题
TCP通道中,需要等待前面的请求返回后才能进行下一次请求,如果某个请求因为某些原因没有及时返回,就会阻塞后面的所有请求,这就是队头阻塞的问题。
HTTP/2
2015年5月正式标准化。
随着web页面日益复杂,所需的数据传输也越来越多,HTTP/1.1开始捉襟见肘。为此,谷歌2010年起实现了一个实验性的协议SPDY,作为客户端和服务端传输数据的替代方案。该方案提升响应能力,解决了重复数据传输的问题,是HTTP/2协议的基础。
HTTP/2比起HTTP/1.1主要有以下不同点:
- 它是二进制协议(binary protocol)而不是文本协议(text protocol),因此无法直接读取和创建。尽管有此弊端,但它是其他优化技术的前提。
- 它是多路复用协议(multiplexed protocol),也就是说同一个连接可以同时支持不同的请求。
- 头部压缩。
- 允许server push,即服务端填充数据到客户端缓存。
多路复用
该机制实现一个域名只使用一个TCP长连接,并消除了队头阻塞问题。多路复用技术能充分利用带宽,最大限度规避了TCP慢启动所带来的问题,使得页面资源的传输速度得到了大幅提升。 使用HTTP/2能带来20% ~ 60%的效率提升。
HTTP/2添加了一个二进制分帧层,将经过的请求转换为一个个带有请求ID编号的帧,服务器接收到所有帧之后,将所有相同ID的帧合并为一条完整的请求信息,处理完请求后,将响应也同样用二进制分帧层转换为一个个带有请求ID编号的帧,浏览器接收到响应帧后根据ID编号将数据提交给对应的请求。
浏览器可以随时发送请求,而不必等待前一个请求接收到响应之后;同样,服务器也可以按需决定优先返回哪些内容,而不必在意顺序,因为每份数据都有ID来标识。这样就实现了资源的并行传输。
HTTP/2的其他特性:
- 可以设置请求的优先级:服务器不需要按顺序处理请求,因此对于一些优先级比较高的请求,如关键资源的加载,可以在发送请求时标注优先级,服务器接收到请求后,会优先处理优先级高的请求。
- 服务器推送:服务器可以将数据提前推送到浏览器,例如:用户请求首页HTML文件后,服务器知道该页面会引用几个重要的JavaScript文件和CSS文件,于是可以附带将这些文件一并发送给浏览器,加快渲染速度。
- 头部压缩:HTTP/2对请求头和响应头进行了压缩,在一些大量发送请求体比较少的请求的情况下,传输效率会得到很大的提升。
HTTP/3
HTTP/2解决了应用层面的队头阻塞问题,但只要还是基于TCP,就避免不了TCP的队头阻塞问题。而TCP在设计之初就是为了单链接而设计。
截至2022年8月,已有26%的网站在使用HTTP/3。
TCP的问题
- 队头阻塞:TCP传输的数据被拆分成一个个按顺序排列的数据包,接收端收到后再按顺序将这些数据包组成原始数据。如果在传输过程中,有一个数据包因为网络故障或其它原因丢失,之后的数据就得等待丢失的数据包被重新传输过来,造成了阻塞。而HTTP/2只有一个连接,其他所有请求都会被阻塞。随着丢包率的增加,HTTP/2的传输效率也越来越差。
- 建立连接的延时:TCP建立连接时的三次握手,如果是HTTPS还有TLS连接握手,整体就需要3~4个RTT(TCP1.5个,TLS根据1.2/1.3版本会有不同的表现)。
- 协议僵化:互联网需要很多中间设备来将多个网络互联,这些中间设备包括路由器、防火墙、NAT、交换机等。它们通常依赖一些很少升级的软件,这些软件使用了大量TCP特性,即便客户端升级了TCP协议,新协议的数据经过这些中间设备时,也可能会因为不被理解而丢弃。此外,TCP协议都是通过操作系统内核来实现的,通常操作系统的更新都滞后于软件的更新,也是导致TCP协议僵化的一个原因。
QUIC协议
HTTP/3基于UDP协议实现了类似于TCP的多路复用数据流、传输可靠性等功能,这套功能被称为QUIC协议。
- 流量控制、传输可靠性功能:QUIC在UDP的基础上增加了一层来保证数据传输可靠性,它提供了数据包重传、拥塞控制、以及其他一些TCP中的特性。
- 集成TLS加密功能:目前QUIC使用TLS1.3,减少了握手所花费的RTT数。
- 多路复用:同一物理连接上可以有多个独立的逻辑数据流,实现了数据流的单独传输,解决了TCP的队头阻塞问题。
- 快速握手:由于基于UDP,可以实现使用0 ~ 1个RTT来建立连接。
HTTP/3的挑战
目前服务器和浏览器端都没有对HTTP/3提供比较完整的支持(目前已被大多数主流浏览器支持)。- 部署问题,系统内核对UDP的优化远达不到TCP的优化程度。
- 中间设备僵化问题,这些设备对UDP的优化程度远低于TCP,据统计使用QUIC协议时,大约有3% ~ 7%的丢包率。
参考资料
- 极客时间
- Evolution of HTTP