rama_http/layer/compression/
layer.rsuse super::predicate::DefaultPredicate;
use super::{Compression, Predicate};
use crate::layer::util::compression::{AcceptEncoding, CompressionLevel};
use rama_core::Layer;
#[derive(Clone, Debug, Default)]
pub struct CompressionLayer<P = DefaultPredicate> {
accept: AcceptEncoding,
predicate: P,
quality: CompressionLevel,
}
impl<S, P> Layer<S> for CompressionLayer<P>
where
P: Predicate,
{
type Service = Compression<S, P>;
fn layer(&self, inner: S) -> Self::Service {
Compression {
inner,
accept: self.accept,
predicate: self.predicate.clone(),
quality: self.quality,
}
}
}
impl CompressionLayer {
pub fn new() -> Self {
Self::default()
}
pub fn gzip(mut self, enable: bool) -> Self {
self.accept.set_gzip(enable);
self
}
pub fn set_gzip(&mut self, enable: bool) -> &mut Self {
self.accept.set_gzip(enable);
self
}
pub fn deflate(mut self, enable: bool) -> Self {
self.accept.set_deflate(enable);
self
}
pub fn set_deflate(&mut self, enable: bool) -> &mut Self {
self.accept.set_deflate(enable);
self
}
pub fn br(mut self, enable: bool) -> Self {
self.accept.set_br(enable);
self
}
pub fn set_br(&mut self, enable: bool) -> &mut Self {
self.accept.set_br(enable);
self
}
pub fn zstd(mut self, enable: bool) -> Self {
self.accept.set_zstd(enable);
self
}
pub fn set_zstd(&mut self, enable: bool) -> &mut Self {
self.accept.set_zstd(enable);
self
}
pub fn quality(mut self, quality: CompressionLevel) -> Self {
self.quality = quality;
self
}
pub fn set_quality(&mut self, quality: CompressionLevel) -> &mut Self {
self.quality = quality;
self
}
pub fn compress_when<C>(self, predicate: C) -> CompressionLayer<C>
where
C: Predicate,
{
CompressionLayer {
accept: self.accept,
predicate,
quality: self.quality,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::dep::http_body_util::BodyExt;
use crate::{header::ACCEPT_ENCODING, Body, Request, Response};
use rama_core::service::service_fn;
use rama_core::{Context, Service};
use std::convert::Infallible;
use tokio::fs::File;
use tokio_util::io::ReaderStream;
async fn handle(_req: Request) -> Result<Response, Infallible> {
let file = File::open("Cargo.toml").await.expect("file missing");
let stream = ReaderStream::new(file);
let body = Body::from_stream(stream);
Ok(Response::new(body))
}
#[tokio::test]
async fn accept_encoding_configuration_works() -> Result<(), rama_core::error::BoxError> {
let deflate_only_layer = CompressionLayer::new()
.quality(CompressionLevel::Best)
.br(false)
.gzip(false);
let service = deflate_only_layer.layer(service_fn(handle));
let request = Request::builder()
.header(ACCEPT_ENCODING, "gzip, deflate, br")
.body(Body::empty())?;
let response = service.serve(Context::default(), request).await?;
assert_eq!(response.headers()["content-encoding"], "deflate");
let body = response.into_body();
let bytes = body.collect().await.unwrap().to_bytes();
let deflate_bytes_len = bytes.len();
let br_only_layer = CompressionLayer::new()
.quality(CompressionLevel::Best)
.gzip(false)
.deflate(false);
let service = br_only_layer.layer(service_fn(handle));
let request = Request::builder()
.header(ACCEPT_ENCODING, "gzip, deflate, br")
.body(Body::empty())?;
let response = service.serve(Context::default(), request).await?;
assert_eq!(response.headers()["content-encoding"], "br");
let body = response.into_body();
let bytes = body.collect().await.unwrap().to_bytes();
let br_byte_length = bytes.len();
assert!(br_byte_length < deflate_bytes_len);
Ok(())
}
#[tokio::test]
async fn zstd_is_web_safe() -> Result<(), rama_core::error::BoxError> {
async fn zeroes(_req: Request<Body>) -> Result<Response<Body>, Infallible> {
Ok(Response::new(Body::from(vec![0u8; 18_874_368])))
}
let zstd_layer = CompressionLayer::new()
.quality(CompressionLevel::Best)
.br(false)
.deflate(false)
.gzip(false);
let service = zstd_layer.layer(service_fn(zeroes));
let request = Request::builder()
.header(ACCEPT_ENCODING, "zstd")
.body(Body::empty())?;
let response = service.serve(Context::default(), request).await?;
assert_eq!(response.headers()["content-encoding"], "zstd");
let body = response.into_body();
let bytes = body.collect().await?.to_bytes();
let mut dec = zstd::Decoder::new(&*bytes)?;
dec.window_log_max(23)?; std::io::copy(&mut dec, &mut std::io::sink())?;
Ok(())
}
}