mod opts;
mod reply;
mod resolve;
mod security;
#[cfg(feature = "directory-listing")]
mod listing;
pub use opts::{HandleOpts, StaticFileResponse};
#[doc(hidden)]
pub use crate::fs::path::sanitize_path;
use hyper::StatusCode;
use crate::Result;
use crate::exts::http::MethodExt;
use crate::fs::meta::FileMetadata;
#[cfg(feature = "mem-cache")]
use crate::mem_cache::cache;
pub async fn handle(opts: &HandleOpts<'_>) -> Result<StaticFileResponse, StatusCode> {
if !opts.method.is_allowed() {
return Err(StatusCode::METHOD_NOT_ALLOWED);
}
let mut file_path = sanitize_path(opts.base_path, opts.uri_path)?;
#[cfg(feature = "mem-cache")]
if let Some(resp) = try_memory_cache(opts, &mut file_path) {
return Ok(resp);
}
let FileMetadata {
file_path,
metadata,
is_dir,
precompressed_variant,
file,
} = resolve::file_metadata(
&mut file_path,
opts.headers,
opts.compression_static,
opts.index_files,
)?;
security::enforce(file_path, is_dir, opts)?;
let resp_file_path = file_path.to_owned();
if let Some(resp) = reply::trailing_slash_redirect(is_dir, opts)? {
return Ok(StaticFileResponse::new(resp, resp_file_path));
}
if opts.method.is_options() {
return Ok(StaticFileResponse::new(
reply::options_reply(),
resp_file_path,
));
}
#[cfg(feature = "directory-listing")]
if let Some(resp) = listing::try_listing(file_path, is_dir, opts)? {
return Ok(StaticFileResponse::new(resp, resp_file_path));
}
let resp =
reply::file_or_precompressed(opts, file_path, &metadata, precompressed_variant, file)?;
Ok(StaticFileResponse::new(resp, resp_file_path))
}
#[cfg(feature = "mem-cache")]
fn try_memory_cache(
opts: &HandleOpts<'_>,
file_path: &mut std::path::PathBuf,
) -> Option<StaticFileResponse> {
opts.memory_cache.as_ref()?;
if opts.redirect_trailing_slash && opts.uri_path.ends_with('/') {
file_path.push("index.html");
}
let result = cache::lookup(file_path.as_path(), opts.headers)?;
match result {
Ok(resp) => Some(StaticFileResponse::new(resp, file_path.clone())),
Err(_) => None,
}
}