96 views

HTTP/2标准的发展 8 – HTTP 头部压缩

 

image

【HTTP/2-Compression】关于HTTP/2协议的头部压缩,因为内容拖沓,有损观瞻,单列出来作为协议补充。头部压缩借鉴SPDY,读完之后,觉得在Google参与协议设计后,IETF的算法应用水平和智商提高了不少。

1.预备知识

阅读之前要有如下:

【Huffman_coding】(哈夫曼编码)

【DEFLATE】(Deflate压缩算法)

2.协议内容

2.1 前身 – SPDY头部压缩

【HTTP/2-Compression 】协议文本共50多页,其实本质不复杂。先从它的前身SPDY头部压缩【SPDY-Header-Block】说起。

SPDY头部压缩分为两个步骤:头部编码和压缩,非常简明:

1.头部编码

头部存放在SYN_STREAM和SYM_REPLY里面。注意这两个消息在HTTP/2已经不存在了,HTTP/2将header放在header block内。

image

上图是SPDY头部编码,很简单,直接把HTTP头部拿过来,编码成LV格式,这里不需要Type,都是字符串。

2.压缩

编码之后直接压缩。压缩使用了zlib的Deflate算法,这种算法可以借助外部字典,SPDY对于首部常出现的字串定义如下的一个静态字典,增加压缩比率:

 

 

从功能上讲,这样的做法是完备的,压缩算法本身也久经考验,但因为这类压缩算法受【CRIME】攻击,只好废弃。有关CRIME攻击,细节写在这里喧宾夺主,会单独写一篇。

2.2 HTTP/2头部压缩

为了避免潜在攻击,HTTP/2中的头部压缩使用了自定义算法,算法思想比较简单

1.用预定义的index mapping table将HTTP/2头部常用字串用Index替换

2.对一定要用文本表示的字串,可选用一个预定义的哈夫曼编码表进行编码

2.2.1被压缩的对象

被压缩对象是整个HTTP header。同SDPY的先编码,后整体压缩不同。这里的方法是,一边用index mapping table压缩,一边编码。

2.2.2 Index Mapping Table

被mapping的是字串对,这些字串可以是header中的header name,status code甚至是具体path的值。

image

上图是预定义的静态index mapping table的一部分,连“/index.html”这样的常见的path的value也被加到mapping table之中。为了压缩比最大,不拘一格用人才。

2.2.2.1静态table和动态table

table分为两种:static table和dynamic table,前者保存最常见字串,是协议预定义的。后者是dncoder/decoder在压缩/解压缩过程中动态build起来的,类似于Cache。并非所有的串运行的时候是可以直接通过index mapping的,因此我们可以归类三种串:

1.static table里面的被双方共享的well-known字串,出现几率最大

2.dynamic table是在压缩解压缩过程中双方各自学到的字串。

3.直接文本编码或者哈夫曼编码表示的串,dynamic table中没有,只能先用文本或者哈夫曼形式传输。传递时被两端的编码和解码器加到了各自dynamic table之中,处于暂态。

dynamic和static table被合成一个整体的Indexing Table,整体表中index的空间是连续的,dynamic table放在static table后面,如下图。

image

 

 

2.2.4 压缩需求

我觉得主要只有一个:解压后header保序。

2.2.5 编码之后的样子

压缩编码之后,会形成一个TLV格式的数组,这里因为定义了哈夫曼编码格式以及对于整数类型的编码,不仅仅是字符串一种type,需要引入type字段。整个header block可以是混合体,混合各种类型的编码。

编解码过程中的dynamic table是比较难理解的,这里结合例子说明白,但仅仅关注dynamic table的变化。协议附录C3部给出了不少例子,取一个:

请求Header如下

 

下面是压缩后结果:

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

(上面的“Add”可以理解为一个基地址)

因为传输中编码后唯一的文本是”www.example.com”,我们的目的是将它加入到dynamic table,这样下次就可以压缩了。这个过程在Client和Server两端其实是在独立同步完成的,也就是双方同步维护用相同算法维护dynamic table,但dynamic table的内容不会传递到线上。

到了线上,发送方其实已经生成了这条dynamic table entry,但是接收方没有,只能将文本表示发送过去,供接收方学习。解码时接收端也会做同样的动作,将authority: www.example.com加入到动态表的尾部。假设这条表项的index在两边是相同的,假设是60。下次发端发送index为60,接收方可以直接从动态表取出它的完全表示。

【参考】

1.HTTP/2-Compression – http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-10

2.Huffman_coding – http://en.wikipedia.org/wiki/Huffman_coding

3.DEFLATE – http://tools.ietf.org/html/rfc1951

4.LZ77- http://zh.wikipedia.org/zh-cn/LZ77%E4%B8%8ELZ78

5.CRIME – Rizzo, J. and T. Duong, “The CRIME Attack”, September 2012, https://docs.google.com/a/twist.com/presentation/d/11eBmGiHbYcHR9gL5nDyZChu_-lCa2GizeuOfaLU2HOU/edit#slide=id.g1eb6c1b5_3_6.

6.SPDY-Header-Block – http://www.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3-1#TOC-2.6.10-Name-Value-Header-Block

发表评论

电子邮件地址不会被公开。 必填项已用*标注

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">