diff options
Diffstat (limited to 'src/handlers')
| -rw-r--r-- | src/handlers/mod.rs | 38 | ||||
| -rw-r--r-- | src/handlers/staticfile.rs | 83 |
2 files changed, 121 insertions, 0 deletions
diff --git a/src/handlers/mod.rs b/src/handlers/mod.rs new file mode 100644 index 0000000..800b61e --- /dev/null +++ b/src/handlers/mod.rs @@ -0,0 +1,38 @@ +use mlua::{FromLua, Value}; + +use crate::{handlers::staticfile::StaticFile, request::Request, response::Response}; + +mod staticfile; + +pub(super) trait Handle<T> { + async fn handle(&self, request: Request<T>) -> Result<Response, Error>; +} + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("unsupported method")] + Unsupported, + + #[error("static file handler error: {0}")] + StaticFile(#[from] staticfile::Error), +} + +#[derive(Debug, Clone)] +pub enum Handlers { + StaticFile(StaticFile), +} + +impl FromLua for Handlers { + fn from_lua(value: Value, lua: &mlua::Lua) -> mlua::Result<Self> { + match value { + Value::Table(table) => match table.get::<String>("handler")?.as_str() { + "staticfile" => Ok(Self::StaticFile(StaticFile::from_lua( + Value::Table(table.clone()), + lua, + )?)), + _ => Err(mlua::Error::runtime("unknown handler")), + }, + _ => Err(mlua::Error::runtime("expected table")), + } + } +} diff --git a/src/handlers/staticfile.rs b/src/handlers/staticfile.rs new file mode 100644 index 0000000..a765315 --- /dev/null +++ b/src/handlers/staticfile.rs @@ -0,0 +1,83 @@ +use std::{ffi::OsString, os::unix::ffi::OsStringExt, path::PathBuf, time}; + +use mlua::{FromLua, Value}; +use tokio::{ + fs::{self, File}, + io, +}; + +use crate::{ + Handle, handlers, + request::Request, + response::{self, Response}, +}; + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("io error: {0}")] + Io(#[from] io::Error), + + #[error("http error: {0}")] + Http(#[from] http::Error), +} + +#[derive(Debug, Clone)] +pub struct StaticFile { + path: PathBuf, + mime: String, +} + +impl<T> Handle<T> for StaticFile { + async fn handle(&self, request: Request<T>) -> Result<Response, handlers::Error> { + if let http::Method::GET | http::Method::HEAD = request.inner().method().clone() { + if !fs::try_exists(&self.path).await.map_err(Error::Io)? { + return Ok(Response::new( + http::Response::builder() + .status(http::StatusCode::NOT_FOUND) + .body(response::Body::Empty) + .map_err(Error::Http)?, + )); + } + + let file = File::open(&self.path).await.map_err(Error::Io)?; + let metadata = file.metadata().await.map_err(Error::Io)?; + + let now = time::SystemTime::now(); + let date = httpdate::fmt_http_date(now); + + let response = http::Response::builder() + .status(http::StatusCode::OK) + .header("CONTENT-LENGTH", metadata.len()) + .header("CONTENT-TYPE", &self.mime) + .header("DATE", date); + + match request.inner().method().clone() { + http::Method::GET => Ok(Response::new( + response + .body(response::Body::File(file)) + .map_err(Error::Http)?, + )), + http::Method::HEAD => Ok(Response::new( + response.body(response::Body::Empty).map_err(Error::Http)?, + )), + _ => unreachable!(), + } + } else { + Err(handlers::Error::Unsupported) + } + } +} + +impl FromLua for StaticFile { + fn from_lua(value: mlua::Value, _: &mlua::Lua) -> mlua::Result<Self> { + match value { + Value::Table(table) => Ok(Self { + path: PathBuf::from(OsString::from_vec( + table.get::<mlua::String>("path")?.as_bytes().to_vec(), + )), + mime: table.get::<String>("mime")?, + }), + _ => Err(mlua::Error::runtime("expected table")), + } + } +} |
