summaryrefslogtreecommitdiff
path: root/src/handlers/staticfile.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/handlers/staticfile.rs')
-rw-r--r--src/handlers/staticfile.rs83
1 files changed, 83 insertions, 0 deletions
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")),
+ }
+ }
+}