Web协议详解与抓包实战之HTTP/2【三】
前言
《Web协议详解与抓包实战》课程学习,陶辉老师主讲
学习内容:
- HTTP–TLS/SSL–TCP/IP自上而下根据应用学习web协议HTTP/2详细知识
- 结合抓包工具实践验证:chrome下的network面板、Tcpdump、Wireshark
文章目录
HTTP/2的概念和特性
HTTP/2(超文本传输协议第2版,最初命名为HTTP 2.0),简称为h2(基于TLS/1.2或以上版本的加密连接)或h2c(非加密连接)[1],是HTTP协议的的第二个主要版本,使用于万维网。
HTTP/2标准于2015年5月以RFC 7540正式发表。
HTTP/2 协议本身未要求必须使用加密,但是多数客户端 (例如 Firefox, Chrome, Safari, Opera, IE, Edge) 的开发者声明,他们只会实现通过TLS加密的HTTP/2协议,这使得经TLS加密的HTTP/2(即h2)成为了事实上的强制标准,而h2c事实上被主流浏览器废弃。
主要特性
- 传输数据量的大幅减少
- 二进制方式传输
- 标头压缩:HPACK算法
- 多路复用及相关功能
- 消息优先级
- 服务器消息推送
- 并行推送
http2.0并没有改变http1.x的语义,只是把原来http1.x的header和body部分用frame重新封装了一层而已
使用WireShark解密TLS-SSL报文
- wireshark获取TLS握手阶段生成的密钥
- wireshark从TLS中解密出来的HTTP/2的报文
h2c:在TCP上从HTTP1.1升级到HTTP/2
h2c与HTTP1.1协商握手升级到WebSocket非常相似
第一步:协商返回101 Switching Protocols
第二步:客户端发动Magic帧
统一的连接过程:h2与h2c统一
h2:在TLS上从HTTP1.1升级到HTTP/2
- ALPN扩展协商协议
升级过程
- Clinet Hello包
- 在alpn扩展中罗列出所支持的协议
- Server Hello包
- 服务端选择h2协议,告诉客户端
- 然后发送Magic帧
帧、消息、流的关系
HTTP/2核心概念
Stream、Message、Frame之间的关系
- Stream与Frame之间通过stream ID对应起来
- Message的组成:HEADERS Frame与 DATA Frame组成
多路复用
-
传输中无序,接收时组装
-
同个stream必须按顺序传输,不同stream之间的顺序可随意
帧格式
Sream ID的作用
9字节标准帧头部
-
【作用一】:实现多路复用的关键
- 接收端的实现可据此并发组装消息
- 同一Stream内的frame必须是有序的
- SETTINGS_MAX_CONCURRENT_STREAMS控制着并发的Stream数
-
【作用二】:推送依赖性请求的关键
- 由客户端建立的流必须是奇数
- 由服务端建立的流必须是偶数
-
【作用三】:流状态管理的约束性规定
-
【作用四】:应用层流控仅影响数据帧
- StreamID为0的流仅用于传输控制帧
帧类型及设置帧的子类型(Setting设置帧)
以9字节标准帧头部为参考,介绍帧长度、帧类型
- 帧长度:Length
- 帧类型:Type及Flags,对不同Type,Flags内容也相呼应不同
-
Setting设置帧的格式(type=0x4):
-
设置帧并不是协商,是发送方向接收方通知其特性及能力
-
一个设置帧可以同时设置多个对象
- Identifier:设置对象
- Value:设置值
-
设置类型:
-
HPACK算法
HPACK如何减少HTTP头部的大小
HPACK压缩算法:在传输过程中,简化消息内容,降低消息大小
-
消息发送端和消息接受端共同维护一份静态表和一份动态表(这两个合起来充当字典的角色)
-
每次请求时,发送方根据字典的内容以及一些特定指定,编码压缩消息头部
-
接收方根据字典进行解码,并且根据指令来判断是否需要更新动态表
-
三种压缩方式
- 静态字典
- 动态字典
- 压缩算法:Huffman编码
静态字典:https://httpwg.org/specs/rfc7541.html#static.table.definition
- 只包含已知的header字段
- name:value都可以确定
- 只有name
静态字典和动态字典又称为索引表
HPACK压缩示意
- 先使用静态表,经过多次请求规范动态字典,最后再结合Huffman编码压缩
索引表用法示意图
HPACK中如何使用Huffman编码
原理:
- 出现概率较大的符号采用较短的编码,概率较小的符号采用较长的编码
静态Huffman编码:https://httpwg.org/specs/rfc7541.html#huffman.code
动态Huffman编码
Huffman树构造过程
HPACK中整型数字的编码(即索引值编码)
- 小于31的整型数字编码
- 整型大数的编码
- 整型大数的解码
HPACK中头部名称与值的编码(HEADER帧)
- HEADER帧的格式
当HEADER帧过大时,后续会跟一个CONTINUATION持续帧(Type=0x9)
-
跟在HEADER帧或者PUSH_PROMISE帧之后,来补充完整的HTTP头部
为了更好的理解Header Block Fragment
-
字面编码
接下来看实践方式:
-
名称和值都在索引表中(包括静态表与动态表)
-
编码方式:首位传1,其余7位传索引号
-
例如:method: GET在静态索引表中的序号为2,应表示为1000 0010,HEX为82
-
wireshark看例子
-
-
名称在索引表中,值需要编码传递,同时新增至动态表中
-
编码方式:前两位01
-
wireshark例子
-
-
名称、值都需要编码传递,同时新增至动态表中
-
前两位传01
-
-
名称在索引表中,值需要编码传递,且不更新至动态表中
-
前四位传0000
-
-
名称、值都需要编码传递,且不更新至动态表中
-
前四位传0000
-
-
名称在索引表中,值需要编码传递,且永远不更新至动态表中
-
前四位传0001
-
-
名称、值都需要编码传递,且永远不更新至动态表中
-
前四位传0001
-
服务器端的主动消息推送(PUSH_PROMISE帧)
- 提前将资源推送至浏览器缓存
- 特性:
- 推送基于已经发送的请求
- 实现方式:
- 推送资源必须对应一个请求
- 请求由服务器端PUSH_PROMISE帧发送
- 响应在偶数ID的STREAM中发送
HTTP/1.1中,获取HTML后,需要CSS资源时:
HTTP2中的PUSH_PROMISE方式
-
PUSH帧的格式
-
type=0x5,并且只能由服务器推送
-
-
wireshark实例
- stream 1 中通知客户端图片资源即将来临
- stream 2 中发送图片资源
-
PUSH推送模式的禁用:
- 帧格式章节:帧类型
Stream流的状态变迁
-
Stream特性
-
Message特性
-
Stream流的状态
-
帧符号及流状态解释
-
流程图
-
RST_STREAM帧及常见的错误码
-
RST_STREAM帧(type=0x3)
-
常见错误码:不仅仅用于RST_STREAM帧中,GOAWAY帧等也会用到
Stream优先级与资源分配原则(PRIORITY帧)
对不同的请求进行优先级的设置调整
-
PRIORITY优先级设置帧
-
exclusive标志位
不同于TCP的流量控制
-
HTTP/1.1中由TCP连接上没有多路复用
-
HTTP/2中,多路复用意味着多个Stream必须共享TCP层的流量控制
-
产生问题:多个Stream争夺TCP的流控制,互相干扰可能造成Stream阻塞
-
代理服务器内存有限,上下游网速不一致时,通过流控管理内存
-
所以,需要HTTP/2进行应用层的流控
-
如何进行应用层的流控?
-
由应用层决定发送速度
-
WINDOW_UPDATE帧
-
wireshark实例
-
流控制窗口
gRPC框架
HTTP/2存在的问题
-
TCP及TCP+TLS建链握手过多问题
-
多路复用与TCP的队头阻塞问题
-
本质上是TCP规定的资源必须有序到达
-
HTTP3 QUIC协议
-
QUIC协议的位置:
-
HTTP/2与QUIC协议比较
-
HTTP3的连接迁移
- 允许客户端更换IP地址、端口后,仍然可以复用前连接
-
解决对头阻塞问题
-
HTTP3:1RTT完全握手
七层负载均衡
-
HTTP转换协议
-
WAF防火墙
-
-
反向代理与缓存功能