feat: enable all HTTP compression algorithms by default
Some checks failed
CI / check (push) Failing after 14m38s
CI / build (push) Has been skipped

- COMPRESSION_ALGORITHMS defaults to 'all' when unset

- Support 'none'/'off' to explicitly disable compression

- Add parse_compression_algorithms helper and unit tests

- Update .env.example comment to reflect new default
This commit is contained in:
xfy 2026-06-17 17:03:17 +08:00
parent 634f733e36
commit 05b9a3a595
2 changed files with 149 additions and 15 deletions

View File

@ -28,7 +28,7 @@ SSR_CACHE_SECS=3600
# Compression algorithms for HTTP responses. # Compression algorithms for HTTP responses.
# Comma-separated list, case-insensitive. Supported: gzip, brotli (or br), deflate, zstd. # Comma-separated list, case-insensitive. Supported: gzip, brotli (or br), deflate, zstd.
# Leave empty or unset to disable compression entirely. # Use "all" to enable everything (default when unset); use "none" or "off" to disable.
COMPRESSION_ALGORITHMS=gzip,brotli,deflate,zstd COMPRESSION_ALGORITHMS=gzip,brotli,deflate,zstd
# Image serving cache headers (hardcoded defaults) # Image serving cache headers (hardcoded defaults)

View File

@ -28,18 +28,42 @@ mod theme;
mod utils; mod utils;
mod webp; mod webp;
/// 根据 COMPRESSION_ALGORITHMS 环境变量构造 CompressionLayer。 /// 压缩算法配置。
/// 环境变量为空或未设置时返回 None表示不启用压缩。
#[cfg(feature = "server")] #[cfg(feature = "server")]
fn compression_layer_from_env() -> Option<tower_http::compression::CompressionLayer> { #[derive(Debug, Clone, Copy, PartialEq, Eq)]
use tower_http::compression::CompressionLayer; struct CompressionAlgorithms {
gzip: bool,
brotli: bool,
deflate: bool,
zstd: bool,
}
let env = std::env::var("COMPRESSION_ALGORITHMS").unwrap_or_default(); #[cfg(feature = "server")]
impl CompressionAlgorithms {
fn all_enabled() -> Self {
Self {
gzip: true,
brotli: true,
deflate: true,
zstd: true,
}
}
fn is_empty(&self) -> bool {
!self.gzip && !self.brotli && !self.deflate && !self.zstd
}
}
/// 解析 COMPRESSION_ALGORITHMS 环境变量值。
/// ""、"none"、"off" 返回 None"all" 或未识别到任何算法时启用全部。
#[cfg(feature = "server")]
fn parse_compression_algorithms(env: &str) -> Option<CompressionAlgorithms> {
let env = env.trim(); let env = env.trim();
if env.is_empty() { if env.is_empty() || env.eq_ignore_ascii_case("none") || env.eq_ignore_ascii_case("off") {
return None; return None;
} }
let mut all = false;
let mut gzip = false; let mut gzip = false;
let mut brotli = false; let mut brotli = false;
let mut deflate = false; let mut deflate = false;
@ -47,6 +71,7 @@ fn compression_layer_from_env() -> Option<tower_http::compression::CompressionLa
for part in env.split(',') { for part in env.split(',') {
match part.trim().to_lowercase().as_str() { match part.trim().to_lowercase().as_str() {
"all" => all = true,
"gzip" => gzip = true, "gzip" => gzip = true,
"brotli" | "br" => brotli = true, "brotli" | "br" => brotli = true,
"deflate" => deflate = true, "deflate" => deflate = true,
@ -58,16 +83,39 @@ fn compression_layer_from_env() -> Option<tower_http::compression::CompressionLa
} }
} }
if !gzip && !brotli && !deflate && !zstd { if all {
return Some(CompressionAlgorithms::all_enabled());
}
let algorithms = CompressionAlgorithms {
gzip,
brotli,
deflate,
zstd,
};
if algorithms.is_empty() {
return None; return None;
} }
let mut layer = CompressionLayer::new(); Some(algorithms)
layer = layer.gzip(gzip); }
layer = layer.br(brotli);
layer = layer.deflate(deflate); /// 根据 COMPRESSION_ALGORITHMS 环境变量构造 CompressionLayer。
layer = layer.zstd(zstd); /// 未设置或设置为 "all" 时启用全部算法;设置为 ""、"none" 或 "off" 时禁用。
Some(layer) #[cfg(feature = "server")]
fn compression_layer_from_env() -> Option<tower_http::compression::CompressionLayer> {
use tower_http::compression::CompressionLayer;
let env = std::env::var("COMPRESSION_ALGORITHMS").unwrap_or_else(|_| "all".to_string());
let algorithms = parse_compression_algorithms(&env)?;
Some(
CompressionLayer::new()
.gzip(algorithms.gzip)
.br(algorithms.brotli)
.deflate(algorithms.deflate)
.zstd(algorithms.zstd),
)
} }
/// 根据请求路径和方法决定公开页面的 Cache-Control 头。 /// 根据请求路径和方法决定公开页面的 Cache-Control 头。
@ -247,7 +295,7 @@ fn main() {
#[cfg(all(test, feature = "server"))] #[cfg(all(test, feature = "server"))]
mod tests { mod tests {
use super::cache_control_for_path; use super::{cache_control_for_path, parse_compression_algorithms, CompressionAlgorithms};
use axum::http::Method; use axum::http::Method;
fn cache_value(path: &str, method: Method) -> Option<String> { fn cache_value(path: &str, method: Method) -> Option<String> {
@ -314,4 +362,90 @@ mod tests {
Some("public, max-age=300, stale-while-revalidate=3600".to_string()) Some("public, max-age=300, stale-while-revalidate=3600".to_string())
); );
} }
#[test]
fn compression_all_enables_everything() {
assert_eq!(
parse_compression_algorithms("all"),
Some(CompressionAlgorithms::all_enabled())
);
}
#[test]
fn compression_default_env_is_all() {
// 模拟未设置环境变量时的默认值
assert_eq!(
parse_compression_algorithms("all"),
Some(CompressionAlgorithms::all_enabled())
);
}
#[test]
fn compression_empty_none_off_disable() {
assert_eq!(parse_compression_algorithms(""), None);
assert_eq!(parse_compression_algorithms("none"), None);
assert_eq!(parse_compression_algorithms("NONE"), None);
assert_eq!(parse_compression_algorithms("off"), None);
assert_eq!(parse_compression_algorithms("OFF"), None);
}
#[test]
fn compression_single_algorithm() {
assert_eq!(
parse_compression_algorithms("gzip"),
Some(CompressionAlgorithms {
gzip: true,
brotli: false,
deflate: false,
zstd: false,
})
);
assert_eq!(
parse_compression_algorithms("br"),
Some(CompressionAlgorithms {
gzip: false,
brotli: true,
deflate: false,
zstd: false,
})
);
}
#[test]
fn compression_multiple_algorithms() {
assert_eq!(
parse_compression_algorithms("gzip, zstd"),
Some(CompressionAlgorithms {
gzip: true,
brotli: false,
deflate: false,
zstd: true,
})
);
}
#[test]
fn compression_case_insensitive_and_whitespace_tolerant() {
assert_eq!(
parse_compression_algorithms("GZIP, Brotli, Deflate, Zstd"),
Some(CompressionAlgorithms::all_enabled())
);
assert_eq!(
parse_compression_algorithms(" gzip , br , deflate , zstd "),
Some(CompressionAlgorithms::all_enabled())
);
}
#[test]
fn compression_unknown_algorithms_are_ignored() {
assert_eq!(
parse_compression_algorithms("gzip, unknown, lz4"),
Some(CompressionAlgorithms {
gzip: true,
brotli: false,
deflate: false,
zstd: false,
})
);
}
} }