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 ( ) ,
0 commit comments