HTTP/2 HPACK 实际应用举例

在上篇文章中,具体说明了 HPACK 算法中的 8 种场景(7 种 Name-value 的场景 + 1 种动态表更新场景)。

动态表大小更新有两种方式,一种是在 HEADERS 帧中直接修改(“001” 3 位模式开始),另外一种方式是通过 SETTINGS 帧中的 SETTINGS_HEADER_TABLE_SIZE 中设置的。

在介绍 HPACK 实际应用之前,需要先来看看静态表的定义和 HTTP/2 中霍夫曼编码的定义。

一. 静态表定义

静态表(请参阅第 2.3.1 节)包含一个预定义且不可更改的 header 字段列表。

静态表是根据流行网站使用的最频繁的 header 字段创建的,并添加了 HTTP/2 特定的伪 header 字段(请参见 [HTTP2]的 8.1.2.1 节)。对于具有一些频繁值的 header 字段,为每个这些频繁值添加了一个条目。对于其他标题字段,添加了带有空值的条目。

表 1 列出了构成静态表的预定义 header 字段,并提供了每个条目的索引。

IndexHeader NameHeader Value
1:authority
2:methodGET
3:methodPOST
4:path/
5:path/index.html
6:schemehttp
7:schemehttps
8:status200
9:status204
10:status206
11:status304
12:status400
13:status404
14:status500
15accept-charset
16accept-encodinggzip, deflate
17accept-language
18accept-ranges
19accept
20access-control-allow-origin
21age
22allow
23authorization
24cache-control
25content-disposition
26content-encoding
27content-language
28content-length
29content-location
30content-range
31content-type
32cookie
33date
34etag
35expect
36expires
37from
38host
39if-match
40if-modified-since
41if-none-match
42if-range
43if-unmodified-since
44last-modified
45link
46location
47max-forwards
48proxy-authenticate
49proxy-authorization
50range
51referer
52refresh
53retry-after
54server
55set-cookie
56strict-transport-security
57transfer-encoding
58user-agent
59vary
60via
61www-authenticate

二. 霍夫曼编码

1. 霍夫曼算法

如果每个字符都是等长的编码形式,是否有一种算法能保证大幅压缩这些数据?等长的编码形式首先面临的一个问题是如何避免解压的时候出现歧义误读。

霍夫曼在 1952 年发现了最优前缀码的算法,算法的核心思想是:出现概率比较大的符号采用较短的编码,概率较小的符号采用较长的编码。

举个例子,一篇文章中有很多单词,我们讲所有字母出现的频率都统计出来,以 a、b、c、d、e、f 这 6 个字母为例,它们的出现频率分别如下:

第一步先从这些频率中选取频率最小的 2 个,进行合并。左子树小,右子树大。合并成新的节点以后,再放回原有的节点中。

重复第一步,直到所有节点都合并到一棵树上。

最后给每个左子树的指针上编码为 0,右子树的指针上编码为 1。以上 6 个字母的最终编码是 a = 0, b = 101, c = 100, d = 111, e = 1101, f = 1100 。

2. 霍夫曼编码在 HTTP/2 中的定义

当使用霍夫曼编码对字符串字面进行编码时,使用以下霍夫曼代码(请参见第 5.2 节)。

此霍夫曼代码是从大量 HTTP header 样本获得的统计信息中生成的。这是规范的霍夫曼代码(请参见 [CANONICAL]),需要进行一些调整以确保没有符号具有唯一的代码长度。

表中的每一行均定义用于表示符号的代码:

sym:要表示的符号。它是八位字节的十进制值,可能以ASCII表示形式为前缀。特定的符号 “EOS” 用于指示字符串字面的结尾。

code as bits:符号的霍夫曼编码,表示为以2为基的整数,在最高有效位(MSB)上对齐。

code as hex:符号的霍夫曼编码,以十六进制整数表示,在最低有效位(LSB)上对齐。

len:代表符号的代码的位数。

例如,符号 47 的代码(对应于 ASCII 字符 “/”)由 6 位 “0”,“1”,“1”,“0”,“0”,“0”组成。这对应于以 6 位编码的值 0x18(以十六进制表示)。

symcode as bitsaligned to MSBcode as hex aligned to LSBlen in bits
( 0)|11111111|110001ff8[13]
( 1)|11111111|11111111|10110007fffd8[23]
( 2)|11111111|11111111|11111110|0010fffffe2[28]
( 3)|11111111|11111111|11111110|0011fffffe3[28]
( 4)|11111111|11111111|11111110|0100fffffe4[28]
( 5)|11111111|11111111|11111110|0101fffffe5[28]
( 6)|11111111|11111111|11111110|0110fffffe6[28]
( 7)|11111111|11111111|11111110|0111fffffe7[28]
( 8)|11111111|11111111|11111110|1000fffffe8[28]
( 9)|11111111|11111111|11101010ffffea[24]
( 10)|11111111|11111111|11111111|1111003ffffffc[30]
( 11)|11111111|11111111|11111110|1001fffffe9[28]
( 12)|11111111|11111111|11111110|1010fffffea[28]
( 13)|11111111|11111111|11111111|1111013ffffffd[30]
( 14)|11111111|11111111|11111110|1011fffffeb[28]
( 15)|11111111|11111111|11111110|1100fffffec[28]
( 16)|11111111|11111111|11111110|1101fffffed[28]
( 17)|11111111|11111111|11111110|1110fffffee[28]
( 18)|11111111|11111111|11111110|1111fffffef[28]
( 19)|11111111|11111111|11111111|0000ffffff0[28]
( 20)|11111111|11111111|11111111|0001ffffff1[28]
( 21)|11111111|11111111|11111111|0010ffffff2[28]
( 22)|11111111|11111111|11111111|1111103ffffffe[30]
( 23)|11111111|11111111|11111111|0011ffffff3[28]
( 24)|11111111|11111111|11111111|0100ffffff4[28]
( 25)|11111111|11111111|11111111|0101ffffff5[28]
( 26)|11111111|11111111|11111111|0110ffffff6[28]
( 27)|11111111|11111111|11111111|0111ffffff7[28]
( 28)|11111111|11111111|11111111|1000ffffff8[28]
( 29)|11111111|11111111|11111111|1001ffffff9[28]
( 30)|11111111|11111111|11111111|1010ffffffa[28]
( 31)|11111111|11111111|11111111|1011ffffffb[28]
' ' ( 32)|01010014[ 6]
‘!’ ( 33)|11111110|003f8[10]
‘"’ ( 34)|11111110|013f9[10]
‘#’ ( 35)|11111111|1010ffa[12]
‘$’ ( 36)|11111111|110011ff9[13]
‘%’ ( 37)|01010115[ 6]
‘&’ ( 38)|11111000f8[ 8]
'’’ ( 39)|11111111|0107fa[11]
‘(’ ( 40)|11111110|103fa[10]
‘)’ ( 41)|11111110|113fb[10]
‘*’ ( 42)|11111001f9[ 8]
‘+’ ( 43)|11111111|0117fb[11]
‘,’ ( 44)|11111010fa[ 8]
‘-’ ( 45)|01011016[ 6]
‘.’ ( 46)|01011117[ 6]
‘/’ ( 47)|01100018[ 6]
‘0’ ( 48)|000000[ 5]
‘1’ ( 49)|000011[ 5]
‘2’ ( 50)|000102[ 5]
‘3’ ( 51)|01100119[ 6]
‘4’ ( 52)|0110101a[ 6]
‘5’ ( 53)|0110111b[ 6]
‘6’ ( 54)|0111001c[ 6]
‘7’ ( 55)|0111011d[ 6]
‘8’ ( 56)|0111101e[ 6]
‘9’ ( 57)|0111111f[ 6]
‘:’ ( 58)|10111005c[ 7]
‘;’ ( 59)|11111011fb[ 8]
‘<’ ( 60)|11111111|11111007ffc[15]
‘=’ ( 61)|10000020[ 6]
‘>’ ( 62)|11111111|1011ffb[12]
‘?’ ( 63)|11111111|003fc[10]
‘@’ ( 64)|11111111|110101ffa[13]
‘A’ ( 65)|10000121[ 6]
‘B’ ( 66)|10111015d[ 7]
‘C’ ( 67)|10111105e[ 7]
‘D’ ( 68)|10111115f[ 7]
‘E’ ( 69)|110000060[ 7]
‘F’ ( 70)|110000161[ 7]
‘G’ ( 71)|110001062[ 7]
‘H’ ( 72)|110001163[ 7]
‘I’ ( 73)|110010064[ 7]
‘J’ ( 74)|110010165[ 7]
‘K’ ( 75)|110011066[ 7]
‘L’ ( 76)|110011167[ 7]
‘M’ ( 77)|110100068[ 7]
‘N’ ( 78)|110100169[ 7]
‘O’ ( 79)|11010106a[ 7]
‘P’ ( 80)|11010116b[ 7]
‘Q’ ( 81)|11011006c[ 7]
‘R’ ( 82)|11011016d[ 7]
‘S’ ( 83)|11011106e[ 7]
‘T’ ( 84)|11011116f[ 7]
‘U’ ( 85)|111000070[ 7]
‘V’ ( 86)|111000171[ 7]
‘W’ ( 87)|111001072[ 7]
‘X’ ( 88)|11111100fc[ 8]
‘Y’ ( 89)|111001173[ 7]
‘Z’ ( 90)|11111101fd[ 8]
‘[’ ( 91)|11111111|110111ffb[13]
‘' ( 92)|11111111|11111110|0007fff0[19]
‘]’ ( 93)|11111111|111001ffc[13]
‘^’ ( 94)|11111111|1111003ffc[14]
‘_’ ( 95)|10001022[ 6]
‘`’ ( 96) | |11111111|1111101 | 7ffd | [15] |
‘a’ ( 97)|000113[ 5]
‘b’ ( 98)|10001123[ 6]
‘c’ ( 99)|001004[ 5]
’d’ (100)|10010024[ 6]
‘e’ (101)|001015[ 5]
‘f’ (102)|10010125[ 6]
‘g’ (103)|10011026[ 6]
‘h’ (104)|10011127[ 6]
‘i’ (105)|001106[ 5]
‘j’ (106)|111010074[ 7]
‘k’ (107)|111010175[ 7]
‘l’ (108)|10100028[ 6]
’m’ (109)|10100129[ 6]
‘n’ (110)|1010102a[ 6]
‘o’ (111)|001117[ 5]
‘p’ (112)|1010112b[ 6]
‘q’ (113)|111011076[ 7]
‘r’ (114)|1011002c[ 6]
’s’ (115)|010008[ 5]
’t’ (116)|010019[ 5]
‘u’ (117)|1011012d[ 6]
‘v’ (118)|111011177[ 7]
‘w’ (119)|111100078[ 7]
‘x’ (120)|111100179[ 7]
‘y’ (121)|11110107a[ 7]
‘z’ (122)|11110117b[ 7]
‘{’ (123)|11111111|11111107ffe[15]
'' (124)|11111111|1007fc
‘}’ (125)|11111111|1111013ffd[14]
‘~’ (126)|11111111|111011ffd[13]
(127)|11111111|11111111|11111111|1100ffffffc[28]
(128)|11111111|11111110|0110fffe6[20]
(129)|11111111|11111111|0100103fffd2[22]
(130)|11111111|11111110|0111fffe7[20]
(131)|11111111|11111110|1000fffe8[20]
(132)|11111111|11111111|0100113fffd3[22]
(133)|11111111|11111111|0101003fffd4[22]
(134)|11111111|11111111|0101013fffd5[22]
(135)|11111111|11111111|10110017fffd9[23]
(136)|11111111|11111111|0101103fffd6[22]
(137)|11111111|11111111|10110107fffda[23]
(138)|11111111|11111111|10110117fffdb[23]
(139)|11111111|11111111|10111007fffdc[23]
(140)|11111111|11111111|10111017fffdd[23]
(141)|11111111|11111111|10111107fffde[23]
(142)|11111111|11111111|11101011ffffeb[24]
(143)|11111111|11111111|10111117fffdf[23]
(144)|11111111|11111111|11101100ffffec[24]
(145)|11111111|11111111|11101101ffffed[24]
(146)|11111111|11111111|0101113fffd7[22]
(147)|11111111|11111111|11000007fffe0[23]
(148)|11111111|11111111|11101110ffffee[24]
(149)|11111111|11111111|11000017fffe1[23]
(150)|11111111|11111111|11000107fffe2[23]
(151)|11111111|11111111|11000117fffe3[23]
(152)|11111111|11111111|11001007fffe4[23]
(153)|11111111|11111110|111001fffdc[21]
(154)|11111111|11111111|0110003fffd8[22]
(155)|11111111|11111111|11001017fffe5[23]
(156)|11111111|11111111|0110013fffd9[22]
(157)|11111111|11111111|11001107fffe6[23]
(158)|11111111|11111111|11001117fffe7[23]
(159)|11111111|11111111|11101111ffffef[24]
(160)|11111111|11111111|0110103fffda[22]
(161)|11111111|11111110|111011fffdd[21]
(162)|11111111|11111110|1001fffe9[20]
(163)|11111111|11111111|0110113fffdb[22]
(164)|11111111|11111111|0111003fffdc[22]
(165)|11111111|11111111|11010007fffe8[23]
(166)|11111111|11111111|11010017fffe9[23]
(167)|11111111|11111110|111101fffde[21]
(168)|11111111|11111111|11010107fffea[23]
(169)|11111111|11111111|0111013fffdd[22]
(170)|11111111|11111111|0111103fffde[22]
(171)|11111111|11111111|11110000fffff0[24]
(172)|11111111|11111110|111111fffdf[21]
(173)|11111111|11111111|0111113fffdf[22]
(174)|11111111|11111111|11010117fffeb[23]
(175)|11111111|11111111|11011007fffec[23]
(176)|11111111|11111111|000001fffe0[21]
(177)|11111111|11111111|000011fffe1[21]
(178)|11111111|11111111|1000003fffe0[22]
(179)|11111111|11111111|000101fffe2[21]
(180)|11111111|11111111|11011017fffed[23]
(181)|11111111|11111111|1000013fffe1[22]
(182)|11111111|11111111|11011107fffee[23]
(183)|11111111|11111111|11011117fffef[23]
(184)|11111111|11111110|1010fffea[20]
(185)|11111111|11111111|1000103fffe2[22]
(186)|11111111|11111111|1000113fffe3[22]
(187)|11111111|11111111|1001003fffe4[22]
(188)|11111111|11111111|11100007ffff0[23]
(189)|11111111|11111111|1001013fffe5[22]
(190)|11111111|11111111|1001103fffe6[22]
(191)|11111111|11111111|11100017ffff1[23]
(192)|11111111|11111111|11111000|003ffffe0[26]
(193)|11111111|11111111|11111000|013ffffe1[26]
(194)|11111111|11111110|1011fffeb[20]
(195)|11111111|11111110|0017fff1[19]
(196)|11111111|11111111|1001113fffe7[22]
(197)|11111111|11111111|11100107ffff2[23]
(198)|11111111|11111111|1010003fffe8[22]
(199)|11111111|11111111|11110110|01ffffec[25]
(200)|11111111|11111111|11111000|103ffffe2[26]
(201)|11111111|11111111|11111000|113ffffe3[26]
(202)|11111111|11111111|11111001|003ffffe4[26]
(203)|11111111|11111111|11111011|1107ffffde[27]
(204)|11111111|11111111|11111011|1117ffffdf[27]
(205)|11111111|11111111|11111001|013ffffe5[26]
(206)|11111111|11111111|11110001fffff1[24]
(207)|11111111|11111111|11110110|11ffffed[25]
(208)|11111111|11111110|0107fff2[19]
(209)|11111111|11111111|000111fffe3[21]
(210)|11111111|11111111|11111001|103ffffe6[26]
(211)|11111111|11111111|11111100|0007ffffe0[27]
(212)|11111111|11111111|11111100|0017ffffe1[27]
(213)|11111111|11111111|11111001|113ffffe7[26]
(214)|11111111|11111111|11111100|0107ffffe2[27]
(215)|11111111|11111111|11110010fffff2[24]
(216)|11111111|11111111|001001fffe4[21]
(217)|11111111|11111111|001011fffe5[21]
(218)|11111111|11111111|11111010|003ffffe8[26]
(219)|11111111|11111111|11111010|013ffffe9[26]
(220)|11111111|11111111|11111111|1101ffffffd[28]
(221)|11111111|11111111|11111100|0117ffffe3[27]
(222)|11111111|11111111|11111100|1007ffffe4[27]
(223)|11111111|11111111|11111100|1017ffffe5[27]
(224)|11111111|11111110|1100fffec[20]
(225)|11111111|11111111|11110011fffff3[24]
(226)|11111111|11111110|1101fffed[20]
(227)|11111111|11111111|001101fffe6[21]
(228)|11111111|11111111|1010013fffe9[22]
(229)|11111111|11111111|001111fffe7[21]
(230)|11111111|11111111|010001fffe8[21]
(231)|11111111|11111111|11100117ffff3[23]
(232)|11111111|11111111|1010103fffea[22]
(233)|11111111|11111111|1010113fffeb[22]
(234)|11111111|11111111|11110111|01ffffee[25]
(235)|11111111|11111111|11110111|11ffffef[25]
(236)|11111111|11111111|11110100\fffff4[24]
(237)|11111111|11111111|11110101\fffff5[24]
(238)|11111111|11111111|11111010|103ffffea[26]
(239)|11111111|11111111|1110100 \7ffff4[23]
(240)|11111111|11111111|11111010|113ffffeb[26]
(241)|11111111|11111111|11111100|1107ffffe6[27]
(242)|11111111|11111111|11111011|003ffffec[26]
(243)|11111111|11111111|11111011|013ffffed[26]
(244)|11111111|11111111|11111100|1117ffffe7[27]
(245)|11111111|11111111|11111101|0007ffffe8[27]
(246)|11111111|11111111|11111101|0017ffffe9[27]
(247)|11111111|11111111|11111101|0107ffffea[27]
(248)|11111111|11111111|11111101|0117ffffeb[27]
(249)|11111111|11111111|11111111|1110ffffffe[28]
(250)|11111111|11111111|11111101|1007ffffec[27]
(251)|11111111|11111111|11111101|1017ffffed[27]
(252)|11111111|11111111|11111101|1107ffffee[27]
(253)|11111111|11111111|11111101|1117ffffef[27]
(254)|11111111|11111111|11111110|0007fffff0[27]
(255)|11111111|11111111|11111011|103ffffee[26]
EOS (256)|11111111|11111111|11111111|1111113fffffff[30]

三. 例子

本章节包含一些示例,这些示例涵盖整数编码,header 字段表示以及使用和不使用霍夫曼编码的请求和响应的 header 字段的整个列表的编码。

1. 整数表示的示例

本节详细显示了整数值的表示形式(请参见第 5.1 节)。

(1). 使用 5 位前缀对 10 进行编码

10 小于 31(2^5-1),并使用 5 位前缀表示。

     0   1   2   3   4   5   6   7
   +---+---+---+---+---+---+---+---+
   | X | X | X | 0 | 1 | 0 | 1 | 0 |   10 stored on 5 bits
   +---+---+---+---+---+---+---+---+

(2). 使用 5 位前缀对 1337 进行编码

1337 大于 31(2^5-1),并使用 5 位前缀表示。5 位前缀使用其最大值(31)填充。

I = 1337 - (2^5 - 1) = 1306。I (1306) 大于等于 128。I % 128 == 26,26 + 128 == 154,154 用 8 位表示为: 10011010。I 现在是 10,(1306 / 128 == 10),用 8 位表示为: 00001010。

     0   1   2   3   4   5   6   7
   +---+---+---+---+---+---+---+---+
   | 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
   | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 |  10<128, encode(10), done
   +---+---+---+---+---+---+---+---+

(3). 从八位字节边界开始对 42 进行编码

42 小于 255 (2^8 - 1) 用 8 位前缀表示。

     0   1   2   3   4   5   6   7
   +---+---+---+---+---+---+---+---+
   | 0 | 0 | 1 | 0 | 1 | 0 | 1 | 0 |   42 stored on 8 bits
   +---+---+---+---+---+---+---+---+

2. header 字段表示的示例

本节显示了几个独立的表示示例。

(1). 带索引的字面 header 字段

header 字段表示使用字面名称 name 和字面值 value。header 字段将添加到动态表。

需要编码的 header 列表:

custom-key: custom-header

编码数据的十六进制表示:

   400a 6375 7374 6f6d 2d6b 6579 0d63 7573 | @.custom-key.cus
   746f 6d2d 6865 6164 6572                | tom-header

解码过程:

   40                                      | == Literal indexed ==
   0a                                      |   Literal name (len = 10)
   6375 7374 6f6d 2d6b 6579                | custom-key
   0d                                      |   Literal value (len = 13)
   6375 7374 6f6d 2d68 6561 6465 72        | custom-header
                                           | -> custom-key:
                                           |   custom-header

由于 H 位传了 0,所以后面字符串用的字面形式表示,即 ASCII 码表示,通过查表可以知道,6375 7374 6f6d 2d6b 6579 表示的值是 custom-key,6375 7374 6f6d 2d68 6561 6465 72 表示的值是 custom-header。

编码后的动态表:

   [  1] (s =  55) custom-key: custom-header
         Table size:  55

解码后的 header 列表:

custom-key: custom-header

(2). 没有索引的字面 header 字段

header 字段表示使用索引名称 name 和字面值 value。header 字段未添加到动态表。

需要编码的 header 列表:

   :path: /sample/path

编码数据的十六进制表示:

   040c 2f73 616d 706c 652f 7061 7468      | ../sample/path

解码过程:

   04                                      | == Literal not indexed ==
                                           |   Indexed name (idx = 4)
                                           |     :path
   0c                                      |   Literal value (len = 12)
   2f73 616d 706c 652f 7061 7468           | /sample/path
                                           | -> :path: /sample/path

由于 H 位传了 0,所以后面字符串用的字面形式表示,即 ASCII 码表示,通过查表可以知道,2f73 616d 706c 652f 7061 7468 表示的值是 /sample/path。由于 :path 存在于静态表中,所以只需要传 index = 4 即可。

编码后的动态表:


解码后的 header 列表:

   :path: /sample/path

(3). 从不索引的字面 header 字段

header 字段表示使用字面名称 name 和字面值 value。header 字段不会添加到动态表中,并且如果由中间件重新编码,则必须使用相同的表示形式。

需要编码的 header 列表:

   password: secret

编码数据的十六进制表示:

   1008 7061 7373 776f 7264 0673 6563 7265 | ..password.secre
   74                                      | t

解码过程:

   10                                      | == Literal never indexed ==
   08                                      |   Literal name (len = 8)
   7061 7373 776f 7264                     | password
   06                                      |   Literal value (len = 6)
   7365 6372 6574                          | secret
                                           | -> password: secret

由于 H 位传了 0,所以后面字符串用的字面形式表示,即 ASCII 码表示,通过查表可以知道,7061 7373 776f 7264 表示的值是 password。7365 6372 6574 表示的值是 secret。

编码后的动态表:


解码后的 header 列表:

   password: secret

(4). 索引的 header 字段

header 字段表示使用静态表中的索引 header 字段。

需要编码的 header 列表:

   :method: GET

编码数据的十六进制表示:

   82                                      | .

解码过程:

   82                                      | == Indexed - Add ==
                                           |   idx = 2
                                           | -> :method: GET

由于 :method 和 GET 都在静态表中,所以用静态表中的 index 即可。

编码后的动态表:


解码后的 header 列表:

   :method: GET

3. 没有霍夫曼编码请求的示例

本节显示了同一连接上与 HTTP 请求相对应的几个连续的 header 列表。

(1). 第一个请求

需要编码的 header 列表:

   :method: GET
   :scheme: http
   :path: /
   :authority: www.example.com

编码数据的十六进制表示:

   8286 8441 0f77 7777 2e65 7861 6d70 6c65 | ...A.www.example
   2e63 6f6d                               | .com

解码过程:

   82                                      | == Indexed - Add ==
                                           |   idx = 2
                                           | -> :method: GET
   86                                      | == Indexed - Add ==
                                           |   idx = 6
                                           | -> :scheme: http
   84                                      | == Indexed - Add ==
                                           |   idx = 4
                                           | -> :path: /
   41                                      | == Literal indexed ==
                                           |   Indexed name (idx = 1)
                                           |     :authority
   0f                                      |   Literal value (len = 15)
   7777 772e 6578 616d 706c 652e 636f 6d   | www.example.com
                                           | -> :authority:
                                           |   www.example.com

编码后的动态表:

   [  1] (s =  57) :authority: www.example.com
         Table size:  57

解码后的 header 列表:

   :method: GET
   :scheme: http
   :path: /
   :authority: www.example.com

(2). 第二个请求

需要编码的 header 列表:

   :method: GET
   :scheme: http
   :path: /
   :authority: www.example.com
   cache-control: no-cache

编码数据的十六进制表示:

   8286 84be 5808 6e6f 2d63 6163 6865      | ....X.no-cache

解码过程:

   82                                      | == Indexed - Add ==
                                           |   idx = 2
                                           | -> :method: GET
   86                                      | == Indexed - Add ==
                                           |   idx = 6
                                           | -> :scheme: http
   84                                      | == Indexed - Add ==
                                           |   idx = 4
                                           | -> :path: /
   be                                      | == Indexed - Add ==
                                           |   idx = 62
                                           | -> :authority:
                                           |   www.example.com
   58                                      | == Literal indexed ==
                                           |   Indexed name (idx = 24)
                                           |     cache-control
   08                                      |   Literal value (len = 8)
   6e6f 2d63 6163 6865                     | no-cache
                                           | -> cache-control: no-cache

编码后的动态表:

   [  1] (s =  53) cache-control: no-cache
   [  2] (s =  57) :authority: www.example.com
         Table size: 110

解码后的 header 列表:

   :method: GET
   :scheme: http
   :path: /
   :authority: www.example.com
   cache-control: no-cache

(3). 第三个请求

需要编码的 header 列表:

   :method: GET
   :scheme: https
   :path: /index.html
   :authority: www.example.com
   custom-key: custom-value

编码数据的十六进制表示:

   8287 85bf 400a 6375 7374 6f6d 2d6b 6579 | ....@.custom-key
   0c63 7573 746f 6d2d 7661 6c75 65        | .custom-value

解码过程:

   82                                      | == Indexed - Add ==
                                           |   idx = 2
                                           | -> :method: GET
   87                                      | == Indexed - Add ==
                                           |   idx = 7
                                           | -> :scheme: https
   85                                      | == Indexed - Add ==
                                           |   idx = 5
                                           | -> :path: /index.html
   bf                                      | == Indexed - Add ==
                                           |   idx = 63
                                           | -> :authority:
                                           |   www.example.com
   40                                      | == Literal indexed ==
   0a                                      |   Literal name (len = 10)
   6375 7374 6f6d 2d6b 6579                | custom-key
   0c                                      |   Literal value (len = 12)
   6375 7374 6f6d 2d76 616c 7565           | custom-value
                                           | -> custom-key:
                                           |   custom-value

编码后的动态表:

   [  1] (s =  54) custom-key: custom-value
   [  2] (s =  53) cache-control: no-cache
   [  3] (s =  57) :authority: www.example.com
         Table size: 164

解码后的 header 列表:

   :method: GET
   :scheme: https
   :path: /index.html
   :authority: www.example.com
   custom-key: custom-value

4. 有霍夫曼编码请求的示例

本节显示与上一节相同的示例,但对字面值 value 使用霍夫曼编码。

(1). 第一个请求

需要编码的 header 列表:

   :method: GET
   :scheme: http
   :path: /
   :authority: www.example.com

编码数据的十六进制表示:

   8286 8441 8cf1 e3c2 e5f2 3a6b a0ab 90f4 | ...A......:k....
   ff                                      | .

解码过程:

   82                                      | == Indexed - Add ==
                                           |   idx = 2
                                           | -> :method: GET
   86                                      | == Indexed - Add ==
                                           |   idx = 6
                                           | -> :scheme: http
   84                                      | == Indexed - Add ==
                                           |   idx = 4
                                           | -> :path: /
   41                                      | == Literal indexed ==
                                           |   Indexed name (idx = 1)
                                           |     :authority
   8c                                      |   Literal value (len = 12)
                                           |     Huffman encoded:
   f1e3 c2e5 f23a 6ba0 ab90 f4ff           | .....:k.....
                                           |     Decoded:
                                           | www.example.com
                                           | -> :authority:
                                           |   www.example.com

编码后的动态表:

   [  1] (s =  57) :authority: www.example.com
         Table size:  57

解码后的 header 列表:

   :method: GET
   :scheme: http
   :path: /
   :authority: www.example.com

(2). 第二个请求

需要编码的 header 列表:

   :method: GET
   :scheme: http
   :path: /
   :authority: www.example.com
   cache-control: no-cache

编码数据的十六进制表示:

   8286 84be 5886 a8eb 1064 9cbf           | ....X....d..

解码过程:

   82                                      | == Indexed - Add ==
                                           |   idx = 2
                                           | -> :method: GET
   86                                      | == Indexed - Add ==
                                           |   idx = 6
                                           | -> :scheme: http
   84                                      | == Indexed - Add ==
                                           |   idx = 4
                                           | -> :path: /
   be                                      | == Indexed - Add ==
                                           |   idx = 62
                                           | -> :authority:
                                           |   www.example.com
   58                                      | == Literal indexed ==
                                           |   Indexed name (idx = 24)
                                           |     cache-control
   86                                      |   Literal value (len = 6)
                                           |     Huffman encoded:
   a8eb 1064 9cbf                          | ...d..
                                           |     Decoded:
                                           | no-cache
                                           | -> cache-control: no-cache

编码后的动态表:

   [  1] (s =  53) cache-control: no-cache
   [  2] (s =  57) :authority: www.example.com
         Table size: 110

解码后的 header 列表:

   :method: GET
   :scheme: http
   :path: /
   :authority: www.example.com
   cache-control: no-cache

(3). 第三个请求

需要编码的 header 列表:

   :method: GET
   :scheme: https
   :path: /index.html
   :authority: www.example.com
   custom-key: custom-value

编码数据的十六进制表示:

   8287 85bf 4088 25a8 49e9 5ba9 7d7f 8925 | ....@.%.I.[.}..%
   a849 e95b b8e8 b4bf                     | .I.[....

解码过程:

   82                                      | == Indexed - Add ==
                                           |   idx = 2
                                           | -> :method: GET
   87                                      | == Indexed - Add ==
                                           |   idx = 7
                                           | -> :scheme: https
   85                                      | == Indexed - Add ==
                                           |   idx = 5
                                           | -> :path: /index.html
   bf                                      | == Indexed - Add ==
                                           |   idx = 63
                                           | -> :authority:
                                           |   www.example.com
   40                                      | == Literal indexed ==
   88                                      |   Literal name (len = 8)
                                           |     Huffman encoded:
   25a8 49e9 5ba9 7d7f                     | %.I.[.}.
                                           |     Decoded:
                                           | custom-key
   89                                      |   Literal value (len = 9)
                                           |     Huffman encoded:
   25a8 49e9 5bb8 e8b4 bf                  | %.I.[....
                                           |     Decoded:
                                           | custom-value
                                           | -> custom-key:
                                           |   custom-value

编码后的动态表:

   [  1] (s =  54) custom-key: custom-value
   [  2] (s =  53) cache-control: no-cache
   [  3] (s =  57) :authority: www.example.com
         Table size: 164

解码后的 header 列表:

   :method: GET
   :scheme: https
   :path: /index.html
   :authority: www.example.com
   custom-key: custom-value

5. 没有霍夫曼编码响应的示例

本节显示了同一连接上对应于 HTTP 响应的几个连续的 header 列表。 HTTP/2 设置参数 SETTINGS_HEADER_TABLE_SIZE 设置为 256 个八位字节的值,导致某些驱逐条目发生。

(1). 第一个响应

需要编码的 header 列表:

   :status: 302
   cache-control: private
   date: Mon, 21 Oct 2013 20:13:21 GMT
   location: https://www.example.com

编码数据的十六进制表示:

   4803 3330 3258 0770 7269 7661 7465 611d | H.302X.privatea.
   4d6f 6e2c 2032 3120 4f63 7420 3230 3133 | Mon, 21 Oct 2013
   2032 303a 3133 3a32 3120 474d 546e 1768 |  20:13:21 GMTn.h
   7474 7073 3a2f 2f77 7777 2e65 7861 6d70 | ttps://www.examp
   6c65 2e63 6f6d                          | le.com

解码过程:

   48                                      | == Literal indexed ==
                                           |   Indexed name (idx = 8)
                                           |     :status
   03                                      |   Literal value (len = 3)
   3330 32                                 | 302
                                           | -> :status: 302
   58                                      | == Literal indexed ==
                                           |   Indexed name (idx = 24)
                                           |     cache-control
   07                                      |   Literal value (len = 7)
   7072 6976 6174 65                       | private
                                           | -> cache-control: private
   61                                      | == Literal indexed ==
                                           |   Indexed name (idx = 33)
                                           |     date
   1d                                      |   Literal value (len = 29)
   4d6f 6e2c 2032 3120 4f63 7420 3230 3133 | Mon, 21 Oct 2013
   2032 303a 3133 3a32 3120 474d 54        |  20:13:21 GMT
                                           | -> date: Mon, 21 Oct 2013
                                           |   20:13:21 GMT
   6e                                      | == Literal indexed ==
                                           |   Indexed name (idx = 46)
   17                                      |   Literal value (len = 23)
   6874 7470 733a 2f2f 7777 772e 6578 616d | https://www.exam
   706c 652e 636f 6d                       | ple.com
                                           | -> location:
                                           |   https://www.example.com

编码后的动态表:

   [  1] (s =  63) location: https://www.example.com
   [  2] (s =  65) date: Mon, 21 Oct 2013 20:13:21 GMT
   [  3] (s =  52) cache-control: private
   [  4] (s =  42) :status: 302
         Table size: 222

解码后的 header 列表:

   :status: 302
   cache-control: private
   date: Mon, 21 Oct 2013 20:13:21 GMT
   location: https://www.example.com

(2). 第二个响应

从动态表中将(“:status”,“302”)header 字段驱逐出可用空间,以允许添加(“:status”,“307”)header 字段。

需要编码的 header 列表:

   :status: 307
   cache-control: private
   date: Mon, 21 Oct 2013 20:13:21 GMT
   location: https://www.example.com

编码数据的十六进制表示:

   4803 3330 37c1 c0bf                     | H.307...

解码过程:

   48                                      | == Literal indexed ==
                                           |   Indexed name (idx = 8)
                                           |     :status
   03                                      |   Literal value (len = 3)
   3330 37                                 | 307
                                           | - evict: :status: 302
                                           | -> :status: 307
   c1                                      | == Indexed - Add ==
                                           |   idx = 65
                                           | -> cache-control: private
   c0                                      | == Indexed - Add ==
                                           |   idx = 64
                                           | -> date: Mon, 21 Oct 2013
                                           |   20:13:21 GMT
   bf                                      | == Indexed - Add ==
                                           |   idx = 63
                                           | -> location:
                                           |   https://www.example.com

编码后的动态表:

   [  1] (s =  42) :status: 307
   [  2] (s =  63) location: https://www.example.com
   [  3] (s =  65) date: Mon, 21 Oct 2013 20:13:21 GMT
   [  4] (s =  52) cache-control: private
         Table size: 222

解码后的 header 列表:

   :status: 307
   cache-control: private
   date: Mon, 21 Oct 2013 20:13:21 GMT
   location: https://www.example.com

(3). 第三个响应

在处理此 header 列表期间,会从动态表中逐出几个 header 字段。

需要编码的 header 列表:

   :status: 200
   cache-control: private
   date: Mon, 21 Oct 2013 20:13:22 GMT
   location: https://www.example.com
   content-encoding: gzip
   set-cookie: foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1

编码数据的十六进制表示:

   88c1 611d 4d6f 6e2c 2032 3120 4f63 7420 | ..a.Mon, 21 Oct
   3230 3133 2032 303a 3133 3a32 3220 474d | 2013 20:13:22 GM
   54c0 5a04 677a 6970 7738 666f 6f3d 4153 | T.Z.gzipw8foo=AS
   444a 4b48 514b 425a 584f 5157 454f 5049 | DJKHQKBZXOQWEOPI
   5541 5851 5745 4f49 553b 206d 6178 2d61 | UAXQWEOIU; max-a
   6765 3d33 3630 303b 2076 6572 7369 6f6e | ge=3600; version
   3d31                                    | =1

解码过程:

   88                                      | == Indexed - Add ==
                                           |   idx = 8
                                           | -> :status: 200
   c1                                      | == Indexed - Add ==
                                           |   idx = 65
                                           | -> cache-control: private
   61                                      | == Literal indexed ==
                                           |   Indexed name (idx = 33)
                                           |     date
   1d                                      |   Literal value (len = 29)
   4d6f 6e2c 2032 3120 4f63 7420 3230 3133 | Mon, 21 Oct 2013
   2032 303a 3133 3a32 3220 474d 54        |  20:13:22 GMT
                                           | - evict: cache-control:
                                           |   private
                                           | -> date: Mon, 21 Oct 2013
                                           |   20:13:22 GMT
   c0                                      | == Indexed - Add ==
                                           |   idx = 64
                                           | -> location:
                                           |   https://www.example.com
   5a                                      | == Literal indexed ==
                                           |   Indexed name (idx = 26)
                                           |     content-encoding
   04                                      |   Literal value (len = 4)
   677a 6970                               | gzip
                                           | - evict: date: Mon, 21 Oct
                                           |    2013 20:13:21 GMT
                                           | -> content-encoding: gzip
   77                                      | == Literal indexed ==
                                           |   Indexed name (idx = 55)
                                           |     set-cookie
   38                                      |   Literal value (len = 56)
   666f 6f3d 4153 444a 4b48 514b 425a 584f | foo=ASDJKHQKBZXO
   5157 454f 5049 5541 5851 5745 4f49 553b | QWEOPIUAXQWEOIU;
   206d 6178 2d61 6765 3d33 3630 303b 2076 |  max-age=3600; v
   6572 7369 6f6e 3d31                     | ersion=1
                                           | - evict: location:
                                           |   https://www.example.com
                                           | - evict: :status: 307
                                           | -> set-cookie: foo=ASDJKHQ
                                           |   KBZXOQWEOPIUAXQWEOIU; ma
                                           |   x-age=3600; version=1

编码后的动态表:

   [  1] (s =  98) set-cookie: foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU;
                    max-age=3600; version=1
   [  2] (s =  52) content-encoding: gzip
   [  3] (s =  65) date: Mon, 21 Oct 2013 20:13:22 GMT
         Table size: 215

解码后的 header 列表:

   :status: 200
   cache-control: private
   date: Mon, 21 Oct 2013 20:13:22 GMT
   location: https://www.example.com
   content-encoding: gzip
   set-cookie: foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1

6. 有霍夫曼编码响应的示例

本节显示与上一节相同的示例,但对字面值使用霍夫曼编码。HTTP/2 设置参数 SETTINGS_HEADER_TABLE_SIZE 设置为 256 个八位字节的值,导致某些驱逐事件发生。驱逐机制使用已解码字面值的长度,因此发生与上一节相同的驱逐。

(1). 第一个响应

需要编码的 header 列表:

   :status: 302
   cache-control: private
   date: Mon, 21 Oct 2013 20:13:21 GMT
   location: https://www.example.com

编码数据的十六进制表示:

   4882 6402 5885 aec3 771a 4b61 96d0 7abe | H.d.X...w.Ka..z.
   9410 54d4 44a8 2005 9504 0b81 66e0 82a6 | ..T.D. .....f...
   2d1b ff6e 919d 29ad 1718 63c7 8f0b 97c8 | -..n..)...c.....
   e9ae 82ae 43d3                          | ....C.

解码过程:

   48                                      | == Literal indexed ==
                                           |   Indexed name (idx = 8)
                                           |     :status
   82                                      |   Literal value (len = 2)
                                           |     Huffman encoded:
   6402                                    | d.
                                           |     Decoded:
                                           | 302
                                           | -> :status: 302
   58                                      | == Literal indexed ==
                                           |   Indexed name (idx = 24)
                                           |     cache-control
   85                                      |   Literal value (len = 5)
                                           |     Huffman encoded:
   aec3 771a 4b                            | ..w.K
                                           |     Decoded:
                                           | private
                                           | -> cache-control: private
   61                                      | == Literal indexed ==
                                           |   Indexed name (idx = 33)
                                           |     date
   96                                      |   Literal value (len = 22)
                                           |     Huffman encoded:
   d07a be94 1054 d444 a820 0595 040b 8166 | .z...T.D. .....f
   e082 a62d 1bff                          | ...-..
                                           |     Decoded:
                                           | Mon, 21 Oct 2013 20:13:21
                                           | GMT
                                           | -> date: Mon, 21 Oct 2013
                                           |   20:13:21 GMT
   6e                                      | == Literal indexed ==
                                           |   Indexed name (idx = 46)
                                           |     location
   91                                      |   Literal value (len = 17)
                                           |     Huffman encoded:
   9d29 ad17 1863 c78f 0b97 c8e9 ae82 ae43 | .)...c.........C
   d3                                      | .
                                           |     Decoded:
                                           | https://www.example.com
                                           | -> location:
                                           |   https://www.example.com

编码后的动态表:

   [  1] (s =  63) location: https://www.example.com
   [  2] (s =  65) date: Mon, 21 Oct 2013 20:13:21 GMT
   [  3] (s =  52) cache-control: private
   [  4] (s =  42) :status: 302
         Table size: 222

解码后的 header 列表:

   :status: 302
   cache-control: private
   date: Mon, 21 Oct 2013 20:13:21 GMT
   location: https://www.example.com

(2). 第二个响应

从动态表中将(“:status”,“302”)头字段驱逐出可用空间,以允许添加(“:status”,“307”)header 字段。

需要编码的 header 列表:

   :status: 307
   cache-control: private
   date: Mon, 21 Oct 2013 20:13:21 GMT
   location: https://www.example.com

编码数据的十六进制表示:

   4883 640e ffc1 c0bf                     | H.d.....

解码过程:

   48                                      | == Literal indexed ==
                                           |   Indexed name (idx = 8)
                                           |     :status
   83                                      |   Literal value (len = 3)
                                           |     Huffman encoded:
   640e ff                                 | d..
                                           |     Decoded:
                                           | 307
                                           | - evict: :status: 302
                                           | -> :status: 307
   c1                                      | == Indexed - Add ==
                                           |   idx = 65
                                           | -> cache-control: private
   c0                                      | == Indexed - Add ==
                                           |   idx = 64
                                           | -> date: Mon, 21 Oct 2013
                                           |   20:13:21 GMT
   bf                                      | == Indexed - Add ==
                                           |   idx = 63
                                           | -> location:
                                           |   https://www.example.com

编码后的动态表:

   [  1] (s =  42) :status: 307
   [  2] (s =  63) location: https://www.example.com
   [  3] (s =  65) date: Mon, 21 Oct 2013 20:13:21 GMT
   [  4] (s =  52) cache-control: private
         Table size: 222

解码后的 header 列表:

   :status: 307
   cache-control: private
   date: Mon, 21 Oct 2013 20:13:21 GMT
   location: https://www.example.com

(3). 第三个响应

在处理此 header 列表期间,会从动态表中逐出几个 header 字段。

需要编码的 header 列表:

   :status: 200
   cache-control: private
   date: Mon, 21 Oct 2013 20:13:22 GMT
   location: https://www.example.com
   content-encoding: gzip
   set-cookie: foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1

编码数据的十六进制表示:

   88c1 6196 d07a be94 1054 d444 a820 0595 | ..a..z...T.D. ..
   040b 8166 e084 a62d 1bff c05a 839b d9ab | ...f...-...Z....
   77ad 94e7 821d d7f2 e6c7 b335 dfdf cd5b | w..........5...[
   3960 d5af 2708 7f36 72c1 ab27 0fb5 291f | 9`..'..6r..'..).
   9587 3160 65c0 03ed 4ee5 b106 3d50 07   | ..1`e...N...=P.

解码过程:

   88                                      | == Indexed - Add ==
                                           |   idx = 8
                                           | -> :status: 200
   c1                                      | == Indexed - Add ==
                                           |   idx = 65
                                           | -> cache-control: private
   61                                      | == Literal indexed ==
                                           |   Indexed name (idx = 33)
                                           |     date
   96                                      |   Literal value (len = 22)
                                           |     Huffman encoded:
   d07a be94 1054 d444 a820 0595 040b 8166 | .z...T.D. .....f
   e084 a62d 1bff                          | ...-..
                                           |     Decoded:
                                           | Mon, 21 Oct 2013 20:13:22
                                           | GMT
                                           | - evict: cache-control:
                                           |   private
                                           | -> date: Mon, 21 Oct 2013
                                           |   20:13:22 GMT
   c0                                      | == Indexed - Add ==
                                           |   idx = 64
                                           | -> location:
                                           |   https://www.example.com
   5a                                      | == Literal indexed ==
                                           |   Indexed name (idx = 26)
                                           |     content-encoding
   83                                      |   Literal value (len = 3)
                                           |     Huffman encoded:
   9bd9 ab                                 | ...
                                           |     Decoded:
                                           | gzip
                                           | - evict: date: Mon, 21 Oct
                                           |    2013 20:13:21 GMT
                                           | -> content-encoding: gzip
   77                                      | == Literal indexed ==
                                           |   Indexed name (idx = 55)
                                           |     set-cookie
   ad                                      |   Literal value (len = 45)
                                           |     Huffman encoded:
   94e7 821d d7f2 e6c7 b335 dfdf cd5b 3960 | .........5...[9`
   d5af 2708 7f36 72c1 ab27 0fb5 291f 9587 | ..'..6r..'..)...
   3160 65c0 03ed 4ee5 b106 3d50 07        | 1`e...N...=P.
                                           |     Decoded:
                                           | foo=ASDJKHQKBZXOQWEOPIUAXQ
                                           | WEOIU; max-age=3600; versi
                                           | on=1
                                           | - evict: location:
                                           |   https://www.example.com
                                           | - evict: :status: 307
                                           | -> set-cookie: foo=ASDJKHQ
                                           |   KBZXOQWEOPIUAXQWEOIU; ma
                                           |   x-age=3600; version=1

编码后的动态表:

   [  1] (s =  98) set-cookie: foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU;
                    max-age=3600; version=1
   [  2] (s =  52) content-encoding: gzip
   [  3] (s =  65) date: Mon, 21 Oct 2013 20:13:22 GMT
         Table size: 215

解码后的 header 列表:

   :status: 200
   cache-control: private
   date: Mon, 21 Oct 2013 20:13:22 GMT
   location: https://www.example.com
   content-encoding: gzip
   set-cookie: foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1

7. 一些抓包的例子

先来看看首次请求中 HPACK 是如何压缩 HEADERS 帧中的首部字段的。

:method:GET 在静态表中的第 2 项。Name 和 Value 都已经存在了。所以直接用 2 即可表示这一项头部字段。

相同的,:path:/ 在静态表中的第 4 项。Name 和 Value 都已经存在了。所以直接用 4 即可表示这一项头部字段。

再来看看第二次请求中,同样是 :method:GET,和第一次请求一样,直接用 2 即可表示这一项头部字段。

回到首次请求中,if-none-match 首部字段,在静态表中的第 41 项,但是静态表里面没有值。根据前一篇文章讲解的 HPACK 算法,压缩串以 01 开头,101001 是 41,10011110,第一个 1 代表是霍夫曼编码,0011110 代表 30,表明 value 是紧接着的 30 个字节里面的内容。

还是首次请求,user-agent 首部字段,在静态表中的第 58 项,但是静态表里面没有值。根据前一篇文章讲解的 HPACK 算法,压缩串以 01 开头,111010 是 58,11011011,第一个 1 代表是霍夫曼编码,1011011 代表 91,表明 value 是紧接着的 91 个字节里面的内容。

到了第二次请求中,user-agent 首部字段在动态表中已经存储了 name 和 value 了,所以直接命中动态表中第 86 项。1010110 代表的就是 86。这个例子可以很明显的看到,动态表大幅缩减了 header 大小。

对比同一个 HTTP/2 连接中的 2 次相同的请求。可以看到首部大小已经大幅减少了。

在首次请求中,HAPCK 使得原有的头部减少了 44%。

在第二次请求中,由于补充了动态表,HAPCK 使得原有的头部减少了 97%。

8. HPACK 优化效果

最后,让我们用工具具体测试一下 HPACK 的“威力”。可以使用 h2load 工具测试

以下分别是 3 个测试用例,第一个测试用例只请求一次,第二个测试用例请求二次,第三个测试用例请求三次,看每次测试用来能缩小头部字段开销。

从上图中可以看到,请求越多,头部字段越来越小。

请求次数首部字段占比节约百分比
11.002%29.89%
20.521%63.75%
30.359%75.04%
50.241%83.28%
100.137%90.48%
200.092%93.65%
300.074%94.85%
500.061%95.75%
1000.052%96.39%

由此可以看出 HTTP/2 中的 HPACK 算法对 header 整体的压缩率还是非常不错的。


Reference:

RFC 7541

GitHub Repo:Halfrost-Field

Follow: halfrost · GitHub

Source: https://halfrost.com/http2-hpack-example/