美文网首页
http2头部压缩-hpack

http2头部压缩-hpack

作者: 竹草席 | 来源:发表于2020-09-05 17:31 被阅读0次

    hpack由来:

    http2以前的头部报文都是文本形式发生,http2为了优化网络对头部报文进行压缩编码使其内容更精简,发送更少的数据加快网络传输。采用压缩算法就是hpack。

    压缩示意图

    该图说明了http2头部报文的压缩过程

    首先把头部的键值对内容根据对应的表进行转换,最后经过编码生成最终的压缩后的数据。

    hpack表

    http2中会把经常使用的头部键值对进行表格化(可以理解为数据缓存),使其可以通过索引进行数据关联。如果头部键值已经存在直接使用索引进行传输,对方便可解析对应数据内容。

    hapck表分为静态表和动态表。

    静态表

    部分静态表内容

    静态表总共61项,动态表索引从62开始

    静态表和动态表索引说明

    hapck编码

    hapck 编码使用两种原始类型: 无符号可变长度整数和八位字节表示的字符串,相应地规定了以下两种编码方式

    整数编码

    一个整数编码可以用于表示字段索引值、首部条目索引值或者字符串长度。

    一个整数编码含两部分: 一个前缀字节和可选的后跟字节序列,只有前缀字节不足以表达整数值时才需要后跟字节,前缀字节中可用比特位 N 是整数编码的一个参数

    比如下面所示的是一个 N=5 的整数编码(前三比特用于其他标识),如果我们要编码的整数值小于 2^N - 1,直接用一个前缀字节表示即可,比如 10 就用 ???01010 表示

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

    | ? | ? | ? |      Value      |

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

    如果要编码的整数值 X 大于等于 2^N - 1,前缀字节的可用比特位都设成 1,然后把 X 减去 2^N - 1 得到值 R,并用一个或多个字节序列表示 R,字节序列中每个字节的最高有效位 (msb) 用于表示是否结束,msb 设为 0 时代表是最后一个字节。具体编码看下面的伪代码和例子

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

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

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

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

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

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

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

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

    比如使用 N=5 的整数编码表示 1337:

    1337 大于 31 (2^5 - 1), 将前缀字节后五位填满 1

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

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

    154 二进制编码: 10011010, 这即是第一个后跟字节

    I = 1306 / 128 = 10, I 小于 128, 循环结束

    将 I 编码成二进制: 00001010, 这即是最后一个字节

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

    | 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

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

    解码时读取第一个字节,发现后五位 (11111) 对应的值 I 等于 31(>= 2^N - 1),说明还有后跟字节;令 M=0,继续读下一个字节 B,I = I + (B & 127) * 2^M = 31 + 26 * 1 = 57,M = M + 7 = 7,最高有效位为 1,表示字节序列未结束,B 指向下一个字节;I = I + (B & 127) * 2^M = 57 + 10 * 128 = 1337,最高有效位为 0,表示字节码结束,返回 I

    字符编码

    一个字符串可能代表 Header 条目的字段或者键值。字符编码使用字节序列表示,要么直接使用字符的八位字节码要么使用哈夫曼编码。

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

    | H |    String Length (7+)    |

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

    |  String Data (Length octets)  |

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

    H: 一个比特位表示是否使用哈夫曼编码

    String Length: 代表字节序列长度,即 String Data 的长度,使用 N=7 的整数编码方式表示

    String Data: 字符串的八位字节码序列表示,如果 H 为 0,则此处就是原字符的八位字节码表示;如果 H 为 1,则此处为原字符的哈夫曼编码

    RFC 7541 给出了一份字符的哈夫曼编码表: Huffman Code,这是基于大量 HTTP 首部数据生成的哈夫曼编码。

    当中第一列 (sym) 表示要编码的字符,最后的特殊字符 “EOS” 代表字符串结束

    第二列 (code as bits) 是二进制哈夫曼编码,向最高有效位对齐

    第三列 (code as hex) 是十六进制哈夫曼编码,向最低有效位对齐

    最后一列 (len) 代表编码长度,单位 bit

    例子:

    :authority: blog.wangriyu.wang 首部对应的编码为:

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

    41 (0100 0001) 表示字段存在索引值 1,即对应静态表中第一项 :authority

    8e (1000 1110) 最高有效位为 1 表示键值使用哈夫曼编码,000 1110 表示字节序列长度为 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’, 101010 -> ‘n’, 100110 -> ‘g’, 101100 -> ‘r’, 00110 -> ‘i’, 1111010 -> ‘y’, 101101 -> ‘u’

    解析过程示例



    hapck编码规范简要说明

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

    以 1 开始为标识,能在索引空间匹配到索引的首部会替换成这种形式,后面的 index 使用上述的整数编码方式且 N = 7。

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

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

    | 1 |        Index (7+)        |

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

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

    尚未被索引的首部有三种表示形式,第一种会添加进索引,第二种对于当前跳来说不会添加进索引,第三种绝对不被允许添加进索引

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

    以 01 开始为标识,此首部会加入到解码后的首部列表 (Header List) 中并且会把它作为新条目插入到动态索引表中

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

    | 0 | 1 |      Index (6+)      |

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

    | H |    Value Length (7+)    |

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

    | Value String (Length octets)  |

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

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

    以 0000 开始为标识,此首部会加入到解码后的首部列表中,但不会插入到动态索引表中

    Literal Header Field without Indexing — Indexed Name

    如果字段已经存在索引,但键值未被索引,则会替换成如下形式 (index 使用 N=4 的整数编码表示)

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

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

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

    | H |    Value Length (7+)    |

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

    | Value String (Length octets)  |

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

    如果字段和键值均未被索引,则会替换成如下形式。

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

    | 0 | 0 | 0 | 0 |      0      |

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

    | H |    Name Length (7+)      |

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

    |  Name String (Length octets)  |

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

    | H |    Value Length (7+)    |

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

    | Value String (Length octets)  |

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

    3.绝对不添加索引的首部 (Literal Header Field Never Indexed)

    这与上一种首部类似,只是标识为 0001,首部也是会添加进解码后的首部列表中但不会插入动态更新表。

    区别在于这类首部发出是什么格式表示,接收也是一样的格式,作用于每一跳 (hop),如果中间通过代理,代理必须原样转发不能另行编码。

    而上一种首部只是作用当前跳,通过代理后可能会被重新编码

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

    | 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

    相关文章

      网友评论

          本文标题:http2头部压缩-hpack

          本文链接:https://www.haomeiwen.com/subject/viaslttx.html