static_web_server/
compression_static.rs1use headers::{HeaderMap, HeaderValue};
10use hyper::{Body, Request, Response};
11use std::ffi::OsStr;
12use std::fs::Metadata;
13use std::path::{Path, PathBuf};
14
15use crate::compression;
16use crate::fs::meta::try_metadata;
17use crate::handler::RequestHandlerOpts;
18use crate::headers_ext::ContentCoding;
19use crate::Error;
20
21pub struct CompressedFileVariant {
23 pub file_path: PathBuf,
25 pub metadata: Metadata,
27 pub encoding: ContentCoding,
29}
30
31pub fn init(enabled: bool, handler_opts: &mut RequestHandlerOpts) {
33 handler_opts.compression_static = enabled;
34 server_info!("compression static: enabled={enabled}");
35}
36
37pub(crate) fn post_process<T>(
39 opts: &RequestHandlerOpts,
40 _req: &Request<T>,
41 mut resp: Response<Body>,
42) -> Result<Response<Body>, Error> {
43 if !opts.compression_static {
44 return Ok(resp);
45 }
46
47 resp.headers_mut().insert(
49 hyper::header::VARY,
50 HeaderValue::from_name(hyper::header::ACCEPT_ENCODING),
51 );
52
53 Ok(resp)
54}
55
56pub fn precompressed_variant(
58 file_path: &Path,
59 headers: &HeaderMap<HeaderValue>,
60) -> Option<CompressedFileVariant> {
61 tracing::trace!(
62 "preparing pre-compressed file variant path of {}",
63 file_path.display()
64 );
65
66 for encoding in compression::get_encodings(headers) {
67 let comp_ext = match encoding {
69 #[cfg(any(
71 feature = "compression",
72 feature = "compression-gzip",
73 feature = "compression-deflate"
74 ))]
75 ContentCoding::GZIP | ContentCoding::DEFLATE => "gz",
76 #[cfg(any(feature = "compression", feature = "compression-brotli"))]
78 ContentCoding::BROTLI => "br",
79 #[cfg(any(feature = "compression", feature = "compression-zstd"))]
81 ContentCoding::ZSTD => "zst",
82 _ => {
83 tracing::trace!(
84 "preferred encoding based on the file extension was not determined, skipping"
85 );
86 continue;
87 }
88 };
89
90 let comp_name = match file_path.file_name().and_then(OsStr::to_str) {
91 Some(v) => v,
92 None => {
93 tracing::trace!("file name was not determined for the current path, skipping");
94 continue;
95 }
96 };
97
98 let file_path = file_path.with_file_name([comp_name, ".", comp_ext].concat());
99 tracing::trace!(
100 "trying to get the pre-compressed file variant metadata for {}",
101 file_path.display()
102 );
103
104 let (metadata, is_dir) = match try_metadata(&file_path) {
105 Ok(v) => v,
106 Err(e) => {
107 tracing::trace!("pre-compressed file variant error: {:?}", e);
108 continue;
109 }
110 };
111
112 if is_dir {
113 tracing::trace!("pre-compressed file variant found but it's a directory, skipping");
114 continue;
115 }
116
117 tracing::trace!("pre-compressed file variant found, serving it directly");
118
119 return Some(CompressedFileVariant {
120 file_path,
121 metadata,
122 encoding,
123 });
124 }
125
126 None
127}