rust_web_server/compression/
mod.rs1#[cfg(test)]
2mod tests;
3
4use flate2::Compression;
5use flate2::write::GzEncoder;
6use std::io::Write;
7use crate::header::Header;
8use crate::request::Request;
9use crate::response::Response;
10
11const COMPRESSIBLE: &[&str] = &[
13 "text/html",
14 "text/css",
15 "text/javascript",
16 "text/plain",
17 "text/xml",
18 "application/json",
19 "application/javascript",
20 "application/xml",
21 "application/xhtml+xml",
22 "image/svg+xml",
23];
24
25pub fn apply_gzip(request: &Request, response: &mut Response) {
29 if response.content_range_list.is_empty() {
30 return;
31 }
32
33 let accepts_gzip = request
34 .get_header(Header::_ACCEPT_ENCODING.to_string())
35 .map(|h| h.value.to_lowercase().contains("gzip"))
36 .unwrap_or(false);
37
38 if !accepts_gzip {
39 return;
40 }
41
42 let content_type = response
43 .content_range_list
44 .first()
45 .map(|cr| cr.content_type.to_lowercase())
46 .unwrap_or_default();
47
48 let is_compressible = COMPRESSIBLE.iter().any(|mime| content_type.starts_with(mime));
49 if !is_compressible {
50 return;
51 }
52
53 let mut compressed_ok = true;
54 for cr in &mut response.content_range_list {
55 let mut encoder = GzEncoder::new(Vec::new(), Compression::default());
56 if encoder.write_all(&cr.body).is_err() {
57 compressed_ok = false;
58 break;
59 }
60 match encoder.finish() {
61 Ok(compressed) => {
62 let new_len = compressed.len() as u64;
63 cr.body = compressed;
64 cr.range.end = new_len;
65 cr.size = new_len.to_string();
66 }
67 Err(_) => {
68 compressed_ok = false;
69 break;
70 }
71 }
72 }
73
74 if !compressed_ok {
75 return;
76 }
77
78 response.headers.push(Header {
79 name: Header::_CONTENT_ENCODING.to_string(),
80 value: "gzip".to_string(),
81 });
82
83 let vary_pos = response.headers.iter().position(|h| h.name == Header::_VARY);
85 if let Some(i) = vary_pos {
86 let current = response.headers[i].value.clone();
87 if !current.to_lowercase().contains("accept-encoding") {
88 response.headers[i].value = format!("{}, Accept-Encoding", current);
89 }
90 } else {
91 response.headers.push(Header {
92 name: Header::_VARY.to_string(),
93 value: "Accept-Encoding".to_string(),
94 });
95 }
96}