wmproxy
已用Rust
实现http/https
代理,socks5
代理, websocket
代理,反向代理, 静态文件服务器,四层TCP/UDP转发,七层负载均衡,内网穿透等,力争打造和nginx的性能。
国内: https://gitee.com/tickbh/wmproxy
github: https://github.com/tickbh/wmproxy
静态文件服务器是一种用于提供静态文件(如HTML、CSS、JavaScript、图片等)的网络服务器。当客户端(如浏览器)请求这些文件时,静态文件服务器会直接从文件系统中获取文件并返回给客户端,而不需要经过任何处理或动态生成。
静态文件服务器的主要特点包括:
以下是此次设计时的两个注意要点:
断点续传支持:静态资源服务器通常支持断点续传。当用户下载大文件时,如果出现网络中断或其他原因导致下载中断,静态资源服务器可以记录中断位置,当用户重新请求下载时,可以恢复到中断的位置继续下载,提供更好的下载体验。
缓存和浏览器缓存支持:静态资源服务器可以通过设置合适的缓存策略,利用浏览器缓存来提高性能和减少网络流量。静态资源可以设置缓存过期时间,当浏览器再次请求相同资源时,可以直接从缓存中获取,减少了网络请求和传输时间。
对当前项目启动8080端口监听
BASHwmproxy file-server -l :8080
浏览器缓存是一种机制,它将已访问过的资源的副本存储在浏览器中,以便在将来更快地加载相同的资源。可以提高网页性能、减轻服务器负担、节省网络带宽并提供更好的用户体验。在开发和优化网站时,合理地利用浏览器缓存可以显著提升网站的整体性能。
一个文件是否被修改过主要依靠以下两个属性:
Etag(Entity Tag):
Last-Modified:
通常文件服务器返回时会附带该两个参数由客户端协带进行是否读取缓存数据。
控制缓存策略: 控制过期时间主要由两种方式:
Expires:
HTTP 1.0
的头部字段,用于指定资源过期的时间。Cache-Control:
HTTP 1.1
的头部字段,提供了更细粒度的缓存控制。由于Expires的客户端时间和服务端时间可能存在的不一致,此处我们服务器不做Expires的实现,如果配置过期1d
之类,将转化成:
cache-control: max-age=86400
进行时间控制。
以下我们进行测试:
我们先对资源wmproxy.md
curl.exe http://127.0.0.1:8082/wmproxy.md -i HTTP/1.1 200 OK content-type: text/plain; charset=utf-8 transfer-encoding: chunked Server: wmproxy cache-control: max-age=1000 Date: Tue, 23 Jan 2024 09:12:05 GMT Last-Modified: Tue, 23 Jan 2024 09:11:30 GMT etag: 65af82c2-11c1
从返回结果中我们可以得知缓存时间1000s,最后修改时间及etag的值。让我们添加IF-NONE-MATCH进行测试
HTTPcurl.exe http://127.0.0.1:8082/wmproxy.md -i -H "If-Modified-Since: Tue, 23 Jan 2024 09:11:30 GMT" HTTP/1.1 304 Not Modified Server: wmproxy cache-control: max-age=1000 Date: Tue, 23 Jan 2024 09:18:58 GMT Last-Modified: Tue, 23 Jan 2024 09:11:30 GMT etag: 65af82c2-11c1 content-length: 0
可以尝试添加最后修改时间的
HTTPcurl.exe http://127.0.0.1:8082/wmproxy.md -i -H "IF-NONE-MATCH: 65af82c2-11c1" HTTP/1.1 304 Not Modified Server: wmproxy cache-control: max-age=1000 Date: Tue, 23 Jan 2024 09:18:58 GMT Last-Modified: Tue, 23 Jan 2024 09:11:30 GMT etag: 65af82c2-11c1 content-length: 0
一样会进行缓存,通常浏览器会将两个值做为传参一起写入,文件发生变更,将会使缓存失败,重新返回200请求
HTTPcurl.exe http://127.0.0.1:8082/wmproxy.md -i -H "IF-NONE-MATCH: 65af82c2-11c1" HTTP/1.1 200 OK ...
断点续传也就是客户端可以指定传输范围进行传输,该标准定义在RFC7233
HTTP 协议范围请求允许服务器只发送 HTTP 消息的一部分到客户端。范围请求在传送大的媒体文件,或者与文件下载的断点续传功能搭配使用时非常有用。
假如在响应中存在 Accept-Ranges
首部(并且它的值不为"none"),那么表示该服务器支持范围请求。例如,你可以使用 cURL 发送一个 HEAD 请求来进行检测。
curl.exe -I http://127.0.0.1:8080/Cargo.toml HTTP/1.1 200 OK ... accept-ranges: bytes content-length: 1565
在上面的响应中, Accept-Ranges: bytes
表示界定范围的单位是 bytes
。这里 Content-Length
也是有效信息,因为它提供了要检索的文件的完整大小。
如果返回的Accept-Ranges: none
则表示不支持,如果未返回则表示可能不支持范围请求。
假如服务器支持范围请求的话,你可以使用 Range
首部来生成该类请求。该首部指示服务器应该返回文件的哪一或哪几部分。
我们可以请求资源的某一部分。这次我们依然用 cURL 来进行测试。"-H" 选项可以在请求中追加一个首部行,在这个例子中,是用 Range 首部来请求图片文件的前 1024 个字节。
curl http://127.0.0.1:8080/Cargo.toml -i -H "Range: bytes=0-1023"
这样生成的请求如下:
httpGET /Cargo.toml HTTP/1.1 Host: 127.0.0.1:8080 User-Agent: curl/8.0.1 Accept: */* Range: bytes=0-1023
服务器端会返回状态码为 206 Partial Content 的响应:
httpHTTP/1.1 206 Partial Content content-type: text/plain; charset=utf-8 transfer-encoding: chunked Server: wmproxy Date: Tue, 23 Jan 2024 07:59:20 +0000 Last-Modified: Tue, 23 Jan 2024 02:33:35 +0000 etag: 65af257f-61d content-range: bytes 0-1023/1565 ... (binary content)
在这里,Content-Length
首部现在用来表示先前请求范围的大小(而不是整个文件的大小)。Content-Range
响应首部则表示这一部分内容在整个资源中所处的位置。
发起请求和单一范围类似,只是在请求的时候多个范围地址,如:
curl http://www.example.com -i -H "Range: bytes=0-50, 100-150"
返回内容为Content-Type: multipart/byteranges boundary=THIS_STRING_SEPARATES
并在body中以该字符做分隔成多数据块,如。
httpHTTP/1.1 206 Partial Content Content-Type: multipart/byteranges; boundary=3d6b6a416f9b5 Content-Length: 282 --3d6b6a416f9b5 Content-Type: text/html Content-Range: bytes 0-50/1270 <!doctype html> <html> <head> <title>Example Do --3d6b6a416f9b5 Content-Type: text/html Content-Range: bytes 100-150/1270 eta http-equiv="Content-type" content="text/html; c --3d6b6a416f9b5--
请求多范围要针对解析body块,相对来说数据块请求比较割裂。在HTTP2中可以多流式请求范围或者用keep-alive同时发起多个请求,相对比较难与处理数据块,暂时不做实现。
当(中断之后)重新开始请求更多资源片段的时候,必须确保自从上一个片段被接收之后该资源没有进行过修改。
通过 If-Range
请求首部可以用来生成条件式范围请求:假如条件满足的话,条件请求就会生效,服务器会返回状态码为 206 Partial
的响应,以及相应的消息主体。假如条件未能得到满足,那么就会返回状态码为 200 OK 的响应,同时返回整个资源。该首部可以与 Last-Modified 验证器或者 ETag 一起使用,但是二者不能同时使用。
If-Range: Wed, 21 Oct 2015 07:28:00 GMT 或者 If-Range: 65af257f-61d
与范围请求相关的有三种状态:
206 Partial Content
状态码。416 Requested Range Not Satisfiable
(请求的范围无法满足)状态码。200 OK
状态码。关于文件服务器的相关源码均在file_server
关于时间格式由RFC2822控制,这里我们用的解析库为chrono
。
关于etag,我们这里采用的与nginx一致的算法,文件最后修改时间16进制-文件长度16进制。例:ETag: 65af8536-11c2
文件长度为:
10进制为->4546 转为16进制->11c2
文件最后修改时间:
标准日期格式->Tue, 23 Jan 2024 09:21:58 GMT 转为秒->1706001718 转为16进制->65af8536
RUSTpub fn calc_etag(data: &Metadata) -> String {
let mut seconds = 0;
let len = data.len();
if let Ok(last) = data.modified() {
if let Ok(n) = last.duration_since(SystemTime::UNIX_EPOCH) {
seconds = n.as_secs();
}
}
format!("{:x}-{:x}", seconds, len)
}
问蒙服务框架
->%E9%97%AE%E8%92%99%E6%9C%8D%E5%8A%A1%E6%A1%86%E6%9E%B6
会进行一次转码,我们在path中如果存在%
的时候,尝试进行一次转码,如果成功取新的path值。RUSTif path.contains("%") {
if let Ok(p) = Url::url_decode(&path) {
path = p;
}
}
start_pos
及end_pos
来表示文件的起始及结束点。RUST#[derive(Debug)]
struct InnerReceiver {
receiver: Option<Receiver<(bool, Binary)>>,
file: Option<Box<File>>,
cache_buf: Vec<u8>,
/// 数据包大小
data_size: u64,
/// 文件专用, 起始点
start_pos: Option<u64>,
/// 文件专用, 结束点
end_pos: Option<u64>,
}
本章中讲述了浏览器缓存的设计(ETAG, Last-Modified, Cache-Control, Expires)及断点续传(Accept-Ranges: bytes)的实现流程及相关的部分源码,希望可以让你更了解文件服务器内部的原理组成。
点击 [关注],[在看],[点赞] 是对作者最大的支持
本文作者:问蒙服务框架
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!