static_web_server/
compression_static.rs1use headers::{HeaderMap, HeaderMapExt, HeaderValue};
10use hyper::{Request, Response};
11use std::ffi::OsStr;
12use std::fs::Metadata;
13use std::path::{Path, PathBuf};
14
15use crate::Error;
16use crate::body::Body;
17use crate::exts::headers::{AcceptEncoding, ContentCoding};
18use crate::exts::http::append_vary_accept_encoding;
19use crate::fs::meta::try_metadata;
20use crate::handler::RequestHandlerOpts;
21
22pub struct CompressedFileVariant {
24 pub file_path: PathBuf,
26 pub metadata: Metadata,
28 pub encoding: ContentCoding,
30}
31
32pub fn init(enabled: bool, handler_opts: &mut RequestHandlerOpts) {
34 handler_opts.compression_static = enabled;
35 tracing::info!(enabled, "compression static");
36}
37
38pub(crate) fn post_process<T>(
40 opts: &RequestHandlerOpts,
41 _req: &Request<T>,
42 mut resp: Response<Body>,
43) -> Result<Response<Body>, Error> {
44 if !opts.compression_static {
45 return Ok(resp);
46 }
47
48 append_vary_accept_encoding(&mut resp);
50
51 Ok(resp)
52}
53
54pub fn precompressed_variant(
56 file_path: &Path,
57 headers: &HeaderMap<HeaderValue>,
58) -> Option<CompressedFileVariant> {
59 tracing::trace!(
60 "preparing pre-compressed file variant path of {}",
61 file_path.display()
62 );
63 if let Some(ref accept_encoding) = headers.typed_get::<AcceptEncoding>() {
64 for encoding in accept_encoding.sorted_encodings() {
65 let comp_ext = match encoding {
67 ContentCoding::GZIP | ContentCoding::DEFLATE => "gz",
69 ContentCoding::BROTLI => "br",
71 ContentCoding::ZSTD => "zst",
73 _ => {
74 tracing::trace!(
75 "preferred encoding based on the file extension was not determined, skipping"
76 );
77 continue;
78 }
79 };
80
81 let Some(comp_name) = file_path.file_name().and_then(OsStr::to_str) else {
82 tracing::trace!("file name was not determined for the current path, skipping");
83 continue;
84 };
85
86 let file_path = file_path.with_file_name([comp_name, ".", comp_ext].concat());
87 tracing::trace!(
88 "trying to get the pre-compressed file variant metadata for {}",
89 file_path.display()
90 );
91
92 let (metadata, is_dir) = match try_metadata(&file_path) {
93 Ok(v) => v,
94 Err(e) => {
95 tracing::trace!("pre-compressed file variant error: {:?}", e);
96 continue;
97 }
98 };
99
100 if is_dir {
101 tracing::trace!("pre-compressed file variant found but it's a directory, skipping");
102 continue;
103 }
104
105 tracing::trace!("pre-compressed file variant found, serving it directly");
106
107 return Some(CompressedFileVariant {
108 file_path,
109 metadata,
110 encoding,
111 });
112 }
113 }
114
115 None
116}