http2頭部壓縮-hpack

hpack由來:

http2以前的頭部報(bào)文都是文本形式發(fā)生,http2為了優(yōu)化網(wǎng)絡(luò)對(duì)頭部報(bào)文進(jìn)行壓縮編碼使其內(nèi)容更精簡,發(fā)送更少的數(shù)據(jù)加快網(wǎng)絡(luò)傳輸。采用壓縮算法就是hpack。


壓縮示意圖

該圖說明了http2頭部報(bào)文的壓縮過程

首先把頭部的鍵值對(duì)內(nèi)容根據(jù)對(duì)應(yīng)的表進(jìn)行轉(zhuǎn)換,最后經(jīng)過編碼生成最終的壓縮后的數(shù)據(jù)。

hpack表

http2中會(huì)把經(jīng)常使用的頭部鍵值對(duì)進(jìn)行表格化(可以理解為數(shù)據(jù)緩存),使其可以通過索引進(jìn)行數(shù)據(jù)關(guān)聯(lián)。如果頭部鍵值已經(jīng)存在直接使用索引進(jìn)行傳輸,對(duì)方便可解析對(duì)應(yīng)數(shù)據(jù)內(nèi)容。

hapck表分為靜態(tài)表和動(dòng)態(tài)表。

靜態(tài)表

部分靜態(tài)表內(nèi)容

靜態(tài)表總共61項(xiàng),動(dòng)態(tài)表索引從62開始


靜態(tài)表和動(dòng)態(tài)表索引說明

hapck編碼

hapck 編碼使用兩種原始類型: 無符號(hào)可變長度整數(shù)和八位字節(jié)表示的字符串,相應(yīng)地規(guī)定了以下兩種編碼方式

整數(shù)編碼

一個(gè)整數(shù)編碼可以用于表示字段索引值、首部條目索引值或者字符串長度。

一個(gè)整數(shù)編碼含兩部分: 一個(gè)前綴字節(jié)和可選的后跟字節(jié)序列,只有前綴字節(jié)不足以表達(dá)整數(shù)值時(shí)才需要后跟字節(jié),前綴字節(jié)中可用比特位 N 是整數(shù)編碼的一個(gè)參數(shù)

比如下面所示的是一個(gè) N=5 的整數(shù)編碼(前三比特用于其他標(biāo)識(shí)),如果我們要編碼的整數(shù)值小于 2^N - 1,直接用一個(gè)前綴字節(jié)表示即可,比如 10 就用????01010?表示

+---+---+---+---+---+---+---+---+

| ? | ? | ? |? ? ? Value? ? ? |

+---+---+---+-------------------+

如果要編碼的整數(shù)值 X 大于等于 2^N - 1,前綴字節(jié)的可用比特位都設(shè)成 1,然后把 X 減去 2^N - 1 得到值 R,并用一個(gè)或多個(gè)字節(jié)序列表示 R,字節(jié)序列中每個(gè)字節(jié)的最高有效位 (msb) 用于表示是否結(jié)束,msb 設(shè)為 0 時(shí)代表是最后一個(gè)字節(jié)。具體編碼看下面的偽代碼和例子

+---+---+---+---+---+---+---+---+

| ? | ? | ? | 1? 1? 1? 1? 1 |

+---+---+---+-------------------+

| 1 |? ? Value-(2^N-1) LSB? ? ? |

+---+---------------------------+

+---+---------------------------+

| 0 |? ? Value-(2^N-1) MSB? ? ? |

+---+---------------------------+

比如使用 N=5 的整數(shù)編碼表示 1337:

1337 大于 31 (2^5 - 1), 將前綴字節(jié)后五位填滿 1

I = 1337 - (2^5 - 1) = 1306

I 仍然大于 128, I % 128 = 26, 26 + 128 = 154

154 二進(jìn)制編碼: 10011010, 這即是第一個(gè)后跟字節(jié)

I = 1306 / 128 = 10, I 小于 128, 循環(huán)結(jié)束

將 I 編碼成二進(jìn)制: 00001010, 這即是最后一個(gè)字節(jié)

+---+---+---+---+---+---+---+---+

| 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=10

| 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 |? 10 < 128, encode(10), done

+---+---+---+---+---+---+---+---+

解碼時(shí)讀取第一個(gè)字節(jié),發(fā)現(xiàn)后五位 (11111) 對(duì)應(yīng)的值 I 等于 31(>= 2^N - 1),說明還有后跟字節(jié);令 M=0,繼續(xù)讀下一個(gè)字節(jié) B,I = I + (B & 127) * 2^M = 31 + 26 * 1 = 57,M = M + 7 = 7,最高有效位為 1,表示字節(jié)序列未結(jié)束,B 指向下一個(gè)字節(jié);I = I + (B & 127) * 2^M = 57 + 10 * 128 = 1337,最高有效位為 0,表示字節(jié)碼結(jié)束,返回 I

字符編碼

一個(gè)字符串可能代表 Header 條目的字段或者鍵值。字符編碼使用字節(jié)序列表示,要么直接使用字符的八位字節(jié)碼要么使用哈夫曼編碼。

+---+---+---+---+---+---+---+---+

| H |? ? String Length (7+)? ? |

+---+---------------------------+

|? String Data (Length octets)? |

+-------------------------------+

H: 一個(gè)比特位表示是否使用哈夫曼編碼

String Length: 代表字節(jié)序列長度,即 String Data 的長度,使用 N=7 的整數(shù)編碼方式表示

String Data: 字符串的八位字節(jié)碼序列表示,如果 H 為 0,則此處就是原字符的八位字節(jié)碼表示;如果 H 為 1,則此處為原字符的哈夫曼編碼

RFC 7541 給出了一份字符的哈夫曼編碼表:?Huffman Code,這是基于大量 HTTP 首部數(shù)據(jù)生成的哈夫曼編碼。

當(dāng)中第一列 (sym) 表示要編碼的字符,最后的特殊字符 “EOS” 代表字符串結(jié)束

第二列 (code as bits) 是二進(jìn)制哈夫曼編碼,向最高有效位對(duì)齊

第三列 (code as hex) 是十六進(jìn)制哈夫曼編碼,向最低有效位對(duì)齊

最后一列 (len) 代表編碼長度,單位 bit

例子:

:authority: blog.wangriyu.wang?首部對(duì)應(yīng)的編碼為:

41 8e 8e 83 cc bf 81 d5 35 86 f5 6a fe 07 54 df

41 (0100 0001) 表示字段存在索引值 1,即對(duì)應(yīng)靜態(tài)表中第一項(xiàng) :authority

8e (1000 1110) 最高有效位為 1 表示鍵值使用哈夫曼編碼,000 1110 表示字節(jié)序列長度為 14

后面?8e 83 cc bf 81 d5 35 86 f5 6a fe 07 54 df?是一段哈夫曼編碼序列

由哈夫曼編碼表可知 100011 -> ‘b’, 101000 -> ‘l’, 00111 -> ‘o’, 100110 -> ‘g’, 010111 -> ‘.’, 1111000 -> ‘w’, 00011 -> ‘a(chǎn)’, 101010 -> ‘n’, 100110 -> ‘g’, 101100 -> ‘r’, 00110 -> ‘i’, 1111010 -> ‘y’, 101101 -> ‘u’


解析過程示例



hapck編碼規(guī)范簡要說明

已索引首部條目表示 (Indexed Header Field Representation)

以 1 開始為標(biāo)識(shí),能在索引空間匹配到索引的首部會(huì)替換成這種形式,后面的 index 使用上述的整數(shù)編碼方式且 N = 7。

比如?:method: GET?可以用 0x82,即 10000010 表示

+---+---+---+---+---+---+---+---+

| 1 |? ? ? ? Index (7+)? ? ? ? |

+---+---------------------------+

未索引文字首部條目表示 (Literal Header Field Representation)

尚未被索引的首部有三種表示形式,第一種會(huì)添加進(jìn)索引,第二種對(duì)于當(dāng)前跳來說不會(huì)添加進(jìn)索引,第三種絕對(duì)不被允許添加進(jìn)索引

1.會(huì)添加索引的文字首部 (Literal Header Field with Incremental Indexing)

以 01 開始為標(biāo)識(shí),此首部會(huì)加入到解碼后的首部列表 (Header List) 中并且會(huì)把它作為新條目插入到動(dòng)態(tài)索引表中

+---+---+---+---+---+---+---+---+

| 0 | 1 |? ? ? Index (6+)? ? ? |

+---+---+-----------------------+

| H |? ? Value Length (7+)? ? |

+---+---------------------------+

| Value String (Length octets)? |

+-------------------------------+

2.不添加索引的首部 (Literal Header Field without Indexing)

以 0000 開始為標(biāo)識(shí),此首部會(huì)加入到解碼后的首部列表中,但不會(huì)插入到動(dòng)態(tài)索引表中

Literal Header Field without Indexing — Indexed Name

如果字段已經(jīng)存在索引,但鍵值未被索引,則會(huì)替換成如下形式 (index 使用 N=4 的整數(shù)編碼表示)

+---+---+---+---+---+---+---+---+

| 0 | 0 | 0 | 0 |? Index (4+)? |

+---+---+-----------------------+

| H |? ? Value Length (7+)? ? |

+---+---------------------------+

| Value String (Length octets)? |

+-------------------------------+

如果字段和鍵值均未被索引,則會(huì)替換成如下形式。

+---+---+---+---+---+---+---+---+

| 0 | 0 | 0 | 0 |? ? ? 0? ? ? |

+---+---+-----------------------+

| H |? ? Name Length (7+)? ? ? |

+---+---------------------------+

|? Name String (Length octets)? |

+---+---------------------------+

| H |? ? Value Length (7+)? ? |

+---+---------------------------+

| Value String (Length octets)? |

+-------------------------------+

3.絕對(duì)不添加索引的首部 (Literal Header Field Never Indexed)

這與上一種首部類似,只是標(biāo)識(shí)為 0001,首部也是會(huì)添加進(jìn)解碼后的首部列表中但不會(huì)插入動(dòng)態(tài)更新表。

區(qū)別在于這類首部發(fā)出是什么格式表示,接收也是一樣的格式,作用于每一跳 (hop),如果中間通過代理,代理必須原樣轉(zhuǎn)發(fā)不能另行編碼。

而上一種首部只是作用當(dāng)前跳,通過代理后可能會(huì)被重新編碼

+---+---+---+---+---+---+---+---+

| 0 | 0 | 0 | 1 |? Index (4+)? |

+---+---+-----------------------+

| H |? ? Value Length (7+)? ? |

+---+---------------------------+

| Value String (Length octets)? |

+-------------------------------+


參考鏈接:

HTTP2 詳解?

Wangriyu’s Blog?Halfrost-Field/HTTP:2_Header-Compression.md at master · halfrost/Halfrost-Field · GitHub

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容