@@ -17,7 +17,6 @@ use std::io;
17
17
use std:: ops:: Bound ;
18
18
use std:: path:: PathBuf ;
19
19
use std:: pin:: Pin ;
20
- use std:: sync:: Arc ;
21
20
use std:: task:: Poll ;
22
21
use std:: time:: { SystemTime , UNIX_EPOCH } ;
23
22
use std:: { cmp, path:: Path } ;
@@ -27,16 +26,6 @@ use tokio_util::io::poll_read_buf;
27
26
28
27
use crate :: error:: Result ;
29
28
30
- /// A small Arch `PathBuf` wrapper since Arc<PathBuf> doesn't implement AsRef<Path>.
31
- #[ derive( Clone , Debug ) ]
32
- pub struct ArcPath ( pub Arc < PathBuf > ) ;
33
-
34
- impl AsRef < Path > for ArcPath {
35
- fn as_ref ( & self ) -> & Path {
36
- ( * self . 0 ) . as_ref ( )
37
- }
38
- }
39
-
40
29
/// Entry point to handle web server requests which map to specific files
41
30
/// on file system and return a file response.
42
31
pub async fn handle_request (
@@ -51,14 +40,13 @@ pub async fn handle_request(
51
40
return Err ( StatusCode :: METHOD_NOT_ALLOWED ) ;
52
41
}
53
42
54
- let base = Arc :: new ( base. into ( ) ) ;
55
- let ( path, meta, auto_index) = path_from_tail ( base, uri_path) . await ?;
43
+ let ( path, meta, auto_index) = path_from_tail ( base. into ( ) , uri_path) . await ?;
56
44
57
45
// Directory listing
58
46
// 1. Check if "directory listing" feature is enabled,
59
47
// if current path is a valid directory and
60
48
// if it does not contain an `index.html` file
61
- if dir_listing && auto_index && !path. as_ref ( ) . exists ( ) {
49
+ if dir_listing && auto_index && !path. exists ( ) {
62
50
// Redirect if current path does not end with a slash
63
51
let current_path = uri_path;
64
52
if !current_path. ends_with ( '/' ) {
@@ -79,10 +67,10 @@ pub async fn handle_request(
79
67
}
80
68
81
69
fn path_from_tail (
82
- base : Arc < PathBuf > ,
70
+ base : PathBuf ,
83
71
tail : & str ,
84
- ) -> impl Future < Output = Result < ( ArcPath , Metadata , bool ) , StatusCode > > + Send {
85
- future:: ready ( sanitize_path ( base. as_ref ( ) , tail) ) . and_then ( |mut buf| async {
72
+ ) -> impl Future < Output = Result < ( PathBuf , Metadata , bool ) , StatusCode > > + Send {
73
+ future:: ready ( sanitize_path ( base, tail) ) . and_then ( |mut buf| async {
86
74
match tokio:: fs:: metadata ( & buf) . await {
87
75
Ok ( meta) => {
88
76
let mut auto_index = false ;
@@ -92,7 +80,7 @@ fn path_from_tail(
92
80
auto_index = true ;
93
81
}
94
82
tracing:: trace!( "dir: {:?}" , buf) ;
95
- Ok ( ( ArcPath ( Arc :: new ( buf) ) , meta, auto_index) )
83
+ Ok ( ( buf, meta, auto_index) )
96
84
}
97
85
Err ( err) => {
98
86
tracing:: debug!( "file not found: {:?}" , err) ;
@@ -104,11 +92,11 @@ fn path_from_tail(
104
92
105
93
fn directory_listing (
106
94
method : & Method ,
107
- res : ( String , ArcPath ) ,
95
+ res : ( String , PathBuf ) ,
108
96
) -> impl Future < Output = Result < Response < Body > , StatusCode > > + Send {
109
97
let ( current_path, path) = res;
110
98
let is_head = method == Method :: HEAD ;
111
- let parent = path. as_ref ( ) . parent ( ) . unwrap ( ) ;
99
+ let parent = path. parent ( ) . unwrap ( ) ;
112
100
let parent = PathBuf :: from ( parent) ;
113
101
114
102
tokio:: fs:: read_dir ( parent) . then ( move |res| match res {
@@ -118,7 +106,7 @@ fn directory_listing(
118
106
Err ( err) => {
119
107
tracing:: error!(
120
108
"error during directory entries reading (path={:?}): {} " ,
121
- path. as_ref ( ) . parent( ) . unwrap( ) . display( ) ,
109
+ path. parent( ) . unwrap( ) . display( ) ,
122
110
err
123
111
) ;
124
112
Err ( StatusCode :: INTERNAL_SERVER_ERROR )
@@ -128,20 +116,17 @@ fn directory_listing(
128
116
Err ( err) => {
129
117
let status = match err. kind ( ) {
130
118
io:: ErrorKind :: NotFound => {
131
- tracing:: debug!( "entry file not found: {:?}" , path. as_ref ( ) . display( ) ) ;
119
+ tracing:: debug!( "entry file not found: {:?}" , path. display( ) ) ;
132
120
StatusCode :: NOT_FOUND
133
121
}
134
122
io:: ErrorKind :: PermissionDenied => {
135
- tracing:: warn!(
136
- "entry file permission denied: {:?}" ,
137
- path. as_ref( ) . display( )
138
- ) ;
123
+ tracing:: warn!( "entry file permission denied: {:?}" , path. display( ) ) ;
139
124
StatusCode :: FORBIDDEN
140
125
}
141
126
_ => {
142
127
tracing:: error!(
143
128
"directory entries error (path={:?}): {} " ,
144
- path. as_ref ( ) . display( ) ,
129
+ path. display( ) ,
145
130
err
146
131
) ;
147
132
StatusCode :: INTERNAL_SERVER_ERROR
@@ -249,7 +234,7 @@ fn parse_last_modified(modified: SystemTime) -> Result<time::Tm, Box<dyn std::er
249
234
/// Reply with a file content.
250
235
fn file_reply (
251
236
headers : & HeaderMap < HeaderValue > ,
252
- res : ( ArcPath , Metadata , bool ) ,
237
+ res : ( PathBuf , Metadata , bool ) ,
253
238
) -> impl Future < Output = Result < Response < Body > , StatusCode > > + Send {
254
239
let ( path, meta, auto_index) = res;
255
240
let conditionals = get_conditional_headers ( headers) ;
@@ -258,19 +243,15 @@ fn file_reply(
258
243
Err ( err) => {
259
244
let status = match err. kind ( ) {
260
245
io:: ErrorKind :: NotFound => {
261
- tracing:: debug!( "file not found: {:?}" , path. as_ref ( ) . display( ) ) ;
246
+ tracing:: debug!( "file not found: {:?}" , path. display( ) ) ;
262
247
StatusCode :: NOT_FOUND
263
248
}
264
249
io:: ErrorKind :: PermissionDenied => {
265
- tracing:: warn!( "file permission denied: {:?}" , path. as_ref ( ) . display( ) ) ;
250
+ tracing:: warn!( "file permission denied: {:?}" , path. display( ) ) ;
266
251
StatusCode :: FORBIDDEN
267
252
}
268
253
_ => {
269
- tracing:: error!(
270
- "file open error (path={:?}): {} " ,
271
- path. as_ref( ) . display( ) ,
272
- err
273
- ) ;
254
+ tracing:: error!( "file open error (path={:?}): {} " , path. display( ) , err) ;
274
255
StatusCode :: INTERNAL_SERVER_ERROR
275
256
}
276
257
} ;
@@ -293,16 +274,15 @@ fn get_conditional_headers(header_list: &HeaderMap<HeaderValue>) -> Conditionals
293
274
}
294
275
}
295
276
296
- fn sanitize_path ( base : impl AsRef < Path > , tail : & str ) -> Result < PathBuf , StatusCode > {
297
- let mut buf = PathBuf :: from ( base. as_ref ( ) ) ;
277
+ fn sanitize_path ( mut buf : PathBuf , tail : & str ) -> Result < PathBuf , StatusCode > {
298
278
let p = match percent_decode_str ( tail) . decode_utf8 ( ) {
299
279
Ok ( p) => p,
300
280
Err ( err) => {
301
281
tracing:: debug!( "dir: failed to decode route={:?}: {:?}" , tail, err) ;
302
282
return Err ( StatusCode :: UNSUPPORTED_MEDIA_TYPE ) ;
303
283
}
304
284
} ;
305
- tracing:: trace!( "dir? base={:?}, route={:?}" , base . as_ref ( ) , p) ;
285
+ tracing:: trace!( "dir? base={:?}, route={:?}" , buf , p) ;
306
286
for seg in p. split ( '/' ) {
307
287
if seg. starts_with ( ".." ) {
308
288
tracing:: warn!( "dir: rejecting segment starting with '..'" ) ;
@@ -381,13 +361,13 @@ impl Conditionals {
381
361
382
362
fn file_conditional (
383
363
f : TkFile ,
384
- path : ArcPath ,
364
+ path : PathBuf ,
385
365
meta : Metadata ,
386
366
auto_index : bool ,
387
367
conditionals : Conditionals ,
388
368
) -> impl Future < Output = Result < Response < Body > , StatusCode > > + Send {
389
369
file_metadata ( f, meta, auto_index)
390
- . map_ok ( |( file, meta) | response_body ( file, meta, path, conditionals) )
370
+ . map_ok ( |( file, meta) | response_body ( file, & meta, path, conditionals) )
391
371
}
392
372
393
373
async fn file_metadata (
@@ -409,8 +389,8 @@ async fn file_metadata(
409
389
410
390
fn response_body (
411
391
file : TkFile ,
412
- meta : Metadata ,
413
- path : ArcPath ,
392
+ meta : & Metadata ,
393
+ path : PathBuf ,
414
394
conditionals : Conditionals ,
415
395
) -> Response < Body > {
416
396
let mut len = meta. len ( ) ;
@@ -421,7 +401,7 @@ fn response_body(
421
401
bytes_range ( range, len)
422
402
. map ( |( start, end) | {
423
403
let sub_len = end - start;
424
- let buf_size = optimal_buf_size ( & meta) ;
404
+ let buf_size = optimal_buf_size ( meta) ;
425
405
let stream = file_stream ( file, buf_size, ( start, end) ) ;
426
406
let body = Body :: wrap_stream ( stream) ;
427
407
@@ -436,7 +416,7 @@ fn response_body(
436
416
len = sub_len;
437
417
}
438
418
439
- let mime = mime_guess:: from_path ( path. as_ref ( ) ) . first_or_octet_stream ( ) ;
419
+ let mime = mime_guess:: from_path ( path) . first_or_octet_stream ( ) ;
440
420
441
421
resp. headers_mut ( ) . typed_insert ( ContentLength ( len) ) ;
442
422
resp. headers_mut ( ) . typed_insert ( ContentType :: from ( mime) ) ;
@@ -602,14 +582,14 @@ mod tests {
602
582
}
603
583
604
584
assert_eq ! (
605
- sanitize_path( base, "/foo.html" ) . unwrap( ) ,
585
+ sanitize_path( base. into ( ) , "/foo.html" ) . unwrap( ) ,
606
586
p( "/var/www/foo.html" )
607
587
) ;
608
588
609
589
// bad paths
610
- sanitize_path ( base, "/../foo.html" ) . expect_err ( "dot dot" ) ;
590
+ sanitize_path ( base. into ( ) , "/../foo.html" ) . expect_err ( "dot dot" ) ;
611
591
612
- sanitize_path ( base, "/C:\\ /foo.html" ) . expect_err ( "C:\\ " ) ;
592
+ sanitize_path ( base. into ( ) , "/C:\\ /foo.html" ) . expect_err ( "C:\\ " ) ;
613
593
}
614
594
615
595
#[ test]
0 commit comments