static_web_server/
maintenance_mode.rs1use hyper::{Method, Request, Response, StatusCode};
10use std::path::{Path, PathBuf};
11
12use crate::body::Body;
13use crate::error_page::build_html_response;
14use crate::{Error, Result, handler::RequestHandlerOpts, helpers};
15
16const DEFAULT_BODY_CONTENT: &str = "The server is in maintenance mode.";
17
18pub(crate) fn init(
20 maintenance_mode: bool,
21 maintenance_mode_status: StatusCode,
22 maintenance_mode_file: PathBuf,
23 handler_opts: &mut RequestHandlerOpts,
24) {
25 handler_opts.maintenance_mode = maintenance_mode;
26 handler_opts.maintenance_mode_status = maintenance_mode_status;
27 handler_opts.maintenance_mode_file = maintenance_mode_file;
28 tracing::info!(
29 "maintenance mode: enabled={}",
30 handler_opts.maintenance_mode
31 );
32 tracing::info!(
33 "maintenance mode status: {}",
34 handler_opts.maintenance_mode_status.as_str()
35 );
36 tracing::info!(
37 "maintenance mode file: \"{}\"",
38 handler_opts.maintenance_mode_file.display()
39 );
40 crate::error_page::cache_page(&handler_opts.maintenance_mode_file);
43}
44
45pub(crate) fn pre_process<T>(
47 opts: &RequestHandlerOpts,
48 req: &Request<T>,
49) -> Option<Result<Response<Body>, Error>> {
50 if opts.maintenance_mode {
51 Some(get_response(
52 req.method(),
53 &opts.maintenance_mode_status,
54 &opts.maintenance_mode_file,
55 ))
56 } else {
57 None
58 }
59}
60
61pub fn get_response(
63 method: &Method,
64 status_code: &StatusCode,
65 file_path: &Path,
66) -> Result<Response<Body>> {
67 tracing::debug!("server has entered into maintenance mode");
68 tracing::debug!("maintenance mode file path to use: {}", file_path.display());
69
70 let body_content = if let Some(cached) = crate::error_page::cached_page(file_path) {
71 cached.as_str().to_owned()
72 } else if file_path.is_file() {
73 crate::error_page::cache_page(file_path);
75 helpers::read_text_default(file_path)
76 } else {
77 tracing::debug!(
78 "maintenance mode file path not found or not a regular file, using a default message"
79 );
80 format!(
81 "<html><head><title>{status_code}</title></head><body><center><h1>{DEFAULT_BODY_CONTENT}</h1></center></body></html>"
82 )
83 };
84
85 Ok(build_html_response(
86 body_content,
87 *status_code,
88 Some(method),
89 ))
90}
91
92#[cfg(test)]
93mod tests {
94 use super::pre_process;
95 use crate::body::Body;
96 use crate::{Error, handler::RequestHandlerOpts};
97 use hyper::{Request, Response, StatusCode};
98
99 fn make_request() -> Request<Body> {
100 Request::builder()
101 .method("GET")
102 .uri("/")
103 .body(crate::body::empty())
104 .unwrap()
105 }
106
107 fn get_status(result: Option<Result<Response<Body>, Error>>) -> Option<StatusCode> {
108 if let Some(Ok(response)) = result {
109 Some(response.status())
110 } else {
111 None
112 }
113 }
114
115 #[test]
116 fn test_maintenance_disabled() {
117 assert!(
118 pre_process(
119 &RequestHandlerOpts {
120 maintenance_mode: false,
121 ..Default::default()
122 },
123 &make_request()
124 )
125 .is_none()
126 );
127 }
128
129 #[test]
130 fn test_maintenance_default() {
131 assert_eq!(
132 get_status(pre_process(
133 &RequestHandlerOpts {
134 maintenance_mode: true,
135 ..Default::default()
136 },
137 &make_request()
138 )),
139 Some(StatusCode::SERVICE_UNAVAILABLE)
140 );
141 }
142
143 #[test]
144 fn test_maintenance_custom_status() {
145 assert_eq!(
146 get_status(pre_process(
147 &RequestHandlerOpts {
148 maintenance_mode: true,
149 maintenance_mode_status: StatusCode::IM_A_TEAPOT,
150 ..Default::default()
151 },
152 &make_request()
153 )),
154 Some(StatusCode::IM_A_TEAPOT)
155 );
156 }
157}