/// A simple handler for disk based files. Will optionally chop off a prefix. use super::{Handler as MajHandler, Request, Result}; use crate::Response; use async_trait::async_trait; use std::ffi::OsStr; pub struct Handler { chop_off: String, base_dir: String, } impl Handler { /// Serves static files from an OS directory with a given prefix chopped off. pub fn new(chop_off: String, base_dir: String) -> Self { Handler { chop_off: chop_off, base_dir: base_dir, } } fn chop(&self, path: String) -> Option { if path.starts_with(&self.chop_off) { path.strip_prefix(&self.chop_off).map(|val| val.to_string()) } else { Some(path) } } } #[async_trait] impl MajHandler for Handler { async fn handle(&self, r: Request) -> Result { let mut path = std::path::PathBuf::from(&self.base_dir); let mut url = r.url.clone(); url.set_path(&self.chop(r.url.path().to_string()).unwrap()); if let Some(segments) = r.url.path_segments() { path.extend(segments); } if async_std::fs::metadata(&path).await?.is_dir() { if url.as_str().ends_with('/') { path.push("index.gmi"); } else { // Send a redirect when the URL for a directory has no trailing slash. return Ok(Response::perm_redirect(format!("{}/", url))); } } let mut file = async_std::fs::File::open(&path).await?; let mut buf: Vec = Vec::new(); async_std::io::copy(&mut file, &mut buf).await?; // Send header. if path.extension() == Some(OsStr::new("gmi")) { return Ok(Response::gemini(buf)); } let mime = mime_guess::from_path(&path).first_or_octet_stream(); Ok(Response::with_body(mime.essence_str().to_string(), buf)) } }