13. 从零用Rust编写正反向代理, HTTP中的压缩gzip,deflate,brotli算法

wmproxy

wmproxy是由Rust编写,已实现http/https代理,socks5代理, 反向代理,静态文件服务器,内网穿透,配置热更新等, 后续将实现websocket代理等,同时会将实现过程分享出来, 感兴趣的可以一起造个轮子法

项目 ++wmproxy++

gite: https://gitee.com/tickbh/wmproxy

github: https://github.com/tickbh/wmproxy

HTTP中压缩的意义

HTTP中压缩的意义在于降低了网络传输的数据量,从而提高客户端浏览器的访问速度。当然,同时也会增加一点服务器的负担。
HTTP/1.1协议中压缩主要包括gzip压缩和deflate压缩两种方法。其中gzip压缩使用的是LZ77和哈夫曼编码,而deflate压缩使用的是LZ77和哈夫曼编码以及霍夫曼编码。
此外在2015年由Google公司开发的Brotli算法是也基本全面普及开来,Brotli算法的核心原理包括两个部分:预定义的字典和无损压缩算法。预定义的字典是Brotli算法中的一项关键技术,它包含了一些常见的字符序列,例如Web标记、HTML、CSS和JavaScript代码等。Brotli算法的无损压缩算法采用了一种基于模式匹配的压缩方法。它通过预测数据中出现的重复模式,对数据进行压缩。
在HTTP的压缩协议中,这三种压缩算法基本上可以全部被支持。

gzip,deflate,brotli的优劣势

gzip、deflate和brotli这三种压缩算法都有各自的优势和劣势,具体如下:

  1. gzip
  • 优势:是Web上最常见的压缩算法之一,具有较高的压缩效率和广泛的支持程度,可以被几乎所有的浏览器和服务器支持。
  • 劣势:算法的压缩比相对较低,可能会增加文件的大小。
  1. deflate
  • 优势:具有较高的压缩效率和广泛的支持程度,同时算法的实现在不同的浏览器和服务器之间非常一致。
  • 劣势:由于某些实现上的缺陷,可能会导致一些浏览器和服务器无法正常解压缩。
  1. brotli
  • 优势:具有更高的压缩效率和更快的压缩速度,可以进一步减少传输数据的大小,从而提高页面加载速度,并且被较新版本的浏览器和服务器支持。
  • 劣势:由于算法目前仅被较新版本的浏览器和服务器支持,因此需要根据实际情况进行选择。

以下是压缩解压的数率图:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

数据来源src

可以看出brotli的压缩比大概在9左右,gzip大概在7左右,deflate也大概在7左右,压缩比brotli最高,适应网络传输慢的情况,压缩速度gzip和deflate相对较快,解压缩deflate较快,brotli和gzip差不多。

rust中三种压缩方式库的选择

通常寻找rust中的第三方库的时候,可以通过https://crates.io/进行选择,这里公开的第三方库都会在这里显示,包括使用次数,流行热度,最近下载量,最近更新时间等,可以从多维度的知道该库的好与坏再进行相应的选择。

  • flate2
    在这里插入图片描述

该库支持三种压缩格式的算法,deflate, zlib, gzip,我们选择用它来做deflate, gzip的支持。

  • brotli
    在这里插入图片描述

该库如库名一般,只支持brotli算法,相对热度较高,算是支持brolti里最好的一个,我们进行选择。

三种方式的压缩实现

三种方式均可实现流式的压缩,即边写入数据,边读出压缩数据,不用完全的写入所有数据,完整的实现方法在 RecvStream里,将压缩的数据缓存到self.cache_body_data

定义压缩方法值

pub const COMPRESS_METHOD_NONE: i8 = 0;
pub const COMPRESS_METHOD_GZIP: i8 = 1;
pub const COMPRESS_METHOD_DEFLATE: i8 = 2;
pub const COMPRESS_METHOD_BROTLI: i8 = 3;
  • gzip

此处利用的是类use flate2::write::GzEncoder,定义为GzEncoder<BinaryMut>,其中BinaryMut为压缩后的数据,需要具备std::io::Write方法。

Consts::COMPRESS_METHOD_GZIP => {// 数据结束,需要主动调用结束以导出全部结果if data.len() == 0 {self.compress.open_write_gz();let gz = self.compress.write_gz.take().unwrap();let value = gz.finish().unwrap();if value.remaining() > 0 {Self::inner_encode_data(&mut self.cache_body_data, &value, self.is_chunked)?;}if self.is_chunked {Helper::encode_chunk_data(&mut self.cache_body_data, data)} else {Ok(0)}} else {self.compress.open_write_gz();let gz = self.compress.write_gz.as_mut().unwrap();gz.write_all(data).unwrap();// 每次写入,在尝试读取出数据if gz.get_mut().remaining() > 0 {let s =Self::inner_encode_data(&mut self.cache_body_data, &gz.get_mut().chunk(), self.is_chunked);gz.get_mut().clear();s} else {Ok(0)}}
}
  • deflate

此处利用的是类use flate2::write::DeflateEncoder,定义为DeflateEncoder<BinaryMut>,其中BinaryMut为压缩后的数据,需要具备std::io::Write方法。

Consts::COMPRESS_METHOD_DEFLATE => {// 数据结束,需要主动调用结束以导出全部结果if data.len() == 0 {self.compress.open_write_de();let de = self.compress.write_de.take().unwrap();let value = de.finish().unwrap();if value.remaining() > 0 {Self::inner_encode_data(&mut self.cache_body_data, &value, self.is_chunked)?;}if self.is_chunked {Helper::encode_chunk_data(&mut self.cache_body_data, data)} else {Ok(0)}} else {self.compress.open_write_de();let de = self.compress.write_de.as_mut().unwrap();de.write_all(data).unwrap();// 每次写入,在尝试读取出数据if de.get_mut().remaining() > 0 {let s =Self::inner_encode_data(&mut self.cache_body_data, &de.get_mut().chunk(), self.is_chunked);de.get_mut().clear();s} else {Ok(0)}}
}
  • brotli

此处利用的是类use brotli::CompressorWriter;,定义为CompressorWriter<BinaryMut>,其中BinaryMut为压缩后的数据,需要具备std::io::Write方法。

Consts::COMPRESS_METHOD_BROTLI => {// 数据结束,需要主动调用结束以导出全部结果if data.len() == 0 {self.compress.open_write_br();let mut de = self.compress.write_br.take().unwrap();de.flush()?;let value = de.into_inner();if value.remaining() > 0 {Self::inner_encode_data(&mut self.cache_body_data, &value, self.is_chunked)?;}if self.is_chunked {Helper::encode_chunk_data(&mut self.cache_body_data, data)} else {Ok(0)}} else {self.compress.open_write_br();let de = self.compress.write_br.as_mut().unwrap();de.write_all(data).unwrap();// 每次写入,在尝试读取出数据if de.get_mut().remaining() > 0 {let s =Self::inner_encode_data(&mut self.cache_body_data, &de.get_mut().chunk(), self.is_chunked);de.get_mut().clear();s} else {Ok(0)}}
}

三种方式的解压实现

和压缩不同的是,解压的时候必须将完整的数据进行解压,所以需要收到全部的数据的时候才尝试进行解压,可能我的理解有误,欢迎指出,当下的实现方式可能会占用大量的内存,非我所愿。主要源码在 SendStream中实现。

三种方式均类似,以下

// 收到数据进行缓存,只有到结束时才进行解压缩
match self.compress_method {Consts::COMPRESS_METHOD_GZIP => {self.cache_body_data.put_slice(data);if self.is_end {self.compress.open_reader_gz(self.cache_body_data.clone());let gz = self.compress.reader_gz.as_mut().unwrap();let s = Self::read_all_data(&mut self.cache_buf, &mut self.real_read_buf, gz);self.cache_body_data.clear();s} else {Ok(0)}}Consts::COMPRESS_METHOD_DEFLATE => {self.cache_body_data.put_slice(data);if self.is_end {self.compress.open_reader_de(self.cache_body_data.clone());let de = self.compress.reader_de.as_mut().unwrap();let s = Self::read_all_data(&mut self.cache_buf, &mut self.real_read_buf, de);self.cache_body_data.clear();s} else {Ok(0)}}Consts::COMPRESS_METHOD_BROTLI => {self.cache_body_data.put_slice(data);if self.is_end {self.compress.open_reader_br(self.cache_body_data.clone());let br = self.compress.reader_br.as_mut().unwrap();let s = Self::read_all_data(&mut self.cache_buf, &mut self.real_read_buf, br);self.cache_body_data.clear();s} else {Ok(0)}}_ => {self.real_read_buf.put_slice(data);Ok(data.len())},
}

如果数据包非常的巨大的时候,可能需要将内存内容写入缓存文件来缓解内存的压力。

结语

压缩为了可以更好的存储,也可以更好的传输,是我们日常生活中必不可少的存在,虽然现在比以前带宽更高,存储比之前的更便宜,但是现在的数据更多,传输延时要求更少,所以高压缩的能力依然非常受欢迎。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/232479.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mysql的索引约束检查触发器

索引&#xff1a; MySQL 索引是一种用于提高查询性能的关键数据库特性。通过在表上创建索引&#xff0c;MySQL 可以更有效地定位和检索数据&#xff0c;从而加速查询操作。以下是关于 MySQL 索引的一些基本信息&#xff1a; 1. **索引类型&#xff1a;** - **单列索引&…

构建智慧储能物联网,4G工业路由器远程监测在线管理

物联网技术的发展为智慧储能管理带来了革命性的变化。其中&#xff0c;4G工业路由器IR5000通过丰富的连接能力如串口RS485/232或网口的方式&#xff0c;实现了与储能现场各设备的连接&#xff0c;包括电表、电能检测器、防孤岛装置、BMS电池管理系统、监控服务器、储能控制器、…

WEB渗透—PHP反序列化(五)

Web渗透—PHP反序列化 课程学习分享&#xff08;课程非本人制作&#xff0c;仅提供学习分享&#xff09; 靶场下载地址&#xff1a;GitHub - mcc0624/php_ser_Class: php反序列化靶场课程&#xff0c;基于课程制作的靶场 课程地址&#xff1a;PHP反序列化漏洞学习_哔哩…

xcrun: error: invalid active developer path

macOS升级完成后出现 xcrun: error: invalid active developer path问题。 xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools), missing xcrun at: /Library/Developer/CommandLineTools/usr/bin/xcrun这是由于 Xcode command line tools 丢…

Python-Selenium-使用 pywinauto 实现 Input 上传文件

当前环境&#xff1a;Win10 Python3.7 pywinauto0.6.8&#xff0c;selenium3.14.1 示例代码 from pywinauto import Desktop import osapp Desktop() dialog app[打开] dialog[Edit].set_edit_text(os.getcwd() .\\example-01.jpg) dialog[Button].click() 其他方法&…

LINUX SD卡备份的镜像+烧录启动时自动扩展最后一个分区

在开发到生产过程中,对系统镜像的备份及再烧录过程是必不可少的。经常看到烧录官方镜像的时候,我们会发现当镜像第一次启动后,磁盘空间会自动扩充到整个TF/SD卡。那么当我们自己制作镜像的时候,能不能去除未使用的空间,制作体积较小的镜像,并在该镜像启动时自动扩展,从而…

【影像组学入门百问】#29---#31

#29-影像组学研究中&#xff0c;消除不同影像间因设备和扫 描参数引起的差异的方法有哪些&#xff1f; 在影像组学研究中&#xff0c;消除不同影像间因设备和扫描参数引起的差异至关重要。以下是一些常用的方法&#xff1a; 1.归一化&#xff08;Normalization&#xff09;&a…

喜报|棱镜七彩获评江苏省专精特新中小企业

近日&#xff0c;江苏省工业和信息化厅发布《关于江苏省2023年专精特新中小企业和2020年度专精特新企业复核通过企业名单的公示》&#xff0c;棱镜七彩成功入选2023年江苏省省级专精特新中小企业名单。 图 2023年省级专精特新中小企业公式名单节选 “专精特新”是国家为鼓励中…

Python纯净式下载与安装

1. 下载 Download Python | Python.org 建议下老版本些的&#xff0c;毕竟求稳。 点击需要的版本&#xff0c;然后滑倒最下面&#xff0c;可以看到不同系统对应的下载选项&#xff1a; 2. 安装 如果下载慢的话&#xff0c;可以复制链接到迅雷下载&#xff0c;下载完成后&…

这个食堂管理大招,再不知道就晚了!

随着社会的不断发展&#xff0c;餐饮行业也在不断创新和进步。在这个数字化时代&#xff0c;智能技术为各行各业提供了更高效、便捷的解决方案。 食堂作为人们日常生活中不可或缺的一部分&#xff0c;也迎来了智能化的时代。智慧收银系统不仅提高了食堂的运营效率&#xff0c;还…

应用Transformer和CNN进行计算机视觉任务各自的优缺点

Transformer 和 CNN&#xff08;卷积神经网络&#xff09;是用于计算机视觉任务的两种不同的深度学习架构&#xff0c;各自具有一些优点和局限性。 一、Transformer: 优点&#xff1a; 全局信息关系建模&#xff1a; Transformer 通过自注意力机制&#xff08;self-attentio…

【算法Hot100系列】删除链表的倒数第 N 个结点

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

探讨二维半导体的概念、应用前景及其与传统半导体的差异

当探讨二维半导体时&#xff0c;我们置身于科技革新的前沿。这种材料以其纳米级薄度和独特电学性质区别于传统半导体&#xff0c;引发了科学界的广泛兴趣。本文将深入探讨二维半导体的概念、应用前景及其与传统半导体的差异。 什么是二维半导体&#xff1f; 二维半导体是由单…

常见的游戏类型

中懿游游戏开发&#xff0c;游戏类型繁多&#xff0c;涵盖了各种不同的主题、玩法和风格。以下是一些常见的游戏类型&#xff1a; 动作游戏&#xff08;Action Games&#xff09;&#xff1a; 包括快节奏的战斗和反应要求高的游戏&#xff0c;例如《侠盗猎车手》、《荣誉战士…

分享4个文件自动备份方法,持续保护重要数据安全!

​如何执行文件自动备份任务&#xff1f;随着网络科技日新月异的高速发展&#xff0c;电脑和手机等电子设备在我们的日常工作生活中扮演着越来越重要的角色&#xff0c;使用频率逐渐增加&#xff0c;慢慢地也就离不开它了&#xff0c;从而导致积累在电脑中的重要数据量也在不断…

HackTheBox-Machines--Broker

文章目录 1 端口扫描2 测试思路3 漏洞探测4 CVE-2023-46604漏洞利用5.权限提升 Broker 测试过程 1 端口扫描 nmap -sC -sV 10.129.41.282 测试思路 目标开启了22、80、61616端口&#xff0c;在服务器开启了非web及一些需要账号密码进行登录的端口时&#xff0c;我们的入手点从这…

测试用例设计方法六脉神剑——第五剑:化气为型,场景用例破云

&#x1f4e2;专注于分享软件测试干货内容&#xff0c;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;交流讨论&#xff1a;欢迎加入我们一起学习&#xff01;&#x1f4e2;资源分享&#xff1a;耗时200小时精选的「软件测试」资…

08‐Mysql全局优化与Mysql 8.0新特详解

文章目录 Mysql全局优化总结配置文件my.ini或my.cnf的全局参数最大连接数允许用户连接的最大数量MySQL能够暂存的连接数量JDBC连接空闲等待时长client连接空闲等待时长innodb线程并发数innodb存储引擎buffer pool缓存大小行锁锁定时间redo log写入策略binlog写入磁盘机制排序线…

22.JSP技术

JSP起源 在很多动态网页中&#xff0c;绝大部分内容都是固定不变的&#xff0c;只有局部内容需要动态产生和改变。如果使用Servlet程序来输出只有局部内容需要动态改变的网页&#xff0c;其中所有的静态内容也需要程序员用Java程序代码产生&#xff0c;整个Servlet程序的代码将…

12.12困境儿童关注日:共同守护儿童健康成长

2023年12月12日是第十三个困境儿童关注日,困境儿童关注日是在2011年由壹基金、北京师范大学社会发展与公共政策学院家庭与儿童研究中心、浙江大学传媒与国际文化学院中国公益传播研究中心共同发起设立的。儿童是国家的未来、民族的希望,困境儿童尤其需要关心和关注。自11月以来…