maj/src/server/files.rs

61 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;
use std::path::PathBuf;
pub struct Handler {
base_dir: PathBuf,
}
impl Handler {
/// Serves static files from an OS directory with a given prefix chopped off.
pub fn new(base_dir: PathBuf) -> Self {
Handler { base_dir: base_dir }
}
}
#[async_trait]
impl MajHandler for Handler {
async fn handle(&self, r: Request) -> Result<Response> {
let mut path = std::path::PathBuf::from(&self.base_dir);
if let Some(segments) = r.url.path_segments() {
path.extend(segments);
}
log::debug!("opening file {:?}", path);
match tokio::fs::metadata(&path).await {
Ok(stat) => {
if stat.is_dir() {
if r.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!("{}/", r.url)));
}
}
}
Err(why) => {
log::error!("file {} not found: {}", path.to_str().unwrap(), why);
return Ok(Response::not_found());
}
}
let mut file = tokio::fs::File::open(&path).await?;
let mut buf: Vec<u8> = Vec::new();
tokio::io::copy(&mut file, &mut buf).await?;
// Send header.
if path.extension() == Some(OsStr::new("gmi"))
|| path.extension() == Some(OsStr::new("gemini"))
{
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))
}
}