14. 从零用Rust编写正反向代理, HTTP文件服务器的实现过程及参数

wmproxy

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

项目 ++wmproxy++

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

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

HTTP文件服务器的意义

HTTP文件服务器的意义是可以放置网站文件,可以放置数据文件
HTTP服务器一般指网站服务器,是指驻留于因特网上某种类型计算机的程序,可以处理浏览器等Web客户端的请求并返回相应响应。
当前大量的应用会依赖到文件服务器,比如我们非常熟悉的网站(会加载index.html)文件及各种css及js文件,比如我们的各种APP会有相对应的版本信息,会有相应的版本文件,又或者小程序本身就是一个可执行文件,当你点击的时候,应用去下载相应的小程序文件,然后在本地进行加载,然后打开提供服务。目前我们的互联网上冲浪完全无法离开文件服务器。

HTTP文件服务器几大作用

1. 文件共享

文件服务器的主要功能是提供文件共享功能。它允许用户从他们自己的计算机或设备访问共享文件和文件夹,而不管他们的物理位置。用户可以查看、编辑和保存存储在服务器上的文件,所有有权访问该文件的用户都会自动更新更改。
以下是我们在聊天软件上发送一张图片给另一个人的流程

将图片共享
将图片上传
返回图片地址
将图片地址推送给
将地址通知给
从文件服务器中获取图片
APP
文件服务器
APP服务器
聊天对象

2. 集中存储

此时你的不小心将数据删除,此时你想找回原来的图片,以下是整个过程

同步旧的聊天记录, 获取图片地址
重新下载图片
获取完图片后推送给
APP
文件服务器
APP服务器

此时文件服务器担任着集中存储的角色,海量的数据将汇聚在中心服务器上,我们可以通过网络访问到海量的数据资源。
3. 备份与恢复
上述过程,相当于服务器帮你备份了图片数据,在你不小心丢失的时候,可以恢复您的数据,我们最经常使用的如图片备份到网盘,一方面可以释放掉本地的空间,另一方面我们可以将数据保存到很久之后。
4. 访问控制
我们在获取到图片地址的时候,并不是任何的角色都可以获取到该图片的资源,在服务器内部中,会有相关的权限验证,在为您提供数据的同时,并保护着您的数据安全。

file_server文件服务器

一个静态文件服务器,支持真实和虚拟文件系统。它通过将请求的URI路径附加到站点的根路径来形成文件路径。
最常见的是,file_server指令与root指令配对,为整个网站设置文件根。其中保证所有的访问仅能在root指定的目录之下,不能访问其上级的任何数据,故在root下的目录理论上即使禁目录访问也可能被全部访问到(暴力遍历),但在root上级的目录不可能被以任何的方式进行访问,即使添加../相对路径也不行。

file_server参数相关

结构定义如下:

pub struct FileServer {#[serde(default = "default_root")]pub root: String,#[serde(default)]pub prefix: String,#[serde(default="default_hide")]pub hide: Vec<String>,#[serde(default = "default_index")]pub index: Vec<String>,#[serde(default = "default_status")]pub status: u16,#[serde(default = "default_precompressed")]pub precompressed: Vec<String>,#[serde(default)]pub disable_compress: bool,#[serde(default = "default_bool_true")]pub browse: bool,
}
  • browse 对没有索引文件的目录的请求,当前又是一个目录的情况下启用文件列表。
  • root 设置网站根目录。指向的是当前文件磁盘下的路径前缀,如/file/,那么提供服务的将是/file/的文件服务
  • prefix Url的前缀,如/static/,如果我们获取到一个请求路径如/static/src/wmproxy.md,那么我们会去掉前缀得到src/wmproxy.md,那么实际的指向为/file/src/wmproxy.md进行文件服务
  • hide 是一个要隐藏的文件或文件夹的列表;如果要求,文件服务器将假装它们不存在。该指令接受占位符和glob模式。注意,这些是 文件系统 路径,不是请求路径。换句话说,相对路径使用当前工作目录作为基础,而不是网站根目录;所有的路径在比较之前都会被转换为绝对形式(如果可能的话)。指定一个没有路径分隔符的文件名或模式,将隐藏所有具有匹配名称的文件,无论其位置如何;
  • index 是一个寻找索引文件的文件名列表。默认:index.html index.htm
  • precompressed 是用于搜索预压缩挎包文件的编码格式列表。支持的格式有gzip(.gz),和br(.br)。所有的文件查找将首先寻找未压缩文件的存在。一旦找到,我们将以未添加之前的格式做mimetype,如README.md.gz取的是md的mimetype,也就是text/plain。并适当地设置Content-Encoding响应头。否则,将以正常的未压缩文件进行响应。如果encode指令被启用,那么如果没有预压缩,它可能会对响应进行即时压缩。如我们访问README.md,但此时目录下存在README.md.gz,那我们我们响应的是gz的文件,并设置Content-Encoding: gzip,如此做的好处,我们对该文件的任何请求,我们都无须耗任何压缩的时间,响应更快,我们可以用更高的压缩比来进行预压缩,可节省更多时间。
  • status 是一个可选的状态代码覆盖,在编写响应时使用。在用自定义错误页面响应请求时特别有用。可以是一个3位数的状态代码,例如:404。支持占位符。默认情况下,写入的状态代码通常是200,或206,用于部分内容。
  • 当前目录外的静态文件服务器。
reverse:file_server:
  • 启用了文件列表:
reverse:file_server:browse: true
  • 只服务于/static文件夹中的静态文件:
reverse:file_server:root: /static/browse: true
  • 隐藏所有.git文件夹及其内容。
reverse:file_server:root: /static/browse: truehide: [.git]
  • 如果客户端支持(Accept-Encoding头),发送gzip,br,则检查请求的文件是否存在预压缩的文件。因此,如果/path/to/file被请求,/path/to/file.br和/path/to/file.gz`,并提供第一个具有相应内容编码的可用文件。
reverse:file_server:root: /static/browse: truehide: [.git]precompressed: [br, gzip]

mimetype作用

多用途互联网邮件扩展(MIME,Multipurpose Internet Mail Extensions)是一个 互联网标准,它扩展了 电子邮件标准,使其能够支持非 ASCII字符、 二进制格式附件等多种格式的邮件消息。
内容类型(Content-Type),这个头部领域用于指定消息的类型。一般以下面的形式出现。[type]/[subtype]

type有下面的形式。

Text:用于标准化地表示的文本信息,文本消息可以是多种字符集和或者多种格式的;
Multipart:用于连接消息体的多个部分构成一个消息,这些部分可以是不同类型的数据;
Application:用于传输应用程序数据或者二进制数据;
Message:用于包装一个E-mail消息;
Image:用于传输静态图片数据;
Audio:用于传输音频或者音声数据;
Video:用于传输动态影像数据,可以是与音频编辑在一起的视频数据格式。

subtype用于指定type的详细形式。type/subtype配对的集合和与此相关的参数,将随着时间而增长。为了确保这些值在一个有序而且公开的状态下开发,MIME使用Internet Assigned Numbers Authority (IANA)作为中心的注册机制来管理这些值。常用的subtype值如下所示:

  • text/plain(纯文本)
  • text/html(HTML文档)
  • application/xhtml+xml(XHTML文档)
  • image/gif(GIF图像)
  • image/jpeg(JPEG图像)
  • image/png(PNG图像)
  • video/mpeg(MPEG动画)
  • application/octet-stream(任意的二进制数据)
  • application/pdf(PDF文档)
  • application/msword(Microsoft Word文件)
  • message/rfc822( RFC 822形式)
  • multipart/alternative(HTML邮件的HTML形式和纯文本形式,相同内容使用不同形式表示)
  • application/x-www-form-urlencoded(使用HTTP的POST方法提交的表单)
  • multipart/form-data(同上,但主要用于表单提交时伴随文件上传的场合)

我们根据现有的已知的,我们用了静态变量做了以下数据定义,后续将会进行数据补充或者自定义

lazy_static! {static ref DEFAULT_MIMETYPE: HashMap<&'static str, &'static str> = {let mut m = HashMap::<&'static str, &'static str>::new();m.insert("doc", "application/msword");m.insert("pdf", "application/pdf");m.insert("rtf", "application/rtf");m.insert("xls", "application/vnd.ms-excel");m.insert("ppt", "application/vnd.ms-powerpoint");m.insert("rar", "application/application/x-rar-compressed");m.insert("swf", "application/x-shockwave-flash");m.insert("zip", "application/zip");m.insert("json", "application/json");m.insert("yaml", "text/plain");m.insert("mid", "audio/midi");m.insert("midi", "audio/midi");m.insert("kar", "audio/midi");m.insert("mp3", "audio/mpeg");m.insert("ogg", "audio/ogg");m.insert("m4a", "audio/m4a");m.insert("ra", "audio/x-realaudio");m.insert("gif", "image/gif");m.insert("jpeg", "image/jpeg");m.insert("jpg", "image/jpeg");m.insert("png", "image/png");m.insert("tif", "image/tiff");m.insert("tiff", "image/tiff");m.insert("wbmp", "image/vnd.wap.wbmp");m.insert("ico", "image/x-icon");m.insert("jng", "image/x-jng");m.insert("bmp", "image/x-ms-bmp");m.insert("svg", "image/svg+xml");m.insert("svgz", "image/svg+xml");m.insert("webp", "image/webp");m.insert("svg", "image/svg+xml");m.insert("css", "text/css");m.insert("html", "text/html");m.insert("htm", "text/html");m.insert("shtml", "text/html");m.insert("txt", "text/plain");m.insert("md", "text/plain");m.insert("xml", "text/xml");m.insert("3gpp", "video/3gpp");m.insert("3gp", "video/3gpp");m.insert("mp4", "video/mp4");m.insert("mpeg", "video/mpeg");m.insert("mpg", "video/mpeg");m.insert("mov", "video/quicktime");m.insert("webm", "video/webm");m.insert("flv", "video/x-flv");m.insert("m4v", "video/x-m4v");m.insert("wmv", "video/x-ms-wmv");m.insert("avi", "video/x-msvideo");m};
}

源码实现

源码主要实现在file_server.rsdeal_request函数。节选

pub async fn deal_request(&self,req: Request<RecvStream>,
) -> ProtResult<Response<RecvStream>> {let path = req.path().clone();// 无效前缀,无法处理if !path.starts_with(&self.prefix) {return Ok(self.ret_error_msg("unknow path"));}let root_path = Path::new(&self.root);let mut real_path = Path::new(&real_path).to_owned();// 必须保证不会跑出root设置的目录之外,如故意访问`../`之类的if !real_path.starts_with(root_path) || self.is_hide_path(root_path.as_ref()) {return Ok(self.ret_error_msg("can't view parent file"));}// 访问路径是目录,尝试是否有index的文件,如果有还是以文件访问if real_path.is_dir() {for index in &self.index {let new_path = real_path.join(index);if new_path.exists() {real_path = new_path;break;}}}// 访问为目录,如果启用目录访问,则返回当前的文件夹的内容if real_path.is_dir() {if !self.browse {return Ok(self.ret_error_msg("can't view parent file"));}let mut binary = BinaryMut::new();// ... let recv = RecvStream::only(binary.freeze());let builder = Response::builder().version(req.version().clone());let mut response = builder.header(HeaderName::CONTENT_TYPE, "text/html; charset=utf-8").body(recv).map_err(|_err| io::Error::new(io::ErrorKind::Other, ""))?;if self.disable_compress {response.headers_mut().insert(HeaderName::CONTENT_ENCODING, "");}return Ok(response);} else {// 访问为文件,判断当前的后缀,返回合适的mimetype,如果有合适的预压缩文件,也及时返回if self.is_hide_path(path.as_ref()) {return Ok(self.ret_error_msg("can't view file"));}// 获取后缀let extension = if let Some(s) = real_path.extension() {s.to_string_lossy().to_string()} else {String::new()};let application = DEFAULT_MIMETYPE.get(&*extension).unwrap_or(&"");//查找是否有合适的预压缩文件if let Some(accept) = req.headers().get_option_value(&HeaderName::ACCEPT_ENCODING) {for pre in &self.precompressed {// 得客户端发送支持该格式if !accept.contains(pre.as_bytes()) {continue;}let mut new = real_path.clone();new.as_mut_os_string().push(".");match &**pre {"gzip" => new.as_mut_os_string().push("gz"),"br" => new.as_mut_os_string().push("br"),_ => continue,};// 如果预压缩文件存在if new.exists() {println!("convert to new file {}", new.to_string_lossy());let file = File::open(new).await?;let mut recv = RecvStream::new_file(file, BinaryMut::new(), false);match &**pre {"gzip" => recv.set_compress_origin_gzip(),"br" => recv.set_compress_brotli(),_ => unreachable!(),}// ...return Ok(response);}}}if !real_path.exists() {return Ok(self.ret_error_msg("can't view file"));}// ...return Ok(response);}
}

结语

如此静态文件服务器则已初步实现,文件服务中的压缩及流式传输已基本完成

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

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

相关文章

12.23C语言 指针

& 地址运算符&#xff0c;用于取地址 /*注释内容*/ //注释一行 *的意思&#xff1a;1.算术运算符 2.用于指针声明int *ptr;表示这个变量是一个指针3.数组元素访问&#xff1a;在数组名后面使用 * 可以表示数组的起始地址。例如&#xff1a; int arr[5] {1, 2, 3, 4, 5…

《LeetCode力扣练习》代码随想录——双指针法(四数之和---Java)

《LeetCode力扣练习》代码随想录——双指针法&#xff08;四数之和—Java&#xff09; 刷题思路来源于 代码随想录 18. 四数之和 双指针 class Solution {public List<List<Integer>> fourSum(int[] nums, int target) {List<List<Integer>> resultnew…

05. Springboot admin集成Actuator(一)

目录 1、前言 2、Actuator监控端点 2.1、健康检查 2.2、信息端点 2.3、环境信息 2.4、度量指标 2.5、日志文件查看 2.6、追踪信息 2.7、Beans信息 2.8、Mappings信息 3、快速使用 2.1、添加依赖 2.2、添加配置文件 2.3、启动程序 4、自定义端点Endpoint 5、自定…

【时序分析】TimeGPT:首个时间序列分析基础大模型

TimeGPT:首个时间序列分析基础大模型 1. 论文解读1.1 研究背景1.2 TimeGPT详解1.2.1 时间序列预测问题基础1.2.2 TimeGPT架构1.2.3 训练数据集1.2.4 训练TimeGPT1.2.5 不确定性量化1.2.6 实验结果1.2.6.1 Zero-shot 推断1.2.6.2 Fine Tuning1.2.6.3 时间对比1.2.7 讨论2. Time…

干洗店预约上门取货小程序与互联网洗鞋店小程序开发制作功能方案

干洗店预约上门取货小程序与互联网洗鞋店小程序开发制作功能方案 一、洗衣洗鞋店小程序功能 1. 预约订单&#xff1a;忙碌时&#xff0c;您可以使用预约功能轻松获取洗衣服务。 2. 在线下单&#xff1a;用户可直接通过小程序在线下单&#xff0c;享受专人上门取货与配送服务。…

Unity2017升级到Unity2018在Window7上输出空异常错误问题

Unity2017升级到Unity2018在Window7上输出空异常错误问题 一、环境Window7二、现象Unity报空异常&#xff08;.NET 4.x Equivalent&#xff09;三、日志四、解决方案第一种解决方案第二种解决方案 一、环境Window7 二、现象Unity报空异常&#xff08;.NET 4.x Equivalent&…

小白入门之安装NodeJS

重生之我在大四学JAVA 第五章 安装NodeJS 如果你在购买我闲鱼的程序&#xff0c;请尽量使用node14版本 修改安装路径 接着傻瓜式NEXT 测试是否安装成功 如果上面没提示版本号&#xff0c;就按照前两章配置环境变量步骤配置下环境变量 设置镜像地址 npm config set re…

基于遗传算法特征选择及单层感知机模型的IMDB电影评论文本分类案例

基于遗传算法特征选择及单层感知机模型的IMDB电影评论文本分类案例 1.数据载入及处理2.感知机模型建立3.模型训练4.遗传算法进行特征选择注意 5.联系我们 1.数据载入及处理 import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import Dat…

【Flutter】黑白图片

一、将图片处理成黑白图片 //第一种方法CachedNetworkImage(imageUrl: imageUrl,width: 80,height: 80,fit: BoxFit.cover,color: Colors.black,//目标颜色colorBlendMode: BlendMode.color,//颜色混合模式)//第二种方法ShaderMask(shaderCallback: (Rect bounds) {return Lin…

lc1:两数之和

一句话描述问题&#xff1a;从数组中找两个数&#xff0c;等于目标值&#xff0c;返回他们的下标。 一句话题解M1&#xff1a;双层下标循环&#xff0c;第二层循环起点为上层起点1,两个循环数组值相加等于目标值&#xff0c;返回对应下标。 一句话题解M2&#xff1a;第一层循环…

基于多反应堆的高并发服务器【C/C++/Reactor】(中)Channel 模块的实现

在这篇文章中虽然实现了能够和多客户端建立连接&#xff0c;并且同时和多个客户端进行通信。 基于多反应堆的高并发服务器【C/C/Reactor】&#xff08;上&#xff09;-CSDN博客https://blog.csdn.net/weixin_41987016/article/details/135141316?spm1001.2014.3001.5501但是有…

ChatGPT4与ArcGIS Pro3助力AI 地理空间分析和可视化及助力科研论文写作

在地学领域&#xff0c;ArcGIS几乎成为了每位科研工作者作图、数据分析的必备工具&#xff0c;而ArcGIS Pro3除了良好地继承了ArcMap强大的数据管理、制图、空间分析等能力&#xff0c;还具有二三维融合、大数据、矢量切片制作及发布、任务工作流、时空立方体等特色功能&#x…

抖店怎么做?新手又该如何从头开始运营?

我是电商珠珠 抖店发展了将近4年时间&#xff0c;一直都备受关注。第一是因为他的门槛低&#xff0c;第二是他的玩法和传统有所差别&#xff0c;第三就是流量来源渠道比较广。 这一年所立的flag不到最后关头绝对不能倒&#xff0c;所以就会有很多人奔着这几点来尝试做店&…

MATLAB遗传算法工具箱的三种使用方法

MATLAB中有三种调用遗传算法的方式&#xff1a; 一、遗传算法的开源文件 下载“gatbx”压缩包文件&#xff0c;解压后&#xff0c;里面有多个.m文件&#xff0c;可以看到这些文件的编辑日期都是1998年&#xff0c;很古老了。 这些文件包含了遗传算法的基础操作&#xff0c;包含…

ebay倒计时活动攻略,ebay倒计时活动怎么做的?——站斧浏览器

ebay倒计时活动攻略 在ebay上做倒计时活动时&#xff0c;可以参考以下攻略&#xff1a; 制定合理的ebay优惠方案。可以根据消费者的需求和购买习惯&#xff0c;制定不同的优惠方案&#xff0c;例如满减、折扣、赠品等。同时&#xff0c;要保证优惠方案的真实性和公平性&#…

wordpress主题modown v8.81+erphpdown v16.0无限制无授权开心版

修复bug&#xff08;v8.81 2023.03.07&#xff09; 新增文章页正文下面常见问题手风琴模块&#xff0c;可设置显示文章的更新日期而不是发布日期&#xff0c;首页幻灯片支持指定文章、支持一个大图4个小图显示&#xff0c;grid网格列表支持显示简介&#xff0c;前台个人中心里显…

Qt 多线程用法

文章目录 开发平台QThread 类 moveToThreadQtConcurrent::run QFutureWatcherQThreadPool QRunnable 开发平台 项目说明OSwin10 x64Qt6.6compilermsvc2022构建工具cmake QThread 类 moveToThread 写一个简单的例子吧,比较容易理解,方便入门. 也可以看出这种方式,对于线程…

如何使用 Selenium 实现自动化操作?

本篇咱们来谈谈Selenium自动化脚本是如何工作的&#xff0c;以及如何实现一个简单的自动化示例&#xff1b; 一、关于Selenium 1.1、为什么选择它作为web自动化的测试工具&#xff1f; 选择Selenium作为web自动化测试工具的原因&#xff08;面试也许会问&#xff09;&#xff…

NiNNet

目录 一、网络介绍 1、全连接层存在的问题 2、NiN的解决方案(NiN块) 3、NiN架构 4、总结 二、代码实现 1、定义NiN卷积块 2、NiN模型 3、训练模型 一、网络介绍 NiN&#xff08;Network in Network&#xff09;是一种用于图像识别任务的卷积神经网络模型。它由谷歌研究…

【电路笔记】-串联电容器

串联电容器 文章目录 串联电容器1、概述2、示例13、示例34、总结 当电容器以菊花链方式连接在一条线上时&#xff0c;它们就串联在一起。 1、概述 对于串联电容器&#xff0c;流过电容器的充电电流 ( i C i_C iC​ ) 对于所有电容器来说都是相同的&#xff0c;因为它只有一条…