前言
在HTTP/1.x时代,只有消息体才支持压缩,因为一般来说,消息体通常比头部要大,对消息体进行压缩,可以减小数据包的大小,提升传输性能。
但是经过长期的观察,人们发现,HTTP协议的头部存在大量重复的数据,例如:cookie
、Accept
、User-Agent
等等,这些信息基本每次请求都不会改变,但是每次请求都需要重复的传输,实在是没有必要。
我们又知道,对于重复的数据,是很好压缩的,只需要为出现频次极高的词组建立一张索引表,传输的时候只需要一个索引号即可,大大减少了数据传输的长度,这就是压缩算法中极为常见的查表法
。
因此,HTTP2协议终于支持对头部进行压缩了!
HPACK
HPACK算法的目标是在HTTP/2中压缩和传输头部字段,以减少数据传输的大小,并使用动态表来提高头部字段的重用性。
HPACK算法的特点有:
具体的压缩过程如下:
静态表
HPACK定义了一个静态表,里面包含61个常用的HTTP头部,如下:
+-------+-----------------------------+---------------+
| Index | Header Name | Header Value |
+-------+-----------------------------+---------------+
| 1 | :authority | |
| 2 | :method | GET |
| 3 | :method | POST |
| 4 | :path | / |
| 5 | :path | /index.html |
| 6 | :scheme | http |
| 7 | :scheme | https |
| 8 | :status | 200 |
| 9 | :status | 204 |
| 10 | :status | 206 |
| 11 | :status | 304 |
| 12 | :status | 400 |
| 13 | :status | 404 |
| 14 | :status | 500 |
| 15 | accept-charset | |
| 16 | accept-encoding | gzip, deflate |
| 17 | accept-language | |
| 18 | accept-ranges | |
| 19 | accept | |
| 20 | access-control-allow-origin | |
| 21 | age | |
| 22 | allow | |
| 23 | authorization | |
| 24 | cache-control | |
| 25 | content-disposition | |
| 26 | content-encoding | |
| 27 | content-language | |
| 28 | content-length | |
| 29 | content-location | |
| 30 | content-range | |
| 31 | content-type | |
| 32 | cookie | |
| 33 | date | |
| 34 | etag | |
| 35 | expect | |
| 36 | expires | |
| 37 | from | |
| 38 | host | |
| 39 | if-match | |
| 40 | if-modified-since | |
| 41 | if-none-match | |
| 42 | if-range | |
| 43 | if-unmodified-since | |
| 44 | last-modified | |
| 45 | link | |
| 46 | location | |
| 47 | max-forwards | |
| 48 | proxy-authenticate | |
| 49 | proxy-authorization | |
| 50 | range | |
| 51 | referer | |
| 52 | refresh | |
| 53 | retry-after | |
| 54 | server | |
| 55 | set-cookie | |
| 56 | strict-transport-security | |
| 57 | transfer-encoding | |
| 58 | user-agent | |
| 59 | vary | |
| 60 | via | |
| 61 | www-authenticate | |
+-------+-----------------------------+---------------+
动态表
除了静态表,HPACK还支持为每个连接单独维护一张动态表。SETTINGS_HEADER_TABLE_SIZE
属性配置了动态表的最大大小,通过SETTINGS
Frame传输,默认是4096个字节。动态表的索引位置从62开始,因为前61属于静态表。
Field Block Fragment
一个HEADERS Frame包含n个Field Block Fragment
,HPACK头部有4种类型:
类型 | 说明 |
---|---|
索引首部字段类型 | 高位以1开头,只有索引号,因为name和value均被索引 |
带递增索引的字符串首部字段 | 高位以01开头,Name 被索引,Value 没被索引,但是会注册到动态表,以备后续使用 |
索引的字符串首部字段 | 高位以 01000000 开头,Name Value 都没被索引,但是会注册到动态表,以备后续使用 |
从不索引的字符串首部字段 | 高位以0000开头,Name 可选是否被索引,适用于频繁变化的头部,绝不会注册到动态表 |
下面是Field Block Fragment各种类型的格式:
哈夫曼编码
未被索引的头部name和value,除了明文传输,还可以选择使用哈夫曼编码进一步压缩。
如何标记是否使用哈夫曼编码呢?
Name Length和Value Length拿出一个Bit来标记是否使用哈夫曼编码,如果最高位是1代表使用哈夫曼编码,否则使用Ascii
字符集。