当前位置: 首页 > news >正文

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_PREFIXname未被索引,读取name长度前缀,判断是否使用哈夫曼编码
READ_LITERAL_HEADER_NAME_LENGTHname未被索引,读取name长度
READ_LITERAL_HEADER_NAMEname未被索引,读取header name
READ_LITERAL_HEADER_VALUE_LENGTH_PREFIX读取value长度前缀,判断是否使用哈夫曼编码
READ_LITERAL_HEADER_VALUE_LENGTH读取value长度
READ_LITERAL_HEADER_VALUEvalue未被索引,读取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. 读取首字节,判断最高位是否是1开头。如果是代表name和value都是被索引的,继续读取索引号从静态/动态表获取header即可。
  2. 判断高位是否是01开头,是的话就需要把读取到的header添加到动态表中,进行维护。
  3. 如果name被索引,则读取索引号,从静态/动态表获取。如果没有被索引,则判断长度的第1Bit是否是1,如果是代表使用了哈夫曼编码,否则使用常规的ASCII字符编码。
  4. 读取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终于支持对头部进行压缩传输了&#xff0c;Netty很早就支持HTTP2了&#xff0c;看下Netty对HPACK的实现源码&#xff0c;可以对HPACK理解的更深一下。 HpackDecoder Netty内置的编解码器Http2FrameCodec专门用来对HTTP2的各种Frame进行编解码&#xff0c;其中就包…...

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中如果大数据类型转小数据类型会发生什么呢?】&#xff0c;今天问ChatGPT一个问题&#xff0c;让它解析下面这个C程序&#xff1a; #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配置 二. 基本语法&#xff0c;事件&#xff0c;单位1. 语法2. 事件3. 单位 三. 数据响应式修改四 . 内置组件1. button2. image3. input4. 组件…...

冒泡排序 简单选择排序 插入排序 快速排序

bubblesort 两个for循环&#xff0c;从最右端开始一个一个逐渐有序 #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

报错原因&#xff1a;jdk 1.8版本与SpringBoot 3.1.2版本不匹配 解决方案&#xff1a;将SpringBoot版本降到2系列版本(例如2.5.4)。如下图&#xff1a; 修改版本后切记刷新Meavn依赖 然后重新启动即可成功。如下图&#xff1a;...

Vue3 setup语法糖 解决富文本编辑器上传图片64位码过长问题 quill-image-extend-module

引言&#xff1a; 富文本编辑器传图片会解码成64位&#xff0c;非常长导致数据库会报错第一种方法&#xff1a;将数据库类型改成 mediumtext第二种办法&#xff1a;本文中的方法 说明&#xff0c;本周文所用语法糖为Vue3 setup语法&#xff0c;即<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:基于因果推断去除机器阅读理解中的名字偏差

笔记整理&#xff1a;朱珈徵&#xff0c;天津大学硕士&#xff0c;研究方向&#xff1a;问答 链接&#xff1a;https://aclanthology.org/2023.findings-acl.812/ 动机 机器阅读理解&#xff08;Machine Reading Comprehension&#xff0c;MRC&#xff09;是根据给定的文章回答…...

【校招VIP】测试计划之黑盒测试白盒测试

考点介绍&#xff1a; 黑盒测试&白盒测试是大厂和三四线公司校招的必考点。黑盒是以结果说话&#xff0c;白盒往往需要理解实现逻辑。现在商业项目的接口测试往往以白盒为主&#xff0c;也就是需要测试同学自己观察和修改数据库的值进行用例的测试。 但是无论采用哪种测试方…...

学习笔记整理-JS-01-语法与变量

文章目录 一、语法与变量1. 初识JavaScript2. JavaScript的历史3. JavaScript与ECMAScript的关系4. JavaScript的体系5. JavaScript的语言风格和特性 二、语法1. JavaScript的书写位置2. 认识输出语句3. REPL环境&#xff0c;交互式解析器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有以下几种常用的数据类型&#xff1a; redis数据是如何组织的 为了实现从键到值的快速访问&#xff0c;Redis 使用了一个哈希表来保存所有键值对。 Redis全局哈希表&#xff08;Global Hash Table&#xff09;是指在Redis数据库内部用于存储所有键值对的主要数据结构。…...

javaScript:如何获取html中的元素对象

目录 前言&#xff1a; 方法 1.通过id获取元素 2.通过标签名获取元素 3.通过类名class获取元素 获取body的方法 1.document.getElementsByTagName(body)[0] 2.document.body 相关代码 前言&#xff1a; 通过获取HTML中的元素对象&#xff0c;JavaScript可以对网页进行动…...

面试总结-webpack/git

说说你对webpack的理解 webpack 是一个静态模块打包器&#xff0c;整个打包过程就像是一条生产线&#xff0c;把资源从入口放进去&#xff0c;经过一系列的加工&#xff08;loader&#xff09;&#xff0c;最终转换成我们想要的结果&#xff0c;整个加工过程还会有监控&#x…...

深入解析美颜SDK:算法、效果与实现

在当今数字化社会中&#xff0c;图像处理和美化技术已经成为了许多应用领域的重要组成部分&#xff0c;尤其在视频直播领域&#xff0c;美颜技术更是无处不在。直播美颜SDK作为一种集成的软件工具包&#xff0c;为开发者和应用提供了强大的美颜功能。 一、算法原理 磨皮算法…...

ChatGPT Plus和ChatGPT对比

模型规模更大&#xff0c;参数数量超过6万亿&#xff0c;比ChatGPT大很多训练数据更丰富&#xff0c;包括不同语言、领域和类型的数据语言理解和生成能力更强&#xff0c;能够更准确地理解和生成文本可解释性和可控性更好&#xff0c;支持更多的调参和控制参数&#xff0c;生成…...

计算机网络 运输层 TCP连接建立、释放

三报文而不是两报文...

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周&#xff0c;有很多同学在写期末Java web作业时&#xff0c;运行tomcat出现乱码问题&#xff0c;经过多次解决与研究&#xff0c;我做了如下整理&#xff1a; 原因&#xff1a; IDEA本身编码与tomcat的编码与Windows编码不同导致&#xff0c;Windows 系统控制台…...

vue3 定时器-定义全局方法 vue+ts

1.创建ts文件 路径&#xff1a;src/utils/timer.ts 完整代码&#xff1a; import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...

leetcodeSQL解题:3564. 季节性销售分析

leetcodeSQL解题&#xff1a;3564. 季节性销售分析 题目&#xff1a; 表&#xff1a;sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...

Yolov8 目标检测蒸馏学习记录

yolov8系列模型蒸馏基本流程&#xff0c;代码下载&#xff1a;这里本人提交了一个demo:djdll/Yolov8_Distillation: Yolov8轻量化_蒸馏代码实现 在轻量化模型设计中&#xff0c;**知识蒸馏&#xff08;Knowledge Distillation&#xff09;**被广泛应用&#xff0c;作为提升模型…...

【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)

本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...

MFC 抛体运动模拟:常见问题解决与界面美化

在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...

Vue ③-生命周期 || 脚手架

生命周期 思考&#xff1a;什么时候可以发送初始化渲染请求&#xff1f;&#xff08;越早越好&#xff09; 什么时候可以开始操作dom&#xff1f;&#xff08;至少dom得渲染出来&#xff09; Vue生命周期&#xff1a; 一个Vue实例从 创建 到 销毁 的整个过程。 生命周期四个…...

在鸿蒙HarmonyOS 5中使用DevEco Studio实现指南针功能

指南针功能是许多位置服务应用的基础功能之一。下面我将详细介绍如何在HarmonyOS 5中使用DevEco Studio实现指南针功能。 1. 开发环境准备 确保已安装DevEco Studio 3.1或更高版本确保项目使用的是HarmonyOS 5.0 SDK在项目的module.json5中配置必要的权限 2. 权限配置 在mo…...

机器学习的数学基础:线性模型

线性模型 线性模型的基本形式为&#xff1a; f ( x ) ω T x b f\left(\boldsymbol{x}\right)\boldsymbol{\omega}^\text{T}\boldsymbol{x}b f(x)ωTxb 回归问题 利用最小二乘法&#xff0c;得到 ω \boldsymbol{\omega} ω和 b b b的参数估计$ \boldsymbol{\hat{\omega}}…...

Python环境安装与虚拟环境配置详解

本文档旨在为Python开发者提供一站式的环境安装与虚拟环境配置指南&#xff0c;适用于Windows、macOS和Linux系统。无论你是初学者还是有经验的开发者&#xff0c;都能在此找到适合自己的环境搭建方法和常见问题的解决方案。 快速开始 一分钟快速安装与虚拟环境配置 # macOS/…...