1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
use std::path::{Path, PathBuf};
use std::io::ErrorKind::NotFound;
use std::fs;
use hyper::method::Method::{Get, Head};
use status::StatusCode;
use request::Request;
use response::Response;
use middleware::{Middleware, MiddlewareResult};
#[derive(Clone)]
pub struct StaticFilesHandler {
root_path: PathBuf
}
impl<D> Middleware<D> for StaticFilesHandler {
fn invoke<'a>(&self, req: &mut Request<D>, res: Response<'a, D>)
-> MiddlewareResult<'a, D> {
match req.origin.method {
Get | Head => self.with_file(self.extract_path(req), res),
_ => res.next_middleware()
}
}
}
impl StaticFilesHandler {
pub fn new<P: AsRef<Path>>(root_path: P) -> StaticFilesHandler {
StaticFilesHandler {
root_path: root_path.as_ref().to_path_buf()
}
}
fn extract_path<'a, D>(&self, req: &'a mut Request<D>) -> Option<&'a str> {
req.path_without_query().map(|path| {
debug!("{:?} {:?}{:?}", req.origin.method, self.root_path.display(), path);
match path {
"/" => "index.html",
path => &path[1..],
}
})
}
fn with_file<'a, 'b, D, P>(&self,
relative_path: Option<P>,
res: Response<'a, D>)
-> MiddlewareResult<'a, D> where P: AsRef<Path> {
if let Some(path) = relative_path {
let path = path.as_ref();
if !safe_path(path) {
let log_msg = format!("The path '{:?}' was denied access.", path);
return res.error(StatusCode::BadRequest, log_msg);
}
let path = self.root_path.join(path);
match fs::metadata(&path) {
Ok(ref attr) if attr.is_file() => return res.send_file(&path),
Err(ref e) if e.kind() != NotFound => debug!("Error getting metadata \
for file '{:?}': {:?}",
path, e),
_ => {}
}
};
res.next_middleware()
}
}
fn safe_path<P: AsRef<Path>>(path: P) -> bool {
use std::path::Component;
path.as_ref().components().all(|c| match c {
Component::CurDir | Component::Normal(_) => true,
_ => false
})
}
#[test]
fn bad_paths() {
let bad_paths = &[
"foo/bar/../baz/index.html",
"foo/bar/../baz",
"../bar/",
"..",
"/"
];
for &path in bad_paths {
assert!(!safe_path(path), "expected {:?} to be suspicious", path);
}
}
#[test]
fn valid_paths() {
let good_paths = &[
"foo/bar/./baz/index.html",
"foo/bar/./baz",
"./bar/",
".",
"index.html"
];
for &path in good_paths {
assert!(safe_path(path), "expected {:?} to not be suspicious", path);
}
}