granian/src/files.rs
2025-05-19 10:25:13 +02:00

71 lines
2.6 KiB
Rust

use anyhow::Result;
use futures::TryStreamExt;
use http_body_util::BodyExt;
use hyper::{
HeaderMap, StatusCode,
header::{HeaderValue, SERVER as HK_SERVER},
};
use std::{io, path::Path};
use tokio::fs::File;
use tokio_util::io::ReaderStream;
use crate::http::{HTTPResponse, HV_SERVER, response_404};
#[inline(always)]
pub(crate) fn match_static_file(uri_path: &str, prefix: &str, mount_point: &str) -> Option<Result<String>> {
if let Some(file_path) = uri_path.strip_prefix(prefix) {
#[cfg(not(windows))]
let fpath = format!("{mount_point}{file_path}");
#[cfg(windows)]
let fpath = format!("{mount_point}{}", file_path.replace("/", "\\"));
match Path::new(&fpath).canonicalize() {
Ok(full_path) => {
#[cfg(windows)]
let full_path = &full_path.display().to_string()[4..];
if full_path.starts_with(mount_point) {
#[cfg(not(windows))]
return full_path.to_str().map(ToOwned::to_owned).map(Ok);
#[cfg(windows)]
return Some(Ok(full_path.to_owned()));
}
return Some(Err(anyhow::anyhow!("outside mount path")));
}
Err(err) if err.kind() == io::ErrorKind::NotFound => {
return Some(Err(err.into()));
}
_ => {}
}
}
None
}
pub(crate) async fn serve_static_file(path: String, expires: String) -> HTTPResponse {
match File::open(&path).await {
Ok(file) => {
let mime = mime_guess::from_path(path).first();
let stream = ReaderStream::with_capacity(file, 131_072);
let stream_body = http_body_util::StreamBody::new(stream.map_ok(hyper::body::Frame::data));
let mut headers = HeaderMap::new();
let mut res = hyper::Response::new(BodyExt::map_err(stream_body, std::convert::Into::into).boxed());
headers.insert(HK_SERVER, HV_SERVER);
headers.insert(
"cache-control",
HeaderValue::from_str(&format!("max-age={expires}")).unwrap(),
);
if let Some(mime) = mime {
if let Ok(hv) = HeaderValue::from_str(mime.essence_str()) {
headers.insert("content-type", hv);
}
}
*res.status_mut() = StatusCode::from_u16(200).unwrap();
*res.headers_mut() = headers;
res
}
Err(_) => {
log::info!("Request static file {path} not found");
response_404()
}
}
}