前言
之前我们分析了BFE转发http请求的流程, 对一款前端流量接入应用来说, 丰富的协议支持非常重要。目前BFE支持了HTTP/2、SPDY、TLS、WebSocket、FastCGI等应用层协议。据W3Techs统计, 截止2022年8月份, 使用HTTP/2协议的站点占比达到44%(数据详情), 同时今年的6月份, IETF正式发布了HTTP/3的标准化文档RFC 9114。网络协议的解析对于7层流量接入是非常重要的一部分, 所以后续计划对HTTP/2
, HTTP/3
协议做一些详细解读, 夯实网络协议基础。
HTTP/2协议标识
HTTP/2有两个版本标识:
- h2: 构建与TLS之上的
HTTP/2
协议实现, 在连接的TLS握手阶段, 通过TLS扩展协议的ALPN
字段标识。 - h2c: 基于明文的TCP传输的协议实现, 在
HTTP1.1
升级协商阶段的Upgrade
字段标识。无安全性保障。
在现代的互联网环境中, 数据的安全传输是非常重要的, 由于h2c
无安全性保障, 很多浏览器厂商如宇宙第一大浏览器chrom
明确表示不支持明文HTTP/2协议, 相关Q&A可见: Googlebot 即将支持 HTTP/2。
启用HTTP/2协议
建立HTTP/2连接分以下三种情况:
- 从
HTTP/1.1
协议协商升级 (h2c
) - 如果在C/S交互中已知服务端支持
HTTP/2
协议, 则可以协商升级 (h2c
) https
schema协议在TLS捂手阶段确认是否可以升级为HTTP/2 (h2
)
前两者基于TCP文明传输, 可升级为h2c
, 目前在浏览器厂商和C/S交互中都是不推荐或者不支持的。本文会对三种方式做详解, 同时对 TLS 升级为h2
做实践记录。
为"http"启用HTTP/2
客户端在发起 "http" URI请求时, 如果事先不知道下一次交互是否支持HTTP/2
协议, 可以使用HTTP Upgrade机制, 尝试协商升级。客户端首先发起一个HTTP/1.1
请求, 请求头Upgrade
字段为h2c
, 同时还需要增加一个HTTP2-Settings
的头部字段, 此字段是HTTP/2
设置项, 使用base64编码。
GET / HTTP/1.1
Host: server.example.com
Connection: Upgrade, HTTP2-Settings
Upgrade: h2c
HTTP2-Settings: <base64url encoding of HTTP/2 SETTINGS payload>
如果协商失败, 服务端响应:
HTTP/1.1 200 OK
Content-Length: 243
Content-Type: text/html
如果服务端支持HTTP/2协议升级, 需要响应101状态码表示接受协议升级的请求, 在结束101响应的文本协议后, 服务端可以发送HTTP/2
帧 。
HTTP/1.1 101 Switching Protocols
Connection: Upgrade
Upgrade: h2c
[ HTTP/2 connection ...
服务端发送的第一个HTTP/2
帧必须是一个SETTINGS
帧, 客户端收到101的状态响应码后也必须发送一个SETTINGS
帧, 组成Connection Preface
(连接序言)。
已知支持HTTP/2协议的启用方式
如果客户端通过方式已经了解到服务端支持HTTP/2
, 必须向服务端发送连接序言, 然后可以立即发送HTTP/2
帧。服务端在接收到连接序言可以识别连接, 然后必须向客户端发送一个连接序言。关于连接序言我们在下一段落会单独详细说明。需要注意的是这种方式只适用于基于TCP明文传的连接, 即h2c
。
TLS捂手协商启用HTTP/2
对于https
发起的请求, 在TLS握手阶段会使用TLS-ALPN(带有应用层协议协商扩展 (ALPN)来切换应用层的协议。运行在TLS之上的HTTP/2
必须使用"h2"协议标识符。一旦 TLS 协商完成,客户端和服务端都必须发送一个连接序言。关于ALPN协议的一些介绍可以参考: 网络协议之:加密传输中的NPN和ALPN 。在实际的h2
应用中绝大部分都是这样的场景, 下文我们将根据一个典型的HTTP/2
请求分析这个过程。
Connection Preface连接序言
在HTTP/2
连接中,要求两端都要发送一个连接序言,作为对所使用协议的最终确认,并确定HTTP/2
连接的初始设置。客户端和服务端各自发送不同的连接序言。连接序言以字符串 "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" 开始。这个序列后面必须跟一个可以为空的SETTINGS
帧。服务端连接前奏包含一个可能为空的SETTINGS
帧,它必须是服务端在HTTP/2
连接中发送的第一个帧。为了避免不必要的时延,允许客户端发送完连接前奏后就立即向服务端发送其他的帧,而不必等待服务端的连接前奏。不过需要注意的是,服务端连接前奏的SETTINGS
帧可能包含一些期望客户端如何与服务端进行通信所必须修改的参数。在收到这些SETTINGS
帧以后,客户端应当遵守所有设置的参数。
这样的定义类似于TCP
的三次握手, 首先客户端告诉服务端合适的初始设置, 服务端接收到后可能会做出一些调整, 并把双方适合的设置返回给客户端, 这样双方就能够在兼容模式下进行通信。
TLS握手阶段启用HTTP/2请求分析
这是一个典型的HTTP/2请求流程, 图中第一部分为TCP三次握手, 第二部分为TLS握手, 第三部分为HTTP/2的协议交互。我们重点分析第二部分和第三部分。
TLS完成切换应用协议
一. Client Hello
请求序号: 247, 客户端发送Client Hello
的请求中会携带应用层扩展协议ALPN, 列出了客户端支持的HTTP
协议, 本次请求客户端支持http/1.1
, h2
协议。
二. Server Hello
请求序号: 249, 服务端响应Server Hello
, ALPN中描述了服务端支持的HTTP
协议。此次请求服务端支持h2
协议。在TLS握手完成后切换回应用协议使用双方都支持的h2
协议进行通讯。
完成连接序言
一. Client SETTINGS帧
请求序号: 257, 客户端发送一个连接序言的SETTINGS
帧完成协议确认。同时立即发送业务请求到服务端, 不需要等待服务端响应SETTINGS
帧。本例中立即发送请求序号: 258, 请求: /monitor_web/settings/browser-settings?bid=juejin_extension 地址。
二. Server SETTINGS帧
请求序号: 261, 服务端发送一个SETTINGS
帧完成连接序言确认。
三. 服务端响应
请求序号: 272, 服务端发送HEADERS
帧, 返回http 200 code码。
请求序号: 275, 服务端发送DARA
帧, 响应请求数据。
请求完成。
hc建连过程
总结
接口耗时是衡量服务质量的重要指标, 在工作中除了在应用程序层面通过合理的方式实现业务之外, 在服务链路中选择合适的通讯协议也可以实现事半功倍的效果。每一次HTTP
协议的版本迭代归因都是当前的标准已经不满足时代发展的需要, 分析协议演进中产生的新特性, 可以学习到在网络通讯的应用层面临的一些问题和对应的解决方案, 所以后续我们也可以仔细分析一下HTTP/2
和HTTP/3
的一些新特性。