rustio_admin/middleware/
compression.rs1use std::io::Write;
10
11use bytes::Bytes;
12use flate2::write::GzEncoder;
13use flate2::Compression;
14
15use crate::error::Result;
16use crate::http::{Request, Response};
17use crate::router::Next;
18
19const MIN_SIZE: usize = 1024;
20
21pub async fn gzip(req: Request, next: Next) -> Result<Response> {
22 let accepts_gzip = req
23 .header("accept-encoding")
24 .map(|v| v.contains("gzip"))
25 .unwrap_or(false);
26 let mut resp = next.run(req).await?;
27
28 if !accepts_gzip || resp.body.len() < MIN_SIZE {
29 return Ok(resp);
30 }
31
32 let content_type = resp
33 .headers
34 .iter()
35 .find(|(n, _)| n.eq_ignore_ascii_case("content-type"))
36 .map(|(_, v)| v.to_ascii_lowercase())
37 .unwrap_or_default();
38
39 if !should_compress(&content_type) {
40 return Ok(resp);
41 }
42
43 let mut encoder = GzEncoder::new(Vec::new(), Compression::fast());
44 if encoder.write_all(&resp.body).is_err() {
45 return Ok(resp);
46 }
47 let compressed = match encoder.finish() {
48 Ok(b) => b,
49 Err(_) => return Ok(resp),
50 };
51
52 resp.body = Bytes::from(compressed);
53 resp.headers
54 .push(("content-encoding".into(), "gzip".into()));
55 resp.headers
57 .retain(|(n, _)| !n.eq_ignore_ascii_case("content-length"));
58 Ok(resp)
59}
60
61fn should_compress(content_type: &str) -> bool {
62 content_type.starts_with("text/")
63 || content_type.starts_with("application/json")
64 || content_type.starts_with("application/javascript")
65 || content_type.starts_with("application/xml")
66 || content_type.contains("svg+xml")
67}