Netty对HPACK头部压缩的支持
前言
HTTP2终于支持对头部进行压缩传输了,Netty很早就支持HTTP2了,看下Netty对HPACK的实现源码,可以对HPACK理解的更深一下。
HpackDecoder
Netty内置的编解码器Http2FrameCodec专门用来对HTTP2的各种Frame进行编解码,其中就包含将ByteBuf解码为HeadersFrame,解码的工作最终交给了io.netty.handler.codec.http2.HpackDecoder。
状态
HpackDecoder维护了一组状态常量,代表的是当前对Header的读取状态,不同的状态做的事情是不一样的,HpackDecoder通过一个While循环来读取Header,因为一个Frame里面包含若干个Header,根据读取到的数据判断,State会不断变化流转,理解了这些State,再看代码就简单的多了。
| State | 说明 |
|---|---|
| READ_HEADER_REPRESENTATION | 读取header的初试状态 |
| READ_INDEXED_HEADER | 读取被索引的完整header,即name和value均被索引 |
| READ_INDEXED_HEADER_NAME | 读取被索引的header name |
| READ_LITERAL_HEADER_NAME_LENGTH_PREFIX | name未被索引,读取name长度前缀,判断是否使用哈夫曼编码 |
| READ_LITERAL_HEADER_NAME_LENGTH | name未被索引,读取name长度 |
| READ_LITERAL_HEADER_NAME | name未被索引,读取header name |
| READ_LITERAL_HEADER_VALUE_LENGTH_PREFIX | 读取value长度前缀,判断是否使用哈夫曼编码 |
| READ_LITERAL_HEADER_VALUE_LENGTH | 读取value长度 |
| READ_LITERAL_HEADER_VALUE | value未被索引,读取value |
decode()
解码的方法是io.netty.handler.codec.http2.HpackDecoder#decode(),这里直接贴出源码,核心代码已写注释:
private void decode(ByteBuf in, Http2HeadersSink sink) throws Http2Exception {int index = 0;int nameLength = 0;int valueLength = 0;byte state = READ_HEADER_REPRESENTATION;// 初始状态 准备读取Headerboolean huffmanEncoded = false;// 是否使用哈夫曼编码 长度的第1个Bit来标记AsciiString name = null;IndexType indexType = IndexType.NONE;// 索引类型while (in.isReadable()) {// 只要有数据,就循环读switch (state) {case READ_HEADER_REPRESENTATION:byte b = in.readByte();// 读取首字节if (maxDynamicTableSizeChangeRequired && (b & 0xE0) != 0x20) {// HpackEncoder MUST signal maximum dynamic table size changethrow MAX_DYNAMIC_TABLE_SIZE_CHANGE_REQUIRED;}if (b < 0) {// 小于0 即最高位是1,代表name和value均被索引// Indexed Header Fieldindex = b & 0x7F;switch (index) {case 0:throw DECODE_ILLEGAL_INDEX_VALUE;case 0x7F: // 索引号超过了1字节,需要继续读取 才能获取最终索引号state = READ_INDEXED_HEADER;break;default:// 索引号没超 直接从静态/动态表读取即可HpackHeaderField indexedHeader = getIndexedHeader(index);sink.appendToHeaderList((AsciiString) indexedHeader.name,(AsciiString) indexedHeader.value);}} else if ((b & 0x40) == 0x40) {// 01开头 header需要加入到动态表// Literal Header Field with Incremental IndexingindexType = IndexType.INCREMENTAL;index = b & 0x3F;switch (index) {case 0:// name未被索引,读取name长度 再读namestate = READ_LITERAL_HEADER_NAME_LENGTH_PREFIX;break;case 0x3F:// name 索引号超了1字节 需要继续读state = READ_INDEXED_HEADER_NAME;break;default:// name被索引,继续读valuename = readName(index);nameLength = name.length();state = READ_LITERAL_HEADER_VALUE_LENGTH_PREFIX;}} else if ((b & 0x20) == 0x20) {// Dynamic Table Size Update// See https://www.rfc-editor.org/rfc/rfc7541.html#section-4.2throw connectionError(COMPRESSION_ERROR, "Dynamic table size update must happen " +"at the beginning of the header block");} else {// Literal Header Field without Indexing / never IndexedindexType = (b & 0x10) == 0x10 ? IndexType.NEVER : IndexType.NONE;index = b & 0x0F;switch (index) {case 0:state = READ_LITERAL_HEADER_NAME_LENGTH_PREFIX;break;case 0x0F:state = READ_INDEXED_HEADER_NAME;break;default:// Index was stored as the prefixname = readName(index);nameLength = name.length();state = READ_LITERAL_HEADER_VALUE_LENGTH_PREFIX;}}break;case READ_INDEXED_HEADER:// 被索引的header,读取可变长度的索引号,从表中获取headerHpackHeaderField indexedHeader = getIndexedHeader(decodeULE128(in, index));sink.appendToHeaderList((AsciiString) indexedHeader.name,(AsciiString) indexedHeader.value);state = READ_HEADER_REPRESENTATION;break;case READ_INDEXED_HEADER_NAME:// Header Name matches an entry in the Header Tablename = readName(decodeULE128(in, index));nameLength = name.length();state = READ_LITERAL_HEADER_VALUE_LENGTH_PREFIX;break;case READ_LITERAL_HEADER_NAME_LENGTH_PREFIX:// 读取name前缀 判断是否使用哈夫曼编码b = in.readByte();huffmanEncoded = (b & 0x80) == 0x80;index = b & 0x7F;if (index == 0x7f) {state = READ_LITERAL_HEADER_NAME_LENGTH;} else {nameLength = index;state = READ_LITERAL_HEADER_NAME;}break;case READ_LITERAL_HEADER_NAME_LENGTH:// Header Name is a Literal StringnameLength = decodeULE128(in, index);state = READ_LITERAL_HEADER_NAME;break;case READ_LITERAL_HEADER_NAME:// Wait until entire name is readableif (in.readableBytes() < nameLength) {throw notEnoughDataException(in);}name = readStringLiteral(in, nameLength, huffmanEncoded);state = READ_LITERAL_HEADER_VALUE_LENGTH_PREFIX;break;case READ_LITERAL_HEADER_VALUE_LENGTH_PREFIX:b = in.readByte();huffmanEncoded = (b & 0x80) == 0x80;index = b & 0x7F;switch (index) {case 0x7f:state = READ_LITERAL_HEADER_VALUE_LENGTH;break;case 0:insertHeader(sink, name, EMPTY_STRING, indexType);state = READ_HEADER_REPRESENTATION;break;default:valueLength = index;state = READ_LITERAL_HEADER_VALUE;}break;case READ_LITERAL_HEADER_VALUE_LENGTH:// Header Value is a Literal StringvalueLength = decodeULE128(in, index);state = READ_LITERAL_HEADER_VALUE;break;case READ_LITERAL_HEADER_VALUE:// Wait until entire value is readableif (in.readableBytes() < valueLength) {throw notEnoughDataException(in);}AsciiString value = readStringLiteral(in, valueLength, huffmanEncoded);insertHeader(sink, name, value, indexType);state = READ_HEADER_REPRESENTATION;break;default:throw new Error("should not reach here state: " + state);}}if (state != READ_HEADER_REPRESENTATION) {throw connectionError(COMPRESSION_ERROR, "Incomplete header block fragment.");}
}
该方法做的事情:
- 读取首字节,判断最高位是否是1开头。如果是代表name和value都是被索引的,继续读取索引号从静态/动态表获取header即可。
- 判断高位是否是01开头,是的话就需要把读取到的header添加到动态表中,进行维护。
- 如果name被索引,则读取索引号,从静态/动态表获取。如果没有被索引,则判断长度的第1Bit是否是1,如果是代表使用了哈夫曼编码,否则使用常规的ASCII字符编码。
- 读取value的规则类似,也是先判断是否使用哈夫曼编码,再按规则读取。
静态表
Netty通过io.netty.handler.codec.http2.HpackStaticTable类来维护静态表,硬编码写死的,不支持动态修改。
final class HpackStaticTable {static final int NOT_FOUND = -1;// Appendix A: Static Table// https://tools.ietf.org/html/rfc7541#appendix-Aprivate static final List<HpackHeaderField> STATIC_TABLE = Arrays.asList(/* 1 */ newEmptyHeaderField(":authority"),/* 2 */ newHeaderField(":method", "GET"),/* 3 */ newHeaderField(":method", "POST"),/* 4 */ newHeaderField(":path", "/"),/* 5 */ newHeaderField(":path", "/index.html"),/* 6 */ newHeaderField(":scheme", "http"),/* 7 */ newHeaderField(":scheme", "https"),/* 61 */ newEmptyHeaderField("www-authenticate")。。。。。。);
}
动态表
Netty通过io.netty.handler.codec.http2.HpackDynamicTable类来维护动态表,动态表初始是空的,只有读到01开头的Header才会加入到动态表中维护。
final class HpackDynamicTable {// a circular queue of header fieldsHpackHeaderField[] hpackHeaderFields;int head;int tail;private long size;private long capacity = -1;public void add(HpackHeaderField header) {int headerSize = header.size();if (headerSize > capacity) {clear();return;}while (capacity - size < headerSize) {remove();}hpackHeaderFields[head++] = header;size += headerSize;if (head == hpackHeaderFields.length) {head = 0;}}。。。。。。
}
相关文章:
Netty对HPACK头部压缩的支持
前言 HTTP2终于支持对头部进行压缩传输了,Netty很早就支持HTTP2了,看下Netty对HPACK的实现源码,可以对HPACK理解的更深一下。 HpackDecoder Netty内置的编解码器Http2FrameCodec专门用来对HTTP2的各种Frame进行编解码,其中就包…...
C++:替换string中的字符
1.按照位置进行替换 string的成员函数replace可以满足这种需求,其变体有很多种,请参考官方文档,以下列举常用的两种: #include <iostream> #include <string> using namespace std;int main() {string s = "hello world";s.replace(s.begin(), s.b…...
【ChatGPT】自我救赎
ChatGPT辅助学习C之【在C中如果大数据类型转小数据类型会发生什么呢?】,今天问ChatGPT一个问题,让它解析下面这个C程序: #include <iostream> #include <cstdio> using namespace std; int main() {int a;long long b532165478…...
微信小程序(由浅到深)
文章目录 一. 项目基本配置1. 项目组成2. 常见的配置文件解析3. app.json全局的五大配置4.单个页面中的page配置5. App函数6.tabBar配置 二. 基本语法,事件,单位1. 语法2. 事件3. 单位 三. 数据响应式修改四 . 内置组件1. button2. image3. input4. 组件…...
冒泡排序 简单选择排序 插入排序 快速排序
bubblesort 两个for循环,从最右端开始一个一个逐渐有序 #include <stdio.h> #include <string.h> #include <stdlib.h>void bubble(int *arr, int len); int main(int argc, char *argv[]) {int arr[] {1, 2, 3, 4, 5, 6, 7};int len sizeof(…...
linux文件I/O之 open() 函数用法
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> typedef unsigned int mode_t ; int open(const char *pathname, int flags); int open(const char *pathname, int flags, mode_t mode); 函数功能 打开或创建一个文件 返回值 成功…...
用Java操作MySQL数据库
新建Maven项目 创建Maven项目 添加依赖 在pom.xml的标签里加上下面的内容 如果是MySQL 5.8那么的版本号是5.x.x, 例如5.1.49 如果是MySQL 8.0那么的版本号是8.x.x, 例如 8.0.28 <dependencies><!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java …...
SpringBoot启动报错:java: 无法访问org.springframework.boot.SpringApplication
报错原因:jdk 1.8版本与SpringBoot 3.1.2版本不匹配 解决方案:将SpringBoot版本降到2系列版本(例如2.5.4)。如下图: 修改版本后切记刷新Meavn依赖 然后重新启动即可成功。如下图:...
Vue3 setup语法糖 解决富文本编辑器上传图片64位码过长问题 quill-image-extend-module
引言: 富文本编辑器传图片会解码成64位,非常长导致数据库会报错第一种方法:将数据库类型改成 mediumtext第二种办法:本文中的方法 说明,本周文所用语法糖为Vue3 setup语法,即<script setup> 思路 拦…...
百度坐标(BD09)、国测局坐标(火星坐标,GCJ02)、和WGS84坐标系之间的转换
<!DOCTYPE html> <html><head><meta charset="UTF-8"><title></title></head><body><script>/*** * 百度坐标(BD09)、国测局坐标(火星坐标,GCJ02)、和WGS84坐标系之间的转换*///定义一些常量var x_PI = …...
论文浅尝 | CI4MRC:基于因果推断去除机器阅读理解中的名字偏差
笔记整理:朱珈徵,天津大学硕士,研究方向:问答 链接:https://aclanthology.org/2023.findings-acl.812/ 动机 机器阅读理解(Machine Reading Comprehension,MRC)是根据给定的文章回答…...
【校招VIP】测试计划之黑盒测试白盒测试
考点介绍: 黑盒测试&白盒测试是大厂和三四线公司校招的必考点。黑盒是以结果说话,白盒往往需要理解实现逻辑。现在商业项目的接口测试往往以白盒为主,也就是需要测试同学自己观察和修改数据库的值进行用例的测试。 但是无论采用哪种测试方…...
学习笔记整理-JS-01-语法与变量
文章目录 一、语法与变量1. 初识JavaScript2. JavaScript的历史3. JavaScript与ECMAScript的关系4. JavaScript的体系5. JavaScript的语言风格和特性 二、语法1. JavaScript的书写位置2. 认识输出语句3. REPL环境,交互式解析器4. 变量是什么5. 重点内容 一、语法与变…...
PHP之PHPExcel
include PHPExcel.php; include PHPExcel/Writer/Excel2007.php; //或者include PHPExcel/Writer/Excel5.php; 用于输出.xls的 //创建一个excel $objPHPExcel new PHPExcel(); // 输出Excel表格到浏览器下载 header(Content-Type: application/vnd.ms-excel); header(Content-…...
Redis系列(一):深入了解Redis数据类型和底层数据结构
Redis有以下几种常用的数据类型: redis数据是如何组织的 为了实现从键到值的快速访问,Redis 使用了一个哈希表来保存所有键值对。 Redis全局哈希表(Global Hash Table)是指在Redis数据库内部用于存储所有键值对的主要数据结构。…...
javaScript:如何获取html中的元素对象
目录 前言: 方法 1.通过id获取元素 2.通过标签名获取元素 3.通过类名class获取元素 获取body的方法 1.document.getElementsByTagName(body)[0] 2.document.body 相关代码 前言: 通过获取HTML中的元素对象,JavaScript可以对网页进行动…...
面试总结-webpack/git
说说你对webpack的理解 webpack 是一个静态模块打包器,整个打包过程就像是一条生产线,把资源从入口放进去,经过一系列的加工(loader),最终转换成我们想要的结果,整个加工过程还会有监控&#x…...
深入解析美颜SDK:算法、效果与实现
在当今数字化社会中,图像处理和美化技术已经成为了许多应用领域的重要组成部分,尤其在视频直播领域,美颜技术更是无处不在。直播美颜SDK作为一种集成的软件工具包,为开发者和应用提供了强大的美颜功能。 一、算法原理 磨皮算法…...
ChatGPT Plus和ChatGPT对比
模型规模更大,参数数量超过6万亿,比ChatGPT大很多训练数据更丰富,包括不同语言、领域和类型的数据语言理解和生成能力更强,能够更准确地理解和生成文本可解释性和可控性更好,支持更多的调参和控制参数,生成…...
计算机网络 运输层 TCP连接建立、释放
三报文而不是两报文...
别再只测accuracy!DeepSeek集成测试必须监控的5个隐性指标(P99首token延迟、context bleed率、tool-call schema漂移)
更多请点击: https://intelliparadigm.com 第一章:DeepSeek集成测试的核心范式演进 DeepSeek大模型的工程化落地对集成测试提出了全新挑战:传统基于接口响应码与字段校验的测试范式已难以覆盖语义一致性、推理链鲁棒性、上下文敏感度等高阶质…...
0.2毫秒快速启动的操作系统
在工业控制以及航空航天等核心场景,极速启动就是高可靠系统的生命线。0.2毫秒超快启动搭配硬件看门狗,让设备在掉电重启、异常恢复时瞬时归位,关键任务永不延误! https://www.bilibili.com/video/BV11mLY6VERt/?spm_id_from333.1…...
如何进行TVA仿真引擎的“光照地狱”训练?
重磅预告:本专栏将独家连载系列丛书《智能体视觉技术与应用》部分精华内容,该书是世界首套系统阐述“因式智能体”视觉理论与实践的专著,特邀美国 TypeOne 公司首席科学家、斯坦福大学博士 Bohan 担任技术顾问。Bohan先生师从美国三院院士、“…...
大佬推荐的网络安全学习路线(从基础到高级,超级详细)
大佬推荐的网络安全学习路线(从基础到高级,超级详细) 说起网络安全,你可能会担心它是一个过时的行业。有人说,网络安全快卷死了,你既要攻又要防,并且随着技术的发展,你还要不断地学…...
深度解析DeTikZify:科研工作者的智能图表生成神器
深度解析DeTikZify:科研工作者的智能图表生成神器 【免费下载链接】DeTikZify Synthesizing Graphics Programs for Scientific Figures and Sketches with TikZ. 项目地址: https://gitcode.com/gh_mirrors/de/DeTikZify 在科研工作中,创建高质量…...
AI圈内火热的Agent、MCP、Skill、CLI是啥?用装修房子讲透,看完秒懂
本文用装修房子的比喻,详细解释了AI领域的四个核心概念:Agent如同会自主规划任务的私人助理;MCP是AI与外部工具数据的统一接口,类似USB-C;Skill是指导AI按标准操作执行的手册;CLI则是不依赖图形界面的命令行…...
学习日志(三)【php语法学习,iscc校赛wp】
1. 任务 1.1.1.1.1.1. 知识部分 rce看【之前的笔记?】php的知识点学习继续jwt token好像是比赛的题目考察内容,我看看php伪协议 1.1.1.1.1.2. 题目 参加iscc比赛【五一】rce题目 1.1.1.1.1.3. 环境配置 把vscode搞好,上学期没有把Php配…...
如何扩展GASShooter:添加新武器、新能力与新游戏机制的终极指南
如何扩展GASShooter:添加新武器、新能力与新游戏机制的终极指南 【免费下载链接】GASShooter Advanced FPS/TPS Sample Project for Unreal Engine 4s GameplayAbilitySystem plugin 项目地址: https://gitcode.com/gh_mirrors/ga/GASShooter GASShooter是Un…...
基于STM32WL与LoRaWAN的远程空气质量监测系统全栈开发实践
1. 项目概述:构建一个远程空气质量监测系统最近在做一个挺有意思的玩意儿:一个能自己找地方待着、靠太阳能供电,然后把周围空气数据悄无声息传回来的远程监测终端。核心想法很简单,就是想知道某个犄角旮旯,比如工厂周边…...
基于Jetson Nano与JNEEG Shield的脑电信号采集与边缘AI处理实战
1. 项目概述:低成本脑机接口的硬件基石 如果你对脑机接口、生物信号处理或者边缘AI应用感兴趣,但又苦于专业设备动辄数万甚至数十万的高昂门槛,那么JNEEG Shield的出现,可能会为你打开一扇新的大门。这是一个专为NVIDIA Jetson Na…...
