1pub mod builder;
4pub use builder::{build_router, build_simple_router};
5
6pub mod response;
7pub mod route;
8pub mod tree;
9
10mod non_match;
11pub use self::non_match::RouteNonMatch;
12
13use std::pin::Pin;
14use std::sync::Arc;
15
16use futures_util::future::{self, FutureExt, TryFutureExt};
17use hyper::header::ALLOW;
18use hyper::{Body, Response, StatusCode};
19use log::{error, trace};
20
21use crate::handler::{Handler, HandlerFuture, IntoResponse, NewHandler};
22use crate::helpers::http::request::path::RequestPathSegments;
23use crate::helpers::http::response::create_empty_response;
24use crate::router::response::ResponseFinalizer;
25use crate::router::route::{Delegation, Route};
26use crate::router::tree::segment::SegmentMapping;
27use crate::router::tree::Tree;
28use crate::state::{request_id, State};
29
30struct RouterData {
31 tree: Tree,
32 response_finalizer: ResponseFinalizer,
33}
34
35impl RouterData {
36 fn new(tree: Tree, response_finalizer: ResponseFinalizer) -> RouterData {
37 RouterData {
38 tree,
39 response_finalizer,
40 }
41 }
42}
43
44#[derive(Clone)]
57pub struct Router {
58 data: Arc<RouterData>,
59}
60
61impl NewHandler for Router {
62 type Instance = Router;
63
64 fn new_handler(&self) -> anyhow::Result<Self::Instance> {
66 trace!(" cloning instance");
67 Ok(self.clone())
68 }
69}
70
71impl Handler for Router {
72 fn handle(self, mut state: State) -> Pin<Box<HandlerFuture>> {
75 trace!("[{}] starting", request_id(&state));
76
77 let future = match state.try_take::<RequestPathSegments>() {
78 Some(rps) => {
79 if let Some((node, params, processed)) = self.data.tree.traverse(rps.segments()) {
80 match node.select_route(&state) {
81 Ok(route) => match route.delegation() {
82 Delegation::External => {
83 trace!("[{}] delegating to secondary router", request_id(&state));
84
85 state.put(rps.subsegments(processed));
86 route.dispatch(state)
87 }
88 Delegation::Internal => {
89 trace!("[{}] dispatching to route", request_id(&state));
90 self.dispatch(state, params, route)
91 }
92 },
93 Err(non_match) => {
94 let (status, allow) = non_match.deconstruct();
95
96 trace!("[{}] responding with error status", request_id(&state));
97 let mut res = create_empty_response(&state, status);
98 if let StatusCode::METHOD_NOT_ALLOWED = status {
99 for allowed in allow {
100 res.headers_mut().append(
101 ALLOW,
102 allowed.as_str().to_string().parse().unwrap(),
103 );
104 }
105 }
106 future::ok((state, res)).boxed()
107 }
108 }
109 } else {
110 trace!("[{}] did not find routable node", request_id(&state));
111 let res = create_empty_response(&state, StatusCode::NOT_FOUND);
112 future::ok((state, res)).boxed()
113 }
114 }
115 None => {
116 trace!("[{}] invalid request path segments", request_id(&state));
117 let res = create_empty_response(&state, StatusCode::INTERNAL_SERVER_ERROR);
118 future::ok((state, res)).boxed()
119 }
120 };
121
122 self.finalize_response(future)
123 }
124}
125
126impl Router {
127 fn new(tree: Tree, response_finalizer: ResponseFinalizer) -> Router {
129 let router_data = RouterData::new(tree, response_finalizer);
130 Router {
131 data: Arc::new(router_data),
132 }
133 }
134
135 fn dispatch<'a>(
136 &self,
137 mut state: State,
138 params: SegmentMapping<'a>,
139 route: &Box<dyn Route<ResBody = Body> + Send + Sync>,
140 ) -> Pin<Box<HandlerFuture>> {
141 match route.extract_request_path(&mut state, params) {
142 Ok(()) => {
143 trace!("[{}] extracted request path", request_id(&state));
144 match route.extract_query_string(&mut state) {
145 Ok(()) => {
146 trace!("[{}] extracted query string", request_id(&state));
147 trace!("[{}] dispatching", request_id(&state));
148 route.dispatch(state)
149 }
150 Err(_) => {
151 error!("[{}] the server cannot or will not process the request due to a client error within the query string",
152 request_id(&state));
153
154 let mut res = Response::new(Body::empty());
155 route.extend_response_on_query_string_error(&mut state, &mut res);
156 future::ok((state, res)).boxed()
157 }
158 }
159 }
160 Err(_) => {
161 error!(
162 "[{}] the server cannot or will not process the request due to a client error on the request path",
163 request_id(&state)
164 );
165 let mut res = Response::new(Body::empty());
166 route.extend_response_on_path_error(&mut state, &mut res);
167 future::ok((state, res)).boxed()
168 }
169 }
170 }
171
172 fn finalize_response(&self, result: Pin<Box<HandlerFuture>>) -> Pin<Box<HandlerFuture>> {
173 let response_finalizer = self.data.response_finalizer.clone();
174 result
175 .or_else(|(state, err)| {
176 trace!(
177 "[{}] converting error into http response \
178 during finalization: {:?}",
179 request_id(&state),
180 err
181 );
182 let response = err.into_response(&state);
183 future::ok((state, response))
184 })
185 .and_then(move |(state, res)| {
186 trace!("[{}] handler complete", request_id(&state));
187 response_finalizer.finalize(state, res)
188 })
189 .boxed()
190 }
191}
192
193#[cfg(test)]
194mod tests {
195 use super::*;
196 use hyper::header::{HeaderMap, CONTENT_LENGTH, CONTENT_TYPE};
197 use hyper::{Body, Method, Uri};
198 use mime::TEXT_PLAIN;
199 use std::str::FromStr;
200
201 use crate::extractor::{NoopPathExtractor, NoopQueryStringExtractor};
202 use crate::handler::HandlerError;
203 use crate::pipeline::{finalize_pipeline_set, new_pipeline_set};
204 use crate::router::response::ResponseFinalizerBuilder;
205 use crate::router::route::dispatch::DispatcherImpl;
206 use crate::router::route::matcher::{
207 AndRouteMatcher, ContentTypeHeaderRouteMatcher, MethodOnlyRouteMatcher,
208 };
209 use crate::router::route::{Extractors, RouteImpl};
210 use crate::router::tree::node::Node;
211 use crate::router::tree::segment::SegmentType;
212 use crate::router::tree::Tree;
213 use crate::state::set_request_id;
214
215 fn handler(state: State) -> (State, Response<Body>) {
216 (state, Response::new(Body::empty()))
217 }
218
219 fn send_request(
220 r: Router,
221 method: Method,
222 uri: &str,
223 ) -> ::std::result::Result<(State, Response<Body>), (State, HandlerError)> {
224 let uri = Uri::from_str(uri).unwrap();
225
226 let mut headers = HeaderMap::new();
227 if method == Method::POST {
228 headers.insert(CONTENT_TYPE, "application/json".parse().unwrap());
229 headers.insert(CONTENT_LENGTH, 0.into());
230 }
231
232 let mut state = State::new();
233 state.put(RequestPathSegments::new(uri.path()));
234 state.put(method);
235 state.put(uri);
236 state.put(headers);
237 set_request_id(&mut state);
238
239 futures_executor::block_on(r.handle(state))
240 }
241
242 #[test]
243 fn internal_server_error_if_no_request_path_segments() {
244 let tree = Tree::new();
245 let router = Router::new(tree, ResponseFinalizerBuilder::new().finalize());
246
247 let method = Method::GET;
248 let uri = Uri::from_str("https://test.gotham.rs").unwrap();
249
250 let mut state = State::new();
251 state.put(method);
252 state.put(uri);
253 state.put(HeaderMap::new());
254 set_request_id(&mut state);
255
256 match futures_executor::block_on(router.handle(state)) {
257 Ok((_state, res)) => {
258 assert_eq!(res.status(), StatusCode::INTERNAL_SERVER_ERROR);
259 }
260 Err(_) => unreachable!("Router should have handled request"),
261 };
262 }
263
264 #[test]
265 fn not_found_error_if_request_path_is_not_found() {
266 let tree = Tree::new();
267 let router = Router::new(tree, ResponseFinalizerBuilder::new().finalize());
268
269 match send_request(router, Method::GET, "https://test.gotham.rs") {
270 Ok((_state, res)) => {
271 assert_eq!(res.status(), StatusCode::NOT_FOUND);
272 }
273 Err(_) => unreachable!("Router should have handled request"),
274 };
275 }
276
277 #[test]
278 fn custom_error_if_leaf_found_but_matching_route_not_found() {
279 let pipeline_set = finalize_pipeline_set(new_pipeline_set());
280 let mut tree = Tree::new();
281
282 let route = {
283 let methods = vec![Method::POST];
284 let matcher = AndRouteMatcher::new(
285 MethodOnlyRouteMatcher::new(methods),
286 ContentTypeHeaderRouteMatcher::new(vec![TEXT_PLAIN]),
287 );
288 let dispatcher = Box::new(DispatcherImpl::new(|| Ok(handler), (), pipeline_set));
289 let extractors: Extractors<NoopPathExtractor, NoopQueryStringExtractor> =
290 Extractors::new();
291 let route = RouteImpl::new(matcher, dispatcher, extractors, Delegation::Internal);
292 Box::new(route)
293 };
294 tree.add_route(route);
295 let router = Router::new(tree, ResponseFinalizerBuilder::new().finalize());
296
297 match send_request(router.clone(), Method::GET, "https://test.gotham.rs") {
298 Ok((_state, res)) => {
299 assert_eq!(res.status(), StatusCode::METHOD_NOT_ALLOWED);
300 assert_eq!(
301 res.headers()
302 .get_all(ALLOW)
303 .iter()
304 .map(|it| it.to_str().unwrap())
305 .collect::<Vec<&str>>(),
306 vec!["POST"]
307 );
308 }
309 Err(_) => unreachable!("Router should have handled request"),
310 };
311
312 match send_request(router, Method::POST, "https://test.gotham.rs") {
313 Ok((_state, res)) => {
314 assert_eq!(res.status(), StatusCode::UNSUPPORTED_MEDIA_TYPE);
315 assert!(res.headers().get_all(ALLOW).iter().next().is_none());
316 }
317 Err(_) => unreachable!("Router should have handled request"),
318 };
319 }
320
321 #[test]
322 fn success_if_leaf_and_route_found() {
323 let pipeline_set = finalize_pipeline_set(new_pipeline_set());
324 let mut tree = Tree::new();
325
326 let route = {
327 let methods = vec![Method::GET];
328 let matcher = MethodOnlyRouteMatcher::new(methods);
329 let dispatcher = Box::new(DispatcherImpl::new(|| Ok(handler), (), pipeline_set));
330 let extractors: Extractors<NoopPathExtractor, NoopQueryStringExtractor> =
331 Extractors::new();
332 let route = RouteImpl::new(matcher, dispatcher, extractors, Delegation::Internal);
333 Box::new(route)
334 };
335 tree.add_route(route);
336 let router = Router::new(tree, ResponseFinalizerBuilder::new().finalize());
337
338 match send_request(router, Method::GET, "https://test.gotham.rs") {
339 Ok((_state, res)) => {
340 assert_eq!(res.status(), StatusCode::OK);
341 }
342 Err(_) => unreachable!("Router should have handled request"),
343 };
344 }
345
346 #[test]
347 fn delegates_to_secondary_router() {
348 let delegated_router = {
349 let pipeline_set = finalize_pipeline_set(new_pipeline_set());
350 let mut tree = Tree::new();
351
352 let route = {
353 let methods = vec![Method::GET];
354 let matcher = MethodOnlyRouteMatcher::new(methods);
355 let dispatcher = Box::new(DispatcherImpl::new(|| Ok(handler), (), pipeline_set));
356 let extractors: Extractors<NoopPathExtractor, NoopQueryStringExtractor> =
357 Extractors::new();
358 let route = RouteImpl::new(matcher, dispatcher, extractors, Delegation::Internal);
359 Box::new(route)
360 };
361 tree.add_route(route);
362
363 Router::new(tree, ResponseFinalizerBuilder::new().finalize())
364 };
365
366 let pipeline_set = finalize_pipeline_set(new_pipeline_set());
367 let mut tree = Tree::new();
368 let mut delegated_node = Node::new("var", SegmentType::Dynamic);
369
370 let route = {
371 let methods = vec![Method::GET];
372 let matcher = MethodOnlyRouteMatcher::new(methods);
373 let dispatcher = Box::new(DispatcherImpl::new(delegated_router, (), pipeline_set));
374 let extractors: Extractors<NoopPathExtractor, NoopQueryStringExtractor> =
375 Extractors::new();
376 let route = RouteImpl::new(matcher, dispatcher, extractors, Delegation::External);
377 Box::new(route)
378 };
379
380 delegated_node.add_route(route);
381 tree.add_child(delegated_node);
382 let router = Router::new(tree, ResponseFinalizerBuilder::new().finalize());
383
384 match send_request(router.clone(), Method::GET, "https://test.gotham.rs") {
386 Ok((_state, res)) => {
387 assert_eq!(res.status(), StatusCode::NOT_FOUND);
388 }
389 Err(_) => unreachable!("Router should have handled request"),
390 };
391
392 match send_request(router, Method::GET, "https://test.gotham.rs/api") {
394 Ok((_state, res)) => {
395 assert_eq!(res.status(), StatusCode::OK);
396 }
397 Err(_) => unreachable!("Router should have handled request"),
398 };
399 }
400
401 #[test]
402 fn executes_response_finalizer_when_present() {
403 let tree = Tree::new();
404
405 let mut response_finalizer_builder = ResponseFinalizerBuilder::new();
406 let not_found_extender = |_s: &mut State, r: &mut Response<Body>| {
407 r.headers_mut()
408 .insert(CONTENT_LENGTH, "3".to_owned().parse().unwrap());
409 };
410 response_finalizer_builder.add(StatusCode::NOT_FOUND, Box::new(not_found_extender));
411 let response_finalizer = response_finalizer_builder.finalize();
412 let router = Router::new(tree, response_finalizer);
413
414 match send_request(router, Method::GET, "https://test.gotham.rs/api") {
415 Ok((_state, res)) => {
416 assert_eq!(res.headers().get(CONTENT_LENGTH).unwrap(), "3");
417 }
418 Err(_) => unreachable!("Router should have correctly handled request"),
419 };
420 }
421}