use axum::http::{HeaderMap, HeaderValue, header::ACCEPT_ENCODING};
fn strip_weak(etag: &str) -> &str {
etag.strip_prefix("W/").unwrap_or(etag)
}
pub(crate) fn if_none_match_matches(header_value: &str, quoted_etag: &str) -> bool {
let target = strip_weak(quoted_etag);
header_value.split(',').any(|candidate| {
let candidate = candidate.trim();
candidate == "*" || strip_weak(candidate) == target
})
}
pub(crate) fn encoding_qvalue(headers: &HeaderMap, encoding: &str) -> Option<f32> {
let header_value = headers
.get(ACCEPT_ENCODING)
.and_then(|v: &HeaderValue| v.to_str().ok())?;
let mut wildcard_q: Option<f32> = None;
for item in header_value
.split_whitespace()
.collect::<String>()
.split(',')
{
if item.is_empty() {
continue;
}
let mut parts = item.splitn(2, ";q=");
let token = parts.next().unwrap_or_default();
let q = parts
.next()
.map_or(1.0, |q| q.parse::<f32>().unwrap_or(0.0));
if token == encoding {
return (q > 0.0).then_some(q);
}
if token == "*" {
wildcard_q = Some(q);
}
}
wildcard_q.filter(|q| *q > 0.0)
}
#[cfg(test)]
mod tests {
use super::{encoding_qvalue, if_none_match_matches};
use axum::http::{HeaderMap, HeaderValue, header::ACCEPT_ENCODING};
fn check(header: &str, encoding: &str) -> bool {
let mut headers = HeaderMap::new();
headers.insert(ACCEPT_ENCODING, HeaderValue::from_str(header).unwrap());
encoding_qvalue(&headers, encoding).is_some()
}
fn qvalue(header: &str, encoding: &str) -> Option<f32> {
let mut headers = HeaderMap::new();
headers.insert(ACCEPT_ENCODING, HeaderValue::from_str(header).unwrap());
encoding_qvalue(&headers, encoding)
}
#[test]
fn qvalues() {
assert_eq!(qvalue("gzip", "gzip"), Some(1.0));
assert_eq!(qvalue("br;q=1.0, gzip;q=0.8", "gzip"), Some(0.8));
assert_eq!(qvalue("br;q=1.0, gzip;q=0.8", "br"), Some(1.0));
assert_eq!(qvalue("*;q=0.5", "gzip"), Some(0.5));
assert_eq!(qvalue("*, gzip;q=0", "gzip"), None);
assert_eq!(qvalue("*;q=0, gzip", "gzip"), Some(1.0));
assert_eq!(qvalue("gzip", "br"), None);
assert_eq!(qvalue("gzip;q=0", "gzip"), None);
}
#[test]
fn if_none_match() {
assert!(if_none_match_matches("\"abc\"", "\"abc\""));
assert!(if_none_match_matches("W/\"abc\"", "\"abc\""));
assert!(if_none_match_matches("\"abc\"", "W/\"abc\""));
assert!(if_none_match_matches("\"x\", \"abc\", \"y\"", "\"abc\""));
assert!(if_none_match_matches("*", "\"abc\""));
assert!(!if_none_match_matches("\"x\", \"y\"", "\"abc\""));
}
#[test]
fn accept_encoding() {
assert!(check("gzip", "gzip"));
assert!(check("gzip, compress, br", "gzip"));
assert!(check("br;q=1.0, gzip;q=0.8, *;q=0.1", "gzip"));
assert!(!check("gzip", "br"));
assert!(check("gzip, compress, br", "br"));
assert!(check("br;q=1.0, gzip;q=0.8, *;q=0.1", "br"));
assert!(!check("gzip", "compress"));
assert!(check("gzip, compress, br", "compress"));
assert!(check("br;q=1.0, gzip;q=0.8, *;q=0.1", "compress"));
assert!(!check("gzip", "zstd"));
assert!(!check("gzip, compress, br", "zstd"));
assert!(check("br;q=1.0, gzip;q=0.8, *;q=0.1", "zstd"));
assert!(!check("gzip;q=0", "gzip"));
assert!(!check("gzip;q=0.0", "gzip"));
assert!(!check("gzip;q=0.000", "gzip"));
assert!(!check("br;q=1.0, gzip;q=0.0", "gzip"));
assert!(!check("*;q=0", "gzip"));
assert!(!check("*;q=0.0", "gzip"));
}
}