0.34.6_repo_cleanup

This commit is contained in:
Mateusz Gruszczyński committed 2026-10-22 09:31:15 +02:00
1 parent ce1a90397c
commit 133d2f0f91
273 files changed
+5969 -6926

No files matched your search

+345
View File
@@ -0,0 +1,345 @@
use anyhow::{ensure, Result};
use axum::{body::Body, response::Response};
use http::{HeaderMap, HeaderValue, Method, Request};
use serde::{Deserialize, Serialize};
use std::convert::Infallible;
use tower::{service_fn, ServiceExt};
use tower_http::compression::{
predicate::{DefaultPredicate, Predicate},
Compression, CompressionLevel,
};
#[derive(Clone, Copy)]
struct MinimumSize(u64);
impl Predicate for MinimumSize {
fn should_compress<B: http_body::Body>(&self, response: &http::Response<B>) -> bool {
let size = response
.headers()
.get(http::header::CONTENT_LENGTH)
.and_then(|v| v.to_str().ok())
.and_then(|v| v.parse::<u64>().ok())
.or_else(|| response.body().size_hint().exact());
size.is_none_or(|size| size >= self.0)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default, deny_unknown_fields)]
pub struct Settings {
pub gzip: bool,
pub br: bool,
pub zstd: bool,
pub deflate: bool,
pub min_bytes: u64,
}
impl Default for Settings {
fn default() -> Self {
Self {
gzip: true,
br: true,
zstd: true,
deflate: false,
min_bytes: 1024,
}
}
}
impl Settings {
pub fn validate(&self) -> Result<()> {
ensure!(
(32..=1_048_576).contains(&self.min_bytes),
"compression.min_bytes must be 32..1048576"
);
Ok(())
}
pub fn enabled(&self) -> bool {
self.gzip || self.br || self.zstd || self.deflate
}
pub fn scoped(&self, enabled: bool) -> Self {
if enabled {
return self.clone();
}
let mut disabled = self.clone();
disabled.gzip = false;
disabled.br = false;
disabled.zstd = false;
disabled.deflate = false;
disabled
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default, deny_unknown_fields)]
pub struct Overrides {
pub enabled: Option<bool>,
pub gzip: Option<bool>,
pub br: Option<bool>,
pub zstd: Option<bool>,
pub deflate: Option<bool>,
}
impl Overrides {
pub fn resolve(&self, global: &Settings, inherited: &Settings) -> Settings {
if self.enabled == Some(false) {
return global.scoped(false);
}
let mut resolved = if self.enabled == Some(true) {
global.clone()
} else {
inherited.clone()
};
if let Some(enabled) = self.gzip {
resolved.gzip = enabled && global.gzip;
}
if let Some(enabled) = self.br {
resolved.br = enabled && global.br;
}
if let Some(enabled) = self.zstd {
resolved.zstd = enabled && global.zstd;
}
if let Some(enabled) = self.deflate {
resolved.deflate = enabled && global.deflate;
}
resolved.min_bytes = global.min_bytes;
resolved
}
}
#[derive(Clone)]
pub struct Skip;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AppliedEncoding {
Gzip,
Brotli,
Zstd,
Deflate,
}
impl AppliedEncoding {
const PREFERENCE: [Self; 4] = [Self::Zstd, Self::Brotli, Self::Gzip, Self::Deflate];
pub(crate) fn as_str(self) -> &'static str {
match self {
Self::Gzip => "gzip",
Self::Brotli => "br",
Self::Zstd => "zstd",
Self::Deflate => "deflate",
}
}
fn enabled(self, settings: &Settings) -> bool {
match self {
Self::Gzip => settings.gzip,
Self::Brotli => settings.br,
Self::Zstd => settings.zstd,
Self::Deflate => settings.deflate,
}
}
fn from_header(value: &str) -> Option<Self> {
match value.trim().to_ascii_lowercase().as_str() {
"gzip" => Some(Self::Gzip),
"br" => Some(Self::Brotli),
"zstd" => Some(Self::Zstd),
"deflate" => Some(Self::Deflate),
_ => None,
}
}
}
pub fn no_transform(headers: &HeaderMap) -> bool {
headers
.get_all("cache-control")
.iter()
.filter_map(|v| v.to_str().ok())
.any(|v| {
v.split(',').any(|directive| {
directive
.trim()
.split('=')
.next()
.unwrap_or("")
.trim()
.eq_ignore_ascii_case("no-transform")
})
})
}
pub(crate) fn request_headers(headers: &HeaderMap) -> HeaderMap {
let mut selected = HeaderMap::new();
for name in ["accept-encoding", "range", "cache-control"] {
for value in headers.get_all(name) {
selected.append(name, value.clone());
}
}
selected
}
fn eligible(
response: &Response,
request: &HeaderMap,
method: &Method,
settings: &Settings,
) -> bool {
if response.extensions().get::<Skip>().is_some()
|| !settings.enabled()
|| method == Method::HEAD
|| response.status().is_informational()
|| response.status().as_u16() == 204
|| response.status().as_u16() == 304
|| response.headers().contains_key("content-encoding")
|| response.headers().contains_key("content-range")
|| response.headers().contains_key("upgrade")
|| request.contains_key("range")
|| no_transform(request)
|| no_transform(response.headers())
{
return false;
}
!response
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.is_some_and(|v| {
v.split(';')
.next()
.unwrap_or("")
.trim()
.eq_ignore_ascii_case("text/event-stream")
})
}
fn quality(raw: &str) -> Option<u16> {
let value = raw.trim().parse::<f32>().ok()?;
if !(0.0..=1.0).contains(&value) {
return None;
}
Some((value * 1000.0).round() as u16)
}
pub(crate) fn preferred_encoding(
headers: &HeaderMap,
settings: &Settings,
) -> Option<AppliedEncoding> {
let mut explicit = [None; 4];
let mut wildcard = None;
for line in headers.get_all("accept-encoding").iter() {
let Ok(line) = line.to_str() else { continue };
for item in line.split(',') {
let mut parts = item.split(';');
let coding = parts.next().unwrap_or("").trim().to_ascii_lowercase();
if coding.is_empty() {
continue;
}
let mut q = 1000;
let mut valid = true;
for parameter in parts {
let Some((name, value)) = parameter.trim().split_once('=') else {
continue;
};
if name.trim().eq_ignore_ascii_case("q") {
if let Some(parsed) = quality(value) {
q = parsed;
} else {
valid = false;
}
}
}
if !valid {
continue;
}
if coding == "*" {
wildcard = Some(wildcard.map_or(q, |current: u16| current.max(q)));
continue;
}
if let Some(encoding) = AppliedEncoding::from_header(&coding) {
let index = match encoding {
AppliedEncoding::Zstd => 0,
AppliedEncoding::Brotli => 1,
AppliedEncoding::Gzip => 2,
AppliedEncoding::Deflate => 3,
};
explicit[index] = Some(explicit[index].map_or(q, |current: u16| current.max(q)));
}
}
}
let mut best = None;
let mut best_q = 0;
for (index, encoding) in AppliedEncoding::PREFERENCE.into_iter().enumerate() {
if !encoding.enabled(settings) {
continue;
}
let q = explicit[index].or(wildcard).unwrap_or(0);
if q > best_q {
best = Some(encoding);
best_q = q;
}
}
best.filter(|_| best_q > 0)
}
pub async fn encode(
response: Response,
headers: &HeaderMap,
method: &Method,
settings: &Settings,
) -> Response {
if !eligible(&response, headers, method, settings) {
return response;
}
let Some(selected) = preferred_encoding(headers, settings) else {
return response;
};
let mut response = Some(response);
let service = service_fn(move |_: Request<Body>| {
let response = response.take().expect("one-shot compression service");
async move { Ok::<_, Infallible>(response) }
});
let compression = Compression::new(service)
.gzip(settings.gzip)
.br(settings.br)
.zstd(settings.zstd)
.deflate(settings.deflate)
.quality(CompressionLevel::Fastest)
.compress_when(DefaultPredicate::new().and(MinimumSize(settings.min_bytes)));
let mut request = Request::new(Body::empty());
*request.method_mut() = method.clone();
request.headers_mut().insert(
"accept-encoding",
HeaderValue::from_static(selected.as_str()),
);
let mut response = match compression.oneshot(request).await {
Ok(response) => response.map(Body::new),
Err(never) => match never {},
};
if response.headers().contains_key("content-encoding") {
if let Some(applied) = response
.headers()
.get("content-encoding")
.and_then(|value| value.to_str().ok())
.and_then(AppliedEncoding::from_header)
{
response.extensions_mut().insert(applied);
}
if let Some(value) = response
.headers()
.get("etag")
.and_then(|v| v.to_str().ok())
.map(str::to_owned)
{
if !value.starts_with("W/") {
if let Ok(weak) = HeaderValue::from_str(&format!("W/{value}")) {
response.headers_mut().insert("etag", weak);
} else {
response.headers_mut().remove("etag");
}
}
}
for field in ["content-md5", "digest", "content-digest", "accept-ranges"] {
response.headers_mut().remove(field);
}
}
response
}
#[cfg(test)]
include!("tests.rs");