一、HTTP2协议简介
1、HTTP2的帧格式
HTTP2的所有帧都是由一个固定的9字节头部(payload之前)和一个指定长度的负载(payload)组成,如下图所示:
其中,Stream Identifier
用作流控制,用31位无符号整数表示。客户端建立的sid
必须为奇数,服务端建立的sid
必须为偶数,值(0x0)保留给与整个连接相关联的帧(连接控制消息),而不是单个流。
Frame Payload
是主体内容,有帧类型决定,HTTP2一共有10种类型的帧:
1 | 名称 描述 type值 |
实际上,HTTP2并没有改变HTTP1.x的语义,只是把原来HTTP1.x的Header
和Body
部分用Frame
重新封装了一层而已。调试的时候浏览器设置会把HTTP2
的Frame
自动还原成HTTP1.x
的格式。两者的帧格式对比如下图:
2、HTTP2的改进及优点
二进制分帧
帧是客户端和服务端之间通信的最小单位,比起
HTTP1.x
这样的文本协议,二进制协议解析起来更高效,且没有冗余字段(HTTP1.x 协议每个包都会有重复传输的头部字段),占用带宽少。多路复用
可以并行交错地发送请求,请求之间互不影响;
可以并行交错地发送响应,响应之间互不干扰;
只使用一个连接即可并行发送多个请求和响应;
消除不必要的延迟,从而减少页面加载的时间;
增加了请求优先级字段,服务器可以根据流的优先级,控制资源分配,而在响应数据准备好之后,将优先级最高的帧发送给客户端。
增加了Header压缩,通信双方各自 cache
一份 header fields
表。
增加了服务器推送,服务器可以对一个客户端请求发送多个响应,服务器向客户端推送资源无需客户端明确地请求。服务器推送通过PUSH
那些它认为客户端将会需要的内容到客户端的缓存中,避免往返的延迟。比如,请求index.html
,但服务器会把style.css, example.png
等资源也发送给浏览器。
流量控制,每个HTTP2
的流都拥有自己公示的流量窗口,它可以限制另一端发送数据。
二、配置Wireshark抓取HTTP2数据包
1、在电脑里创建一个后缀名为 .log
的文件,记住其路径。
2、在电脑的系统变量里创建一个新的变量,名为SSLKEYLOGFILE
。配置了此变量后,浏览器会到这里来记录通信过程中密钥。
3、在wireshark
中,分析 >> 已解析的协议...
中,启用HTTP2
的解析。并在 编辑 >> 首选项 >> Protocols >> TLS
中,配置 (Pre)-Master-Secret
为之前创建的log
文件,之后,wireshark
就可以凭借此文件中的秘钥解密HTTP2
中的各个加密字段,便于我们进行协议分析。
三、HTTP2协议数据包解析
数据包点击下载
1、从上图可以看到,HTTP2
协议的工作流程为:建立TCP
连接、建立TLS
连接、HTTP2
通信,断开TCP
连接 四个过程。在Client Hello
中,客户端再扩展字段中会说明其所支持的协议,指定ALPN Next Protocol
为 h2
或 HTTP/1.1
。之后,服务端在Server Hello
中,回复所协商的接下来使用的应用层协议。如下图:
2、在TLS
连接过程中,如果服务器支持session ticket
,则发送New Session Ticket
类型的握手报文,其中包含了能够恢复包括主密钥在内的会话信息。为了不让中间人可见,这个session ticket
部分会进行编码、加密等操作,同时,还会指定此ticket
的过期时间等。
3、从第一幅图中可以看出,在客户端发送Finished
消息(#11)之后,就开始将HTTP
封装到Application Data
协议中发送给服务器(#12,#13,#14,#15,#16)了,此时,TLS
握手还没有完全完成。
HTTP2
中基本的协议单位是帧,每个帧都有不同的类型和用途。例如,报头(HEADERS)
和数据(DATA)
帧组成了基本的HTTP
请求和响应;其他帧如设置(SETTINGS)
,窗口更新(WINDOW_UPDATE)
和推送承诺(PUSH_PROMISE)
是用来实现HTTP2
的其他功能。
4、(#12)帧分析:在HTTP2
请求创建连接发送SETTINGS
帧初始化之前有一个Magic
帧,为建立HTTP2
请求的前言(connection preface)
,此前言作为对所使用协议的最终确认,并确定HTTP2
连接的初始设置。在发送完前言后,双方都得向对方发送带有ACK
标识的SETTINGS
帧标识确认,对应图中的#21 和 #22 号帧:
12号帧,组合了Magic
、SETTINGS
和WINDOW_UPDATE
三种帧为一个数据包,其中包含的参数如下图:
WINDOW_UPDATE
中的stream identifier
为0
表示此窗口更新帧作用于整个连接,指定其他具体值可使其作用于某个特定的单独连接。
5、(#13)号帧分析,客户端在发送完连接前言后,可立即跟上一个请求(request)
。这里客户端向服务器发送GET /HEADERS
,HEADERS
帧用来打开一个流或携带一个首部块片段,此HEADERS
帧包括了请求行和请求头的内容,属于1
号流:
其中,Exclusive
:一个比特位声明流的依赖性是否是排它的,这里为 1
,代表此流不依赖其他的流。Weight
:代表当前流的优先级权重。End Headers
:这里为 1
代表header
块结束。Priority
设置为 1
,代表存在Exclusive
、Stream Dependency
和 Weight
。
6、(#19) 号包分析,服务器向客户端发送SETTINGS
和 WINDOW_UPDATE
帧,SETTINGS
帧为连接前言 (connection preface)
,帧中设置了最大并行流数量、初始窗口大小、最大帧长度,WINDOW_UPDATE
给出扩大窗口的大小。这两个帧属于 0
号流。
7、DATA
帧,服务器向客户端发送DATA
帧,即响应主体。DATA
帧用来装填主体信息,可以用一个或多个DATA
帧来返回一个请求的响应主体。
上图左边为HEADERS
帧,服务器向客户端返回响应(response)
,此报头帧包含了状态行和响应头的内容,此帧属于 3
号流。 Status: 200 OK
表示状态码为 200,客户端请求成功。此外,响应头还给出了服务器所使用的的服务器型号、内容类型等。注意观察图中的标志位 End Stream
和 End Headers
的变化。