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 = ReaderStream::new(BrotliEncoder::with_params(
758 file_bufreader,
759 EncoderParams::default()
760 .quality(Level::Precise(4))
761 .window_size(17)
762 .block_size(18),
763 ));
764 let stream_body = StreamBody::new(reader_stream.map_ok(Frame::data));
765 stream_body.boxed()
766 } else if use_zstd {
767 let reader_stream = ReaderStream::new(ZstdEncoder::with_quality_and_params(
770 file_bufreader,
771 Level::Default,
772 &[CParameter::window_log(17), CParameter::hash_log(10)],
773 ));
774 let stream_body = StreamBody::new(reader_stream.map_ok(Frame::data));
775 stream_body.boxed()
776 } else if use_deflate {
777 let reader_stream = ReaderStream::new(DeflateEncoder::new(file_bufreader));
778 let stream_body = StreamBody::new(reader_stream.map_ok(Frame::data));
779 stream_body.boxed()
780 } else if use_gzip {
781 let reader_stream = ReaderStream::new(GzipEncoder::new(file_bufreader));
782 let stream_body = StreamBody::new(reader_stream.map_ok(Frame::data));
783 stream_body.boxed()
784 } else {
785 let reader_stream = ReaderStream::new(file_bufreader);
786 let stream_body = StreamBody::new(reader_stream.map_ok(Frame::data));
787 stream_body.boxed()
788 };
789
790 response_builder.body(boxed_body)?
791 }
792 };
793
794 return Ok(ResponseData::builder(request).response(response).build());
795 }
796 } else if metadata.is_dir() {
797 if config["enableDirectoryListing"].as_bool() == Some(true) {
798 let joined_maindesc_pathbuf = joined_pathbuf.join(".maindesc");
799 let directory = match fs::read_dir(joined_pathbuf).await {
800 Ok(directory) => directory,
801 Err(err) => match err.kind() {
802 tokio::io::ErrorKind::NotFound => {
803 return Ok(
804 ResponseData::builder(request)
805 .status(StatusCode::NOT_FOUND)
806 .build(),
807 );
808 }
809 tokio::io::ErrorKind::PermissionDenied => {
810 return Ok(
811 ResponseData::builder(request)
812 .status(StatusCode::FORBIDDEN)
813 .build(),
814 );
815 }
816 _ => Err(err)?,
817 },
818 };
819
820 let description = (fs::read_to_string(joined_maindesc_pathbuf).await).ok();
821
822 let directory_listing_html =
823 generate_directory_listing(directory, original_request_path, description).await?;
824 let content_length: Option<u64> = directory_listing_html.len().try_into().ok();
825
826 let mut response_builder = Response::builder().status(StatusCode::OK);
827
828 if let Some(content_length) = content_length {
829 response_builder = response_builder.header(header::CONTENT_LENGTH, content_length)
830 }
831 response_builder = response_builder.header(header::CONTENT_TYPE, "text/html");
832
833 let response = response_builder.body(
834 Full::new(Bytes::from(directory_listing_html))
835 .map_err(|e| match e {})
836 .boxed(),
837 )?;
838
839 return Ok(ResponseData::builder(request).response(response).build());
840 } else {
841 return Ok(
842 ResponseData::builder(request)
843 .status(StatusCode::FORBIDDEN)
844 .build(),
845 );
846 }
847 } else {
848 return Ok(
849 ResponseData::builder(request)
850 .status(StatusCode::NOT_IMPLEMENTED)
851 .build(),
852 );
853 }
854 }
855 Err(err) => match err.kind() {
856 tokio::io::ErrorKind::NotFound | tokio::io::ErrorKind::NotADirectory => {
857 return Ok(
858 ResponseData::builder(request)
859 .status(StatusCode::NOT_FOUND)
860 .build(),
861 );
862 }
863 tokio::io::ErrorKind::PermissionDenied => {
864 return Ok(
865 ResponseData::builder(request)
866 .status(StatusCode::FORBIDDEN)
867 .build(),
868 );
869 }
870 _ => Err(err)?,
871 },
872 }
873 }
874
875 Ok(ResponseData::builder(request).build())
876 })
877 .await
878 }
879
880 async fn proxy_request_handler(
881 &mut self,
882 request: RequestData,
883 _config: &ServerConfig,
884 _socket_data: &SocketData,
885 _error_logger: &ErrorLogger,
886 ) -> Result<ResponseData, Box<dyn Error + Send + Sync>> {
887 Ok(ResponseData::builder(request).build())
888 }
889
890 async fn response_modifying_handler(
891 &mut self,
892 response: HyperResponse,
893 ) -> Result<HyperResponse, Box<dyn Error + Send + Sync>> {
894 Ok(response)
895 }
896
897 async fn proxy_response_modifying_handler(
898 &mut self,
899 response: HyperResponse,
900 ) -> Result<HyperResponse, Box<dyn Error + Send + Sync>> {
901 Ok(response)
902 }
903
904 async fn connect_proxy_request_handler(
905 &mut self,
906 _upgraded_request: HyperUpgraded,
907 _connect_address: &str,
908 _config: &ServerConfig,
909 _socket_data: &SocketData,
910 _error_logger: &ErrorLogger,
911 ) -> Result<(), Box<dyn Error + Send + Sync>> {
912 Ok(())
913 }
914
915 fn does_connect_proxy_requests(&mut self) -> bool {
916 false
917 }
918
919 async fn websocket_request_handler(
920 &mut self,
921 _websocket: HyperWebsocket,
922 _uri: &hyper::Uri,
923 _headers: &hyper::HeaderMap,
924 _config: &ServerConfig,
925 _socket_data: &SocketData,
926 _error_logger: &ErrorLogger,
927 ) -> Result<(), Box<dyn Error + Send + Sync>> {
928 Ok(())
929 }
930
931 fn does_websocket_requests(&mut self, _config: &ServerConfig, _socket_data: &SocketData) -> bool {
932 false
933 }
934}