HPACK和twitter hpack源码解析

HPACK和twitter hpack源码解析

HPACK是用于压缩HTTP/2中header信息的压缩算法。

引言

在HTTP/1.x中,header信息以字符串的方式进行传输,随着大量并发的网络请求,冗余的header字段会造成没必要的带宽浪费,从而增加网络时延。

HTTP/2的对这个问题进行了优化,它对header信息进行压缩编码,提高了的带宽利用率,其中的压缩编码规范就是HPACK

HPACK中将 header 字段列表视为 name-value 对的有序集合,其中可以包括重复的对。名称和值被认为是八位字节的不透明序列,并且 header 字段的顺序在压缩和解压缩后保持不变。

Static Table Definition

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

从Static Table可以看出压缩的核心之一就是把http协议中常用的字符串用数字来代替,以此来减少header中的字节数。

编号的范围是1-61,很容易想到用一个8bit的byte来表示就可以了,但是实际上像1这种数字我们用1bit也可以表示,由此引入了霍夫曼编码,编码的作用就是使用频次高的数字用更少的字节来表示,这样可以使总体的字节数更少。

Dynamic Table

Dynamic Table是可以看做是一个先进先出的队列,新加入的entry的index最小,最早的entry的index最大。

Dynamic Table初始是空的。当每个header块被解压缩时,将添加条目。Dynamic Table有容量限制,当容量不足以添加新的条目时会将按照最老的条目先移除的顺序移除条目,直到容量足够添加新的条目为止,如果新添加条目比Dynamic Table最大容量还要大,那么清空Dynamic Table。

Dynamic Table是针对一个连接的,因此HTTP/2提倡使用尽可能少的连接头,这样Dynamic Table会更全,头部压缩效果更好。

Index Address Space

Static table和Dynamic Table组成了一个连续的地址空间,如下所示。

       <----------  Index Address Space ---------->
       <-- Static  Table -->  <-- Dynamic Table -->
       +---+-----------+---+  +---+-----------+---+
       | 1 |    ...    | s |  |s+1|    ...    |s+k|
       +---+-----------+---+  +---+-----------+---+
                              ^                   |
                              |                   V
                       Insertion Point      Dropping Point

                   Index Address Space

Header Filed Representation

编码的header字段可以表示为index或者literal,index为static table或者dynamic table中的引用;header字段name可以用literal形式表示,也可以作为static table或者dynamic table中的引用。

3中不同的literal表示定义:
1. 在dynamic table开头添加header字段作为新条目的literal
2. 不将header字段添加到dynamic table
3. 不将header字段添加到dynamic table,并且规定header字段时钟使用literal presentation。

header字段name或者value可以直接或者使用Huffman对8位字节序列进行编码。

Primitive Type Representations

HPACK使用两种编码类型:无符号可变长整数和8位的字节串

1. Integer representation

整数用于表示name索引,header字段索引或字符串长度。整数表示可以在8位字节内的任何位置开始。为了优化处理,整数表示总是在8位字节的末尾结束。

整数表示分为两个部分:填充8位字节的前缀和可选的8位字节列表,如果整数值不适合该前缀,则使用这些可选的8位字节。前缀的位数(N)是整数表示的参数。

如果整数小于2^N-1,则将其编码在N位前缀中

   0   1   2   3   4   5   6   7
   +---+---+---+---+---+---+---+---+
   | ? | ? | ? |       Value       |
   +---+---+---+-------------------+
   
   Integer Value Encoded within the Prefix (Shown for N = 5)

否则,将前缀的所有位设置为1,并使用多个连续的8位字节列表对减少了2^N-1的值进行编码。每个8位字节列表的最高位除了最后一个是0,其它全部设置为1,剩余的7位用来表示减少后的整数。

  0   1   2   3   4   5   6   7
   +---+---+---+---+---+---+---+---+
   | ? | ? | ? | 1   1   1   1   1 |
   +---+---+---+-------------------+
   | 1 |    Value-(2^N-1) LSB      |
   +---+---------------------------+
                  ...
   +---+---------------------------+
   | 0 |    Value-(2^N-1) MSB      |
   +---+---------------------------+

    Integer Value Encoded after the Prefix (Shown for N = 5)

表示整数I的伪代码如下:

 if I < 2^N - 1, encode I on N bits
   else
       encode (2^N - 1) on N bits
       I = I - (2^N - 1)
       while I >= 128
            encode (I % 128 + 128) on 8 bits
            I = I / 128
       encode I on 8 bits
For example
   1. i = 10,N = 5;
   i < 31(2^5-1)

   0   1   2   3   4   5   6   7
   +---+---+---+---+---+---+---+---+
   | X | X | X | 0 | 1 | 0 | 1 | 0 |   10 stored on 5 bits
   +---+---+---+---+---+---+---+---+
   
   2. i = 1337,N = 5;
   i > 31(2^5-1)
   i = 1337-(2^5-1) = 1306 > 31
   encode i%128 + 128 = 154
   i = 1306/128 = 10
   
      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
   +---+---+---+---+---+---+---+---+
   

从下一个N bits中解码I的伪代码:

if I < 2^N - 1, return I
   else
       M = 0
       repeat
           B = next octet
           I = I + (B & 127) * 2^M
           M = M + 7
       while B & 128 == 128
       return I

2. String Literal Representation

Header字段的name和value可以使用字符串字面量。一个字符串可以使用8位字节或使用Huffman编码将字符串编码成8位字节序列。

String Literal编码按照Appendix B中所定义Huffman编码进行编码。

由于Huffman编码的数据并不总是在8位字节的边界结束,因此在其后插入EOS(end of string)符号对应的最高有效位。

解码时,编码数据末尾的不完整数据被当做填充和丢弃。大于7 bits长度的填充被当做解码错误,与EOS 符号的代码的最高有效位不对应的填充必须被视为解码错误。包含EOS符号的霍夫曼编码的字符串字面必须被视为解码错误。

  0   1   2   3   4   5   6   7
   +---+---+---+---+---+---+---+---+
   | H |    String Length (7+)     |
   +---+---------------------------+
   |  String Data (Length octets)  |
   +-------------------------------+
   
   String Literal Representation

如上图,H是String Data是否使用Huffman编码的标志,如果H等于0则使用原始字符串,如果H等于1,则使用Huffman编码。

Binary Format

1. 索引Header字段表示

索引header字段表示可表示static Table或者dynamic table中的条目。

   0   1   2   3   4   5   6   7
   +---+---+---+---+---+---+---+---+
   | 1 |        Index (7+)         |
   +---+---------------------------+
   
        Indexed Header Field

如果index为0,则解码错误。

2. 字面量header字段表示

2.1 带有增加索引的字面量header字段
  0   1   2   3   4   5   6   7
   +---+---+---+---+---+---+---+---+
   | 0 | 1 |      Index (6+)       |
   +---+---+-----------------------+
   | H |     Value Length (7+)     |
   +---+---------------------------+
   | Value String (Length octets)  |
   +-------------------------------+
   
   Literal Header Field with Incremental Indexing -- Indexed
                                   Name
   

前两个bit为01为标识,通过index从index address space中获取到name,会将解码的结果加入到解码header列表并插入到dynamic table中。

   0   1   2   3   4   5   6   7
   +---+---+---+---+---+---+---+---+
   | 0 | 1 |           0           |
   +---+---+-----------------------+
   | H |     Name Length (7+)      |
   +---+---------------------------+
   |  Name String (Length octets)  |
   +---+---------------------------+
   | H |     Value Length (7+)     |
   +---+---------------------------+
   | Value String (Length octets)  |
   +-------------------------------+
   
   Literal Header Field with Incremental Indexing -- New Name

如果index值为0,那么接下来的字节表示的是name的String Literal Representation。

2.2 没有索引的字面header字段
  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值