1
1
use crate::log;
2
2
use crate::{Body, Endpoint, Request, Response, Result, StatusCode};
3
3
4
-
use async_std::path::PathBuf as AsyncPathBuf;
4
+
use async_std::io::BufReader;
5
5
6
-
use std::path::{Path, PathBuf};
7
-
use std::{ffi::OsStr, io};
6
+
use cap_async_std::fs;
8
7
9
8
pub(crate) struct ServeDir {
10
9
prefix: String,
11
-
dir: PathBuf,
10
+
dir: fs::Dir,
12
11
}
13
12
14
13
impl ServeDir {
15
14
/// Create a new instance of `ServeDir`.
16
-
pub(crate) fn new(prefix: String, dir: PathBuf) -> Self {
15
+
pub(crate) fn new(prefix: String, dir: fs::Dir) -> Self {
17
16
Self { prefix, dir }
18
17
}
19
18
}
@@ -29,57 +28,56 @@ where
29
28
.strip_prefix(&self.prefix.trim_end_matches('*'))
30
29
.unwrap();
31
30
let path = path.trim_start_matches('/');
32
-
let mut file_path = self.dir.clone();
33
-
for p in Path::new(path) {
34
-
if p == OsStr::new(".") {
35
-
continue;
36
-
} else if p == OsStr::new("..") {
37
-
file_path.pop();
38
-
} else {
39
-
file_path.push(&p);
31
+
32
+
log::info!("Requested file: {:?}", path);
33
+
34
+
let file = match self.dir.open(path).await {
35
+
Ok(file) => file,
36
+
Err(e) if e.kind() == std::io::ErrorKind::PermissionDenied => {
37
+
log::warn!("Unauthorized attempt to read: {:?}", path);
38
+
return Ok(Response::new(StatusCode::Forbidden));
40
39
}
41
-
}
42
-
43
-
log::info!("Requested file: {:?}", file_path);
44
-
45
-
let file_path = AsyncPathBuf::from(file_path);
46
-
if !file_path.starts_with(&self.dir) {
47
-
log::warn!("Unauthorized attempt to read: {:?}", file_path);
48
-
Ok(Response::new(StatusCode::Forbidden))
49
-
} else {
50
-
match Body::from_file(&file_path).await {
51
-
Ok(body) => Ok(Response::builder(StatusCode::Ok).body(body).build()),
52
-
Err(e) if e.kind() == io::ErrorKind::NotFound => {
53
-
log::warn!("File not found: {:?}", &file_path);
54
-
Ok(Response::new(StatusCode::NotFound))
55
-
}
56
-
Err(e) => Err(e.into()),
40
+
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
41
+
log::warn!("File not found: {:?}", path);
42
+
return Ok(Response::new(StatusCode::NotFound));
57
43
}
58
-
}
44
+
Err(e) => return Err(e.into()),
45
+
};
46
+
47
+
// TODO: This always uses `mime::BYTE_STREAM`; with http-types 3.0
48
+
// we'll be able to use `Body::from_open_file` which fixes this.
49
+
let body = Body::from_reader(BufReader::new(file), None);
50
+
Ok(Response::builder(StatusCode::Ok).body(body).build())
59
51
}
60
52
}
61
53
62
54
#[cfg(test)]
63
55
mod test {
64
56
use super::*;
65
57
66
-
use std::fs::{self, File};
67
-
use std::io::Write;
58
+
use async_std::io::WriteExt;
59
+
use cap_async_std::ambient_authority;
60
+
use cap_async_std::fs::Dir;
68
61
69
62
fn serve_dir(tempdir: &tempfile::TempDir) -> crate::Result<ServeDir> {
70
-
let static_dir = tempdir.path().join("static");
71
-
fs::create_dir(&static_dir)?;
72
-
73
-
let file_path = static_dir.join("foo");
74
-
let mut file = File::create(&file_path)?;
75
-
write!(file, "Foobar")?;
63
+
let static_dir = async_std::task::block_on(async { setup_static_dir(tempdir).await })?;
76
64
77
65
Ok(ServeDir {
78
66
prefix: "/static/".to_string(),
79
67
dir: static_dir,
80
68
})
81
69
}
82
70
71
+
async fn setup_static_dir(tempdir: &tempfile::TempDir) -> crate::Result<Dir> {
72
+
let static_dir = tempdir.path().join("static");
73
+
Dir::create_ambient_dir_all(&static_dir, ambient_authority()).await?;
74
+
75
+
let static_dir = Dir::open_ambient_dir(static_dir, ambient_authority()).await?;
76
+
let mut file = static_dir.create("foo").await?;
77
+
write!(file, "Foobar").await?;
78
+
Ok(static_dir)
79
+
}
80
+
83
81
fn request(path: &str) -> crate::Request<()> {
84
82
let request = crate::http::Request::get(
85
83
crate::http::Url::parse(&format!("http://localhost/{}", path)).unwrap(),
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4