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 字段,并提供了每个条目的索引。
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 |
二. 霍夫曼编码
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(以十六进制表示)。
sym | code as bits aligned to MSB |
code as hex aligned to LSB |
len in bits |
---|---|---|---|
( 0) | |11111111|11000 | 1ff8 | [13] |
( 1) | |11111111|11111111|1011000 | 7fffd8 | [23] |
( 2) | |11111111|11111111|11111110|0010 | fffffe2 | [28] |
( 3) | |11111111|11111111|11111110|0011 | fffffe3 | [28] |
( 4) | |11111111|11111111|11111110|0100 | fffffe4 | [28] |
( 5) | |11111111|11111111|11111110|0101 | fffffe5 | [28] |
( 6) | |11111111|11111111|11111110|0110 | fffffe6 | [28] |
( 7) | |11111111|11111111|11111110|0111 | fffffe7 | [28] |
( 8) | |11111111|11111111|11111110|1000 | fffffe8 | [28] |
( 9) | |11111111|11111111|11101010 | ffffea | [24] |
( 10) | |11111111|11111111|11111111|111100 | 3ffffffc | [30] |
( 11) | |11111111|11111111|11111110|1001 | fffffe9 | [28] |
( 12) | |11111111|11111111|11111110|1010 | fffffea | [28] |
( 13) | |11111111|11111111|11111111|111101 | 3ffffffd | [30] |
( 14) | |11111111|11111111|11111110|1011 | fffffeb | [28] |
( 15) | |11111111|11111111|11111110|1100 | fffffec | [28] |
( 16) | |11111111|11111111|11111110|1101 | fffffed | [28] |
( 17) | |11111111|11111111|11111110|1110 | fffffee | [28] |
( 18) | |11111111|11111111|11111110|1111 | fffffef | [28] |
( 19) | |11111111|11111111|11111111|0000 | ffffff0 | [28] |
( 20) | |11111111|11111111|11111111|0001 | ffffff1 | [28] |
( 21) | |11111111|11111111|11111111|0010 | ffffff2 | [28] |
( 22) | |11111111|11111111|11111111|111110 | 3ffffffe | [30] |
( 23) | |11111111|11111111|11111111|0011 | ffffff3 | [28] |
( 24) | |11111111|11111111|11111111|0100 | ffffff4 | [28] |
( 25) | |11111111|11111111|11111111|0101 | ffffff5 | [28] |
( 26) | |11111111|11111111|11111111|0110 | ffffff6 | [28] |
( 27) | |11111111|11111111|11111111|0111 | ffffff7 | [28] |
( 28) | |11111111|11111111|11111111|1000 | ffffff8 | [28] |
( 29) | |11111111|11111111|11111111|1001 | ffffff9 | [28] |
( 30) | |11111111|11111111|11111111|1010 | ffffffa | [28] |
( 31) | |11111111|11111111|11111111|1011 | ffffffb | [28] |
' ' ( 32) | |010100 | 14 | [ 6] |
'!' ( 33) | |11111110|00 | 3f8 | [10] |
'"' ( 34) | |11111110|01 | 3f9 | [10] |
'#' ( 35) | |11111111|1010 | ffa | [12] |
'$' ( 36) | |11111111|11001 | 1ff9 | [13] |
'%' ( 37) | |010101 | 15 | [ 6] |
'&' ( 38) | |11111000 | f8 | [ 8] |
''' ( 39) | |11111111|010 | 7fa | [11] |
'(' ( 40) | |11111110|10 | 3fa | [10] |
')' ( 41) | |11111110|11 | 3fb | [10] |
'*' ( 42) | |11111001 | f9 | [ 8] |
'+' ( 43) | |11111111|011 | 7fb | [11] |
',' ( 44) | |11111010 | fa | [ 8] |
'-' ( 45) | |010110 | 16 | [ 6] |
'.' ( 46) | |010111 | 17 | [ 6] |
'/' ( 47) | |011000 | 18 | [ 6] |
'0' ( 48) | |00000 | 0 | [ 5] |
'1' ( 49) | |00001 | 1 | [ 5] |
'2' ( 50) | |00010 | 2 | [ 5] |
'3' ( 51) | |011001 | 19 | [ 6] |
'4' ( 52) | |011010 | 1a | [ 6] |
'5' ( 53) | |011011 | 1b | [ 6] |
'6' ( 54) | |011100 | 1c | [ 6] |
'7' ( 55) | |011101 | 1d | [ 6] |
'8' ( 56) | |011110 | 1e | [ 6] |
'9' ( 57) | |011111 | 1f | [ 6] |
':' ( 58) | |1011100 | 5c | [ 7] |
';' ( 59) | |11111011 | fb | [ 8] |
'<' ( 60) | |11111111|1111100 | 7ffc | [15] |
'=' ( 61) | |100000 | 20 | [ 6] |
'>' ( 62) | |11111111|1011 | ffb | [12] |
'?' ( 63) | |11111111|00 | 3fc | [10] |
'@' ( 64) | |11111111|11010 | 1ffa | [13] |
'A' ( 65) | |100001 | 21 | [ 6] |
'B' ( 66) | |1011101 | 5d | [ 7] |
'C' ( 67) | |1011110 | 5e | [ 7] |
'D' ( 68) | |1011111 | 5f | [ 7] |
'E' ( 69) | |1100000 | 60 | [ 7] |
'F' ( 70) | |1100001 | 61 | [ 7] |
'G' ( 71) | |1100010 | 62 | [ 7] |
'H' ( 72) | |1100011 | 63 | [ 7] |
'I' ( 73) | |1100100 | 64 | [ 7] |
'J' ( 74) | |1100101 | 65 | [ 7] |
'K' ( 75) | |1100110 | 66 | [ 7] |
'L' ( 76) | |1100111 | 67 | [ 7] |
'M' ( 77) | |1101000 | 68 | [ 7] |
'N' ( 78) | |1101001 | 69 | [ 7] |
'O' ( 79) | |1101010 | 6a | [ 7] |
'P' ( 80) | |1101011 | 6b | [ 7] |
'Q' ( 81) | |1101100 | 6c | [ 7] |
'R' ( 82) | |1101101 | 6d | [ 7] |
'S' ( 83) | |1101110 | 6e | [ 7] |
'T' ( 84) | |1101111 | 6f | [ 7] |
'U' ( 85) | |1110000 | 70 | [ 7] |
'V' ( 86) | |1110001 | 71 | [ 7] |
'W' ( 87) | |1110010 | 72 | [ 7] |
'X' ( 88) | |11111100 | fc | [ 8] |
'Y' ( 89) | |1110011 | 73 | [ 7] |
'Z' ( 90) | |11111101 | fd | [ 8] |
'[' ( 91) | |11111111|11011 | 1ffb | [13] |
'' ( 92) | |11111111|11111110|000 | 7fff0 | [19] |
']' ( 93) | |11111111|11100 | 1ffc | [13] |
'^' ( 94) | |11111111|111100 | 3ffc | [14] |
'_' ( 95) | |100010 | 22 | [ 6] |
'`' ( 96) | |11111111|1111101 | 7ffd | [15] |
'a' ( 97) | |00011 | 3 | [ 5] |
'b' ( 98) | |100011 | 23 | [ 6] |
'c' ( 99) | |00100 | 4 | [ 5] |
'd' (100) | |100100 | 24 | [ 6] |
'e' (101) | |00101 | 5 | [ 5] |
'f' (102) | |100101 | 25 | [ 6] |
'g' (103) | |100110 | 26 | [ 6] |
'h' (104) | |100111 | 27 | [ 6] |
'i' (105) | |00110 | 6 | [ 5] |
'j' (106) | |1110100 | 74 | [ 7] |
'k' (107) | |1110101 | 75 | [ 7] |
'l' (108) | |101000 | 28 | [ 6] |
'm' (109) | |101001 | 29 | [ 6] |
'n' (110) | |101010 | 2a | [ 6] |
'o' (111) | |00111 | 7 | [ 5] |
'p' (112) | |101011 | 2b | [ 6] |
'q' (113) | |1110110 | 76 | [ 7] |
'r' (114) | |101100 | 2c | [ 6] |
's' (115) | |01000 | 8 | [ 5] |
't' (116) | |01001 | 9 | [ 5] |
'u' (117) | |101101 | 2d | [ 6] |
'v' (118) | |1110111 | 77 | [ 7] |
'w' (119) | |1111000 | 78 | [ 7] |
'x' (120) | |1111001 | 79 | [ 7] |
'y' (121) | |1111010 | 7a | [ 7] |
'z' (122) | |1111011 | 7b | [ 7] |
'{' (123) | |11111111|1111110 | 7ffe | [15] |
' | ' (124) | |11111111|100 | 7fc |
'}' (125) | |11111111|111101 | 3ffd | [14] |
'~' (126) | |11111111|11101 | 1ffd | [13] |
(127) | |11111111|11111111|11111111|1100 | ffffffc | [28] |
(128) | |11111111|11111110|0110 | fffe6 | [20] |
(129) | |11111111|11111111|010010 | 3fffd2 | [22] |
(130) | |11111111|11111110|0111 | fffe7 | [20] |
(131) | |11111111|11111110|1000 | fffe8 | [20] |
(132) | |11111111|11111111|010011 | 3fffd3 | [22] |
(133) | |11111111|11111111|010100 | 3fffd4 | [22] |
(134) | |11111111|11111111|010101 | 3fffd5 | [22] |
(135) | |11111111|11111111|1011001 | 7fffd9 | [23] |
(136) | |11111111|11111111|010110 | 3fffd6 | [22] |
(137) | |11111111|11111111|1011010 | 7fffda | [23] |
(138) | |11111111|11111111|1011011 | 7fffdb | [23] |
(139) | |11111111|11111111|1011100 | 7fffdc | [23] |
(140) | |11111111|11111111|1011101 | 7fffdd | [23] |
(141) | |11111111|11111111|1011110 | 7fffde | [23] |
(142) | |11111111|11111111|11101011 | ffffeb | [24] |
(143) | |11111111|11111111|1011111 | 7fffdf | [23] |
(144) | |11111111|11111111|11101100 | ffffec | [24] |
(145) | |11111111|11111111|11101101 | ffffed | [24] |
(146) | |11111111|11111111|010111 | 3fffd7 | [22] |
(147) | |11111111|11111111|1100000 | 7fffe0 | [23] |
(148) | |11111111|11111111|11101110 | ffffee | [24] |
(149) | |11111111|11111111|1100001 | 7fffe1 | [23] |
(150) | |11111111|11111111|1100010 | 7fffe2 | [23] |
(151) | |11111111|11111111|1100011 | 7fffe3 | [23] |
(152) | |11111111|11111111|1100100 | 7fffe4 | [23] |
(153) | |11111111|11111110|11100 | 1fffdc | [21] |
(154) | |11111111|11111111|011000 | 3fffd8 | [22] |
(155) | |11111111|11111111|1100101 | 7fffe5 | [23] |
(156) | |11111111|11111111|011001 | 3fffd9 | [22] |
(157) | |11111111|11111111|1100110 | 7fffe6 | [23] |
(158) | |11111111|11111111|1100111 | 7fffe7 | [23] |
(159) | |11111111|11111111|11101111 | ffffef | [24] |
(160) | |11111111|11111111|011010 | 3fffda | [22] |
(161) | |11111111|11111110|11101 | 1fffdd | [21] |
(162) | |11111111|11111110|1001 | fffe9 | [20] |
(163) | |11111111|11111111|011011 | 3fffdb | [22] |
(164) | |11111111|11111111|011100 | 3fffdc | [22] |
(165) | |11111111|11111111|1101000 | 7fffe8 | [23] |
(166) | |11111111|11111111|1101001 | 7fffe9 | [23] |
(167) | |11111111|11111110|11110 | 1fffde | [21] |
(168) | |11111111|11111111|1101010 | 7fffea | [23] |
(169) | |11111111|11111111|011101 | 3fffdd | [22] |
(170) | |11111111|11111111|011110 | 3fffde | [22] |
(171) | |11111111|11111111|11110000 | fffff0 | [24] |
(172) | |11111111|11111110|11111 | 1fffdf | [21] |
(173) | |11111111|11111111|011111 | 3fffdf | [22] |
(174) | |11111111|11111111|1101011 | 7fffeb | [23] |
(175) | |11111111|11111111|1101100 | 7fffec | [23] |
(176) | |11111111|11111111|00000 | 1fffe0 | [21] |
(177) | |11111111|11111111|00001 | 1fffe1 | [21] |
(178) | |11111111|11111111|100000 | 3fffe0 | [22] |
(179) | |11111111|11111111|00010 | 1fffe2 | [21] |
(180) | |11111111|11111111|1101101 | 7fffed | [23] |
(181) | |11111111|11111111|100001 | 3fffe1 | [22] |
(182) | |11111111|11111111|1101110 | 7fffee | [23] |
(183) | |11111111|11111111|1101111 | 7fffef | [23] |
(184) | |11111111|11111110|1010 | fffea | [20] |
(185) | |11111111|11111111|100010 | 3fffe2 | [22] |
(186) | |11111111|11111111|100011 | 3fffe3 | [22] |
(187) | |11111111|11111111|100100 | 3fffe4 | [22] |
(188) | |11111111|11111111|1110000 | 7ffff0 | [23] |
(189) | |11111111|11111111|100101 | 3fffe5 | [22] |
(190) | |11111111|11111111|100110 | 3fffe6 | [22] |
(191) | |11111111|11111111|1110001 | 7ffff1 | [23] |
(192) | |11111111|11111111|11111000|00 | 3ffffe0 | [26] |
(193) | |11111111|11111111|11111000|01 | 3ffffe1 | [26] |
(194) | |11111111|11111110|1011 | fffeb | [20] |
(195) | |11111111|11111110|001 | 7fff1 | [19] |
(196) | |11111111|11111111|100111 | 3fffe7 | [22] |
(197) | |11111111|11111111|1110010 | 7ffff2 | [23] |
(198) | |11111111|11111111|101000 | 3fffe8 | [22] |
(199) | |11111111|11111111|11110110|0 | 1ffffec | [25] |
(200) | |11111111|11111111|11111000|10 | 3ffffe2 | [26] |
(201) | |11111111|11111111|11111000|11 | 3ffffe3 | [26] |
(202) | |11111111|11111111|11111001|00 | 3ffffe4 | [26] |
(203) | |11111111|11111111|11111011|110 | 7ffffde | [27] |
(204) | |11111111|11111111|11111011|111 | 7ffffdf | [27] |
(205) | |11111111|11111111|11111001|01 | 3ffffe5 | [26] |
(206) | |11111111|11111111|11110001 | fffff1 | [24] |
(207) | |11111111|11111111|11110110|1 | 1ffffed | [25] |
(208) | |11111111|11111110|010 | 7fff2 | [19] |
(209) | |11111111|11111111|00011 | 1fffe3 | [21] |
(210) | |11111111|11111111|11111001|10 | 3ffffe6 | [26] |
(211) | |11111111|11111111|11111100|000 | 7ffffe0 | [27] |
(212) | |11111111|11111111|11111100|001 | 7ffffe1 | [27] |
(213) | |11111111|11111111|11111001|11 | 3ffffe7 | [26] |
(214) | |11111111|11111111|11111100|010 | 7ffffe2 | [27] |
(215) | |11111111|11111111|11110010 | fffff2 | [24] |
(216) | |11111111|11111111|00100 | 1fffe4 | [21] |
(217) | |11111111|11111111|00101 | 1fffe5 | [21] |
(218) | |11111111|11111111|11111010|00 | 3ffffe8 | [26] |
(219) | |11111111|11111111|11111010|01 | 3ffffe9 | [26] |
(220) | |11111111|11111111|11111111|1101 | ffffffd | [28] |
(221) | |11111111|11111111|11111100|011 | 7ffffe3 | [27] |
(222) | |11111111|11111111|11111100|100 | 7ffffe4 | [27] |
(223) | |11111111|11111111|11111100|101 | 7ffffe5 | [27] |
(224) | |11111111|11111110|1100 | fffec | [20] |
(225) | |11111111|11111111|11110011 | fffff3 | [24] |
(226) | |11111111|11111110|1101 | fffed | [20] |
(227) | |11111111|11111111|00110 | 1fffe6 | [21] |
(228) | |11111111|11111111|101001 | 3fffe9 | [22] |
(229) | |11111111|11111111|00111 | 1fffe7 | [21] |
(230) | |11111111|11111111|01000 | 1fffe8 | [21] |
(231) | |11111111|11111111|1110011 | 7ffff3 | [23] |
(232) | |11111111|11111111|101010 | 3fffea | [22] |
(233) | |11111111|11111111|101011 | 3fffeb | [22] |
(234) | |11111111|11111111|11110111|0 | 1ffffee | [25] |
(235) | |11111111|11111111|11110111|1 | 1ffffef | [25] |
(236) | |11111111|11111111|11110100\ | fffff4 | [24] |
(237) | |11111111|11111111|11110101\ | fffff5 | [24] |
(238) | |11111111|11111111|11111010|10 | 3ffffea | [26] |
(239) | |11111111|11111111|1110100 \ | 7ffff4 | [23] |
(240) | |11111111|11111111|11111010|11 | 3ffffeb | [26] |
(241) | |11111111|11111111|11111100|110 | 7ffffe6 | [27] |
(242) | |11111111|11111111|11111011|00 | 3ffffec | [26] |
(243) | |11111111|11111111|11111011|01 | 3ffffed | [26] |
(244) | |11111111|11111111|11111100|111 | 7ffffe7 | [27] |
(245) | |11111111|11111111|11111101|000 | 7ffffe8 | [27] |
(246) | |11111111|11111111|11111101|001 | 7ffffe9 | [27] |
(247) | |11111111|11111111|11111101|010 | 7ffffea | [27] |
(248) | |11111111|11111111|11111101|011 | 7ffffeb | [27] |
(249) | |11111111|11111111|11111111|1110 | ffffffe | [28] |
(250) | |11111111|11111111|11111101|100 | 7ffffec | [27] |
(251) | |11111111|11111111|11111101|101 | 7ffffed | [27] |
(252) | |11111111|11111111|11111101|110 | 7ffffee | [27] |
(253) | |11111111|11111111|11111101|111 | 7ffffef | [27] |
(254) | |11111111|11111111|11111110|000 | 7fffff0 | [27] |
(255) | |11111111|11111111|11111011|10 | 3ffffee | [26] |
EOS (256) | |11111111|11111111|11111111|111111 | 3fffffff | [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 个测试用例,第一个测试用例只请求一次,第二个测试用例请求二次,第三个测试用例请求三次,看每次测试用来能缩小头部字段开销。
从上图中可以看到,请求越多,头部字段越来越小。
请求次数 | 首部字段占比 | 节约百分比 |
---|---|---|
1 | 1.002% | 29.89% |
2 | 0.521% | 63.75% |
3 | 0.359% | 75.04% |
5 | 0.241% | 83.28% |
10 | 0.137% | 90.48% |
20 | 0.092% | 93.65% |
30 | 0.074% | 94.85% |
50 | 0.061% | 95.75% |
100 | 0.052% | 96.39% |
由此可以看出 HTTP/2 中的 HPACK 算法对 header 整体的压缩率还是非常不错的。
Reference:
GitHub Repo:Halfrost-Field
Follow: halfrost · GitHub
Source: GHOST_URL/http2-hpack-example/