gotham/router/builder/draw.rs
1use std::marker::PhantomData;
2use std::panic::RefUnwindSafe;
3
4use hyper::Method;
5use log::trace;
6
7use crate::extractor::{NoopPathExtractor, NoopQueryStringExtractor};
8use crate::helpers::http::request::path::split_path_segments;
9use crate::pipeline::{PipelineHandleChain, PipelineSet};
10use crate::router::builder::{
11 AssociatedRouteBuilder, DelegateRouteBuilder, RouterBuilder, ScopeBuilder, SingleRouteBuilder,
12};
13use crate::router::route::matcher::{
14 AnyRouteMatcher, IntoRouteMatcher, MethodOnlyRouteMatcher, RouteMatcher,
15};
16use crate::router::tree::node::Node;
17use crate::router::tree::regex::ConstrainedSegmentRegex;
18use crate::router::tree::segment::SegmentType;
19
20/// The type returned when building a route that only considers path and http verb(s) when
21/// determining if it matches a request.
22///
23/// See `router::builder::DefineSingleRoute` for an overview of route specification.
24pub(crate) type DefaultSingleRouteBuilder<'a, C, P> = SingleRouteBuilder<
25 'a,
26 MethodOnlyRouteMatcher,
27 C,
28 P,
29 NoopPathExtractor,
30 NoopQueryStringExtractor,
31>;
32
33/// The type returned when building a route with explicit matching requirements.
34///
35/// See `router::builder::DefineSingleRoute` for an overview of route specification.
36pub(crate) type ExplicitSingleRouteBuilder<'a, M, C, P> =
37 SingleRouteBuilder<'a, M, C, P, NoopPathExtractor, NoopQueryStringExtractor>;
38
39/// The type passed to the function used when building associated routes. See
40/// `AssociatedRouteBuilder` for information about the API available for associated routes.
41pub(crate) type DefaultAssociatedRouteBuilder<'a, M, C, P> =
42 AssociatedRouteBuilder<'a, M, C, P, NoopPathExtractor, NoopQueryStringExtractor>;
43
44/// Defines functions used by a builder to determine which request paths will be dispatched to a
45/// route. This trait is implemented by the top-level `RouterBuilder`, and also the `ScopedBuilder`
46/// created by `DrawRoutes::scope`.
47pub trait DrawRoutes<C, P>
48where
49 C: PipelineHandleChain<P> + Copy + Send + Sync + 'static,
50 P: RefUnwindSafe + Send + Sync + 'static,
51{
52 /// Creates a route which matches `GET` and `HEAD` requests to the given path.
53 ///
54 /// # Examples
55 ///
56 /// ```rust
57 /// # use hyper::{Body, Response, StatusCode};
58 /// # use gotham::state::State;
59 /// # use gotham::router::Router;
60 /// # use gotham::router::builder::*;
61 /// # use gotham::test::TestServer;
62 /// #
63 /// # fn my_handler(state: State) -> (State, Response<Body>) {
64 /// # (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap())
65 /// # }
66 /// #
67 /// # fn router() -> Router {
68 /// build_simple_router(|route| {
69 /// route.get_or_head("/request/path").to(my_handler);
70 /// })
71 /// # }
72 /// #
73 /// # fn main() {
74 /// # let test_server = TestServer::new(router()).unwrap();
75 /// #
76 /// # let response = test_server.client()
77 /// # .get("https://example.com/request/path")
78 /// # .perform()
79 /// # .unwrap();
80 /// # assert_eq!(response.status(), StatusCode::ACCEPTED);
81 /// #
82 /// # let response = test_server.client()
83 /// # .head("https://example.com/request/path")
84 /// # .perform()
85 /// # .unwrap();
86 /// # assert_eq!(response.status(), StatusCode::ACCEPTED);
87 /// # }
88 /// ```
89 fn get_or_head<'b>(&'b mut self, path: &str) -> DefaultSingleRouteBuilder<'b, C, P> {
90 self.request(vec![Method::GET, Method::HEAD], path)
91 }
92
93 /// Creates a route which matches **only** `GET` requests to the given path (ignoring `HEAD`
94 /// requests).
95 ///
96 /// # Examples
97 ///
98 /// ```rust
99 /// # extern crate gotham;
100 /// # extern crate hyper;
101 /// #
102 /// # use hyper::{Body, Response, StatusCode};
103 /// # use gotham::state::State;
104 /// # use gotham::router::Router;
105 /// # use gotham::router::builder::*;
106 /// # use gotham::test::TestServer;
107 /// #
108 /// # fn my_handler(state: State) -> (State, Response<Body>) {
109 /// # (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap())
110 /// # }
111 /// #
112 /// # fn router() -> Router {
113 /// build_simple_router(|route| {
114 /// route.get("/request/path").to(my_handler);
115 /// })
116 /// # }
117 /// #
118 /// # fn main() {
119 /// # let test_server = TestServer::new(router()).unwrap();
120 /// # let response = test_server.client()
121 /// # .get("https://example.com/request/path")
122 /// # .perform()
123 /// # .unwrap();
124 /// # assert_eq!(response.status(), StatusCode::ACCEPTED);
125 /// # }
126 /// ```
127 fn get<'b>(&'b mut self, path: &str) -> DefaultSingleRouteBuilder<'b, C, P> {
128 self.request(vec![Method::GET], path)
129 }
130
131 /// Creates a route which matches `HEAD` requests to the given path.
132 ///
133 /// # Examples
134 ///
135 /// ```rust
136 /// # extern crate gotham;
137 /// # extern crate hyper;
138 /// #
139 /// # use hyper::{Body, Response, StatusCode};
140 /// # use gotham::state::State;
141 /// # use gotham::router::Router;
142 /// # use gotham::router::builder::*;
143 /// # use gotham::test::TestServer;
144 /// #
145 /// # fn my_handler(state: State) -> (State, Response<Body>) {
146 /// # (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap())
147 /// # }
148 /// #
149 /// # fn router() -> Router {
150 /// build_simple_router(|route| {
151 /// route.head("/request/path").to(my_handler);
152 /// })
153 /// # }
154 /// #
155 /// # fn main() {
156 /// # let test_server = TestServer::new(router()).unwrap();
157 /// # let response = test_server.client()
158 /// # .head("https://example.com/request/path")
159 /// # .perform()
160 /// # .unwrap();
161 /// # assert_eq!(response.status(), StatusCode::ACCEPTED);
162 /// # }
163 /// ```
164 fn head<'b>(&'b mut self, path: &str) -> DefaultSingleRouteBuilder<'b, C, P> {
165 self.request(vec![Method::HEAD], path)
166 }
167
168 /// Creates a route which matches `POST` requests to the given path.
169 ///
170 /// # Examples
171 ///
172 /// ```rust
173 /// # extern crate gotham;
174 /// # extern crate hyper;
175 /// # extern crate mime;
176 /// #
177 /// # use hyper::{Body, Response, StatusCode};
178 /// # use gotham::state::State;
179 /// # use gotham::router::Router;
180 /// # use gotham::router::builder::*;
181 /// # use gotham::test::TestServer;
182 /// #
183 /// # fn my_handler(state: State) -> (State, Response<Body>) {
184 /// # (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap())
185 /// # }
186 /// #
187 /// # fn router() -> Router {
188 /// build_simple_router(|route| {
189 /// route.post("/request/path").to(my_handler);
190 /// })
191 /// # }
192 /// #
193 /// # fn main() {
194 /// # let test_server = TestServer::new(router()).unwrap();
195 /// # let response = test_server.client()
196 /// # .post("https://example.com/request/path", b"".to_vec(), mime::TEXT_PLAIN)
197 /// # .perform()
198 /// # .unwrap();
199 /// # assert_eq!(response.status(), StatusCode::ACCEPTED);
200 /// # }
201 /// ```
202 fn post<'b>(&'b mut self, path: &str) -> DefaultSingleRouteBuilder<'b, C, P> {
203 self.request(vec![Method::POST], path)
204 }
205
206 /// Creates a route which matches `PUT` requests to the given path.
207 ///
208 /// # Examples
209 ///
210 /// ```rust
211 /// # extern crate gotham;
212 /// # extern crate hyper;
213 /// # extern crate mime;
214 /// #
215 /// # use hyper::{Body, Response, StatusCode};
216 /// # use gotham::state::State;
217 /// # use gotham::router::Router;
218 /// # use gotham::router::builder::*;
219 /// # use gotham::test::TestServer;
220 /// #
221 /// # fn my_handler(state: State) -> (State, Response<Body>) {
222 /// # (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap())
223 /// # }
224 /// #
225 /// # fn router() -> Router {
226 /// build_simple_router(|route| {
227 /// route.put("/request/path").to(my_handler);
228 /// })
229 /// # }
230 /// #
231 /// # fn main() {
232 /// # let test_server = TestServer::new(router()).unwrap();
233 /// # let response = test_server.client()
234 /// # .put("https://example.com/request/path", b"".to_vec(), mime::TEXT_PLAIN)
235 /// # .perform()
236 /// # .unwrap();
237 /// # assert_eq!(response.status(), StatusCode::ACCEPTED);
238 /// # }
239 /// ```
240 fn put<'b>(&'b mut self, path: &str) -> DefaultSingleRouteBuilder<'b, C, P> {
241 self.request(vec![Method::PUT], path)
242 }
243
244 /// Creates a route which matches `PATCH` requests to the given path.
245 ///
246 /// # Examples
247 ///
248 /// ```rust
249 /// # extern crate gotham;
250 /// # extern crate hyper;
251 /// # extern crate mime;
252 /// #
253 /// # use hyper::{Body, Response, StatusCode};
254 /// # use gotham::state::State;
255 /// # use gotham::router::Router;
256 /// # use gotham::router::builder::*;
257 /// # use gotham::test::TestServer;
258 /// #
259 /// # fn my_handler(state: State) -> (State, Response<Body>) {
260 /// # (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap())
261 /// # }
262 /// #
263 /// # fn router() -> Router {
264 /// build_simple_router(|route| {
265 /// route.patch("/request/path").to(my_handler);
266 /// })
267 /// # }
268 /// #
269 /// # fn main() {
270 /// # let test_server = TestServer::new(router()).unwrap();
271 /// # let response = test_server.client()
272 /// # .patch("https://example.com/request/path", b"".to_vec(), mime::TEXT_PLAIN)
273 /// # .perform()
274 /// # .unwrap();
275 /// # assert_eq!(response.status(), StatusCode::ACCEPTED);
276 /// # }
277 /// ```
278 fn patch<'b>(&'b mut self, path: &str) -> DefaultSingleRouteBuilder<'b, C, P> {
279 self.request(vec![Method::PATCH], path)
280 }
281
282 /// Creates a route which matches `DELETE` requests to the given path.
283 ///
284 /// # Examples
285 ///
286 /// ```rust
287 /// # extern crate gotham;
288 /// # extern crate hyper;
289 /// #
290 /// # use hyper::{Body, Response, StatusCode};
291 /// # use gotham::state::State;
292 /// # use gotham::router::Router;
293 /// # use gotham::router::builder::*;
294 /// # use gotham::test::TestServer;
295 /// #
296 /// # fn my_handler(state: State) -> (State, Response<Body>) {
297 /// # (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap())
298 /// # }
299 /// #
300 /// # fn router() -> Router {
301 /// build_simple_router(|route| {
302 /// route.delete("/request/path").to(my_handler);
303 /// })
304 /// # }
305 /// #
306 /// # fn main() {
307 /// # let test_server = TestServer::new(router()).unwrap();
308 /// # let response = test_server.client()
309 /// # .delete("https://example.com/request/path")
310 /// # .perform()
311 /// # .unwrap();
312 /// # assert_eq!(response.status(), StatusCode::ACCEPTED);
313 /// # }
314 /// ```
315 fn delete<'b>(&'b mut self, path: &str) -> DefaultSingleRouteBuilder<'b, C, P> {
316 self.request(vec![Method::DELETE], path)
317 }
318
319 /// Creates a route which matches `OPTIONS` requests to the given path.
320 ///
321 /// # Examples
322 ///
323 /// ```rust
324 /// # extern crate gotham;
325 /// # extern crate hyper;
326 /// #
327 /// # use hyper::{Body, Response, StatusCode};
328 /// # use gotham::state::State;
329 /// # use gotham::router::Router;
330 /// # use gotham::router::builder::*;
331 /// # use gotham::test::TestServer;
332 /// #
333 /// # fn my_handler(state: State) -> (State, Response<Body>) {
334 /// # (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap())
335 /// # }
336 /// #
337 /// # fn router() -> Router {
338 /// build_simple_router(|route| {
339 /// route.options("/request/path").to(my_handler);
340 /// })
341 /// # }
342 /// #
343 /// # fn main() {
344 /// # let test_server = TestServer::new(router()).unwrap();
345 /// # let response = test_server.client()
346 /// # .options("https://example.com/request/path")
347 /// # .perform()
348 /// # .unwrap();
349 /// # assert_eq!(response.status(), StatusCode::ACCEPTED);
350 /// # }
351 /// ```
352 fn options<'b>(&'b mut self, path: &str) -> DefaultSingleRouteBuilder<'b, C, P> {
353 self.request(vec![Method::OPTIONS], path)
354 }
355
356 /// Creates a single route which matches any requests to the given `path` with one of the
357 /// given `methods`. The `path` can consist of static or dynamic segments, for example:
358 ///
359 /// * `"/hello/world"` - a static path, matching only a request for exactly `"/hello/world"`
360 /// * `"/hello/:name"` - a dynamic path, matching requests for `"/hello/any_value_here"`
361 ///
362 /// # Examples
363 ///
364 /// ```rust
365 /// # extern crate gotham;
366 /// # extern crate hyper;
367 /// #
368 /// # use hyper::{Body, Response, StatusCode};
369 /// # use hyper::Method;
370 /// # use gotham::state::State;
371 /// # use gotham::router::Router;
372 /// # use gotham::router::builder::*;
373 /// # use gotham::test::TestServer;
374 /// #
375 /// # fn my_handler(state: State) -> (State, Response<Body>) {
376 /// # (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap())
377 /// # }
378 /// #
379 /// # fn router() -> Router {
380 /// build_simple_router(|route| {
381 /// route.request(vec![Method::GET, Method::HEAD], "/request/path").to(my_handler);
382 /// })
383 /// # }
384 /// #
385 /// # fn main() {
386 /// # let test_server = TestServer::new(router()).unwrap();
387 /// #
388 /// # let response = test_server.client()
389 /// # .get("https://example.com/request/path")
390 /// # .perform()
391 /// # .unwrap();
392 /// # assert_eq!(response.status(), StatusCode::ACCEPTED);
393 /// #
394 /// # let response = test_server.client()
395 /// # .head("https://example.com/request/path")
396 /// # .perform()
397 /// # .unwrap();
398 /// # assert_eq!(response.status(), StatusCode::ACCEPTED);
399 /// # }
400 /// ```
401 ///
402 /// ```
403 /// # extern crate gotham;
404 /// # extern crate hyper;
405 /// # extern crate mime;
406 /// #
407 /// # use hyper::{Body, Response, StatusCode};
408 /// # use hyper::header::ACCEPT;
409 /// # use gotham::state::State;
410 /// # use gotham::router::route::matcher::AcceptHeaderRouteMatcher;
411 /// # use gotham::router::Router;
412 /// # use gotham::router::builder::*;
413 /// # use gotham::test::TestServer;
414 /// #
415 /// # fn my_handler(state: State) -> (State, Response<Body>) {
416 /// # (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap())
417 /// # }
418 /// #
419 /// # fn router() -> Router {
420 /// build_simple_router(|route| {
421 /// // All we match on is the Accept header, the method is not considered.
422 /// let matcher = AcceptHeaderRouteMatcher::new(vec![mime::APPLICATION_JSON]);
423 /// route.request(matcher, "/request/path").to(my_handler);
424 /// })
425 /// # }
426 /// #
427 /// # fn main() {
428 /// # let test_server = TestServer::new(router()).unwrap();
429 /// #
430 /// # let response = test_server.client()
431 /// # .get("https://example.com/request/path")
432 /// # .with_header(ACCEPT, mime::APPLICATION_JSON.to_string().parse().unwrap())
433 /// # .perform()
434 /// # .unwrap();
435 /// # assert_eq!(response.status(), StatusCode::ACCEPTED);
436 /// #
437 /// # let response = test_server.client()
438 /// # .get("https://example.com/request/path")
439 /// # .with_header(ACCEPT, mime::TEXT_PLAIN.to_string().parse().unwrap())
440 /// # .perform()
441 /// # .unwrap();
442 /// # assert_eq!(response.status(), StatusCode::NOT_ACCEPTABLE);
443 /// #
444 /// # // No Accept type being provided is valid for the AcceptHeaderRouterMatcher
445 /// # // Proves the method is not considered
446 /// # let response = test_server.client()
447 /// # .delete("https://example.com/request/path")
448 /// # .perform()
449 /// # .unwrap();
450 /// # assert_eq!(response.status(), StatusCode::ACCEPTED);
451 /// # }
452 /// ```
453 fn request<'b, IRM, M>(
454 &'b mut self,
455 matcher: IRM,
456 path: &str,
457 ) -> ExplicitSingleRouteBuilder<'b, M, C, P>
458 where
459 IRM: IntoRouteMatcher<Output = M>,
460 M: RouteMatcher + Send + Sync + 'static,
461 {
462 let (node_builder, pipeline_chain, pipelines) = self.component_refs();
463 let node_builder = descend(node_builder, path);
464 let matcher = matcher.into_route_matcher();
465
466 SingleRouteBuilder {
467 matcher,
468 node_builder,
469 pipeline_chain: *pipeline_chain,
470 pipelines: pipelines.clone(),
471 phantom: PhantomData,
472 }
473 }
474
475 /// Begins defining a new scope, based on a given `path` prefix.
476 ///
477 /// # Examples
478 ///
479 /// ```rust
480 /// # extern crate gotham;
481 /// # extern crate hyper;
482 ///
483 /// # use hyper::{Body, Response, StatusCode};
484 /// # use gotham::state::State;
485 /// # use gotham::router::Router;
486 /// # use gotham::router::builder::*;
487 /// # use gotham::test::TestServer;
488 /// #
489 /// # mod api {
490 /// # use super::*;
491 /// # pub fn list(state: State) -> (State, Response<Body>) {
492 /// # (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap())
493 /// # }
494 /// # }
495 /// #
496 /// # fn router() -> Router {
497 /// build_simple_router(|route| {
498 /// route.scope("/api", |route| {
499 /// // Match requests to `/api/list`
500 /// route.get("/list").to(api::list);
501 /// });
502 /// })
503 /// # }
504 /// #
505 /// # fn main() {
506 /// # let test_server = TestServer::new(router()).unwrap();
507 /// # let response = test_server.client()
508 /// # .get("https://example.com/api/list")
509 /// # .perform()
510 /// # .unwrap();
511 /// # assert_eq!(response.status(), StatusCode::ACCEPTED);
512 /// # }
513 /// ```
514 fn scope<F>(&mut self, path: &str, f: F)
515 where
516 F: FnOnce(&mut ScopeBuilder<'_, C, P>),
517 {
518 let (node_builder, pipeline_chain, pipelines) = self.component_refs();
519 let node_builder = descend(node_builder, path);
520
521 let mut scope_builder = ScopeBuilder {
522 node_builder,
523 pipeline_chain: *pipeline_chain,
524 pipelines: pipelines.clone(),
525 };
526
527 f(&mut scope_builder)
528 }
529
530 /// Begins a new scope at the current location, with an alternate pipeline chain.
531 ///
532 /// # Examples
533 ///
534 /// ```rust
535 /// # use hyper::{Body, Response, StatusCode};
536 /// # use gotham::state::State;
537 /// # use gotham::middleware::session::{NewSessionMiddleware, SessionData};
538 /// # use gotham::router::Router;
539 /// # use gotham::router::builder::*;
540 /// # use gotham::pipeline::{finalize_pipeline_set, new_pipeline_set, new_pipeline};
541 /// # use gotham::test::TestServer;
542 /// # use serde::{Deserialize, Serialize};
543 /// #
544 /// # #[derive(Default, Serialize, Deserialize)]
545 /// # struct Session;
546 /// #
547 /// # #[derive(Default, Serialize, Deserialize)]
548 /// # struct AdminSession;
549 /// #
550 /// # mod resource {
551 /// # use super::*;
552 /// # pub fn list(state: State) -> (State, Response<Body>) {
553 /// # assert!(state.has::<SessionData<Session>>());
554 /// # assert!(!state.has::<SessionData<AdminSession>>());
555 /// # (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap())
556 /// # }
557 /// # }
558 /// #
559 /// # mod admin {
560 /// # use super::*;
561 /// # pub fn handler(state: State) -> (State, Response<Body>) {
562 /// # assert!(state.has::<SessionData<Session>>());
563 /// # assert!(state.has::<SessionData<AdminSession>>());
564 /// # (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap())
565 /// # }
566 /// # }
567 /// #
568 /// # fn handler(state: State) -> (State, Response<Body>) {
569 /// # assert!(!state.has::<SessionData<Session>>());
570 /// # assert!(!state.has::<SessionData<AdminSession>>());
571 /// # (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap())
572 /// # }
573 /// #
574 /// # fn router() -> Router {
575 /// let pipelines = new_pipeline_set();
576 /// let (pipelines, default) = pipelines.add(
577 /// new_pipeline()
578 /// .add(NewSessionMiddleware::default().with_session_type::<Session>())
579 /// .build()
580 /// );
581 /// let (pipelines, extended) = pipelines.add(
582 /// new_pipeline()
583 /// .add(NewSessionMiddleware::default().with_session_type::<AdminSession>())
584 /// .build()
585 /// );
586 /// let pipeline_set = finalize_pipeline_set(pipelines);
587 ///
588 /// let default_chain = (default, ());
589 /// let extended_chain = (extended, default_chain);
590 ///
591 /// build_router(default_chain, pipeline_set, |route| {
592 /// // Requests for the root handler use an empty set of pipelines, skipping the session
593 /// // middlewares.
594 /// route.with_pipeline_chain((), |route| {
595 /// route.get("/").to(handler);
596 /// });
597 ///
598 /// // Requests dispatched to the resource module will only invoke one session
599 /// // middleware which is the default behavior.
600 /// route.get("/resource/list").to(resource::list);
601 ///
602 /// // Requests for the admin handler will additionally invoke the admin session
603 /// // middleware.
604 /// route.with_pipeline_chain(extended_chain, |route| {
605 /// route.get("/admin").to(admin::handler);
606 /// });
607 /// })
608 /// # }
609 /// #
610 /// # fn main() {
611 /// # let test_server = TestServer::new(router()).unwrap();
612 /// #
613 /// # let response = test_server.client()
614 /// # .get("https://example.com/")
615 /// # .perform()
616 /// # .unwrap();
617 /// # assert_eq!(response.status(), StatusCode::ACCEPTED);
618 /// #
619 /// # let response = test_server.client()
620 /// # .get("https://example.com/resource/list")
621 /// # .perform()
622 /// # .unwrap();
623 /// # assert_eq!(response.status(), StatusCode::ACCEPTED);
624 /// # }
625 /// ```
626 fn with_pipeline_chain<F, NC>(&mut self, pipeline_chain: NC, f: F)
627 where
628 F: FnOnce(&mut ScopeBuilder<'_, NC, P>),
629 NC: PipelineHandleChain<P> + Copy + Send + Sync + 'static,
630 {
631 let (node_builder, _pipeline_chain, pipelines) = self.component_refs();
632
633 let mut scope_builder = ScopeBuilder {
634 node_builder,
635 pipeline_chain,
636 pipelines: pipelines.clone(),
637 };
638
639 f(&mut scope_builder)
640 }
641
642 /// Begins delegating a subpath of the tree.
643 ///
644 /// # Examples
645 ///
646 /// ```rust
647 /// # extern crate gotham;
648 /// # extern crate hyper;
649 /// #
650 /// # use hyper::{Body, Response, StatusCode};
651 /// # use gotham::router::Router;
652 /// # use gotham::router::builder::*;
653 /// # use gotham::state::State;
654 /// # use gotham::test::TestServer;
655 /// #
656 /// fn admin_router() -> Router {
657 /// // Implementation elided
658 /// # fn handler(state: State) -> (State, Response<Body>) {
659 /// # (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap())
660 /// # }
661 /// #
662 /// # build_simple_router(|route| {
663 /// # route.get("/").to(handler);
664 /// # })
665 /// }
666 ///
667 /// # fn router() -> Router {
668 /// build_simple_router(|route| {
669 /// route.delegate("/admin").to_router(admin_router());
670 /// })
671 /// # }
672 /// #
673 /// # fn main() {
674 /// # let test_server = TestServer::new(router()).unwrap();
675 /// # let response = test_server.client()
676 /// # .get("https://example.com/admin")
677 /// # .perform()
678 /// # .unwrap();
679 /// # assert_eq!(response.status(), StatusCode::ACCEPTED);
680 /// # }
681 /// ```
682 fn delegate<'b>(&'b mut self, path: &str) -> DelegateRouteBuilder<'b, AnyRouteMatcher, C, P> {
683 let (node_builder, pipeline_chain, pipelines) = self.component_refs();
684 let node_builder = descend(node_builder, path);
685
686 DelegateRouteBuilder {
687 matcher: AnyRouteMatcher::new(),
688 node_builder,
689 pipeline_chain: *pipeline_chain,
690 pipelines: pipelines.clone(),
691 }
692 }
693
694 /// Begins delegating a subpath of the tree, but does not dispatch the requests via this
695 /// router's `PipelineChain`.
696 ///
697 /// # Examples
698 ///
699 /// ```rust
700 /// # use hyper::{Body, Response, StatusCode};
701 /// # use gotham::router::Router;
702 /// # use gotham::router::builder::*;
703 /// # use gotham::pipeline::{new_pipeline, single_pipeline};
704 /// # use gotham::state::State;
705 /// # use gotham::middleware::session::{NewSessionMiddleware, SessionData};
706 /// # use gotham::test::TestServer;
707 /// # use serde::{Deserialize, Serialize};
708 /// #
709 /// # #[derive(Default, Serialize, Deserialize)]
710 /// # struct Session;
711 /// #
712 /// // API routes which don't require sessions.
713 /// fn api_router() -> Router {
714 /// // Implementation elided
715 /// # fn handler(state: State) -> (State, Response<Body>) {
716 /// # assert!(!state.has::<SessionData<Session>>());
717 /// # (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap())
718 /// # }
719 /// #
720 /// # build_simple_router(|route| {
721 /// # route.get("/").to(handler);
722 /// # })
723 /// }
724 /// # fn handler(state: State) -> (State, Response<Body>) {
725 /// # assert!(state.has::<SessionData<Session>>());
726 /// # (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap())
727 /// # }
728 ///
729 /// # fn router() -> Router {
730 /// let (chain, pipelines) = single_pipeline(
731 /// new_pipeline()
732 /// .add(NewSessionMiddleware::default().with_session_type::<Session>())
733 /// .build()
734 /// );
735 ///
736 /// build_router(chain, pipelines, |route| {
737 /// // Requests dispatched to the `/api` router will not invoke the session middleware.
738 /// route.delegate_without_pipelines("/api").to_router(api_router());
739 ///
740 /// // Other requests will invoke the session middleware as normal.
741 /// route.get("/").to(handler);
742 /// })
743 /// # }
744 /// #
745 /// # fn main() {
746 /// # let test_server = TestServer::new(router()).unwrap();
747 /// # let response = test_server.client()
748 /// # .get("https://example.com/")
749 /// # .perform()
750 /// # .unwrap();
751 /// # assert_eq!(response.status(), StatusCode::ACCEPTED);
752 /// #
753 /// # let response = test_server.client()
754 /// # .get("https://example.com/api")
755 /// # .perform()
756 /// # .unwrap();
757 /// # assert_eq!(response.status(), StatusCode::ACCEPTED);
758 /// # }
759 /// ```
760 fn delegate_without_pipelines<'b>(
761 &'b mut self,
762 path: &str,
763 ) -> DelegateRouteBuilder<'b, AnyRouteMatcher, (), P> {
764 let (node_builder, _pipeline_chain, pipelines) = self.component_refs();
765 let node_builder = descend(node_builder, path);
766
767 DelegateRouteBuilder {
768 matcher: AnyRouteMatcher::new(),
769 node_builder,
770 pipeline_chain: (),
771 pipelines: pipelines.clone(),
772 }
773 }
774
775 /// Begins associating routes with a fixed path in the tree. In this way, multiple routes can
776 /// be quickly associated with a single location.
777 ///
778 /// # Examples
779 ///
780 /// ```rust
781 /// # extern crate gotham;
782 /// # extern crate hyper;
783 /// # extern crate mime;
784 /// #
785 /// # use hyper::{Body, Response, StatusCode};
786 /// # use gotham::router::Router;
787 /// # use gotham::router::builder::*;
788 /// # use gotham::state::State;
789 /// # use gotham::test::TestServer;
790 /// #
791 /// mod resource {
792 /// # use super::*;
793 /// pub fn show(state: State) -> (State, Response<Body>) {
794 /// // Implementation elided.
795 /// # (state, Response::builder().status(StatusCode::NO_CONTENT).body(Body::empty()).unwrap())
796 /// }
797 ///
798 /// pub fn update(state: State) -> (State, Response<Body>) {
799 /// // Implementation elided.
800 /// # (state, Response::builder().status(StatusCode::CREATED).body(Body::empty()).unwrap())
801 /// }
802 ///
803 /// pub fn delete(state: State) -> (State, Response<Body>) {
804 /// // Implementation elided.
805 /// # (state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap())
806 /// }
807 /// }
808 ///
809 /// #
810 /// # fn router() -> Router {
811 /// build_simple_router(|route| {
812 /// route.associate("/resource", |assoc| {
813 /// assoc.get_or_head().to(resource::show);
814 /// assoc.patch().to(resource::update);
815 /// assoc.delete().to(resource::delete);
816 /// });
817 /// })
818 /// # }
819 /// #
820 /// # fn main() {
821 /// # let test_server = TestServer::new(router()).unwrap();
822 /// # let response = test_server.client()
823 /// # .get("https://example.com/resource")
824 /// # .perform()
825 /// # .unwrap();
826 /// # assert_eq!(response.status(), StatusCode::NO_CONTENT);
827 /// #
828 /// # let response = test_server.client()
829 /// # .patch("https://example.com/resource", b"".to_vec(), mime::TEXT_PLAIN)
830 /// # .perform()
831 /// # .unwrap();
832 /// # assert_eq!(response.status(), StatusCode::CREATED);
833 /// #
834 /// # let response = test_server.client()
835 /// # .delete("https://example.com/resource")
836 /// # .perform()
837 /// # .unwrap();
838 /// # assert_eq!(response.status(), StatusCode::ACCEPTED);
839 /// # }
840 /// ```
841 fn associate<'b, F>(&'b mut self, path: &str, f: F)
842 where
843 F: FnOnce(&mut DefaultAssociatedRouteBuilder<'b, AnyRouteMatcher, C, P>),
844 {
845 let (node_builder, pipeline_chain, pipelines) = self.component_refs();
846 let node_builder = descend(node_builder, path);
847
848 let mut builder =
849 AssociatedRouteBuilder::new(node_builder, *pipeline_chain, pipelines.clone());
850
851 f(&mut builder)
852 }
853
854 /// Return the components that comprise this builder. For internal use only.
855 #[doc(hidden)]
856 fn component_refs(&mut self) -> (&mut Node, &mut C, &PipelineSet<P>);
857}
858
859fn descend<'n>(node_builder: &'n mut Node, path: &str) -> &'n mut Node {
860 trace!("[walking to: {}]", path);
861
862 let path = path.strip_prefix('/').unwrap_or(path);
863 if path.is_empty() {
864 node_builder
865 } else {
866 build_subtree(node_builder, split_path_segments(path))
867 }
868}
869
870fn build_subtree<'n, 's, I>(node: &'n mut Node, mut i: I) -> &'n mut Node
871where
872 I: Iterator<Item = &'s str>,
873{
874 match i.next() {
875 Some(segment) => {
876 trace!("[descending into {}]", segment);
877
878 let (segment, segment_type) = match segment.chars().next() {
879 Some(':') => {
880 let segment = &segment[1..];
881 match segment.find(':') {
882 Some(n) => {
883 let (segment, pattern) = segment.split_at(n);
884 let regex = Box::new(ConstrainedSegmentRegex::new(&pattern[1..]));
885 (segment, SegmentType::Constrained { regex })
886 }
887 None => (segment, SegmentType::Dynamic),
888 }
889 }
890 Some('*') if segment.len() == 1 => (segment, SegmentType::Glob),
891 Some('*') => (&segment[1..], SegmentType::Glob),
892 Some('\\') => (&segment[1..], SegmentType::Static),
893 _ => (segment, SegmentType::Static),
894 };
895
896 if !node.has_child(segment, segment_type.clone()) {
897 node.add_child(Node::new(segment, segment_type.clone()));
898 }
899
900 let child = node.borrow_child_mut(segment, segment_type).unwrap();
901 build_subtree(child, i)
902 }
903 None => {
904 trace!("[reached node]");
905 node
906 }
907 }
908}
909
910impl<'a, C, P> DrawRoutes<C, P> for RouterBuilder<'a, C, P>
911where
912 C: PipelineHandleChain<P> + Copy + Send + Sync + 'static,
913 P: RefUnwindSafe + Send + Sync + 'static,
914{
915 fn component_refs(&mut self) -> (&mut Node, &mut C, &PipelineSet<P>) {
916 (self.node_builder, &mut self.pipeline_chain, &self.pipelines)
917 }
918}
919
920impl<'a, C, P> DrawRoutes<C, P> for ScopeBuilder<'a, C, P>
921where
922 C: PipelineHandleChain<P> + Copy + Send + Sync + 'static,
923 P: RefUnwindSafe + Send + Sync + 'static,
924{
925 fn component_refs(&mut self) -> (&mut Node, &mut C, &PipelineSet<P>) {
926 (self.node_builder, &mut self.pipeline_chain, &self.pipelines)
927 }
928}
929
930#[cfg(test)]
931mod tests {
932 use std::pin::Pin;
933
934 use futures_util::future::{self, FutureExt};
935 use hyper::{Body, Response, StatusCode};
936
937 use crate::handler::HandlerFuture;
938 use crate::helpers::http::response::create_empty_response;
939 use crate::hyper::header::ACCEPT;
940 use crate::middleware::{Middleware, NewMiddleware};
941 use crate::pipeline::*;
942 use crate::router::builder::*;
943 use crate::router::route::matcher::AcceptHeaderRouteMatcher;
944 use crate::state::State;
945 use crate::test::TestServer;
946
947 #[derive(Clone, Copy)]
948 struct QuickExitMiddleware;
949
950 impl NewMiddleware for QuickExitMiddleware {
951 type Instance = Self;
952
953 fn new_middleware(&self) -> anyhow::Result<Self> {
954 Ok(*self)
955 }
956 }
957
958 impl Middleware for QuickExitMiddleware {
959 fn call<Chain>(self, state: State, _chain: Chain) -> Pin<Box<HandlerFuture>>
960 where
961 Chain: FnOnce(State) -> Pin<Box<HandlerFuture>> + 'static,
962 {
963 let f = future::ok((
964 state,
965 Response::builder()
966 .status(StatusCode::INTERNAL_SERVER_ERROR)
967 .body(Body::empty())
968 .unwrap(),
969 ));
970
971 f.boxed()
972 }
973 }
974
975 fn test_handler(state: State) -> (State, Response<Body>) {
976 let response = create_empty_response(&state, StatusCode::ACCEPTED);
977 (state, response)
978 }
979
980 #[test]
981 fn delegate_with_matcher() {
982 let test_router = build_simple_router(|route| {
983 route.get("/").to(test_handler);
984 });
985
986 let router = build_simple_router(|route| {
987 let matcher = AcceptHeaderRouteMatcher::new(vec![mime::APPLICATION_JSON]);
988 route
989 .delegate("/test")
990 .add_route_matcher(matcher)
991 .to_router(test_router);
992 });
993
994 let test_server = TestServer::new(router).unwrap();
995 let response = test_server
996 .client()
997 .get("http://localhost/test/")
998 .with_header(ACCEPT, mime::JAVASCRIPT.to_string().parse().unwrap())
999 .perform()
1000 .unwrap();
1001 assert_eq!(response.status(), StatusCode::NOT_ACCEPTABLE);
1002 let response = test_server
1003 .client()
1004 .get("http://localhost/test/")
1005 .with_header(ACCEPT, mime::APPLICATION_JSON.to_string().parse().unwrap())
1006 .perform()
1007 .unwrap();
1008 assert_eq!(response.status(), StatusCode::ACCEPTED);
1009 }
1010
1011 #[test]
1012 fn delegate_includes_pipelines() {
1013 let (chain, pipelines) = single_pipeline(new_pipeline().add(QuickExitMiddleware).build());
1014
1015 let test_router = build_simple_router(|route| {
1016 route.get("/").to(test_handler);
1017 });
1018
1019 let router = build_router(chain, pipelines, |route| {
1020 route.delegate("/test").to_router(test_router);
1021 });
1022
1023 let test_server = TestServer::new(router).unwrap();
1024 let response = test_server
1025 .client()
1026 .get("http://localhost/test/")
1027 .perform()
1028 .unwrap();
1029 assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
1030 }
1031
1032 #[test]
1033 fn delegate_without_pipelines_skips_pipelines() {
1034 let (chain, pipelines) = single_pipeline(new_pipeline().add(QuickExitMiddleware).build());
1035
1036 let test_router = build_simple_router(|route| {
1037 route.get("/").to(test_handler);
1038 });
1039
1040 let router = build_router(chain, pipelines, |route| {
1041 route
1042 .delegate_without_pipelines("/test")
1043 .to_router(test_router);
1044 });
1045
1046 let test_server = TestServer::new(router).unwrap();
1047 let response = test_server
1048 .client()
1049 .get("http://localhost/test/")
1050 .perform()
1051 .unwrap();
1052
1053 assert_eq!(response.status(), StatusCode::ACCEPTED);
1054 }
1055}