9. 用Rust手把手编写一个wmproxy(代理,内网穿透等), HTTP2改造篇之HPACK示 ...

打印 上一主题 下一主题

主题 913|帖子 913|积分 2739

9. 用Rust手把手编写一个wmproxy(代理,内网穿透等), HTTP2改造篇之HPACK示例, 了解http2头信息如何处理

项目 ++wmproxy++

gite: https://gitee.com/tickbh/wmproxy
github: https://github.com/tickbh/wmproxy
关于HPACK相关数据的示例

长度编码的示例,用5位的前缀示例


  • 将10进行编码,10小于2^5-1,故
  1.   0   1   2   3   4   5   6   7
  2. +---+---+---+---+---+---+---+---+
  3. | X | X | X | 0 | 1 | 0 | 1 | 0 |   10 stored on 5 bits
  4. +---+---+---+---+---+---+---+---+
复制代码

  • 将1337进行编码

  • 1337大于2^5-1,故前5位填充
  1.   0   1   2   3   4   5   6   7
  2. +---+---+---+---+---+---+---+---+
  3. | X | X | X | 1 | 1 | 1 | 1 | 1 |  31
  4. +---+---+---+---+---+---+---+---+
复制代码

  • 1337 - 31 = 1306,大于128,故需要二次填充,用1306 mod 128 = 21,首位填充1,故8位填充为,当前偏移值为7
  1.   0   1   2   3   4   5   6   7
  2. +---+---+---+---+---+---+---+---+
  3. | 1 | 0 | 0 | 1 | 1 | 0 | 1 | 0 |  26
  4. +---+---+---+---+---+---+---+---+
复制代码

  • 对1301-21=1280,1280 / 128 = 10, 10 < 128,故已经完成,首位填0
  1.   0   1   2   3   4   5   6   7
  2. +---+---+---+---+---+---+---+---+
  3. | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 |  10
  4. +---+---+---+---+---+---+---+---+
复制代码
最终的填充值:
  1.   0   1   2   3   4   5   6   7
  2. +---+---+---+---+---+---+---+---+
  3. | X | X | X | 1 | 1 | 1 | 1 | 1 |  Prefix = 31, I = 1306
  4. | 1 | 0 | 0 | 1 | 1 | 0 | 1 | 0 |  1306>=128, encode(154), I=1306/128
  5. | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 |  10<128, encode(10), done
  6. +---+---+---+---+---+---+---+---+
复制代码
动态表 (解码之后):
[  1] (占用  55) custom-key: custom-header
占用长度:  10+13+32=55
名字在列表中,但不索引,未使用HUFFMAN

以下示例
  1. :method: GET
复制代码
十六进制表示
  1. custom-key: custom-header
复制代码
解码过程
  1. 04                                      | == 0000开头,请求不索引 ==                                        |   name从索引取 (idx = 4)                                        |   值为:path0c                                      |   value (长度12)2f73 616d 706c 652f 7061 7468           | /sample/path                                        | -> :method: GET
复制代码
永不索引,未使用HUFFMAN

以下示例
  1. 40                                      | == 01开头请求索引 ==
  2. 0a                                      |   name (长度 10)
  3. 6375 7374 6f6d 2d6b 6579                | custom-key
  4. 0d                                      |   value (长度 13)
  5. 6375 7374 6f6d 2d68 6561 6465 72        | custom-header
  6.                                         | -> custom-key:
  7.                                         |   custom-header
复制代码
十六进制表示
  1. :path: /sample/path
复制代码
解码过程
  1. 10                                      | == 0001开头不索引 ==08                                      |   name (长度8)7061 7373 776f 7264                     | password06                                      |   value (长度6)7365 6372 6574                          | secret                                        | -> 40                                      | == 01开头请求索引 ==
  2. 0a                                      |   name (长度 10)
  3. 6375 7374 6f6d 2d6b 6579                | custom-key
  4. 0d                                      |   value (长度 13)
  5. 6375 7374 6f6d 2d68 6561 6465 72        | custom-header
  6.                                         | -> custom-key:
  7.                                         |   custom-header
复制代码
完整的请求示例,不使用HUFFMAN

以下几个示例将连接请求,后续的会用到前面的动态列表
第一次请求. 示例如下
  1. 04                                      | == 0000开头,请求不索引 ==
  2.                                         |   name从索引取 (idx = 4)
  3.                                         |   值为:path
  4. 0c                                      |   value (长度12)
  5. 2f73 616d 706c 652f 7061 7468           | /sample/path
  6.                                         | -> :path: /sample/path
复制代码
十六进制表示
  1. password: secret
复制代码
解码过程
  1. 1008 7061 7373 776f 7264 0673 6563 7265 | ..password.secre
  2. 74                                      | t
复制代码
动态列表 (解码后):
[  1->62] (s =  57) :authority: www.example.com
列表长度:  57
第二次请求. 示例如下,新加了cache-control字段,其它和第一次一样
  1. 04                                      | == 0000开头,请求不索引 ==
  2.                                         |   name从索引取 (idx = 4)
  3.                                         |   值为:path
  4. 0c                                      |   value (长度12)
  5. 2f73 616d 706c 652f 7061 7468           | /sample/path
  6.                                         | -> :path: /sample/pathcache-control: no-cache
复制代码
十六进制表示
  1. :method: GET
  2. :scheme: http
  3. :path: /
  4. :authority: www.example.com
复制代码
解码过程
  1. 8286 8441 0f77 7777 2e65 7861 6d70 6c65 | ...A.www.example
  2. 2e63 6f6d                               | .com
复制代码
动态列表 (解码后):
[  1->62] (s =  53) cache-control: no-cache
[  2->63] (s =  57) :authority: www.example.com
总长度: 110
第三次请求. 示例如下
  1. 82                                      | == Indexed - 静态表 ==
  2.                                         |   idx = 2
  3.                                         | -> :method: GET
  4. 86                                      | == Indexed - 静态表 ==
  5.                                         |   idx = 6
  6.                                         | -> :scheme: http
  7. 84                                      | == Indexed - 静态表 ==
  8.                                         |   idx = 4
  9.                                         | -> :path: /
  10. 41                                      | == 01开头请求索引 indexed ==
  11.                                         |   Indexed name (idx = 1)
  12.                                         |     :authority
  13. 0f                                      |   Literal value (长度15)
  14. 7777 772e 6578 616d 706c 652e 636f 6d   | www.example.com
  15.                                         | -> :authority:
  16.                                         |   www.example.com
复制代码
十六进制表示
  1. :method: GET
  2. :scheme: http
  3. :path: /
  4. :authority: www.example.com
  5. cache-control: no-cache
复制代码
解码过程
  1. 8286 84be 5808 6e6f 2d63 6163 6865      | ....X.no-cache
复制代码
动态列表 (解码后):
[  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)

以下几个示例将连接请求,后续的会用到前面的动态列表
第一次请求. 示例如下
  1. 04                                      | == 0000开头,请求不索引 ==
  2.                                         |   name从索引取 (idx = 4)
  3.                                         |   值为:path
  4. 0c                                      |   value (长度12)
  5. 2f73 616d 706c 652f 7061 7468           | /sample/path
  6.                                         | -> :path: /sample/path
复制代码
十六进制表示
  1. :method: GET
  2. :scheme: https
  3. :path: /index.html
  4. :authority: www.example.com
  5. custom-key: custom-value
复制代码
比之前少了3字节
解码过程
  1. 8287 85bf 400a 6375 7374 6f6d 2d6b 6579 | ....@.custom-key
  2. 0c63 7573 746f 6d2d 7661 6c75 65        | .custom-value
复制代码
动态列表 (解码后):
  1. 82                                      | == Indexed - 静态表 ==
  2.                                         |   idx = 2
  3.                                         | -> :method: GET
  4. 87                                      | == Indexed - 静态表 ==
  5.                                         |   idx = 7
  6.                                         | -> :scheme: https
  7. 85                                      | == Indexed - 静态表 ==
  8.                                         |   idx = 5
  9.                                         | -> :path: /index.html
  10. bf                                      | == Indexed - 动态表 ==
  11.                                         |   idx = 63
  12.                                         | -> :authority:
  13.                                         |   www.example.com
  14. 40                                      | == Literal indexed ==
  15. 0a                                      |   Literal name (长度10)
  16. 6375 7374 6f6d 2d6b 6579                | custom-key
  17. 0c                                      |   Literal value (长度12)
  18. 6375 7374 6f6d 2d76 616c 7565           | custom-value
  19.                                         | -> custom-key:
  20.                                         |   custom-value
复制代码
第二次请求. 示例如下,新加了cache-control字段,其它和第一次一样
  1. 04                                      | == 0000开头,请求不索引 ==
  2.                                         |   name从索引取 (idx = 4)
  3.                                         |   值为:path
  4. 0c                                      |   value (长度12)
  5. 2f73 616d 706c 652f 7061 7468           | /sample/path
  6.                                         | -> :path: /sample/pathcache-control: no-cache
复制代码
十六进制表示
  1. 8286 8441 8cf1 e3c2 e5f2 3a6b a0ab 90f4 | ...A......:k....
  2. ff                                      | .
复制代码
比之前少了2字节
解码过程
  1. 82                                      | == Indexed - 静态表 ==
  2.                                         |   idx = 2
  3.                                         | -> :method: GET
  4. 86                                      | == Indexed - 静态表 ==
  5.                                         |   idx = 6
  6.                                         | -> :scheme: http
  7. 84                                      | == Indexed - 静态表 ==
  8.                                         |   idx = 4
  9.                                         | -> :path: /
  10. 41                                      | == Literal indexed ==
  11.                                         |   Indexed name (idx = 1)
  12.                                         |     :authority
  13. 8c                                      |   Literal value (长度12)
  14.                                         |     Huffman encoded:
  15. f1e3 c2e5 f23a 6ba0 ab90 f4ff           | .....:k.....
  16.                                         |     Decoded:
  17.                                         | www.example.com
  18.                                         | -> :authority:
  19.                                         |   www.example.com
复制代码
动态列表 (解码后):
  1. [  1->62] (s =  57) :authority: www.example.com
  2.       列表长度:  57
复制代码
第三次请求. 示例如下
  1. 82                                      | == Indexed - 静态表 ==
  2.                                         |   idx = 2
  3.                                         | -> :method: GET
  4. 86                                      | == Indexed - 静态表 ==
  5.                                         |   idx = 6
  6.                                         | -> :scheme: http
  7. 84                                      | == Indexed - 静态表 ==
  8.                                         |   idx = 4
  9.                                         | -> :path: /
  10. 41                                      | == 01开头请求索引 indexed ==
  11.                                         |   Indexed name (idx = 1)
  12.                                         |     :authority
  13. 0f                                      |   Literal value (长度15)
  14. 7777 772e 6578 616d 706c 652e 636f 6d   | www.example.com
  15.                                         | -> :authority:
  16.                                         |   www.example.com
复制代码
十六进制表示
  1. 8286 84be 5886 a8eb 1064 9cbf           | ....X....d..
复制代码
比之前少了5字节
解码过程
  1. 82                                      | == Indexed - 静态表 ==
  2.                                         |   idx = 2
  3.                                         | -> :method: GET
  4. 86                                      | == Indexed - 静态表 ==
  5.                                         |   idx = 6
  6.                                         | -> :scheme: http
  7. 84                                      | == Indexed - 静态表 ==
  8.                                         |   idx = 4
  9.                                         | -> :path: /
  10. be                                      | == Indexed - 动态表 ==
  11.                                         |   idx = 62
  12.                                         | -> :authority:
  13.                                         |   www.example.com
  14. 58                                      | == Literal indexed ==
  15.                                         |   Indexed name (idx = 24)
  16.                                         |     cache-control
  17. 86                                      |   Literal value (长度6)
  18.                                         |     Huffman encoded:
  19. a8eb 1064 9cbf                          | ...d..
  20.                                         |     Decoded:
  21.                                         | no-cache
  22.                                         | -> cache-control: no-cache
复制代码
动态列表 (解码后):
  1. [  1->62] (s =  53) cache-control: no-cache
  2. [  2->63] (s =  57) :authority: www.example.com
  3.       列表长度: 110
复制代码
HUFFMAN编码在于首次如果数据较大的时候优势会更加明显,如果数据较小,或者在后续的时候与普通编码命中索引时基本一致。
完整的返回示例(HUFFMAN)

HUFFMAN与普通的差别在于字符串编解码时的差别,这里只介绍一种,并且设置SETTINGS_HEADER_TABLE_SIZE为256
以下几个示例将连接请求,后续的会用到前面的动态列表
第一次返回. 示例如下
  1. :method: GET
  2. :scheme: https
  3. :path: /index.html
  4. :authority: www.example.com
  5. custom-key: custom-value
复制代码
十六进制表示
  1. 8287 85bf 4088 25a8 49e9 5ba9 7d7f 8925 | ....@.%.I.[.}..%
  2. a849 e95b b8e8 b4bf                     | .I.[....
复制代码
解码过程
  1. 82                                      | == Indexed - 静态表 ==
  2.                                         |   idx = 2
  3.                                         | -> :method: GET
  4. 87                                      | == Indexed - 静态表 ==
  5.                                         |   idx = 7
  6.                                         | -> :scheme: https
  7. 85                                      | == Indexed - 静态表 ==
  8.                                         |   idx = 5
  9.                                         | -> :path: /index.html
  10. bf                                      | == Indexed - 动态表 ==
  11.                                         |   idx = 63
  12.                                         | -> :authority:
  13.                                         |   www.example.com
  14. 40                                      | == Literal indexed ==
  15. 88                                      |   Literal name (长度8)
  16.                                         |     Huffman encoded:
  17. 25a8 49e9 5ba9 7d7f                     | %.I.[.}.
  18.                                         |     Decoded:
  19.                                         | custom-key
  20. 89                                      |   Literal value (长度9)
  21.                                         |     Huffman encoded:
  22. 25a8 49e9 5bb8 e8b4 bf                  | %.I.[....
  23.                                         |     Decoded:
  24.                                         | custom-value
  25.                                         | -> custom-key:
  26.                                         |   custom-value
复制代码
动态列表 (解码后):
  1. [  1->62] (s =  54) custom-key: custom-value
  2. [  2->63] (s =  53) cache-control: no-cache
  3. [  3->64] (s =  57) :authority: www.example.com
  4.       总长度: 164
复制代码
第二次请求. 示例如下,只是状态码发生了变更
  1. :status: 302
  2. cache-control: private
  3. date: Mon, 21 Oct 2013 20:13:21 GMT
  4. location: https://www.example.com
复制代码
十六进制表示
  1. 4882 6402 5885 aec3 771a 4b61 96d0 7abe | H.d.X...w.Ka..z.
  2. 9410 54d4 44a8 2005 9504 0b81 66e0 82a6 | ..T.D. .....f...
  3. 2d1b ff6e 919d 29ad 1718 63c7 8f0b 97c8 | -..n..)...c.....
  4. e9ae 82ae 43d3                          | ....C.
复制代码
解码过程
  1. 48                                      | == Literal indexed ==
  2.                                         |   Indexed name (idx = 8)
  3.                                         |     :status
  4. 82                                      |   Literal value (长度2)
  5.                                         |     Huffman encoded:
  6. 6402                                    | d.
  7.                                         |     Decoded:
  8.                                         | 302
  9.                                         | -> :status: 302
  10. 58                                      | == Literal indexed ==
  11.                                         |   Indexed name (idx = 24)
  12.                                         |     cache-control
  13. 85                                      |   Literal value (长度5)
  14.                                         |     Huffman encoded:
  15. aec3 771a 4b                            | ..w.K
  16.                                         |     Decoded:
  17.                                         | private
  18.                                         | -> cache-control: private
  19. 61                                      | == Literal indexed ==
  20.                                         |   Indexed name (idx = 33)
  21.                                         |     date
  22. 96                                      |   Literal value (长度22)
  23.                                         |     Huffman encoded:
  24. d07a be94 1054 d444 a820 0595 040b 8166 | .z...T.D. .....f
  25. e082 a62d 1bff                          | ...-..
  26.                                         |     Decoded:
  27.                                         | Mon, 21 Oct 2013 20:13:21
  28.                                         | GMT
  29.                                         | -> date: Mon, 21 Oct 2013
  30.                                         |   20:13:21 GMT
  31. 6e                                      | == Literal indexed ==
  32.                                         |   Indexed name (idx = 46)
  33.                                         |     location
  34. 91                                      |   Literal value (长度17)
  35.                                         |     Huffman encoded:
  36. 9d29 ad17 1863 c78f 0b97 c8e9 ae82 ae43 | .)...c.........C
  37. d3                                      | .
  38.                                         |     Decoded:
  39.                                         | https://www.example.com
  40.                                         | -> location:
  41.                                         |   https://www.example.com
复制代码
动态列表 (解码后):
  1. [  1->62] (s =  63) location: https://www.example.com
  2. [  2->63] (s =  65) date: Mon, 21 Oct 2013 20:13:21 GMT
  3. [  3->64] (s =  52) cache-control: private
  4. [  4->65] (s =  42) :status: 302
  5.       Table size: 222
复制代码
由于(:status, 302)的长度为42,且42+222=264>256,所以舍弃最大值
第三次请求. 示例如下
  1. :status: 307
  2. cache-control: private
  3. date: Mon, 21 Oct 2013 20:13:21 GMT
  4. location: https://www.example.com
复制代码
十六进制表示
  1. 4883 640e ffc1 c0bf                     | H.d.....
复制代码
比之前少了5字节
解码过程
  1. 48                                      | == Literal indexed ==
  2.                                         |   Indexed name (idx = 8)
  3.                                         |     :status
  4. 83                                      |   Literal value (长度3)
  5.                                         |     Huffman encoded:
  6. 640e ff                                 | d..
  7.                                         |     Decoded:
  8.                                         | 307
  9.                                         | - evict: :status: 302
  10.                                         | -> :status: 307
  11. c1                                      | == Indexed - Add ==
  12.                                         |   idx = 65
  13.                                         | -> cache-control: private
  14. c0                                      | == Indexed - Add ==
  15.                                         |   idx = 64
  16.                                         | -> date: Mon, 21 Oct 2013
  17.                                         |   20:13:21 GMT
  18. bf                                      | == Indexed - Add ==
  19.                                         |   idx = 63
  20.                                         | -> location:
  21.                                         |   https://www.example.com
复制代码
动态列表 (解码后):
  1. [  1->62] (s =  42) :status: 307
  2. [  2->63] (s =  63) location: https://www.example.com
  3. [  3->64] (s =  65) date: Mon, 21 Oct 2013 20:13:21 GMT
  4. [  4->65] (s =  52) cache-control: private
  5.       Table size: 222
复制代码
动态列表保留着一个最大的缓存大小值,每一个键值对的计算为name的字节数+value的字节数+32为确定的大小值。超出大小部分则丢弃不缓存,默认大小为4096。
总结

HPACK管理着HTTP2的头部的协议部分,有着高压缩比和重复请求的高复用性,双方编码解码需要各自维持一份动态表,动态根据处理数据来动态拓展,保证双方维持的表一模一样。从而保证ID索引不会乱。Huffman编码把头里面需要用到字符串的数据进行进一步的压缩,相对来说整个过程复杂度比HTTP1高很多,但相对的对使用者完全透明,在不影响其使用的情况下提高传输效率,并减少带宽的使用量。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

缠丝猫

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表