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
use iron::{ Handler, AfterMiddleware };
use iron::prelude::*;
use iron::status::Status;
use std::path::{ PathBuf };
use std::fs::{ metadata, read_dir, ReadDir };
use std::any::Any;
use errors::{ io_to_iron };
use url::percent_encoding::percent_decode;
pub trait ResponseStrategy {
fn make_response(&self, dir: ReadDir) -> IronResult<Response>;
}
pub struct StaticDir<T> {
pub root: PathBuf,
response_strategy: Box<T>,
}
impl<T> StaticDir<T> {
pub fn new<P>(root: P, response_strategy: T) -> StaticDir<T> where P: Into<PathBuf> {
StaticDir{ root: root.into(), response_strategy: Box::new(response_strategy) }
}
}
impl<T> StaticDir<T> where T: Send + Sync + Any + ResponseStrategy {
fn provide_dir<P>(&self, path: P) -> IronResult<Response> where P: Into<PathBuf> {
let path = add_trailing_slash(path);
metadata(&path)
.map_err(io_to_iron)
.and_then(|meta| {
match meta.is_dir() {
true => read_dir(&path).map_err(io_to_iron),
false => unreachable!(),
}
})
.and_then(|dir| self.response_strategy.make_response(dir))
}
}
#[inline]
fn extend_req_path<P>(request: &Request, root_path: P) -> PathBuf where P: Into<PathBuf> {
let mut path = root_path.into();
let decoded_req_path = request.url.path.iter().map(|part| String::from_utf8(percent_decode(part.as_bytes())).unwrap());
path.extend(decoded_req_path);
path
}
#[inline]
fn add_trailing_slash<P>(path: P) -> PathBuf where P: Into<PathBuf> {
let mut path = path.into();
path.push("");
path
}
impl<T> Handler for StaticDir<T> where T: Send + Sync + Any + ResponseStrategy {
fn handle(&self, req: &mut Request) -> IronResult<Response> {
let requested_path = extend_req_path(req, &self.root);
self.provide_dir(&requested_path)
}
}
impl<T> AfterMiddleware for StaticDir<T> where T: Send + Sync + Any + ResponseStrategy {
fn after(&self, req: &mut Request, res: Response) -> IronResult<Response> {
match res.status {
Some(Status::MovedPermanently) => {
let requested_path = extend_req_path(req, &self.root);
self.provide_dir(&requested_path)
},
_ => Ok(res),
}
}
fn catch(&self, req: &mut Request, err: IronError) -> IronResult<Response> {
match err.response.status {
Some(Status::NotFound) => {
let requested_path = extend_req_path(req, &self.root);
self.provide_dir(&requested_path)
},
_ => Err(err),
}
}
}