static_web_server/
error_page.rs1use headers::{AcceptRanges, ContentLength, ContentType, HeaderMapExt};
10use hyper::{Body, Method, Response, StatusCode, Uri};
11use maud::{DOCTYPE, html};
12use mime_guess::mime;
13use std::path::Path;
14
15use crate::{Result, helpers, http_ext::MethodExt};
16
17pub fn error_response(
19 uri: &Uri,
20 method: &Method,
21 status_code: &StatusCode,
22 page404: &Path,
23 page50x: &Path,
24) -> Result<Response<Body>> {
25 tracing::warn!(
26 method = ?method, uri = ?uri, status = status_code.as_u16(),
27 error = status_code.canonical_reason().unwrap_or_default()
28 );
29
30 let mut page_content = String::new();
32 let status_code = match status_code {
33 &StatusCode::BAD_REQUEST
35 | &StatusCode::UNAUTHORIZED
36 | &StatusCode::PAYMENT_REQUIRED
37 | &StatusCode::FORBIDDEN
38 | &StatusCode::NOT_FOUND
39 | &StatusCode::METHOD_NOT_ALLOWED
40 | &StatusCode::NOT_ACCEPTABLE
41 | &StatusCode::PROXY_AUTHENTICATION_REQUIRED
42 | &StatusCode::REQUEST_TIMEOUT
43 | &StatusCode::CONFLICT
44 | &StatusCode::GONE
45 | &StatusCode::LENGTH_REQUIRED
46 | &StatusCode::PRECONDITION_FAILED
47 | &StatusCode::PAYLOAD_TOO_LARGE
48 | &StatusCode::URI_TOO_LONG
49 | &StatusCode::UNSUPPORTED_MEDIA_TYPE
50 | &StatusCode::RANGE_NOT_SATISFIABLE
51 | &StatusCode::EXPECTATION_FAILED => {
52 if status_code == &StatusCode::NOT_FOUND {
54 if page404.is_file() {
55 String::from_utf8_lossy(&helpers::read_bytes_default(page404))
56 .trim()
57 .clone_into(&mut page_content);
58 } else {
59 tracing::debug!(
60 "page404 file path not found or not a regular file: {}",
61 page404.display()
62 );
63 }
64 }
65 status_code
66 }
67 &StatusCode::INTERNAL_SERVER_ERROR
69 | &StatusCode::NOT_IMPLEMENTED
70 | &StatusCode::BAD_GATEWAY
71 | &StatusCode::SERVICE_UNAVAILABLE
72 | &StatusCode::GATEWAY_TIMEOUT
73 | &StatusCode::HTTP_VERSION_NOT_SUPPORTED
74 | &StatusCode::VARIANT_ALSO_NEGOTIATES
75 | &StatusCode::INSUFFICIENT_STORAGE
76 | &StatusCode::LOOP_DETECTED => {
77 if page50x.is_file() {
79 String::from_utf8_lossy(&helpers::read_bytes_default(page50x))
80 .trim()
81 .clone_into(&mut page_content);
82 } else {
83 tracing::debug!(
84 "page50x file path not found or not a regular file: {}",
85 page50x.display()
86 );
87 }
88 status_code
89 }
90 _ => status_code,
92 };
93
94 if page_content.is_empty() {
95 let reason = status_code.canonical_reason().unwrap_or_default();
96 let title = [status_code.as_str(), " ", reason].concat();
97
98 page_content = html! {
99 (DOCTYPE)
100 html {
101 head {
102 meta charset="utf-8";
103 meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1";
104 title {
105 (title)
106 }
107 style {
108 "html { color-scheme: light dark; } body { font-family: sans-serif; text-align: center; }"
109 }
110 }
111 body {
112 h1 {
113 (title)
114 }
115 }
116 }
117 }.into();
118 }
119
120 let mut body = Body::empty();
121 let len = page_content.len() as u64;
122
123 if !method.is_head() {
124 body = Body::from(page_content)
125 }
126
127 let mut resp = Response::new(body);
128 *resp.status_mut() = *status_code;
129 resp.headers_mut()
130 .typed_insert(ContentType::from(mime::TEXT_HTML_UTF_8));
131 resp.headers_mut().typed_insert(ContentLength(len));
132 resp.headers_mut().typed_insert(AcceptRanges::bytes());
133
134 Ok(resp)
135}