maj/src/server/files.rs

62 lines
1.9 KiB
Rust

/// 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<String> {
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<Response> {
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<u8> = 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))
}
}