1use std::error::Error;
2use std::fmt::Write;
3use std::io::SeekFrom;
4use std::path::{Path, PathBuf};
5use std::str::FromStr;
6use std::sync::Arc;
7use std::time::Duration;
8
9use crate::ferron_common::{
10 ErrorLogger, HyperResponse, RequestData, ResponseData, ServerConfig, ServerModule,
11 ServerModuleHandlers, SocketData,
12};
13use crate::ferron_common::{HyperUpgraded, WithRuntime};
14use async_compression::brotli::EncoderParams;
15use async_compression::tokio::bufread::{BrotliEncoder, DeflateEncoder, GzipEncoder, ZstdEncoder};
16use async_compression::zstd::CParameter;
17use async_compression::Level;
18use async_trait::async_trait;
19use chrono::offset::Local;
20use chrono::DateTime;
21use futures_util::TryStreamExt;
22use hashlink::LruCache;
23use http::HeaderValue;
24use http_body_util::{BodyExt, Empty, Full, StreamBody};
25use hyper::body::Bytes;
26use hyper::{body::Frame, Response, StatusCode};
27use hyper::{header, HeaderMap, Method};
28use hyper_tungstenite::HyperWebsocket;
29use sha2::{Digest, Sha256};
30use tokio::fs;
31use tokio::io::{AsyncReadExt, AsyncSeekExt, BufReader};
32use tokio::runtime::Handle;
33use tokio::sync::RwLock;
34use tokio_util::io::ReaderStream;
35
36use crate::ferron_util::generate_directory_listing::generate_directory_listing;
37use crate::ferron_util::ttl_cache::TtlCache;
38
39pub fn server_module_init(
40) -> Result<Box<dyn ServerModule + Send + Sync>, Box<dyn Error + Send + Sync>> {
41 let pathbuf_cache = Arc::new(RwLock::new(TtlCache::new(Duration::from_millis(100))));
42 let etag_cache = Arc::new(RwLock::new(LruCache::new(1000)));
43 Ok(Box::new(StaticFileServingModule::new(
44 pathbuf_cache,
45 etag_cache,
46 )))
47}
48
49struct StaticFileServingModule {
50 pathbuf_cache: Arc<RwLock<TtlCache<String, PathBuf>>>,
51 etag_cache: Arc<RwLock<LruCache<String, String>>>,
52}
53
54impl StaticFileServingModule {
55 fn new(
56 pathbuf_cache: Arc<RwLock<TtlCache<String, PathBuf>>>,
57 etag_cache: Arc<RwLock<LruCache<String, String>>>,
58 ) -> Self {
59 Self {
60 pathbuf_cache,
61 etag_cache,
62 }
63 }
64}
65
66impl ServerModule for StaticFileServingModule {
67 fn get_handlers(&self, handle: Handle) -> Box<dyn ServerModuleHandlers + Send> {
68 Box::new(StaticFileServingModuleHandlers {
69 pathbuf_cache: self.pathbuf_cache.clone(),
70 etag_cache: self.etag_cache.clone(),
71 handle,
72 })
73 }
74}
75struct StaticFileServingModuleHandlers {
76 pathbuf_cache: Arc<RwLock<TtlCache<String, PathBuf>>>,
77 etag_cache: Arc<RwLock<LruCache<String, String>>>,
78 handle: Handle,
79}
80
81fn parse_range_header(range_str: &str, default_end: u64) -> Option<(u64, u64)> {
82 if let Some(range_part) = range_str.strip_prefix("bytes=") {
83 let parts: Vec<&str> = range_part.split('-').collect();
84 if parts.len() == 2 {
85 if parts[0].is_empty() {
86 if let Ok(end) = u64::from_str(parts[1]) {
87 return Some((default_end - end + 1, default_end));
88 }
89 } else if parts[1].is_empty() {
90 if let Ok(start) = u64::from_str(parts[0]) {
91 return Some((start, default_end));
92 }
93 } else if !parts[0].is_empty() && !parts[1].is_empty() {
94 if let (Ok(start), Ok(end)) = (u64::from_str(parts[0]), u64::from_str(parts[1])) {
95 return Some((start, end));
96 }
97 }
98 }
99 }
100 None
101}
102
103fn extract_etag_inner(input: &str) -> Option<String> {
104 let trimmed = input.trim_matches('"');
106
107 let parts: Vec<&str> = trimmed.split('-').collect();
109 if parts.is_empty() {
110 None
111 } else {
112 Some(parts[0].to_string())
113 }
114}
115
116#[async_trait]
117impl ServerModuleHandlers for StaticFileServingModuleHandlers {
118 async fn request_handler(
119 &mut self,
120 request: RequestData,
121 config: &ServerConfig,
122 _socket_data: &SocketData,
123 _error_logger: &ErrorLogger,
124 ) -> Result<ResponseData, Box<dyn Error + Send + Sync>> {
125 WithRuntime::new(self.handle.clone(), async move {
126 if let Some(wwwroot) = config["wwwroot"].as_str() {
127 let hyper_request = request.get_hyper_request();
128 let request_path = hyper_request.uri().path();
129 let mut request_path_bytes = request_path.bytes();
130 if request_path_bytes.len() < 1 || request_path_bytes.nth(0) != Some(b'/') {
131 return Ok(
132 ResponseData::builder(request)
133 .status(StatusCode::BAD_REQUEST)
134 .build(),
135 );
136 }
137
138 let original_request_path = request
139 .get_original_url()
140 .map_or(request_path, |u| u.path());
141
142 let cache_key = format!(
143 "{}{}{}",
144 match config["ip"].as_str() {
145 Some(ip) => format!("{ip}-"),
146 None => String::from(""),
147 },
148 match config["domain"].as_str() {
149 Some(domain) => format!("{domain}-"),
150 None => String::from(""),
151 },
152 request_path
153 );
154
155 let rwlock_read = self.pathbuf_cache.read().await;
156 let joined_pathbuf_option = rwlock_read.get(&cache_key);
157 drop(rwlock_read);
158
159 let joined_pathbuf_cached = joined_pathbuf_option.is_some();
160 let mut joined_pathbuf = match joined_pathbuf_option {
161 Some(joined_pathbuf) => joined_pathbuf,
162 None => {
163 let path = Path::new(wwwroot);
164 let mut relative_path = &request_path[1..];
165 while relative_path.as_bytes().first().copied() == Some(b'/') {
166 relative_path = &relative_path[1..];
167 }
168
169 let decoded_relative_path = match urlencoding::decode(relative_path) {
170 Ok(path) => path.to_string(),
171 Err(_) => {
172 return Ok(
173 ResponseData::builder(request)
174 .status(StatusCode::BAD_REQUEST)
175 .build(),
176 );
177 }
178 };
179
180 path.join(decoded_relative_path)
181 }
182 };
183
184 match fs::metadata(&joined_pathbuf).await {
185 Ok(mut metadata) => {
186 if !joined_pathbuf_cached {
187 if metadata.is_dir() {
188 let indexes = vec!["index.html", "index.htm", "index.xhtml"];
189 for index in indexes {
190 let temp_joined_pathbuf = joined_pathbuf.join(index);
191 match fs::metadata(&temp_joined_pathbuf).await {
192 Ok(temp_metadata) => {
193 if temp_metadata.is_file() {
194 metadata = temp_metadata;
195 joined_pathbuf = temp_joined_pathbuf;
196 break;
197 }
198 }
199 Err(err) => match err.kind() {
200 tokio::io::ErrorKind::NotFound | tokio::io::ErrorKind::NotADirectory => {
201 continue;
202 }
203 tokio::io::ErrorKind::PermissionDenied => {
204 return Ok(
205 ResponseData::builder(request)
206 .status(StatusCode::FORBIDDEN)
207 .build(),
208 );
209 }
210 _ => Err(err)?,
211 },
212 };
213 }
214 }
215 let mut rwlock_write = self.pathbuf_cache.write().await;
216 rwlock_write.cleanup();
217 rwlock_write.insert(cache_key, joined_pathbuf.clone());
218 drop(rwlock_write);
219 }
220
221 if metadata.is_file() {
222 let mut compression_possible = false;
224
225 if config["enableCompression"].as_bool() != Some(false) {
226 let non_compressible_file_extensions = vec![
228 "7z",
229 "air",
230 "amlx",
231 "apk",
232 "apng",
233 "appinstaller",
234 "appx",
235 "appxbundle",
236 "arj",
237 "au",
238 "avif",
239 "bdoc",
240 "boz",
241 "br",
242 "bz",
243 "bz2",
244 "caf",
245 "class",
246 "doc",
247 "docx",
248 "dot",
249 "dvi",
250 "ear",
251 "epub",
252 "flv",
253 "gdoc",
254 "gif",
255 "gsheet",
256 "gslides",
257 "gz",
258 "iges",
259 "igs",
260 "jar",
261 "jnlp",
262 "jp2",
263 "jpe",
264 "jpeg",
265 "jpf",
266 "jpg",
267 "jpg2",
268 "jpgm",
269 "jpm",
270 "jpx",
271 "kmz",
272 "latex",
273 "m1v",
274 "m2a",
275 "m2v",
276 "m3a",
277 "m4a",
278 "mesh",
279 "mk3d",
280 "mks",
281 "mkv",
282 "mov",
283 "mp2",
284 "mp2a",
285 "mp3",
286 "mp4",
287 "mp4a",
288 "mp4v",
289 "mpe",
290 "mpeg",
291 "mpg",
292 "mpg4",
293 "mpga",
294 "msg",
295 "msh",
296 "msix",
297 "msixbundle",
298 "odg",
299 "odp",
300 "ods",
301 "odt",
302 "oga",
303 "ogg",
304 "ogv",
305 "ogx",
306 "opus",
307 "p12",
308 "pdf",
309 "pfx",
310 "pgp",
311 "pkpass",
312 "png",
313 "pot",
314 "pps",
315 "ppt",
316 "pptx",
317 "qt",
318 "ser",
319 "silo",
320 "sit",
321 "snd",
322 "spx",
323 "stpxz",
324 "stpz",
325 "swf",
326 "tif",
327 "tiff",
328 "ubj",
329 "usdz",
330 "vbox-extpack",
331 "vrml",
332 "war",
333 "wav",
334 "weba",
335 "webm",
336 "wmv",
337 "wrl",
338 "x3dbz",
339 "x3dvz",
340 "xla",
341 "xlc",
342 "xlm",
343 "xls",
344 "xlsx",
345 "xlt",
346 "xlw",
347 "xpi",
348 "xps",
349 "zip",
350 "zst",
351 ];
352 let file_extension = joined_pathbuf
353 .extension()
354 .map_or_else(|| "".to_string(), |ext| ext.to_string_lossy().to_string());
355 let file_extension_compressible =
356 !non_compressible_file_extensions.contains(&(&file_extension as &str));
357
358 if metadata.len() > 256 && file_extension_compressible {
359 compression_possible = true;
360 }
361 }
362
363 let vary;
364
365 let mut etag_option = None;
367 if config["enableETag"].as_bool() != Some(false) {
368 let etag_cache_key = format!(
369 "{}-{}-{}",
370 joined_pathbuf.to_string_lossy(),
371 metadata.len(),
372 match metadata.modified() {
373 Ok(mtime) => {
374 let datetime: DateTime<Local> = mtime.into();
375 datetime.format("%Y-%m-%d %H:%M:%S").to_string()
376 }
377 Err(_) => String::from(""),
378 }
379 );
380 let rwlock_read = self.etag_cache.read().await;
381 let etag_locked_option = rwlock_read.peek(&etag_cache_key).cloned();
383 drop(rwlock_read);
384 let etag = match etag_locked_option {
385 Some(etag) => etag,
386 None => {
387 let etag_cache_key_clone = etag_cache_key.clone();
388 let etag = tokio::task::spawn_blocking(move || {
389 let mut hasher = Sha256::new();
390 hasher.update(etag_cache_key_clone);
391 hasher
392 .finalize()
393 .iter()
394 .fold(String::new(), |mut output, b| {
395 let _ = write!(output, "{b:02x}");
396 output
397 })
398 })
399 .await?;
400
401 let mut rwlock_write = self.etag_cache.write().await;
402 rwlock_write.insert(etag_cache_key, etag.clone());
403 drop(rwlock_write);
404
405 etag
406 }
407 };
408
409 vary = if compression_possible {
410 "Accept-Encoding, If-Match, If-None-Match, Range"
411 } else {
412 "If-Match, If-None-Match, Range"
413 };
414
415 if let Some(if_none_match_value) =
416 hyper_request.headers().get(header::IF_NONE_MATCH)
417 {
418 match if_none_match_value.to_str() {
419 Ok(if_none_match) => {
420 if let Some(etag_extracted) = extract_etag_inner(if_none_match) {
421 if etag_extracted == etag {
422 let etag_original = if_none_match.to_string();
423 return Ok(
424 ResponseData::builder(request)
425 .response(
426 Response::builder()
427 .status(StatusCode::NOT_MODIFIED)
428 .header(header::ETAG, etag_original)
429 .header(header::VARY, vary)
430 .body(Empty::new().map_err(|e| match e {}).boxed())?,
431 )
432 .build(),
433 );
434 }
435 }
436 }
437 Err(_) => {
438 let mut header_map = HeaderMap::new();
439 if let Ok(vary) = HeaderValue::from_str(vary) {
440 header_map.insert(header::VARY, vary);
441 }
442 return Ok(
443 ResponseData::builder(request)
444 .status(StatusCode::BAD_REQUEST)
445 .headers(header_map)
446 .build(),
447 );
448 }
449 }
450 }
451
452 if let Some(if_match_value) = hyper_request.headers().get(header::IF_MATCH) {
453 match if_match_value.to_str() {
454 Ok(if_match) => {
455 if if_match != "*" {
456 if let Some(etag_extracted) = extract_etag_inner(if_match) {
457 if etag_extracted != etag {
458 let mut header_map = HeaderMap::new();
459 header_map.insert(header::ETAG, if_match_value.clone());
460 if let Ok(vary) = HeaderValue::from_str(vary) {
461 header_map.insert(header::VARY, vary);
462 }
463 return Ok(
464 ResponseData::builder(request)
465 .status(StatusCode::PRECONDITION_FAILED)
466 .headers(header_map)
467 .build(),
468 );
469 }
470 }
471 }
472 }
473 Err(_) => {
474 let mut header_map = HeaderMap::new();
475 if let Ok(vary) = HeaderValue::from_str(vary) {
476 header_map.insert(header::VARY, vary);
477 }
478 return Ok(
479 ResponseData::builder(request)
480 .status(StatusCode::BAD_REQUEST)
481 .headers(header_map)
482 .build(),
483 );
484 }
485 }
486 }
487 etag_option = Some(etag);
488 } else {
489 vary = if compression_possible {
490 "Accept-Encoding, Range"
491 } else {
492 "Range"
493 };
494 }
495
496 let content_type_option = new_mime_guess::from_path(&joined_pathbuf)
497 .first()
498 .map(|mime_type| mime_type.to_string());
499
500 let range_header = match hyper_request.headers().get(header::RANGE) {
501 Some(value) => match value.to_str() {
502 Ok(value) => Some(value),
503 Err(_) => {
504 let mut header_map = HeaderMap::new();
505 if let Ok(vary) = HeaderValue::from_str(vary) {
506 header_map.insert(header::VARY, vary);
507 }
508 return Ok(
509 ResponseData::builder(request)
510 .status(StatusCode::BAD_REQUEST)
511 .headers(header_map)
512 .build(),
513 );
514 }
515 },
516 None => None,
517 };
518
519 if let Some(range_header) = range_header {
520 let file_length = metadata.len();
521 if file_length == 0 {
522 let mut header_map = HeaderMap::new();
523 if let Ok(vary) = HeaderValue::from_str(vary) {
524 header_map.insert(header::VARY, vary);
525 }
526 return Ok(
527 ResponseData::builder(request)
528 .status(StatusCode::RANGE_NOT_SATISFIABLE)
529 .headers(header_map)
530 .build(),
531 );
532 }
533 if let Some((range_begin, range_end)) =
534 parse_range_header(range_header, file_length - 1)
535 {
536 if range_end > file_length - 1
537 || range_begin > file_length - 1
538 || range_begin > range_end
539 {
540 let mut header_map = HeaderMap::new();
541 if let Ok(vary) = HeaderValue::from_str(vary) {
542 header_map.insert(header::VARY, vary);
543 }
544 return Ok(
545 ResponseData::builder(request)
546 .status(StatusCode::RANGE_NOT_SATISFIABLE)
547 .headers(header_map)
548 .build(),
549 );
550 }
551
552 let request_method = hyper_request.method();
553 let content_length = range_end - range_begin + 1;
554
555 let mut response_builder = Response::builder()
557 .status(StatusCode::PARTIAL_CONTENT)
558 .header(header::CONTENT_LENGTH, content_length)
559 .header(
560 header::CONTENT_RANGE,
561 format!("bytes {range_begin}-{range_end}/{file_length}"),
562 );
563
564 if let Some(etag) = etag_option {
565 response_builder = response_builder.header(header::ETAG, format!("\"{etag}\""));
566 }
567
568 if let Some(content_type) = content_type_option {
569 response_builder = response_builder.header(header::CONTENT_TYPE, content_type);
570 }
571
572 response_builder = response_builder.header(header::VARY, vary);
573
574 let response = match request_method {
575 &Method::HEAD => {
576 response_builder.body(Empty::new().map_err(|e| match e {}).boxed())?
577 }
578 _ => {
579 let mut file = match fs::File::open(joined_pathbuf).await {
581 Ok(file) => file,
582 Err(err) => match err.kind() {
583 tokio::io::ErrorKind::NotFound | tokio::io::ErrorKind::NotADirectory => {
584 return Ok(
585 ResponseData::builder(request)
586 .status(StatusCode::NOT_FOUND)
587 .build(),
588 );
589 }
590 tokio::io::ErrorKind::PermissionDenied => {
591 return Ok(
592 ResponseData::builder(request)
593 .status(StatusCode::FORBIDDEN)
594 .build(),
595 );
596 }
597 _ => Err(err)?,
598 },
599 };
600
601 file.seek(SeekFrom::Start(range_begin)).await?;
603 let file_limited = file.take(content_length);
604
605 let file_bufreader = BufReader::with_capacity(12800, file_limited);
607
608 let reader_stream = ReaderStream::new(file_bufreader);
610 let stream_body = StreamBody::new(reader_stream.map_ok(Frame::data));
611 let boxed_body = stream_body.boxed();
612
613 response_builder.body(boxed_body)?
614 }
615 };
616
617 return Ok(ResponseData::builder(request).response(response).build());
618 } else {
619 let mut header_map = HeaderMap::new();
620 if let Ok(vary) = HeaderValue::from_str(vary) {
621 header_map.insert(header::VARY, vary);
622 }
623
624 return Ok(
625 ResponseData::builder(request)
626 .status(StatusCode::RANGE_NOT_SATISFIABLE)
627 .headers(header_map)
628 .build(),
629 );
630 }
631 } else {
632 let mut use_gzip = false;
633 let mut use_deflate = false;
634 let mut use_brotli = false;
635 let mut use_zstd = false;
636
637 if compression_possible {
638 let user_agent = match hyper_request.headers().get(header::USER_AGENT) {
639 Some(user_agent_value) => user_agent_value.to_str().unwrap_or_default(),
640 None => "",
641 };
642
643 let is_netscape_4_broken_html_compression = user_agent.starts_with("Mozilla/4.");
645 let is_netscape_4_broken_compression = match user_agent.strip_prefix("Mozilla/4.")
646 {
647 Some(stripped_user_agent) => matches!(
648 stripped_user_agent.chars().nth(0),
649 Some('6') | Some('7') | Some('8')
650 ),
651 None => false,
652 };
653 let is_w3m_broken_html_compression = user_agent.starts_with("w3m/");
654 if !(content_type_option == Some("text/html".to_string())
655 && (is_netscape_4_broken_html_compression || is_w3m_broken_html_compression))
656 && !is_netscape_4_broken_compression
657 {
658 let accept_encoding = match hyper_request.headers().get(header::ACCEPT_ENCODING)
659 {
660 Some(header_value) => header_value.to_str().unwrap_or_default(),
661 None => "",
662 };
663
664 if accept_encoding.contains("br") {
666 use_brotli = true;
667 } else if accept_encoding.contains("zstd") {
668 use_zstd = true;
669 } else if accept_encoding.contains("deflate") {
670 use_deflate = true;
671 } else if accept_encoding.contains("gzip") {
672 use_gzip = true;
673 }
674 }
675 }
676
677 let request_method = hyper_request.method();
678 let content_length = metadata.len();
679
680 let mut response_builder = Response::builder()
682 .status(StatusCode::OK)
683 .header(header::ACCEPT_RANGES, "bytes");
684
685 if let Some(etag) = etag_option {
686 if use_brotli {
687 response_builder =
688 response_builder.header(header::ETAG, format!("\"{etag}-br\""));
689 } else if use_zstd {
690 response_builder =
691 response_builder.header(header::ETAG, format!("\"{etag}-zstd\""));
692 } else if use_deflate {
693 response_builder =
694 response_builder.header(header::ETAG, format!("\"{etag}-deflate\""));
695 } else if use_gzip {
696 response_builder =
697 response_builder.header(header::ETAG, format!("\"{etag}-gzip\""));
698 } else {
699 response_builder = response_builder.header(header::ETAG, format!("\"{etag}\""));
700 }
701 }
702
703 response_builder = response_builder.header(header::VARY, vary);
704
705 if let Some(content_type) = content_type_option {
706 response_builder = response_builder.header(header::CONTENT_TYPE, content_type);
707 }
708
709 if use_brotli {
710 response_builder = response_builder.header(header::CONTENT_ENCODING, "br");
711 } else if use_zstd {
712 response_builder = response_builder.header(header::CONTENT_ENCODING, "zstd");
713 } else if use_deflate {
714 response_builder = response_builder.header(header::CONTENT_ENCODING, "deflate");
715 } else if use_gzip {
716 response_builder = response_builder.header(header::CONTENT_ENCODING, "gzip");
717 } else {
718 response_builder =
720 response_builder.header(header::CONTENT_LENGTH, content_length);
721 }
722
723 let response = match request_method {
724 &Method::HEAD => {
725 response_builder.body(Empty::new().map_err(|e| match e {}).boxed())?
726 }
727 _ => {
728 let file = match fs::File::open(joined_pathbuf).await {
730 Ok(file) => file,
731 Err(err) => match err.kind() {
732 tokio::io::ErrorKind::NotFound | tokio::io::ErrorKind::NotADirectory => {
733 return Ok(
734 ResponseData::builder(request)
735 .status(StatusCode::NOT_FOUND)
736 .build(),
737 );
738 }
739 tokio::io::ErrorKind::PermissionDenied => {
740 return Ok(
741 ResponseData::builder(request)
742 .status(StatusCode::FORBIDDEN)
743 .build(),
744 );
745 }
746 _ => Err(err)?,
747 },
748 };
749
750 let file_bufreader = BufReader::with_capacity(12800, file);
752
753 let boxed_body = if use_brotli {
755 let reader_stream =
758 ReaderStream::new(BrotliEncoder::with_quality_and_params(
759 file_bufreader,
760 Level::Precise(4),
761 EncoderParams::default().window_size(17).block_size(18),
762 ));
763 let stream_body = StreamBody::new(reader_stream.map_ok(Frame::data));
764 stream_body.boxed()
765 } else if use_zstd {
766 let reader_stream = ReaderStream::new(ZstdEncoder::with_quality_and_params(
769 file_bufreader,
770 Level::Default,
771 &[CParameter::window_log(17), CParameter::hash_log(10)],
772 ));
773 let stream_body = StreamBody::new(reader_stream.map_ok(Frame::data));
774 stream_body.boxed()
775 } else if use_deflate {
776 let reader_stream = ReaderStream::new(DeflateEncoder::new(file_bufreader));
777 let stream_body = StreamBody::new(reader_stream.map_ok(Frame::data));
778 stream_body.boxed()
779 } else if use_gzip {
780 let reader_stream = ReaderStream::new(GzipEncoder::new(file_bufreader));
781 let stream_body = StreamBody::new(reader_stream.map_ok(Frame::data));
782 stream_body.boxed()
783 } else {
784 let reader_stream = ReaderStream::new(file_bufreader);
785 let stream_body = StreamBody::new(reader_stream.map_ok(Frame::data));
786 stream_body.boxed()
787 };
788
789 response_builder.body(boxed_body)?
790 }
791 };
792
793 return Ok(ResponseData::builder(request).response(response).build());
794 }
795 } else if metadata.is_dir() {
796 if config["enableDirectoryListing"].as_bool() == Some(true) {
797 let joined_maindesc_pathbuf = joined_pathbuf.join(".maindesc");
798 let directory = match fs::read_dir(joined_pathbuf).await {
799 Ok(directory) => directory,
800 Err(err) => match err.kind() {
801 tokio::io::ErrorKind::NotFound => {
802 return Ok(
803 ResponseData::builder(request)
804 .status(StatusCode::NOT_FOUND)
805 .build(),
806 );
807 }
808 tokio::io::ErrorKind::PermissionDenied => {
809 return Ok(
810 ResponseData::builder(request)
811 .status(StatusCode::FORBIDDEN)
812 .build(),
813 );
814 }
815 _ => Err(err)?,
816 },
817 };
818
819 let description = (fs::read_to_string(joined_maindesc_pathbuf).await).ok();
820
821 let directory_listing_html =
822 generate_directory_listing(directory, original_request_path, description).await?;
823 let content_length: Option<u64> = directory_listing_html.len().try_into().ok();
824
825 let mut response_builder = Response::builder().status(StatusCode::OK);
826
827 if let Some(content_length) = content_length {
828 response_builder = response_builder.header(header::CONTENT_LENGTH, content_length)
829 }
830 response_builder = response_builder.header(header::CONTENT_TYPE, "text/html");
831
832 let response = response_builder.body(
833 Full::new(Bytes::from(directory_listing_html))
834 .map_err(|e| match e {})
835 .boxed(),
836 )?;
837
838 return Ok(ResponseData::builder(request).response(response).build());
839 } else {
840 return Ok(
841 ResponseData::builder(request)
842 .status(StatusCode::FORBIDDEN)
843 .build(),
844 );
845 }
846 } else {
847 return Ok(
848 ResponseData::builder(request)
849 .status(StatusCode::NOT_IMPLEMENTED)
850 .build(),
851 );
852 }
853 }
854 Err(err) => match err.kind() {
855 tokio::io::ErrorKind::NotFound | tokio::io::ErrorKind::NotADirectory => {
856 return Ok(
857 ResponseData::builder(request)
858 .status(StatusCode::NOT_FOUND)
859 .build(),
860 );
861 }
862 tokio::io::ErrorKind::PermissionDenied => {
863 return Ok(
864 ResponseData::builder(request)
865 .status(StatusCode::FORBIDDEN)
866 .build(),
867 );
868 }
869 _ => Err(err)?,
870 },
871 }
872 }
873
874 Ok(ResponseData::builder(request).build())
875 })
876 .await
877 }
878
879 async fn proxy_request_handler(
880 &mut self,
881 request: RequestData,
882 _config: &ServerConfig,
883 _socket_data: &SocketData,
884 _error_logger: &ErrorLogger,
885 ) -> Result<ResponseData, Box<dyn Error + Send + Sync>> {
886 Ok(ResponseData::builder(request).build())
887 }
888
889 async fn response_modifying_handler(
890 &mut self,
891 response: HyperResponse,
892 ) -> Result<HyperResponse, Box<dyn Error + Send + Sync>> {
893 Ok(response)
894 }
895
896 async fn proxy_response_modifying_handler(
897 &mut self,
898 response: HyperResponse,
899 ) -> Result<HyperResponse, Box<dyn Error + Send + Sync>> {
900 Ok(response)
901 }
902
903 async fn connect_proxy_request_handler(
904 &mut self,
905 _upgraded_request: HyperUpgraded,
906 _connect_address: &str,
907 _config: &ServerConfig,
908 _socket_data: &SocketData,
909 _error_logger: &ErrorLogger,
910 ) -> Result<(), Box<dyn Error + Send + Sync>> {
911 Ok(())
912 }
913
914 fn does_connect_proxy_requests(&mut self) -> bool {
915 false
916 }
917
918 async fn websocket_request_handler(
919 &mut self,
920 _websocket: HyperWebsocket,
921 _uri: &hyper::Uri,
922 _headers: &hyper::HeaderMap,
923 _config: &ServerConfig,
924 _socket_data: &SocketData,
925 _error_logger: &ErrorLogger,
926 ) -> Result<(), Box<dyn Error + Send + Sync>> {
927 Ok(())
928 }
929
930 fn does_websocket_requests(&mut self, _config: &ServerConfig, _socket_data: &SocketData) -> bool {
931 false
932 }
933}