1
0
mirror of https://github.com/fafhrd91/actix-web synced 2025-07-01 16:55:08 +02:00

add setup to commpress middleware

This commit is contained in:
Denis Kayshev
2023-01-18 20:44:29 +03:00
parent 6627109984
commit 21219e0843
2 changed files with 200 additions and 27 deletions

View File

@ -30,6 +30,22 @@ use crate::{
const MAX_CHUNK_SIZE_ENCODE_IN_PLACE: usize = 1024;
const DEFLATE_MIN_LEVEL: u32 = 0;
const DEFLATE_MAX_LEVEL: u32 = 9;
const DEFLATE_DEFAULT: u32 = 1;
const GZIP_MIN_LEVEL: u32 = 0;
const GZIP_MAX_LEVEL: u32 = 9;
const GZIP_DEFAULT: u32 = 1;
const BROTLI_MIN_QUALITY: u32 = 0;
const BROTLI_MAX_QUALITY: u32 = 11;
const BROTLI_DEFAULT: u32 = 3;
const ZSTD_MIN_LEVEL: i32 = 0;
const ZSTD_MAX_LEVEL: i32 = 22;
const ZSTD_DEFAULT: i32 = 3;
pin_project! {
pub struct Encoder<B> {
#[pin]
@ -53,6 +69,15 @@ impl<B: MessageBody> Encoder<B> {
}
pub fn response(encoding: ContentEncoding, head: &mut ResponseHead, body: B) -> Self {
Encoder::response_with_level(encoding, head, body, None)
}
pub fn response_with_level(
encoding: ContentEncoding,
head: &mut ResponseHead,
body: B,
level: Option<u32>,
) -> Self {
// no need to compress an empty body
if matches!(body.size(), BodySize::None) {
return Self::none();
@ -69,8 +94,9 @@ impl<B: MessageBody> Encoder<B> {
};
if should_encode {
let enconding_level = ContentEncodingWithLevel::new(encoding, level);
// wrap body only if encoder is feature-enabled
if let Some(enc) = ContentEncoder::select(encoding) {
if let Some(enc) = ContentEncoder::select(enconding_level) {
update_head(encoding, head);
return Encoder {
@ -278,27 +304,73 @@ enum ContentEncoder {
Zstd(ZstdEncoder<'static, Writer>),
}
enum ContentEncodingWithLevel {
Deflate(u32),
Gzip(u32),
Brotli(u32),
Zstd(i32),
Identity,
}
impl ContentEncodingWithLevel {
pub fn new(encoding: ContentEncoding, level: Option<u32>) -> Self {
match encoding {
ContentEncoding::Deflate => {
let level = level
.filter(|l| (DEFLATE_MIN_LEVEL..(DEFLATE_MAX_LEVEL + 1)).contains(l))
.unwrap_or(DEFLATE_DEFAULT);
ContentEncodingWithLevel::Deflate(level)
}
ContentEncoding::Gzip => {
let level = level
.filter(|l| (GZIP_MIN_LEVEL..(GZIP_MAX_LEVEL + 1)).contains(l))
.unwrap_or(GZIP_DEFAULT);
ContentEncodingWithLevel::Gzip(level)
}
ContentEncoding::Brotli => {
let level = level
.filter(|l| (BROTLI_MIN_QUALITY..(BROTLI_MAX_QUALITY + 1)).contains(l))
.unwrap_or(BROTLI_DEFAULT);
ContentEncodingWithLevel::Brotli(level)
}
ContentEncoding::Zstd => {
let level = level
.map(|l| l as i32)
.filter(|l| (ZSTD_MIN_LEVEL..(ZSTD_MAX_LEVEL + 1)).contains(l))
.unwrap_or(ZSTD_DEFAULT);
ContentEncodingWithLevel::Zstd(level)
}
ContentEncoding::Identity => ContentEncodingWithLevel::Identity,
}
}
}
impl ContentEncoder {
fn select(encoding: ContentEncoding) -> Option<Self> {
fn select(encoding: ContentEncodingWithLevel) -> Option<Self> {
match encoding {
#[cfg(feature = "compress-gzip")]
ContentEncoding::Deflate => Some(ContentEncoder::Deflate(ZlibEncoder::new(
Writer::new(),
flate2::Compression::fast(),
))),
ContentEncodingWithLevel::Deflate(level) => Some(ContentEncoder::Deflate(
ZlibEncoder::new(Writer::new(), flate2::Compression::new(level)),
)),
#[cfg(feature = "compress-gzip")]
ContentEncoding::Gzip => Some(ContentEncoder::Gzip(GzEncoder::new(
Writer::new(),
flate2::Compression::fast(),
))),
ContentEncodingWithLevel::Gzip(level) => Some(ContentEncoder::Gzip(
GzEncoder::new(Writer::new(), flate2::Compression::new(level)),
)),
#[cfg(feature = "compress-brotli")]
ContentEncoding::Brotli => Some(ContentEncoder::Brotli(new_brotli_compressor())),
ContentEncodingWithLevel::Brotli(level) => Some(ContentEncoder::Brotli(Box::new(
brotli::CompressorWriter::new(
Writer::new(),
32 * 1024, // 32 KiB buffer
level, // BROTLI_PARAM_QUALITY
22, // BROTLI_PARAM_LGWIN
),
))),
#[cfg(feature = "compress-zstd")]
ContentEncoding::Zstd => {
let encoder = ZstdEncoder::new(Writer::new(), 3).ok()?;
ContentEncodingWithLevel::Zstd(level) => {
let encoder = ZstdEncoder::new(Writer::new(), level).ok()?;
Some(ContentEncoder::Zstd(encoder))
}
@ -392,16 +464,6 @@ impl ContentEncoder {
}
}
#[cfg(feature = "compress-brotli")]
fn new_brotli_compressor() -> Box<brotli::CompressorWriter<Writer>> {
Box::new(brotli::CompressorWriter::new(
Writer::new(),
32 * 1024, // 32 KiB buffer
3, // BROTLI_PARAM_QUALITY
22, // BROTLI_PARAM_LGWIN
))
}
#[derive(Debug, Display)]
#[non_exhaustive]
pub enum EncoderError {