|
1 | 1 | use arc_cell::ArcCell;
|
2 |
| -use futures::{self, Future, Stream}; |
| 2 | +use futures::{self, BoxFuture, Future, Stream}; |
3 | 3 | use futures_cpupool::CpuPool;
|
4 | 4 | use hyper::{self, Get, Post, StatusCode};
|
5 | 5 | use hyper::header::{ContentLength, ContentType};
|
6 | 6 | use hyper::server::{Http, Request, Response, Service};
|
| 7 | +use route_recognizer::{Match, Params, Router}; |
7 | 8 |
|
8 | 9 | use serde::Serialize;
|
9 | 10 | use serde::de::DeserializeOwned;
|
10 | 11 | use serde_json;
|
11 | 12 | use std::env;
|
12 |
| -use std::fs::File; |
13 |
| -use std::io::Read; |
14 | 13 | use std::net::SocketAddr;
|
15 |
| -use std::path::Path; |
16 | 14 | use std::str;
|
17 | 15 | use std::sync::Arc;
|
18 | 16 |
|
19 | 17 | mod api;
|
20 | 18 |
|
21 | 19 | pub struct Data;
|
22 | 20 |
|
| 21 | +type Handler = |
| 22 | + Box<Fn(&Server, Request, Params) -> BoxFuture<Response, hyper::Error> + Sync + Send + 'static>; |
23 | 23 | struct Server {
|
| 24 | + router: Router<Handler>, |
24 | 25 | data: ArcCell<Data>,
|
25 | 26 | pool: CpuPool,
|
26 | 27 | }
|
27 | 28 |
|
28 | 29 | impl Server {
|
29 |
| - fn handle_get<F, S>(&self, req: &Request, handler: F) -> <Server as Service>::Future |
30 |
| - where F: FnOnce(&Data) -> S, |
| 30 | + fn handle_get<F, S>(&self, |
| 31 | + req: Request, |
| 32 | + params: Params, |
| 33 | + handler: F) |
| 34 | + -> <Server as Service>::Future |
| 35 | + where F: FnOnce(&Data, Params) -> S, |
31 | 36 | S: Serialize
|
32 | 37 | {
|
33 |
| - assert_eq!(*req.method(), Get); |
| 38 | + if *req.method() != Get { |
| 39 | + return self.error(StatusCode::BadRequest); |
| 40 | + }; |
34 | 41 | let data = self.data.get();
|
35 |
| - let result = handler(&data); |
| 42 | + let result = handler(&data, params); |
36 | 43 | let response = Response::new()
|
37 | 44 | .with_header(ContentType::json())
|
38 | 45 | .with_body(serde_json::to_string(&result).unwrap());
|
39 | 46 | futures::future::ok(response).boxed()
|
40 | 47 | }
|
41 | 48 |
|
42 |
| - fn handle_post<F, D, S>(&self, req: Request, handler: F) -> <Server as Service>::Future |
43 |
| - where F: FnOnce(D, &Data) -> S + Send + 'static, |
| 49 | + fn handle_static(&self, |
| 50 | + req: Request, |
| 51 | + _params: Params, |
| 52 | + content_type: ContentType, |
| 53 | + body: &'static str) |
| 54 | + -> <Server as Service>::Future { |
| 55 | + if *req.method() != Get { |
| 56 | + return self.error(StatusCode::BadRequest); |
| 57 | + }; |
| 58 | + let response = Response::new().with_header(content_type).with_body(body); |
| 59 | + futures::future::ok(response).boxed() |
| 60 | + } |
| 61 | + |
| 62 | + fn handle_post<F, D, S>(&self, |
| 63 | + req: Request, |
| 64 | + params: Params, |
| 65 | + handler: F) |
| 66 | + -> <Server as Service>::Future |
| 67 | + where F: FnOnce(D, &Data, Params) -> S + Send + 'static, |
44 | 68 | D: DeserializeOwned,
|
45 | 69 | S: Serialize
|
46 | 70 | {
|
47 |
| - assert_eq!(*req.method(), Post); |
| 71 | + if *req.method() != Post { |
| 72 | + return self.error(StatusCode::BadRequest); |
| 73 | + }; |
48 | 74 | let length = req.headers()
|
49 | 75 | .get::<ContentLength>()
|
50 | 76 | .expect("content-length to exist")
|
@@ -74,67 +100,78 @@ impl Server {
|
74 | 100 | err));
|
75 | 101 | }
|
76 | 102 | };
|
77 |
| - let result = handler(body, &data); |
| 103 | + let result = handler(body, &data, params); |
78 | 104 | Response::new()
|
79 | 105 | .with_header(ContentType::json())
|
80 | 106 | .with_body(serde_json::to_string(&result).unwrap())
|
81 | 107 | })
|
82 | 108 | })
|
83 | 109 | .boxed()
|
84 | 110 | }
|
| 111 | + |
| 112 | + fn error(&self, status: StatusCode) -> <Server as Service>::Future { |
| 113 | + futures::future::ok(Response::new() |
| 114 | + .with_header(ContentType::html()) |
| 115 | + .with_status(status)) |
| 116 | + .boxed() |
| 117 | + } |
85 | 118 | }
|
86 | 119 |
|
87 | 120 | impl Service for Server {
|
88 | 121 | type Request = Request;
|
89 | 122 | type Response = Response;
|
90 | 123 | type Error = hyper::Error;
|
91 |
| - type Future = Box<Future<Item = Self::Response, Error = Self::Error>>; |
| 124 | + type Future = BoxFuture<Self::Response, Self::Error>; |
92 | 125 |
|
93 | 126 | fn call(&self, req: Request) -> Self::Future {
|
94 |
| - let fs_path = format!("static{}", |
95 |
| - if req.path() == "" || req.path() == "/" { |
96 |
| - "/index.html" |
97 |
| - } else { |
98 |
| - req.path() |
99 |
| - }); |
100 |
| - |
101 |
| - info!("handling: req.path()={:?}, fs_path={:?}", |
102 |
| - req.path(), |
103 |
| - fs_path); |
104 |
| - |
105 |
| - if fs_path.contains("./") | fs_path.contains("../") { |
106 |
| - return futures::future::ok(Response::new() |
107 |
| - .with_header(ContentType::html()) |
108 |
| - .with_status(StatusCode::NotFound)) |
109 |
| - .boxed(); |
110 |
| - } |
| 127 | + info!("handling: req.path()={:?}", req.path()); |
111 | 128 |
|
112 |
| - if Path::new(&fs_path).is_file() { |
113 |
| - return self.pool |
114 |
| - .spawn_fn(move || { |
115 |
| - let mut f = File::open(&fs_path).unwrap(); |
116 |
| - let mut source = Vec::new(); |
117 |
| - f.read_to_end(&mut source).unwrap(); |
118 |
| - futures::future::ok(Response::new().with_body(source)) |
119 |
| - }) |
120 |
| - .boxed(); |
| 129 | + match self.router.recognize(req.path()) { |
| 130 | + Ok(Match { handler, params }) => handler(self, req, params), |
| 131 | + Err(_) => self.error(StatusCode::NotFound), |
121 | 132 | }
|
122 | 133 |
|
123 |
| - match req.path() { |
124 |
| - "/api/get" => self.handle_get(&req, api::get::handler), |
125 |
| - "/api/post" => self.handle_post(req, api::post::handler), |
126 |
| - _ => { |
127 |
| - futures::future::ok(Response::new() |
128 |
| - .with_header(ContentType::html()) |
129 |
| - .with_status(StatusCode::NotFound)) |
130 |
| - .boxed() |
131 |
| - } |
132 |
| - } |
| 134 | + |
133 | 135 | }
|
134 | 136 | }
|
135 | 137 |
|
| 138 | +macro_rules! route { |
| 139 | + ($router:ident, $path:expr, $method:ident, $($handler:tt)* ) => ( |
| 140 | + $router.add($path, |
| 141 | + Box::new(|server: &Server, req, params| server.$method(req, params, $($handler)*))); |
| 142 | + ) |
| 143 | +} |
| 144 | + |
136 | 145 | pub fn start(data: Data) {
|
| 146 | + let mut router = Router::<Handler>::new(); |
| 147 | + route!(router, "/api/get", handle_get, api::get::handler); |
| 148 | + route!(router, "/api/post", handle_post, api::post::handler); |
| 149 | + route!(router, |
| 150 | + "/api/ex/:experiment/results", |
| 151 | + handle_get, |
| 152 | + api::ex_report::handler); |
| 153 | + route!(router, |
| 154 | + "/api/ex/:experiment/config", |
| 155 | + handle_get, |
| 156 | + api::ex_config::handler); |
| 157 | + route!(router, |
| 158 | + "/static/report.html", |
| 159 | + handle_static, |
| 160 | + ContentType::html(), |
| 161 | + include_str!("../../static/report.html")); |
| 162 | + route!(router, |
| 163 | + "/static/report.js", |
| 164 | + handle_static, |
| 165 | + ContentType(mime!(Application / Javascript)), |
| 166 | + include_str!("../../static/report.js")); |
| 167 | + route!(router, |
| 168 | + "/static/report.css", |
| 169 | + handle_static, |
| 170 | + ContentType(mime!(Text / Css)), |
| 171 | + include_str!("../../static/report.css")); |
| 172 | + |
137 | 173 | let server = Arc::new(Server {
|
| 174 | + router, |
138 | 175 | data: ArcCell::new(Arc::new(data)),
|
139 | 176 | pool: CpuPool::new_num_cpus(),
|
140 | 177 | });
|
|
0 commit comments