rust_webx_host/
compression.rs1use flate2::write::GzEncoder;
8use flate2::Compression;
9use rust_webx_core::error::Result;
10use rust_webx_core::http::IHttpContext;
11use rust_webx_core::middleware::IMiddleware;
12use std::io::Write;
13use std::ops::ControlFlow;
14
15pub fn compress_gzip(data: &[u8], level: u32) -> Option<Vec<u8>> {
17 let mut encoder = GzEncoder::new(Vec::with_capacity(data.len() / 2), Compression::new(level));
18 encoder.write_all(data).ok()?;
19 encoder.finish().ok()
20}
21
22pub struct CompressionConfig {
24 pub level: u32,
25 pub min_size: usize,
26}
27
28impl Default for CompressionConfig {
29 fn default() -> Self {
30 Self {
31 level: 6,
32 min_size: 1024,
33 }
34 }
35}
36
37impl CompressionConfig {
38 pub fn new() -> Self {
39 Self::default()
40 }
41 pub fn level(mut self, l: u32) -> Self {
42 self.level = l;
43 self
44 }
45 pub fn min_size(mut self, s: usize) -> Self {
46 self.min_size = s;
47 self
48 }
49}
50
51#[derive(Default)]
63pub struct CompressionMiddleware {
64 config: CompressionConfig,
65}
66
67impl CompressionMiddleware {
68 pub fn new() -> Self {
69 Self::default()
70 }
71 pub fn with_config(config: CompressionConfig) -> Self {
72 Self { config }
73 }
74}
75
76#[async_trait::async_trait]
77impl IMiddleware for CompressionMiddleware {
78 async fn invoke(&self, ctx: &mut dyn IHttpContext) -> Result<ControlFlow<()>> {
79 ctx.response_mut().set_header("vary", "accept-encoding");
80 Ok(ControlFlow::Continue(()))
81 }
82
83 async fn after(&self, ctx: &mut dyn IHttpContext) -> Result<()> {
84 let accept = ctx.request().header("accept-encoding").unwrap_or("");
85 if !accept.to_lowercase().contains("gzip") {
86 return Ok(());
87 }
88
89 let body = ctx.response().body_bytes();
90 if body.len() < self.config.min_size {
91 return Ok(());
92 }
93
94 let ct = ctx
95 .response()
96 .header("content-type")
97 .unwrap_or("")
98 .to_lowercase();
99 if ct.starts_with("image/") || ct.starts_with("video/") || ct.starts_with("audio/") {
100 return Ok(());
101 }
102
103 if let Some(compressed) = compress_gzip(&body, self.config.level) {
104 ctx.response_mut().set_header("content-encoding", "gzip");
105 ctx.response_mut().remove_header("content-length");
106 ctx.response_mut().write_bytes(compressed).await?;
107 }
108 Ok(())
109 }
110}