2个不稳定版本
0.2.0 | 2024年5月12日 |
---|---|
0.1.0 | 2024年5月8日 |
#494 在 HTTP服务器 中
在 虚拟主机模块 中使用
120KB
2.5K SLoC
Pingora的静态文件模块
该Crate允许通过Pingora代理扩展功能,以便从目录中提供静态文件。
支持的功能
GET
和HEAD
请求- 可配置的目录索引文件(默认为
index.html
) - 可配置的页面,在404未找到错误时显示,而不是标准的错误页面
- 通过
If-Modified-Since
、If-Unmodified-Since
、If-Match
、If-None-Match
匹配HTTP头部进行条件请求 - 通过
Range
和If-Range
HTTP头部进行字节范围请求 - 压缩支持:提供文件的预压缩版本(支持gzip、zlib deflate、compress、Brotli、Zstandard算法)
- 压缩支持:通过Pingora进行动态压缩(目前支持gzip、Brotli和Zstandard算法)
已知限制
- 不支持多个字节范围的请求,将会返回整个文件。实现此功能的复杂度不值得在如此罕见的使用场景中使用。
- 在Pingora框架中目前无法支持零拷贝数据传输(即sendfile)。
代码示例
通常您会在 request_filter
阶段创建一个 StaticFilesHandler
实例并调用它。如果无条件调用,它将处理所有请求,因此后续阶段将完全无法到达。
use async_trait::async_trait;
use pingora_core::Result;
use pingora_core::upstreams::peer::HttpPeer;
use pingora_proxy::{ProxyHttp, Session};
use module_utils::RequestFilter;
use static_files_module::StaticFilesHandler;
pub struct MyServer {
static_files_handler: StaticFilesHandler,
}
#[async_trait]
impl ProxyHttp for MyServer {
type CTX = <StaticFilesHandler as RequestFilter>::CTX;
fn new_ctx(&self) -> Self::CTX {
StaticFilesHandler::new_ctx()
}
async fn request_filter(
&self,
session: &mut Session,
ctx: &mut Self::CTX
) -> Result<bool> {
self.static_files_handler.handle(session, ctx).await
}
async fn upstream_peer(
&self,
_session: &mut Session,
_ctx: &mut Self::CTX,
) -> Result<Box<HttpPeer>> {
panic!("Unexpected, upstream_peer stage reached");
}
}
您可以通过直接指定其配置来创建一个 StaticFilesHandler
实例。
use static_files_module::{StaticFilesConf, StaticFilesHandler};
let conf = StaticFilesConf {
root: Some("/var/www/html".into()),
..Default::default()
};
let static_files_handler: StaticFilesHandler = conf.try_into().unwrap();
还可以从命令行选项和配置文件创建配置,扩展默认的Pingora数据结构。宏module_utils::merge_opt
和module_utils::merge_conf
分别帮助合并命令行选项和配置结构,而module_utils::FromYaml
特质帮助读取配置文件。
use log::error;
use pingora_core::server::configuration::{Opt as ServerOpt, ServerConf};
use pingora_core::server::Server;
use module_utils::{FromYaml, merge_opt, merge_conf};
use serde::Deserialize;
use static_files_module::{StaticFilesConf, StaticFilesHandler, StaticFilesOpt};
use std::fs::File;
use std::io::BufReader;
use structopt::StructOpt;
// The command line flags from both structures are merged, so that the user doesn't need to
// care which structure they belong to.
#[merge_opt]
struct MyServerOpt {
server: ServerOpt,
static_files: StaticFilesOpt,
}
// The configuration settings from both structures are merged, so that the user doesn't need to
// care which structure they belong to.
#[merge_conf]
struct MyServerConf {
server: ServerConf,
static_files: StaticFilesConf,
}
let opt = MyServerOpt::from_args();
let conf = opt
.server
.conf
.as_ref()
.and_then(|path| MyServerConf::load_from_yaml(path).ok())
.unwrap_or_else(MyServerConf::default);
let mut server = Server::new_with_opt_and_conf(opt.server, conf.server);
server.bootstrap();
let mut static_files_conf = conf.static_files;
static_files_conf.merge_with_opt(opt.static_files);
let static_files_handler: StaticFilesHandler = static_files_conf.try_into().unwrap();
要查看完整的更全面的代码,请参阅存储库中的single-static-root示例。
压缩支持
您可以通过precompressed
配置设置激活对所选压缩算法的支持
use static_files_module::{CompressionAlgorithm, StaticFilesConf};
let conf = StaticFilesConf {
root: Some("/var/www/html".into()),
precompressed: vec![CompressionAlgorithm::Gzip, CompressionAlgorithm::Brotli],
..Default::default()
};
这将使StaticFilesHandler
寻找请求文件的gzip(.gz
)和Brotli(.br
)版本,并在客户端支持的情况下提供这些预压缩文件。例如,请求file.txt
并发送HTTP头Accept-Encoding: br, gzip
的客户端将收到file.txt.br
文件,如果找不到,则收到file.txt.gz
文件。StaticFilesHandler
寻找预压缩文件的顺序由客户端的压缩算法首选项决定。
还可以通过Pingora的下游压缩动态地实时压缩文件。为此,在调用StaticFilesHandler
之前激活会话的压缩
async fn request_filter(
&self,
session: &mut Session,
ctx: &mut Self::CTX
) -> Result<bool> {
session.downstream_compression.adjust_level(3);
self.static_files_handler.handle(session, ctx).await
}
依赖关系
~38-53MB
~1M SLoC