From 2f432862e1464a5d377d090cdd252003ea292ddf Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Thu, 5 Mar 2020 15:33:00 +0100 Subject: [PATCH] Add static file serving This depends on https://github.com/http-rs/tide/pull/414 --- Cargo.toml | 1 + examples/static_file.rs | 68 +++++++++++++++++++++++++++++++++++++++++ src/server/mod.rs | 1 - src/server/route.rs | 18 +++++++++++ 4 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 examples/static_file.rs diff --git a/Cargo.toml b/Cargo.toml index f10c29904..6d9259990 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,6 +61,7 @@ serde = { version = "1.0.102", features = ["derive"] } structopt = "0.3.3" surf = "2.0.0-alpha.1" futures = "0.3.1" +femme = "1.3.0" [[test]] name = "nested" diff --git a/examples/static_file.rs b/examples/static_file.rs new file mode 100644 index 000000000..84c38b068 --- /dev/null +++ b/examples/static_file.rs @@ -0,0 +1,68 @@ +use async_std::fs::File; +use async_std::io::BufReader; +use async_std::task; +use http_types::StatusCode; + +use std::io; +use tide::{Endpoint, Request, Response}; + +use std::path::{Path, PathBuf}; + +type BoxFuture<'a, T> = std::pin::Pin + 'a + Send>>; + +fn main() -> Result<(), std::io::Error> { + femme::start(log::LevelFilter::Info).unwrap(); + task::block_on(async { + let mut app = tide::new(); + app.at("/").get(|_| async move { "Hello, world!" }); + serve_dir(&mut app.at("/foo"), "src/")?; + app.listen("127.0.0.1:8080").await?; + Ok(()) + }) +} + +fn serve_dir( + route: &mut tide::Route, + dir: impl AsRef, +) -> io::Result<()> { + // Verify path exists, return error if it doesn't. + let dir = dir.as_ref().to_owned().canonicalize()?; + let serve = ServeDir { + prefix: route.path().to_string(), + dir, + }; + route.at("*").get(serve); + Ok(()) +} + +pub struct ServeDir { + prefix: String, + dir: PathBuf, +} + +impl Endpoint for ServeDir { + fn call<'a>(&'a self, req: Request) -> BoxFuture<'a, Response> { + let path = req.uri().path(); + let path = path.replace(&self.prefix, ""); + let path = path.trim_start_matches('/'); + let dir = self.dir.clone(); + let dir = dir.join(&path); + log::info!("Requested file: {:?}", dir); + + Box::pin(async move { + let file = match async_std::fs::canonicalize(&dir).await { + Err(_) => { + log::info!("File not found: {:?}", dir); + return Response::new(StatusCode::NotFound); + } + Ok(file) => { + log::info!("Serving file: {:?}", file); + File::open(file).await.unwrap() // TODO: remove unwrap + } + }; + + // TODO: fix related bug where async-h1 crashes on large files + Response::new(StatusCode::Ok).body(BufReader::new(file)) + }) + } +} diff --git a/src/server/mod.rs b/src/server/mod.rs index f9b794aed..367678484 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -5,7 +5,6 @@ use async_std::io; use async_std::net::ToSocketAddrs; use async_std::sync::Arc; use async_std::task::{Context, Poll}; - use http_service::HttpService; use std::pin::Pin; diff --git a/src/server/route.rs b/src/server/route.rs index 1e4b0d9ef..268bf6f16 100644 --- a/src/server/route.rs +++ b/src/server/route.rs @@ -54,6 +54,24 @@ impl<'a, State: 'static> Route<'a, State> { } } + /// Get the current path. + pub fn path(&self) -> &str { + &self.path + } + + // /// Serve up a static directory. + // pub fn dir(&mut self, dir: &Path) -> http_types::Result<()> { + // for entry in WalkDir::new(dir) { + // let entry = entry?; + // if !entry.file_type().is_dir() { + // let p = entry.into_path(); + // continue; + // } + // // self.at(entry.name()).get(/* serve file */) + // } + // todo!(); + // } + /// Treat the current path as a prefix, and strip prefixes from requests. /// /// This method is marked unstable as its name might change in the near future.