Review
- 2024-10-29 07:20
[!Summary] HTTP/1.x 支持HTTP正文压缩,但是到了 HTTP/2 之后才支持HTTP Headers 压缩。 HPACK 基于查询表和Huffman编码,但(关键)不是基于反查的压缩方法。 HTTP/2首部压缩是有状态的。 HPACK 格式特意被设计成简单且不灵活的形式。两种特性都降低了由于实现错误而引起的互操作性或安全性问题的风险。没有定义扩展机制;只能通过定义完整的替换来更改格式。
一、基础知识解释 #
压缩分类
- 有损压缩
- 无损压缩
有损压缩 #
因为用不到,所以数据中的一些细节可以忽略。这类的压缩通常用于媒体内容如音乐文件、图像和视频的压缩,可以对它们进行大量压缩,而不会使数据的整体含义受损。但如果压缩太多,会丢掉细节,这时候像图片就不能再放大。
无损压缩 #
无损压缩的原理主要基于数据冗余的去除。通过分析数据中的重复模式、冗余信息等,找到更有效的编码方式,从而达到压缩的目的。工作方式是,移除那些在解压时可以很容易恢复的重复数据。
无损压缩方案:
- 查表法
- 高效的编码技术:如 Huffman编码,Lempel-Ziv(LZ)算法,LZ77和LZ78是LZ的常见变体。
- 反查压缩(Lookback compression):反查压缩在当前位置放置引用,指向重复文本。
查询表:只有表里的值经常重复时查询表才有意义,有如下分类
- 静态查询表
- 动态查询表
动态表可以包含重复的条目(有相同的名称和值的条目)。所以解码器不能将重复的条目当作错误处理。
编码 #
[!Tip] Huffman编码 是更先进的可变长度编码。它的工作原理是根据每个值的使用频率为其分配一个唯一代码,并且保证没有一个代码是其他代码的前缀。
Huffman代码压缩是查询表的扩展。Huffman表可以基于数据可能相似的已知结构(如英文文本)提前定义,也可以基于要加密的数据动态生成,或者可以同时使用两种方式。
一些编码技术 #
- 像素图片:采用1bit像素范围(黑或白)
- 假设图片只有红色和黄色,可以使用一个8位的色板,并且只使用这两个颜色。或者可以使用一个1bit的色板,提前说明0代表黄色1代表红色
- 文本编码:ASCII编码(7bit),UTF8编码(通常用8bit表示ASCII字符,16位表示东方文字,其他的24位、32位可以表示Emoji或者偏僻字符)
变长编码:ASCII 定长编码:UTF8
Unicode(UTF-8和UTF-16)在某种程度上使用这种方式,针对不同使用频率的字符,分配不同的字符区间(1~4个8位字节)。这种方式复杂的地方在于识别字符之间的边界(因为这个格式的字符的长度不是固定的7位)。
HTTP正文压缩通常用于文本数据。媒体数据一般通过指定的格式提前压缩过了,不需要再压缩。
服务器和浏览器使用的技术(deflate、gzip和brotli)很相似,它们都是基于deflate算法的变种。基于deflate的压缩算法有一个主要问题:被证实是不安全的。它的问题是,你可以使用数据长度来猜测内容,特别是当你能影响内容中的一部分时。
HPACK #
HPACK格式被有意保持简单且死板。这两个特征都会减少因为实现错误所带来的互操作风险,或者安全问题。没有定义扩展机制,如要改变当前格式,只能使用一个完整的替代品。
HPACK有一个静态表,包含61个常见的HTTP首部名称。
除了静态表以外,HPACK还有一个连接级的动态表,从位置62开始(跟在静态表之后),最大到SETTINGS帧的 SETTINGS_HEADER_TABLE_SIZE 所定义的大小。如果没有明确指定,默认是4 096个8位字节。当到达表的最大尺寸时,最老的记录会被删除。
所以在HTTP/2中,每个TCP连接有一个唯一的动态表。
HPACK首部类型(可以据此判断要不要将首部添加到动态表) #
- 索引首部字段类型:类型以
1开头,对表直接查询(包括静态表和动态表),当首部名称和值都在表中时才使用该类型 - 带递增索引的字符串首部字段:类型以
01开头,当首部值不在表中,要将其添加到动态表中备后序使用时,使用此类型 - 不索引的字符串首部字段:类型以
0000开头,适用于可能会在每个请求中变化的首部,如果将其加入到动态表中会产生浪费(如 path) - 从不索引的字符串首部字段:类型以
0001开头,和前一个类似,但是这个值一定不能在任何后续的重新编码流程中被添加到动态表。
cookie是敏感数据,并且看起来正是最后这种类型(从不索引的字符串首部字段)适用的对象。不存储的缺点是,对后续请求cookie的压缩减少了。cookie会很大,而且重复,所以理想情况下,应该将它们压缩。
一般而言,Huffman编码通常比ASCII编码更高效。有一部分原因是,虽然ASCII编码只需要7位,但它使用完整的8位字节,因此每个ASCII编码都浪费1位。Huffman编码可以是变长的,因此理论上不会浪费。然而,在这种变长编码中,较少使用的字符所占空间多于8位,所以如果都是较少使用的字符,反而使用ASCII编码更高效。按照定义,这些字符应该很少被用到(假设HPACK Huffman编码表反映了实际使用情况)。最后,从静态或动态表中查找总是比Huffman或ASCII格式编码更高效。