use hyper::{Method, Request, Response, StatusCode};
use std::path::{Path, PathBuf};
use crate::body::Body;
use crate::error_page::build_html_response;
use crate::{Error, Result, handler::RequestHandlerOpts, helpers};
const DEFAULT_BODY_CONTENT: &str = "The server is in maintenance mode.";
pub(crate) fn init(
maintenance_mode: bool,
maintenance_mode_status: StatusCode,
maintenance_mode_file: PathBuf,
handler_opts: &mut RequestHandlerOpts,
) {
handler_opts.maintenance_mode = maintenance_mode;
handler_opts.maintenance_mode_status = maintenance_mode_status;
handler_opts.maintenance_mode_file = maintenance_mode_file;
tracing::info!(
"maintenance mode: enabled={}",
handler_opts.maintenance_mode
);
tracing::info!(
"maintenance mode status: {}",
handler_opts.maintenance_mode_status.as_str()
);
tracing::info!(
"maintenance mode file: \"{}\"",
handler_opts.maintenance_mode_file.display()
);
crate::error_page::cache_page(&handler_opts.maintenance_mode_file);
}
pub(crate) fn pre_process<T>(
opts: &RequestHandlerOpts,
req: &Request<T>,
) -> Option<Result<Response<Body>, Error>> {
if opts.maintenance_mode {
Some(get_response(
req.method(),
&opts.maintenance_mode_status,
&opts.maintenance_mode_file,
))
} else {
None
}
}
pub fn get_response(
method: &Method,
status_code: &StatusCode,
file_path: &Path,
) -> Result<Response<Body>> {
tracing::debug!("server has entered into maintenance mode");
tracing::debug!("maintenance mode file path to use: {}", file_path.display());
let body_content = if let Some(cached) = crate::error_page::cached_page(file_path) {
cached.as_str().to_owned()
} else if file_path.is_file() {
crate::error_page::cache_page(file_path);
helpers::read_text_default(file_path)
} else {
tracing::debug!(
"maintenance mode file path not found or not a regular file, using a default message"
);
format!(
"<html><head><title>{status_code}</title></head><body><center><h1>{DEFAULT_BODY_CONTENT}</h1></center></body></html>"
)
};
Ok(build_html_response(
body_content,
*status_code,
Some(method),
))
}
#[cfg(test)]
mod tests {
use super::pre_process;
use crate::body::Body;
use crate::{Error, handler::RequestHandlerOpts};
use hyper::{Request, Response, StatusCode};
fn make_request() -> Request<Body> {
Request::builder()
.method("GET")
.uri("/")
.body(crate::body::empty())
.unwrap()
}
fn get_status(result: Option<Result<Response<Body>, Error>>) -> Option<StatusCode> {
if let Some(Ok(response)) = result {
Some(response.status())
} else {
None
}
}
#[test]
fn test_maintenance_disabled() {
assert!(
pre_process(
&RequestHandlerOpts {
maintenance_mode: false,
..Default::default()
},
&make_request()
)
.is_none()
);
}
#[test]
fn test_maintenance_default() {
assert_eq!(
get_status(pre_process(
&RequestHandlerOpts {
maintenance_mode: true,
..Default::default()
},
&make_request()
)),
Some(StatusCode::SERVICE_UNAVAILABLE)
);
}
#[test]
fn test_maintenance_custom_status() {
assert_eq!(
get_status(pre_process(
&RequestHandlerOpts {
maintenance_mode: true,
maintenance_mode_status: StatusCode::IM_A_TEAPOT,
..Default::default()
},
&make_request()
)),
Some(StatusCode::IM_A_TEAPOT)
);
}
}