use std::fs::File; use std::io::{ErrorKind, Read}; use std::path::PathBuf; use hyper::{Body, Request, Response, StatusCode}; use super::{async_trait, HttpHandler}; #[derive(Clone, Debug)] pub struct Params { root: PathBuf, } impl Params { pub fn new(root: PathBuf) -> Self { let root = root.canonicalize().unwrap(); Self { root } } } #[async_trait] impl HttpHandler for Params { async fn handle(&self, request: Request) -> Response { let Params { root } = self; eprintln!("handling request {:?}", request); let request_path = request.uri().path(); let request_path = request_path.trim_start_matches("/"); let target = root.join(request_path); let target = if target.is_dir() { target.join("index.html") } else { target }; eprintln!("target is {}", target.display()); let response = if target.ancestors().all(|ancestor| ancestor != root) { // oops! all directory traversal Response::builder() .status(StatusCode::BAD_REQUEST) .body(Body::empty()) } else { let file = File::open(target); match file { Ok(mut file) => { let mut result = Vec::new(); file.read_to_end(&mut result).unwrap(); Response::builder().body(Body::from(result)) } Err(err) => { let status_code = match err.kind() { ErrorKind::NotFound => StatusCode::NOT_FOUND, _ => StatusCode::BAD_REQUEST, }; Response::builder() .status(status_code) .body(Body::from(status_code.canonical_reason().unwrap_or(""))) } } }; response.unwrap() } }