Skip to content

Commit

Permalink
Port to cap-async-std.
Browse files Browse the repository at this point in the history
  • Loading branch information
sunfishcode committed Jun 23, 2022
1 parent 20fe435 commit 9b4c544
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 39 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ async-session = { version = "3.0", optional = true }
async-sse = { version = "5.1.0", optional = true }
async-std = { version = "1.6.5", features = ["unstable"] }
async-trait = "0.1.41"
cap-async-std = "0.25.0"
femme = { version = "2.1.1", optional = true }
futures-util = "0.3.6"
http-client = { version = "6.1.0", default-features = false }
Expand Down
74 changes: 36 additions & 38 deletions src/fs/serve_dir.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
use crate::log;
use crate::{Body, Endpoint, Request, Response, Result, StatusCode};

use async_std::path::PathBuf as AsyncPathBuf;
use async_std::io::BufReader;

use std::path::{Path, PathBuf};
use std::{ffi::OsStr, io};
use cap_async_std::fs;

pub(crate) struct ServeDir {
prefix: String,
dir: PathBuf,
dir: fs::Dir,
}

impl ServeDir {
/// Create a new instance of `ServeDir`.
pub(crate) fn new(prefix: String, dir: PathBuf) -> Self {
pub(crate) fn new(prefix: String, dir: fs::Dir) -> Self {
Self { prefix, dir }
}
}
Expand All @@ -29,57 +28,56 @@ where
.strip_prefix(&self.prefix.trim_end_matches('*'))
.unwrap();
let path = path.trim_start_matches('/');
let mut file_path = self.dir.clone();
for p in Path::new(path) {
if p == OsStr::new(".") {
continue;
} else if p == OsStr::new("..") {
file_path.pop();
} else {
file_path.push(&p);

log::info!("Requested file: {:?}", path);

let file = match self.dir.open(path).await {
Ok(file) => file,
Err(e) if e.kind() == std::io::ErrorKind::PermissionDenied => {
log::warn!("Unauthorized attempt to read: {:?}", path);
return Ok(Response::new(StatusCode::Forbidden));
}
}

log::info!("Requested file: {:?}", file_path);

let file_path = AsyncPathBuf::from(file_path);
if !file_path.starts_with(&self.dir) {
log::warn!("Unauthorized attempt to read: {:?}", file_path);
Ok(Response::new(StatusCode::Forbidden))
} else {
match Body::from_file(&file_path).await {
Ok(body) => Ok(Response::builder(StatusCode::Ok).body(body).build()),
Err(e) if e.kind() == io::ErrorKind::NotFound => {
log::warn!("File not found: {:?}", &file_path);
Ok(Response::new(StatusCode::NotFound))
}
Err(e) => Err(e.into()),
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
log::warn!("File not found: {:?}", path);
return Ok(Response::new(StatusCode::NotFound));
}
}
Err(e) => return Err(e.into()),
};

// TODO: This always uses `mime::BYTE_STREAM`; with http-types 3.0
// we'll be able to use `Body::from_open_file` which fixes this.
let body = Body::from_reader(BufReader::new(file), None);
Ok(Response::builder(StatusCode::Ok).body(body).build())
}
}

#[cfg(test)]
mod test {
use super::*;

use std::fs::{self, File};
use std::io::Write;
use async_std::io::WriteExt;
use cap_async_std::ambient_authority;
use cap_async_std::fs::Dir;

fn serve_dir(tempdir: &tempfile::TempDir) -> crate::Result<ServeDir> {
let static_dir = tempdir.path().join("static");
fs::create_dir(&static_dir)?;

let file_path = static_dir.join("foo");
let mut file = File::create(&file_path)?;
write!(file, "Foobar")?;
let static_dir = async_std::task::block_on(async { setup_static_dir(tempdir).await })?;

Ok(ServeDir {
prefix: "/static/".to_string(),
dir: static_dir,
})
}

async fn setup_static_dir(tempdir: &tempfile::TempDir) -> crate::Result<Dir> {
let static_dir = tempdir.path().join("static");
Dir::create_ambient_dir_all(&static_dir, ambient_authority()).await?;

let static_dir = Dir::open_ambient_dir(static_dir, ambient_authority()).await?;
let mut file = static_dir.create("foo").await?;
write!(file, "Foobar").await?;
Ok(static_dir)
}

fn request(path: &str) -> crate::Request<()> {
let request = crate::http::Request::get(
crate::http::Url::parse(&format!("http://localhost/{}", path)).unwrap(),
Expand Down
6 changes: 5 additions & 1 deletion src/route.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
use async_std::task;
use cap_async_std::ambient_authority;
use cap_async_std::fs::Dir;
use std::fmt::Debug;
use std::io;
use std::path::Path;
Expand Down Expand Up @@ -165,7 +168,8 @@ impl<'a, State: Clone + Send + Sync + 'static> Route<'a, State> {
/// ```
pub fn serve_dir(&mut self, dir: impl AsRef<Path>) -> io::Result<()> {
// Verify path exists, return error if it doesn't.
let dir = dir.as_ref().to_owned().canonicalize()?;
let path = dir.as_ref().to_owned().canonicalize()?;
let dir = task::block_on(async { Dir::open_ambient_dir(path, ambient_authority()).await })?;
let prefix = self.path().to_string();
self.get(ServeDir::new(prefix, dir));
Ok(())
Expand Down

0 comments on commit 9b4c544

Please sign in to comment.