## **首部压缩**
服务器推送,此推送非彼推送,一开始以为,是不是以后可以抛弃轮询这种技术了?并不是,该轮询还是要轮询。那么,在开启keep-alive的情况下,轮询在HTTP/2中的性能没什么提升吗?也并不是。
在HTTP/1.x中首部是没有压缩的,gzip只会压缩body,HTTP/2提供了首部压缩方案。一般轮询请求首部,特别是cookie占用很多大部份空间,首部压缩使得整个HTTP数据包小了很多,传输也就会更快。
刚开始spdy提出的首部压缩方案比较简单粗暴,直接像压缩body那样压缩首部,这看起来好像没什么不妥,但是有安全隐患,会有受到CRIME式攻击的可能性。这种攻击方法简单说,就是不断地利用已知数据去探测密文,达到破解的目的。无损压缩算法会有个特性,数据越冗余,压缩效率越好。而首部中的很多字段是已知的,我们只要构造个请求,请求中带有首部的某个字段,经压缩再加密后的密文长度就会有所变化,然后不断构造猜测该字段的值,同时观察密文的长度,慢慢地确定首部字段的值。
~~~
GET /pwd=0 HTTP/1.1
Cookie: pwd=123
GET /pwd=1 HTTP/1.1
Cookie: pwd=123
~~~
我们会发现,前者的密文长度比后者长,这样就确定了“d”,再慢慢的猜测,达到破解的目的。
HTTP/2中抛弃了这种方案,用专门设计的HPACK。它是在服务器和客户端各维护一个“首部表”,表中用索引代表首部名,或者首部键-值对,上一次发送两端都会记住已发送过哪些首部,下一次发送只需要传输差异的数据,相同的数据直接用索引表示即可,另外还可以选择地对首部值压缩后再传输。按照这样的设计,两次轮询请求的首部基本是一样的,那之后的请求基本只需要发送几个索引就可以了。
![](https://box.kancloud.cn/2015-10-27_562ee2ca87b49.jpg)
“首部表”有两种,一种是静态表,即HTTP/2协议内置了常用的一些首部名和首部键值对。另一种是动态表,保存自定义的首部或五花八门的键值对等,动态表可以通过`SETTINGS`帧的SETTINGS_HEADER_TABLE_SIZE规定大小。