wmproxy
wmproxy
是由Rust
編寫,已實現http/https
代理,socks5
代理, 反向代理,靜態文件服務器,內網穿透,配置熱更新等, 后續將實現websocket
代理等,同時會將實現過程分享出來, 感興趣的可以一起造個輪子法
項目 ++wmproxy++
gite: https://gitee.com/tickbh/wmproxy
github: https://github.com/tickbh/wmproxy
關于HPACK相關數據的示例
長度編碼的示例,用5位的前綴示例
- 將10進行編碼,10小于2^5-1,故
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| X | X | X | 0 | 1 | 0 | 1 | 0 | 10 stored on 5 bits
+---+---+---+---+---+---+---+---+
- 將1337進行編碼
- 1337大于2^5-1,故前5位填充
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| X | X | X | 1 | 1 | 1 | 1 | 1 | 31
+---+---+---+---+---+---+---+---+
- 1337 - 31 = 1306,大于128,故需要二次填充,用1306 mod 128 = 21,首位填充1,故8位填充為,當前偏移值為7
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 1 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 26
+---+---+---+---+---+---+---+---+
- 對1301-21=1280,1280 / 128 = 10, 10 < 128,故已經完成,首位填0
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 10
+---+---+---+---+---+---+---+---+
最終的填充值:
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 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
| 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 10<128, encode(10), done
+---+---+---+---+---+---+---+---+
頭部編碼示例
在靜態列表中索引
以下待索引的值
:method: GET
十六進制表示值82
,二進制表示10000010
,表示取靜態表2的值,查表為(:method, GET)
不在列表中,但請求索引,未使用HUFFMAN
以下示例
custom-key: custom-header
十六進制表示
400a 6375 7374 6f6d 2d6b 6579 0d63 7573 | @.custom-key.cus
746f 6d2d 6865 6164 6572 | tom-header
解碼過程
40 | == 01開頭請求索引 ==
0a | name (長度 10)
6375 7374 6f6d 2d6b 6579 | custom-key
0d | value (長度 13)
6375 7374 6f6d 2d68 6561 6465 72 | custom-header| -> custom-key:| custom-header
動態表 (解碼之后):
[ 1] (占用 55) custom-key: custom-header
占用長度: 10+13+32=55
名字在列表中,但不索引,未使用HUFFMAN
以下示例
:path: /sample/path
十六進制表示
040c 2f73 616d 706c 652f 7061 7468 | ../sample/path
解碼過程
04 | == 0000開頭,請求不索引 ==| name從索引取 (idx = 4)| 值為:path
0c | value (長度12)
2f73 616d 706c 652f 7061 7468 | /sample/path| -> :path: /sample/path
永不索引,未使用HUFFMAN
以下示例
password: secret
十六進制表示
1008 7061 7373 776f 7264 0673 6563 7265 | ..password.secre
74 | t
解碼過程
10 | == 0001開頭不索引 ==
08 | name (長度8)
7061 7373 776f 7264 | password
06 | value (長度6)
7365 6372 6574 | secret| -> password: secret
完整的請求示例,不使用HUFFMAN
以下幾個示例將連接請求,后續的會用到前面的動態列表
第一次請求. 示例如下
:method: GET
:scheme: http
:path: /
:authority: www.example.com
十六進制表示
8286 8441 0f77 7777 2e65 7861 6d70 6c65 | ...A.www.example
2e63 6f6d | .com
解碼過程
82 | == Indexed - 靜態表 ==| idx = 2| -> :method: GET
86 | == Indexed - 靜態表 ==| idx = 6| -> :scheme: http
84 | == Indexed - 靜態表 ==| idx = 4| -> :path: /
41 | == 01開頭請求索引 indexed ==| Indexed name (idx = 1)| :authority
0f | Literal value (長度15)
7777 772e 6578 616d 706c 652e 636f 6d | www.example.com| -> :authority: | www.example.com
動態列表 (解碼后):
[ 1->62] (s = 57) :authority: www.example.com
列表長度: 57
第二次請求. 示例如下,新加了cache-control字段,其它和第一次一樣
:method: GET
:scheme: http
:path: /
:authority: www.example.com
cache-control: no-cache
十六進制表示
8286 84be 5808 6e6f 2d63 6163 6865 | ....X.no-cache
解碼過程
82 | == Indexed - 靜態表 ==| idx = 2| -> :method: GET
86 | == Indexed - 靜態表 ==| idx = 6| -> :scheme: http
84 | == Indexed - 靜態表 ==| idx = 4| -> :path: /
be | == Indexed - 動態表,索引值62及以上的為動態表 ==| idx = 62| -> :authority:| www.example.com
58 | == Literal indexed ==| Indexed name (idx = 24)| cache-control
08 | Literal value (8)
6e6f 2d63 6163 6865 | no-cache| -> cache-control: no-cache
動態列表 (解碼后):
[ 1->62] (s = 53) cache-control: no-cache
[ 2->63] (s = 57) :authority: www.example.com
總長度: 110
第三次請求. 示例如下
:method: GET
:scheme: https
:path: /index.html
:authority: www.example.com
custom-key: custom-value
十六進制表示
8287 85bf 400a 6375 7374 6f6d 2d6b 6579 | ....@.custom-key
0c63 7573 746f 6d2d 7661 6c75 65 | .custom-value
解碼過程
82 | == Indexed - 靜態表 ==| idx = 2| -> :method: GET
87 | == Indexed - 靜態表 ==| idx = 7| -> :scheme: https
85 | == Indexed - 靜態表 ==| idx = 5| -> :path: /index.html
bf | == Indexed - 動態表 ==| idx = 63| -> :authority:| www.example.com
40 | == Literal indexed ==
0a | Literal name (長度10)
6375 7374 6f6d 2d6b 6579 | custom-key
0c | Literal value (長度12)
6375 7374 6f6d 2d76 616c 7565 | custom-value| -> custom-key:| custom-value
動態列表 (解碼后):
[ 1->62] (s = 54) custom-key: custom-value
[ 2->63] (s = 53) cache-control: no-cache
[ 3->64] (s = 57) :authority: www.example.com
總長度: 164
完整的請求示例(和上述例子一模一樣,但是使用HUFFMAN)
以下幾個示例將連接請求,后續的會用到前面的動態列表
第一次請求. 示例如下
:method: GET
:scheme: http
:path: /
:authority: www.example.com
十六進制表示
8286 8441 8cf1 e3c2 e5f2 3a6b a0ab 90f4 | ...A......:k....
ff | .
比之前少了3字節
解碼過程
82 | == Indexed - 靜態表 ==| idx = 2| -> :method: GET
86 | == Indexed - 靜態表 ==| idx = 6| -> :scheme: http
84 | == Indexed - 靜態表 ==| idx = 4| -> :path: /
41 | == Literal indexed ==| Indexed name (idx = 1)| :authority
8c | Literal value (長度12)| Huffman encoded:
f1e3 c2e5 f23a 6ba0 ab90 f4ff | .....:k.....| Decoded:| www.example.com| -> :authority:| www.example.com
動態列表 (解碼后):
[ 1->62] (s = 57) :authority: www.example.com列表長度: 57
第二次請求. 示例如下,新加了cache-control字段,其它和第一次一樣
:method: GET
:scheme: http
:path: /
:authority: www.example.com
cache-control: no-cache
十六進制表示
8286 84be 5886 a8eb 1064 9cbf | ....X....d..
比之前少了2字節
解碼過程
82 | == Indexed - 靜態表 ==| idx = 2| -> :method: GET
86 | == Indexed - 靜態表 ==| idx = 6| -> :scheme: http
84 | == Indexed - 靜態表 ==| idx = 4| -> :path: /
be | == Indexed - 動態表 ==| idx = 62| -> :authority:| www.example.com
58 | == Literal indexed ==| Indexed name (idx = 24)| cache-control
86 | Literal value (長度6)| Huffman encoded:
a8eb 1064 9cbf | ...d..| Decoded:| no-cache| -> cache-control: no-cache
動態列表 (解碼后):
[ 1->62] (s = 53) cache-control: no-cache
[ 2->63] (s = 57) :authority: www.example.com列表長度: 110
第三次請求. 示例如下
:method: GET
:scheme: https
:path: /index.html
:authority: www.example.com
custom-key: custom-value
十六進制表示
8287 85bf 4088 25a8 49e9 5ba9 7d7f 8925 | ....@.%.I.[.}..%
a849 e95b b8e8 b4bf | .I.[....
比之前少了5字節
解碼過程
82 | == Indexed - 靜態表 ==| idx = 2| -> :method: GET
87 | == Indexed - 靜態表 ==| idx = 7| -> :scheme: https
85 | == Indexed - 靜態表 ==| idx = 5| -> :path: /index.html
bf | == Indexed - 動態表 ==| idx = 63| -> :authority:| www.example.com
40 | == Literal indexed ==
88 | Literal name (長度8)| Huffman encoded:
25a8 49e9 5ba9 7d7f | %.I.[.}.| Decoded:| custom-key
89 | Literal value (長度9)| Huffman encoded:
25a8 49e9 5bb8 e8b4 bf | %.I.[....| Decoded:| custom-value| -> custom-key:| custom-value
動態列表 (解碼后):
[ 1->62] (s = 54) custom-key: custom-value
[ 2->63] (s = 53) cache-control: no-cache
[ 3->64] (s = 57) :authority: www.example.com總長度: 164
HUFFMAN編碼在于首次如果數據較大的時候優勢會更加明顯,如果數據較小,或者在后續的時候與普通編碼命中索引時基本一致。
完整的返回示例(HUFFMAN)
HUFFMAN與普通的差別在于字符串編解碼時的差別,這里只介紹一種,并且設置
SETTINGS_HEADER_TABLE_SIZE
為256
以下幾個示例將連接請求,后續的會用到前面的動態列表
第一次返回. 示例如下
:status: 302
cache-control: private
date: Mon, 21 Oct 2013 20:13:21 GMT
location: https://www.example.com
十六進制表示
4882 6402 5885 aec3 771a 4b61 96d0 7abe | H.d.X...w.Ka..z.
9410 54d4 44a8 2005 9504 0b81 66e0 82a6 | ..T.D. .....f...
2d1b ff6e 919d 29ad 1718 63c7 8f0b 97c8 | -..n..)...c.....
e9ae 82ae 43d3 | ....C.
解碼過程
48 | == Literal indexed ==| Indexed name (idx = 8)| :status
82 | Literal value (長度2)| Huffman encoded:
6402 | d.| Decoded:| 302| -> :status: 302
58 | == Literal indexed ==| Indexed name (idx = 24)| cache-control
85 | Literal value (長度5)| Huffman encoded:
aec3 771a 4b | ..w.K| Decoded:| private| -> cache-control: private
61 | == Literal indexed ==| Indexed name (idx = 33)| date
96 | Literal value (長度22)| Huffman encoded:
d07a be94 1054 d444 a820 0595 040b 8166 | .z...T.D. .....f
e082 a62d 1bff | ...-..| Decoded:| Mon, 21 Oct 2013 20:13:21| GMT| -> date: Mon, 21 Oct 2013| 20:13:21 GMT
6e | == Literal indexed ==| Indexed name (idx = 46)| location
91 | Literal value (長度17)| Huffman encoded:
9d29 ad17 1863 c78f 0b97 c8e9 ae82 ae43 | .)...c.........C
d3 | .| Decoded:| https://www.example.com| -> location:| https://www.example.com
動態列表 (解碼后):
[ 1->62] (s = 63) location: https://www.example.com
[ 2->63] (s = 65) date: Mon, 21 Oct 2013 20:13:21 GMT
[ 3->64] (s = 52) cache-control: private
[ 4->65] (s = 42) :status: 302Table size: 222
第二次請求. 示例如下,只是狀態碼發生了變更
:status: 307
cache-control: private
date: Mon, 21 Oct 2013 20:13:21 GMT
location: https://www.example.com
十六進制表示
4883 640e ffc1 c0bf | H.d.....
解碼過程
48 | == Literal indexed ==| Indexed name (idx = 8)| :status
83 | Literal value (長度3)| Huffman encoded:
640e ff | d..| Decoded:| 307| - evict: :status: 302| -> :status: 307
c1 | == Indexed - Add ==| idx = 65| -> cache-control: private
c0 | == Indexed - Add ==| idx = 64| -> date: Mon, 21 Oct 2013| 20:13:21 GMT
bf | == Indexed - Add ==| idx = 63| -> location:| https://www.example.com
動態列表 (解碼后):
[ 1->62] (s = 42) :status: 307
[ 2->63] (s = 63) location: https://www.example.com
[ 3->64] (s = 65) date: Mon, 21 Oct 2013 20:13:21 GMT
[ 4->65] (s = 52) cache-control: privateTable size: 222
由于(:status, 302)的長度為42,且42+222=264>256,所以舍棄最大值
第三次請求. 示例如下
:status: 200
cache-control: private
date: Mon, 21 Oct 2013 20:13:22 GMT
location: https://www.example.com
content-encoding: gzip
set-cookie: foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1
十六進制表示
88c1 6196 d07a be94 1054 d444 a820 0595 | ..a..z...T.D. ..
040b 8166 e084 a62d 1bff c05a 839b d9ab | ...f...-...Z....
77ad 94e7 821d d7f2 e6c7 b335 dfdf cd5b | w..........5...[
3960 d5af 2708 7f36 72c1 ab27 0fb5 291f | 9`..'..6r..'..).
9587 3160 65c0 03ed 4ee5 b106 3d50 07 | ..1`e...N...=P.
比之前少了5字節
解碼過程
88 | == Indexed - 靜態表 ==| idx = 8| -> :status: 200
c1 | == Indexed - 動態表 ==| idx = 65| -> cache-control: private
61 | == Literal indexed ==| Indexed name (idx = 33)| date
96 | Literal value (長度22)| Huffman encoded:
d07a be94 1054 d444 a820 0595 040b 8166 | .z...T.D. .....f
e084 a62d 1bff | ...-..| Decoded:| Mon, 21 Oct 2013 20:13:22| GMT| - evict: cache-control:| private| -> date: Mon, 21 Oct 2013 | 20:13:22 GMT
c0 | == Indexed - Add ==| idx = 64| -> location:| https://www.example.com
5a | == Literal indexed ==| Indexed name (idx = 26)| content-encoding
83 | Literal value (長度3)| Huffman encoded:
9bd9 ab | ...| Decoded:| gzip| - evict: date: Mon, 21 Oct| 2013 20:13:21 GMT| -> content-encoding: gzip
77 | == Literal indexed ==| Indexed name (idx = 55)| set-cookie
ad | Literal value (長度45)| Huffman encoded:
94e7 821d d7f2 e6c7 b335 dfdf cd5b 3960 | .........5...[9`
d5af 2708 7f36 72c1 ab27 0fb5 291f 9587 | ..'..6r..'..)...
3160 65c0 03ed 4ee5 b106 3d50 07 | 1`e...N...=P.| Decoded:| foo=ASDJKHQKBZXOQWEOPIUAXQ| WEOIU; max-age=3600; versi| on=1| - evict: location:| https://www.example.com| - evict: :status: 307| -> set-cookie: foo=ASDJKHQ| KBZXOQWEOPIUAXQWEOIU; ma| x-age=3600; version=1
動態列表 (解碼后):
[ 1->62] (s = 98) set-cookie: foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU;max-age=3600; version=1
[ 2->63] (s = 52) content-encoding: gzip
[ 3->64] (s = 65) date: Mon, 21 Oct 2013 20:13:22 GMT
總長度: 215
動態列表保留著一個最大的緩存大小值,每一個鍵值對的計算為name的字節數+value的字節數+32為確定的大小值。超出大小部分則丟棄不緩存,默認大小為4096。
總結
HPACK管理著HTTP2的頭部的協議部分,有著高壓縮比和重復請求的高復用性,雙方編碼解碼需要各自維持一份動態表,動態根據處理數據來動態拓展,保證雙方維持的表一模一樣。從而保證ID索引不會亂。Huffman編碼把頭里面需要用到字符串的數據進行進一步的壓縮,相對來說整個過程復雜度比HTTP1高很多,但相對的對使用者完全透明,在不影響其使用的情況下提高傳輸效率,并減少帶寬的使用量。