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

这篇具有很好参考价值的文章主要介绍了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
  • 优势:具有更高的压缩效率和更快的压缩速度,可以进一步减少传输数据的大小,从而提高页面加载速度,并且被较新版本的浏览器和服务器支持。
  • 劣势:由于算法目前仅被较新版本的浏览器和服务器支持,因此需要根据实际情况进行选择。

以下是压缩解压的数率图:
13. 从零用Rust编写正反向代理, HTTP中的压缩gzip,deflate,brotli算法,rust,http,算法

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

数据来源src

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

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

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

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

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

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

该库如库名一般,只支持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())
    },
}

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

结语

压缩为了可以更好的存储,也可以更好的传输,是我们日常生活中必不可少的存在,虽然现在比以前带宽更高,存储比之前的更便宜,但是现在的数据更多,传输延时要求更少,所以高压缩的能力依然非常受欢迎。文章来源地址https://www.toymoban.com/news/detail-787094.html

到了这里,关于13. 从零用Rust编写正反向代理, HTTP中的压缩gzip,deflate,brotli算法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • linux中的压缩解压命令:gzip,tar,zip,bzip2

    命令名称:gzip 语法:gzip  文件 功能:压缩文件 压缩后文件格式:.gz 例子1  如上图所示,先用touch命令创建一个新文件file1,然后使用gzip压缩该文件。 例子2 如上图所示,先用cp命令将/etc/services文件复制到/root目录下并改名为file2,通过ls -l 命令可见该文件的大小为670293B,

    2024年02月05日
    浏览(78)
  • 【Web】HTTP代理和反向代理

    就是从客户端直接访问服务端,相当于我直接去厂家买可乐,没有中间商赚差价 HTTP代理指在客户端先访问代理服务器,然后由代理服务器去访问服务端,代理服务器收到响应后再转发个客户端,就像我去商店买可乐的过程 浏览器并不知道代理服务器的位置,所以我们要主动

    2024年02月06日
    浏览(30)
  • 深入理解 http 反向代理

    要理解什么是 反向代理(reverse proxy) , 自然你得先知道什么是 正向代理(forward proxy) . 另外需要说的是, 一般提到反向代理, 通常是指 http 反向代理, 但反向代理的范围可以更大, 比如 tcp 反向代理, 在这里, 不打算讨论 tcp 之类的反向代理, 当文中说到反向代理时, 指的就是 http 反向

    2024年02月12日
    浏览(27)
  • Caddy反向代理转发修改http请求路径

    Caddy是个非常不错的开源服务器产品,简单易用,自带ssl。只是没啥详细的中文文档,遇到问题只能看官方文档。 记录一下使用Caddy转发http请求的方法。 问题:将http://192.168.1.10:7077/product/*的请求转发到http://192.168.1.12:7078/*。这里其实是两个需求,一个是转发端口,还有个是去

    2024年02月12日
    浏览(33)
  • Nginx HTTP和反向代理服务器

    1、概念: Nginx  (engine x) 是一个高性能的HTTP和反向代理web服务器。 2、什么是代理服务器: 概念: 代理服务器是介于客户端和Web服务器之间的另一台服务器,有了它之后,浏览器不是直接到Web服务器去取回网页信息,而是通过向代理服务器发送请求,信号会先送到代理服务

    2024年02月04日
    浏览(36)
  • Varnish开源HTTP反向代理缓存服务器

    第三阶段基础 时  间:2023年6月13日 参加人:全班人员 内  容: Varnish 目录 Varnish 端口号:TCP/6081  TCP/6082 配置文件:/etc/varnish/default.vcl 安装部署: 测试功能: Varnish是一个高性能的 开源HTTP反向代理缓存服务器 ,它可以加速动态内容的交付并降低服务器的负载。 Varnish常被

    2024年02月10日
    浏览(521)
  • nginx-反向代理是设置传输协议http/https

            X-Forwarded-Proto (XFP) 是一个事实上的标准首部,用来确定客户端与代理服务器或者负载均衡服务器之间的连接所采用的传输协议(HTTP 或 HTTPS)。在服务器的访问日志中记录的是负载均衡服务器与服务器之间的连接所使用的传输协议,而非客户端与负载均衡服务器

    2024年02月14日
    浏览(35)
  • nginx 开启https时反向代理http服务的问题

    当我们用nginx开启https时,反向代理一个本地的http服务,会遭遇跨域问题,报错 strict-origin-when-cross-origin ,导致很多资源无法加载。 这时只要在反向代理部分的配置文件中加入这一条语句即可:

    2024年02月14日
    浏览(25)
  • Vue 3中的反向代理 和如何在服务器配置反向代理

    如何在Vue 3项目中配置反向代理,让前端开发变得爽到爆!还有个小插曲,Vite为我们提供了更简单的方式,就像找对象一样直接。 首先,我们来谈谈反向代理是什么。简单来说,反向代理就像是前端和后端之间的婚姻介绍所。前端需要向后端请求数据,但由于某些原因(比如

    2024年04月13日
    浏览(26)
  • 前端(二十六)——常见的HTTP异常状态码以及正反向代理配置

    👵博主:小猫娃来啦 👵文章核心: 前端常见的HTTP异常状态码以及正反向代理配置 当客户端发送了一个请求,但服务器无法找到对应的资源时,会返回404错误码。可能的原因包括: 请求的URL路径错误,服务器没有对应的资源。 资源被删除或者被移动到其他位置。 资源名称

    2024年01月25日
    浏览(30)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包