use http::{header, status::StatusCode};
use pingora::{Error};
use pingora::modules::http::compression::{ResponseCompression};
use pingora::http::ResponseHeader;
use std::path::{Path, PathBuf};
use pingora::proxy::Session;
use crate::compression_algorithm::{find_matches, CompressionAlgorithm};
pub(crate) struct Compression<'a> {
precompressed: &'a [CompressionAlgorithm],
precompressed_active: Option<CompressionAlgorithm>,
dynamic: bool,
}
impl<'a> Compression<'a> {
pub(crate) fn new(
session: &Session,
precompressed: &'a [CompressionAlgorithm],
) -> Self {
Self {
precompressed,
precompressed_active: None,
dynamic: session
.downstream_modules_ctx
.get::<ResponseCompression>()
.is_some_and(|compression| compression.is_enabled()),
}
}
pub(crate) fn rewrite_path(
&mut self,
session: &Session,
path: &Path,
) -> Option<PathBuf> {
if self.precompressed.is_empty() {
return None;
}
let filename = path.file_name()?;
let requested = session.req_header().headers.get(header::ACCEPT_ENCODING)?;
let overlap = find_matches(requested.to_str().ok()?, self.precompressed);
for algorithm in overlap {
let mut candidate_name = filename.to_os_string();
candidate_name.push(".");
candidate_name.push(algorithm.ext());
let mut candidate_path = path.to_path_buf();
candidate_path.set_file_name(candidate_name);
if candidate_path.is_file() {
self.precompressed_active = Some(algorithm);
return Some(candidate_path);
}
}
None
}
pub(crate) fn transform_header(
&mut self,
_session: &mut Session,
mut header: Box<ResponseHeader>,
) -> Result<Box<ResponseHeader>, Box<Error>> {
let mut header =
if header.status != StatusCode::OK && header.status != StatusCode::PARTIAL_CONTENT {
header
} else if let Some(algorithm) = self.precompressed_active {
header.insert_header(header::CONTENT_ENCODING, algorithm.name())?;
header
} else {
header
};
if !self.precompressed.is_empty() || self.dynamic {
header.insert_header(header::VARY, "Accept-Encoding")?;
}
Ok(header)
}
}