HTTP2之HPACK头部压缩

2023年 10月 11日 27.5k 0

前言

在HTTP/1.x时代,只有消息体才支持压缩,因为一般来说,消息体通常比头部要大,对消息体进行压缩,可以减小数据包的大小,提升传输性能。
但是经过长期的观察,人们发现,HTTP协议的头部存在大量重复的数据,例如:cookieAcceptUser-Agent等等,这些信息基本每次请求都不会改变,但是每次请求都需要重复的传输,实在是没有必要。
我们又知道,对于重复的数据,是很好压缩的,只需要为出现频次极高的词组建立一张索引表,传输的时候只需要一个索引号即可,大大减少了数据传输的长度,这就是压缩算法中极为常见的查表法
因此,HTTP2协议终于支持对头部进行压缩了!

HPACK

HPACK算法的目标是在HTTP/2中压缩和传输头部字段,以减少数据传输的大小,并使用动态表来提高头部字段的重用性。
HPACK算法的特点有:

  • 静态字典:HPACK算法使用一个静态的字典,包含了常见的HTTP头部字段名称和一些常见的值的索引。这些索引可以直接引用,而不需要传输它们的完整字符串。
  • 动态表:HPACK算法还使用了一个动态表,用于在传输过程中存储和重用头部字段。动态表可以存储服务器发送的字段,以及客户端发送的字段。
  • 基于引用的编码:HPACK算法使用基于引用的编码,即通过引用静态字典和动态表中的索引号来表示头部字段。这样就可以减少传输数据的大小。
  • 链表表达:HPACK算法使用链表表达头部字段,每个字段包含名称和值。这种表达方式使得字段的重排和扩展更加容易。
  • 具体的压缩过程如下:

  • 首先,HPACK算法对需要压缩的头部字段进行检查,判断是否在动态表中已经存在。如果字段已经存在且值相同,可以直接引用动态表中的索引号。如果字段已经存在但值不同,需要更新动态表中的值,并生成新的索引号。
  • 如果字段不在动态表中,HPACK算法会检查字段是否存在于静态字典中。如果在静态字典中找到了对应的索引号,可以直接引用。
  • 如果字段既不在动态表中,也不在静态字典中,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属性配置了动态表的最大大小,通过SETTINGSFrame传输,默认是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各种类型的格式:
    image.png
    image.png
    image.png
    image.png
    image.png

    哈夫曼编码

    未被索引的头部name和value,除了明文传输,还可以选择使用哈夫曼编码进一步压缩。
    如何标记是否使用哈夫曼编码呢?
    Name Length和Value Length拿出一个Bit来标记是否使用哈夫曼编码,如果最高位是1代表使用哈夫曼编码,否则使用Ascii字符集。

    相关文章

    JavaScript2024新功能:Object.groupBy、正则表达式v标志
    PHP trim 函数对多字节字符的使用和限制
    新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
    使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
    为React 19做准备:WordPress 6.6用户指南
    如何删除WordPress中的所有评论

    发布评论