http2头部压缩-hpack

hpack由来:

http2以前的头部报文都是文本形式发生,http2为了优化网络对头部报文进行压缩编码使其内容更精简,发送更少的数据加快网络传输。采用压缩算法就是hpack。


压缩示意图

该图说明了http2头部报文的压缩过程

首先把头部的键值对内容根据对应的表进行转换,最后经过编码生成最终的压缩后的数据。

hpack表

http2中会把经常使用的头部键值对进行表格化(可以理解为数据缓存),使其可以通过索引进行数据关联。如果头部键值已经存在直接使用索引进行传输,对方便可解析对应数据内容。

hapck表分为静态表和动态表。

静态表

部分静态表内容

静态表总共61项,动态表索引从62开始


静态表和动态表索引说明

hapck编码

hapck 编码使用两种原始类型: 无符号可变长度整数和八位字节表示的字符串,相应地规定了以下两种编码方式

整数编码

一个整数编码可以用于表示字段索引值、首部条目索引值或者字符串长度。

一个整数编码含两部分: 一个前缀字节和可选的后跟字节序列,只有前缀字节不足以表达整数值时才需要后跟字节,前缀字节中可用比特位 N 是整数编码的一个参数

比如下面所示的是一个 N=5 的整数编码(前三比特用于其他标识),如果我们要编码的整数值小于 2^N - 1,直接用一个前缀字节表示即可,比如 10 就用 ???01010 表示

+---+---+---+---+---+---+---+---+

| ? | ? | ? |      Value      |

+---+---+---+-------------------+

如果要编码的整数值 X 大于等于 2^N - 1,前缀字节的可用比特位都设成 1,然后把 X 减去 2^N - 1 得到值 R,并用一个或多个字节序列表示 R,字节序列中每个字节的最高有效位 (msb) 用于表示是否结束,msb 设为 0 时代表是最后一个字节。具体编码看下面的伪代码和例子

+---+---+---+---+---+---+---+---+

| ? | ? | ? | 1  1  1  1  1 |

+---+---+---+-------------------+

| 1 |    Value-(2^N-1) LSB      |

+---+---------------------------+

+---+---------------------------+

| 0 |    Value-(2^N-1) MSB      |

+---+---------------------------+

比如使用 N=5 的整数编码表示 1337:

1337 大于 31 (2^5 - 1), 将前缀字节后五位填满 1

I = 1337 - (2^5 - 1) = 1306

I 仍然大于 128, I % 128 = 26, 26 + 128 = 154

154 二进制编码: 10011010, 这即是第一个后跟字节

I = 1306 / 128 = 10, I 小于 128, 循环结束

将 I 编码成二进制: 00001010, 这即是最后一个字节

+---+---+---+---+---+---+---+---+

| X | X | X | 1 | 1 | 1 | 1 | 1 |  Prefix = 31, I = 1306

| 1 | 0 | 0 | 1 | 1 | 0 | 1 | 0 |  1306 >= 128, encode(154), I=1306/128=10

| 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 |  10 < 128, encode(10), done

+---+---+---+---+---+---+---+---+

解码时读取第一个字节,发现后五位 (11111) 对应的值 I 等于 31(>= 2^N - 1),说明还有后跟字节;令 M=0,继续读下一个字节 B,I = I + (B & 127) * 2^M = 31 + 26 * 1 = 57,M = M + 7 = 7,最高有效位为 1,表示字节序列未结束,B 指向下一个字节;I = I + (B & 127) * 2^M = 57 + 10 * 128 = 1337,最高有效位为 0,表示字节码结束,返回 I

字符编码

一个字符串可能代表 Header 条目的字段或者键值。字符编码使用字节序列表示,要么直接使用字符的八位字节码要么使用哈夫曼编码。

+---+---+---+---+---+---+---+---+

| H |    String Length (7+)    |

+---+---------------------------+

|  String Data (Length octets)  |

+-------------------------------+

H: 一个比特位表示是否使用哈夫曼编码

String Length: 代表字节序列长度,即 String Data 的长度,使用 N=7 的整数编码方式表示

String Data: 字符串的八位字节码序列表示,如果 H 为 0,则此处就是原字符的八位字节码表示;如果 H 为 1,则此处为原字符的哈夫曼编码

RFC 7541 给出了一份字符的哈夫曼编码表: Huffman Code,这是基于大量 HTTP 首部数据生成的哈夫曼编码。

当中第一列 (sym) 表示要编码的字符,最后的特殊字符 “EOS” 代表字符串结束

第二列 (code as bits) 是二进制哈夫曼编码,向最高有效位对齐

第三列 (code as hex) 是十六进制哈夫曼编码,向最低有效位对齐

最后一列 (len) 代表编码长度,单位 bit

例子:

:authority: blog.wangriyu.wang 首部对应的编码为:

41 8e 8e 83 cc bf 81 d5 35 86 f5 6a fe 07 54 df

41 (0100 0001) 表示字段存在索引值 1,即对应静态表中第一项 :authority

8e (1000 1110) 最高有效位为 1 表示键值使用哈夫曼编码,000 1110 表示字节序列长度为 14

后面 8e 83 cc bf 81 d5 35 86 f5 6a fe 07 54 df 是一段哈夫曼编码序列

由哈夫曼编码表可知 100011 -> ‘b’, 101000 -> ‘l’, 00111 -> ‘o’, 100110 -> ‘g’, 010111 -> ‘.’, 1111000 -> ‘w’, 00011 -> ‘a’, 101010 -> ‘n’, 100110 -> ‘g’, 101100 -> ‘r’, 00110 -> ‘i’, 1111010 -> ‘y’, 101101 -> ‘u’


解析过程示例



hapck编码规范简要说明

已索引首部条目表示 (Indexed Header Field Representation)

以 1 开始为标识,能在索引空间匹配到索引的首部会替换成这种形式,后面的 index 使用上述的整数编码方式且 N = 7。

比如 :method: GET 可以用 0x82,即 10000010 表示

+---+---+---+---+---+---+---+---+

| 1 |        Index (7+)        |

+---+---------------------------+

未索引文字首部条目表示 (Literal Header Field Representation)

尚未被索引的首部有三种表示形式,第一种会添加进索引,第二种对于当前跳来说不会添加进索引,第三种绝对不被允许添加进索引

1.会添加索引的文字首部 (Literal Header Field with Incremental Indexing)

以 01 开始为标识,此首部会加入到解码后的首部列表 (Header List) 中并且会把它作为新条目插入到动态索引表中

+---+---+---+---+---+---+---+---+

| 0 | 1 |      Index (6+)      |

+---+---+-----------------------+

| H |    Value Length (7+)    |

+---+---------------------------+

| Value String (Length octets)  |

+-------------------------------+

2.不添加索引的首部 (Literal Header Field without Indexing)

以 0000 开始为标识,此首部会加入到解码后的首部列表中,但不会插入到动态索引表中

Literal Header Field without Indexing — Indexed Name

如果字段已经存在索引,但键值未被索引,则会替换成如下形式 (index 使用 N=4 的整数编码表示)

+---+---+---+---+---+---+---+---+

| 0 | 0 | 0 | 0 |  Index (4+)  |

+---+---+-----------------------+

| H |    Value Length (7+)    |

+---+---------------------------+

| Value String (Length octets)  |

+-------------------------------+

如果字段和键值均未被索引,则会替换成如下形式。

+---+---+---+---+---+---+---+---+

| 0 | 0 | 0 | 0 |      0      |

+---+---+-----------------------+

| H |    Name Length (7+)      |

+---+---------------------------+

|  Name String (Length octets)  |

+---+---------------------------+

| H |    Value Length (7+)    |

+---+---------------------------+

| Value String (Length octets)  |

+-------------------------------+

3.绝对不添加索引的首部 (Literal Header Field Never Indexed)

这与上一种首部类似,只是标识为 0001,首部也是会添加进解码后的首部列表中但不会插入动态更新表。

区别在于这类首部发出是什么格式表示,接收也是一样的格式,作用于每一跳 (hop),如果中间通过代理,代理必须原样转发不能另行编码。

而上一种首部只是作用当前跳,通过代理后可能会被重新编码

+---+---+---+---+---+---+---+---+

| 0 | 0 | 0 | 1 |  Index (4+)  |

+---+---+-----------------------+

| H |    Value Length (7+)    |

+---+---------------------------+

| Value String (Length octets)  |

+-------------------------------+


参考链接:

HTTP2 详解 

Wangriyu’s Blog Halfrost-Field/HTTP:2_Header-Compression.md at master · halfrost/Halfrost-Field · GitHub

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,542评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,822评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,912评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,449评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,500评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,370评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,193评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,074评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,505评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,722评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,841评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,569评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,168评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,783评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,918评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,962评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,781评论 2 354