HTTP/2的建立连接过程

1,163 阅读7分钟

前言

之前我们分析了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请求分析

image.png

这是一个典型的HTTP/2请求流程, 图中第一部分为TCP三次握手, 第二部分为TLS握手, 第三部分为HTTP/2的协议交互。我们重点分析第二部分和第三部分。

TLS完成切换应用协议

一. Client Hello

请求序号: 247, 客户端发送Client Hello的请求中会携带应用层扩展协议ALPN, 列出了客户端支持的HTTP协议, 本次请求客户端支持http/1.1, h2协议。

image.png

二. Server Hello

请求序号: 249, 服务端响应Server Hello, ALPN中描述了服务端支持的HTTP协议。此次请求服务端支持h2协议。在TLS握手完成后切换回应用协议使用双方都支持的h2协议进行通讯。

image.png

完成连接序言

一. Client SETTINGS帧

请求序号: 257, 客户端发送一个连接序言的SETTINGS帧完成协议确认。同时立即发送业务请求到服务端, 不需要等待服务端响应SETTINGS帧。本例中立即发送请求序号: 258, 请求: /monitor_web/settings/browser-settings?bid=juejin_extension 地址。

image.png

二. Server SETTINGS帧

请求序号: 261, 服务端发送一个SETTINGS帧完成连接序言确认。

三. 服务端响应

请求序号: 272, 服务端发送HEADERS帧, 返回http 200 code码。 image.png

请求序号: 275, 服务端发送DARA帧, 响应请求数据。 image.png

请求完成。

hc建连过程

image.png

总结

接口耗时是衡量服务质量的重要指标, 在工作中除了在应用程序层面通过合理的方式实现业务之外, 在服务链路中选择合适的通讯协议也可以实现事半功倍的效果。每一次HTTP协议的版本迭代归因都是当前的标准已经不满足时代发展的需要, 分析协议演进中产生的新特性, 可以学习到在网络通讯的应用层面临的一些问题和对应的解决方案, 所以后续我们也可以仔细分析一下HTTP/2HTTP/3的一些新特性。

参考

  1. RFC 7540 – 超文本传输协议第2版(HTTP / 2)
  2. FC 7541 – HPACK:HTTP / 2的头压缩