use crate::server::server::AppState;
use humphrey::http::headers::HeaderType;
use humphrey::http::mime::MimeType;
use humphrey::http::{Request, Response, StatusCode};
use humphrey::route::{try_find_path, LocatedPath};
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;
use std::sync::Arc;
const INDEX_FILES: [&str; 2] = ["index.html", "index.htm"];
pub fn file_handler(request: Request, state: Arc<AppState>, file: &str, host: usize) -> Response {
if let Some(response) = blacklist_check(&request, state.clone()) {
return response;
}
if let Some(response) = cache_check(&request, state.clone(), host) {
return response;
}
inner_file_handler(request, state, file.into(), host)
}
pub fn directory_handler(
request: Request,
state: Arc<AppState>,
directory: &str,
matches: &str,
host: usize,
) -> Response {
if let Some(response) = blacklist_check(&request, state.clone()) {
return response;
}
if let Some(response) = cache_check(&request, state.clone(), host) {
return response;
}
let mut simplified_uri = request.uri.clone();
for ch in matches.chars() {
if ch != '*' {
simplified_uri.remove(0);
} else {
break;
}
}
if let Some(located) = try_find_path(directory, &simplified_uri, &INDEX_FILES) {
match located {
LocatedPath::Directory => {
state.logger.info(&format!(
"{}: 301 Moved Permanently {}",
request.address, request.uri
));
Response::empty(StatusCode::MovedPermanently)
.with_header(HeaderType::Location, format!("{}/", &request.uri))
}
LocatedPath::File(path) => inner_file_handler(request, state, path, host),
}
} else {
state.logger.warn(&format!(
"{}: 404 Not Found {}",
request.address, request.uri
));
not_found()
}
}
pub fn redirect_handler(request: Request, state: Arc<AppState>, target: &str) -> Response {
if let Some(response) = blacklist_check(&request, state.clone()) {
return response;
}
state.logger.info(&format!(
"{}: 301 Moved Permanently {}",
request.address, request.uri
));
Response::empty(StatusCode::MovedPermanently).with_header(HeaderType::Location, target)
}
fn inner_file_handler(
request: Request,
state: Arc<AppState>,
path: PathBuf,
host: usize,
) -> Response {
let file_extension = path.extension().map(|s| s.to_str().unwrap()).unwrap_or("");
let mime_type = MimeType::from_extension(file_extension);
let mut contents: Vec<u8> = Vec::new();
let mut file = File::open(path).unwrap();
file.read_to_end(&mut contents).unwrap();
if state.config.cache.size_limit >= contents.len() {
let mut cache = state.cache.write().unwrap();
cache.set(&request.uri, host, contents.clone(), mime_type);
state.logger.debug(&format!("Cached route {}", request.uri));
} else if state.config.cache.size_limit > 0 {
state
.logger
.warn(&format!("Couldn't cache, cache too small {}", request.uri));
}
state
.logger
.info(&format!("{}: 200 OK {}", request.address, request.uri));
Response::empty(StatusCode::OK)
.with_header(HeaderType::ContentType, mime_type.to_string())
.with_bytes(contents)
}
fn blacklist_check(request: &Request, state: Arc<AppState>) -> Option<Response> {
if state
.config
.blacklist
.list
.contains(&request.address.origin_addr)
{
state.logger.warn(&format!(
"{}: Blacklisted IP attempted to request {}",
request.address, request.uri
));
return Some(
Response::empty(StatusCode::Forbidden)
.with_header(HeaderType::ContentType, "text/html")
.with_bytes(b"<h1>403 Forbidden</h1>"),
);
}
None
}
fn cache_check(request: &Request, state: Arc<AppState>, host: usize) -> Option<Response> {
if state.config.cache.size_limit > 0 {
let cache = state.cache.read().unwrap();
if let Some(cached) = cache.get(&request.uri, host) {
state.logger.info(&format!(
"{}: 200 OK (cached) {}",
request.address, request.uri
));
return Some(
Response::empty(StatusCode::OK)
.with_header(HeaderType::ContentType, cached.mime_type.to_string())
.with_bytes(cached.data.clone()),
);
}
drop(cache);
}
None
}
pub fn not_found() -> Response {
Response::empty(StatusCode::NotFound)
.with_header(HeaderType::ContentType, "text/html")
.with_bytes(b"<h1>404 Not Found</h1>")
}