HTTP头部压缩(HPACK)算法详解
字数 2210 2025-12-15 08:30:11
HTTP头部压缩(HPACK)算法详解
一、问题描述
HTTP/2 通过压缩请求和响应头部来减少网络开销,其压缩算法称为 HPACK。该算法旨在解决早期 SPDY 协议中因动态压缩导致的安全漏洞(如 CRIME 攻击),同时提升压缩效率。问题核心在于:HPACK 如何通过静态表、动态表和霍夫曼编码来压缩字段,以及其工作流程是怎样的?
二、知识点拆解
-
为什么需要头部压缩?
HTTP/1.x 的头部以纯文本形式传输,且每个请求会重复发送大量相同字段(如 User-Agent、Cookie),导致带宽浪费。例如,一个包含 10 个请求的页面可能重复传输数千字节的冗余头部。 -
HPACK 的设计目标
- 安全性:避免因压缩泄露敏感信息(如 Cookie 被猜测)。
- 高效性:利用静态表和动态表减少冗余。
- 单向性:仅需一端(如客户端)维护编解码上下文,避免同步问题。
三、HPACK 的三大核心组件
-
静态表(Static Table)
- 预定义了 61 个常见头部字段及其值(如
:method: GET、:status: 200)。 - 每个条目有固定索引(1~61),编码时直接用索引代替完整字段,例如索引
2代表:method: GET。 - 静态表在编码器和解码器中预先存储,无需传输。
- 预定义了 61 个常见头部字段及其值(如
-
动态表(Dynamic Table)
- 用于缓存当前连接中发送过的自定义头部字段(如自定义 Cookie)。
- 表大小有限制,由解码端通过
SETTINGS_HEADER_TABLE_SIZE协商(默认 4096 字节)。 - 新头部字段会被添加到动态表头部,旧条目可能被淘汰(FIFO 机制)。
- 动态表索引从 62 开始(静态表占 1~61)。
-
霍夫曼编码(Huffman Coding)
- 对头部值(如 URL 路径、Cookie 值)进行高频字符优化编码,进一步压缩体积。
- 使用预定义的霍夫曼码表,字符出现频率越高,编码越短。
- 编码时可选原始字符串或霍夫曼编码,由标志位决定。
四、HPACK 编码过程详解
假设客户端发送请求:
GET /index.html HTTP/2
User-Agent: MyBrowser
Cookie: session=abc123
步骤 1:处理 :method: GET
- 查询静态表,发现索引
2对应:method: GET。 - 直接编码为 索引表示形式:二进制
10000010(前缀 1 表示完整匹配)。 - 仅用 1 字节代替原始字段。
步骤 2:处理 :path: /index.html
- 静态表中无完整匹配,但
:path字段名在索引4(值不同)。 - 采用 字面编码-增量索引:
a. 发送索引4表示字段名。
b. 对值/index.html使用霍夫曼编码(假设压缩后 12 字节)。
c. 添加头部:01(前缀表示“增量索引”)。 - 解码端收到后,会将
:path: /index.html加入动态表(假设分配索引 62)。
步骤 3:处理 User-Agent: MyBrowser
- 字段名
User-Agent在静态表索引58(值不匹配)。 - 采用 字面编码-不索引(敏感字段如 Cookie 适用):
a. 发送0000(前缀表示“不索引”)。
b. 字段名用霍夫曼编码。
c. 值MyBrowser用霍夫曼编码。 - 此条目不加入动态表,避免泄露隐私。
步骤 4:处理 Cookie: session=abc123
- 类似步骤 3,但可能用 字面编码-从不索引(特殊标志保护敏感数据)。
五、动态表更新与同步机制
-
添加新条目
- 编码端发送“增量索引”字面值时,解码端将其插入动态表头部。
- 示例:动态表原为
[索引62: /index.html],新增User-Agent: MyBrowser后变为:[索引63: User-Agent: MyBrowser] [索引62: /index.html]
-
表大小管理
- 每个条目占用空间 = 字段名长度 + 值长度 + 32 字节(开销)。
- 若新增条目导致超出
SETTINGS_HEADER_TABLE_SIZE,则淘汰最旧条目(动态表尾部)。 - 编码端可通过
Dynamic Table Size Update指令调整最大表大小。
六、安全性与性能优势
-
防御 CRIME 攻击
- HPACK 压缩时,编码器不依赖未知数据调整输出,攻击者无法通过猜测内容观察压缩比变化。
- 敏感字段用“不索引”或“从不索引”避免加入动态表。
-
典型压缩率
- 静态表覆盖 40%~60% 常见字段(如方法、状态码、常见头部名)。
- 动态表针对同一连接重复字段压缩率近 90%。
- 霍夫曼编码可额外减少 20%~30% 体积。
七、示例对比:HTTP/1.1 vs HPACK 编码
原始请求(约 120 字节):
GET /index.html HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0
Accept: text/html
HPACK 编码后(约 40 字节):
:method: GET→ 索引2(1字节):path: /index.html→ 增量索引+霍夫曼编码(约 12 字节)Host→ 索引38+ 值霍夫曼编码(约 8 字节)- 其他字段类似处理。
八、关键注意事项
- 动态表按连接独立维护,不同连接不共享。
- 服务器推送响应头部时,使用自身动态表编码,客户端用相同表解码。
- 编码端需跟踪解码端动态表状态,否则可能因索引不一致导致解码错误。
通过以上步骤,HPACK 在安全的前提下实现了高效的头部压缩,成为 HTTP/2 性能提升的关键。