rocket/router/
router.rs

1use std::collections::HashMap;
2
3use crate::request::Request;
4use crate::http::{Method, Status};
5
6use crate::{Route, Catcher};
7use crate::router::Collide;
8
9#[derive(Debug, Default)]
10pub(crate) struct Router {
11    routes: HashMap<Method, Vec<Route>>,
12    catchers: HashMap<Option<u16>, Vec<Catcher>>,
13}
14
15#[derive(Debug)]
16pub struct Collisions {
17    pub routes: Vec<(Route, Route)>,
18    pub catchers: Vec<(Catcher, Catcher)>,
19}
20
21impl Router {
22    pub fn new() -> Self {
23        Self::default()
24    }
25
26    pub fn add_route(&mut self, route: Route) {
27        let routes = self.routes.entry(route.method).or_default();
28        routes.push(route);
29        routes.sort_by_key(|r| r.rank);
30    }
31
32    pub fn add_catcher(&mut self, catcher: Catcher) {
33        let catchers = self.catchers.entry(catcher.code).or_default();
34        catchers.push(catcher);
35        catchers.sort_by(|a, b| b.base.path().segments().len().cmp(&a.base.path().segments().len()))
36    }
37
38    #[inline]
39    pub fn routes(&self) -> impl Iterator<Item = &Route> + Clone {
40        self.routes.values().flat_map(|v| v.iter())
41    }
42
43    #[inline]
44    pub fn catchers(&self) -> impl Iterator<Item = &Catcher> + Clone {
45        self.catchers.values().flat_map(|v| v.iter())
46    }
47
48    pub fn route<'r, 'a: 'r>(
49        &'a self,
50        req: &'r Request<'r>
51    ) -> impl Iterator<Item = &'a Route> + 'r {
52        // Note that routes are presorted by ascending rank on each `add`.
53        self.routes.get(&req.method())
54            .into_iter()
55            .flat_map(move |routes| routes.iter().filter(move |r| r.matches(req)))
56    }
57
58    // For many catchers, using aho-corasick or similar should be much faster.
59    pub fn catch<'r>(&self, status: Status, req: &'r Request<'r>) -> Option<&Catcher> {
60        // Note that catchers are presorted by descending base length.
61        let explicit = self.catchers.get(&Some(status.code))
62            .and_then(|c| c.iter().find(|c| c.matches(status, req)));
63
64        let default = self.catchers.get(&None)
65            .and_then(|c| c.iter().find(|c| c.matches(status, req)));
66
67        match (explicit, default) {
68            (None, None) => None,
69            (None, c@Some(_)) | (c@Some(_), None) => c,
70            (Some(a), Some(b)) => {
71                if b.base.path().segments().len() > a.base.path().segments().len() {
72                    Some(b)
73                } else {
74                    Some(a)
75                }
76            }
77        }
78    }
79
80    fn collisions<'a, I, T>(&self, items: I) -> impl Iterator<Item = (T, T)> + 'a
81        where I: Iterator<Item = &'a T> + Clone + 'a, T: Collide + Clone + 'a,
82    {
83        items.clone().enumerate()
84            .flat_map(move |(i, a)| {
85                items.clone()
86                    .skip(i + 1)
87                    .filter(move |b| a.collides_with(b))
88                    .map(move |b| (a.clone(), b.clone()))
89            })
90    }
91
92    pub fn finalize(&self) -> Result<(), Collisions> {
93        let routes: Vec<_> = self.collisions(self.routes()).collect();
94        let catchers: Vec<_> = self.collisions(self.catchers()).collect();
95
96        if !routes.is_empty() || !catchers.is_empty() {
97            return Err(Collisions { routes, catchers })
98        }
99
100        Ok(())
101    }
102}
103
104#[cfg(test)]
105mod test {
106    use super::*;
107
108    use crate::route::dummy_handler;
109    use crate::local::blocking::Client;
110    use crate::http::{Method, Method::*, uri::Origin};
111
112    impl Router {
113        fn has_collisions(&self) -> bool {
114            self.finalize().is_err()
115        }
116    }
117
118    fn router_with_routes(routes: &[&'static str]) -> Router {
119        let mut router = Router::new();
120        for route in routes {
121            let route = Route::new(Get, route, dummy_handler);
122            router.add_route(route);
123        }
124
125        router
126    }
127
128    fn router_with_ranked_routes(routes: &[(isize, &'static str)]) -> Router {
129        let mut router = Router::new();
130        for &(rank, route) in routes {
131            let route = Route::ranked(rank, Get, route, dummy_handler);
132            router.add_route(route);
133        }
134
135        router
136    }
137
138    fn router_with_rankless_routes(routes: &[&'static str]) -> Router {
139        let mut router = Router::new();
140        for route in routes {
141            let route = Route::ranked(0, Get, route, dummy_handler);
142            router.add_route(route);
143        }
144
145        router
146    }
147
148    fn rankless_route_collisions(routes: &[&'static str]) -> bool {
149        let router = router_with_rankless_routes(routes);
150        router.has_collisions()
151    }
152
153    fn default_rank_route_collisions(routes: &[&'static str]) -> bool {
154        let router = router_with_routes(routes);
155        router.has_collisions()
156    }
157
158    #[test]
159    fn test_rankless_collisions() {
160        assert!(rankless_route_collisions(&["/hello", "/hello"]));
161        assert!(rankless_route_collisions(&["/<a>", "/hello"]));
162        assert!(rankless_route_collisions(&["/<a>", "/<b>"]));
163        assert!(rankless_route_collisions(&["/hello/bob", "/hello/<b>"]));
164        assert!(rankless_route_collisions(&["/a/b/<c>/d", "/<a>/<b>/c/d"]));
165
166        assert!(rankless_route_collisions(&["/a/b", "/<a..>"]));
167        assert!(rankless_route_collisions(&["/a/b/c", "/a/<a..>"]));
168        assert!(rankless_route_collisions(&["/<a>/b", "/a/<a..>"]));
169        assert!(rankless_route_collisions(&["/a/<b>", "/a/<a..>"]));
170        assert!(rankless_route_collisions(&["/a/b/<c>", "/a/<a..>"]));
171        assert!(rankless_route_collisions(&["/<a..>", "/a/<a..>"]));
172        assert!(rankless_route_collisions(&["/a/<a..>", "/a/<a..>"]));
173        assert!(rankless_route_collisions(&["/a/b/<a..>", "/a/<a..>"]));
174        assert!(rankless_route_collisions(&["/a/b/c/d", "/a/<a..>"]));
175        assert!(rankless_route_collisions(&["/", "/<a..>"]));
176        assert!(rankless_route_collisions(&["/a/<_>", "/a/<a..>"]));
177        assert!(rankless_route_collisions(&["/a/<_>", "/a/<_..>"]));
178        assert!(rankless_route_collisions(&["/<_>", "/a/<_..>"]));
179        assert!(rankless_route_collisions(&["/foo", "/foo/<_..>"]));
180        assert!(rankless_route_collisions(&["/foo/bar/baz", "/foo/<_..>"]));
181        assert!(rankless_route_collisions(&["/a/d/<b..>", "/a/d"]));
182        assert!(rankless_route_collisions(&["/a/<_..>", "/<_>"]));
183        assert!(rankless_route_collisions(&["/a/<_..>", "/a"]));
184        assert!(rankless_route_collisions(&["/<a>", "/a/<a..>"]));
185
186        assert!(rankless_route_collisions(&["/<_>", "/<_>"]));
187        assert!(rankless_route_collisions(&["/a/<_>", "/a/b"]));
188        assert!(rankless_route_collisions(&["/a/<_>", "/a/<b>"]));
189        assert!(rankless_route_collisions(&["/<_..>", "/a/b"]));
190        assert!(rankless_route_collisions(&["/<_..>", "/<_>"]));
191        assert!(rankless_route_collisions(&["/<_>/b", "/a/b"]));
192        assert!(rankless_route_collisions(&["/", "/<foo..>"]));
193    }
194
195    #[test]
196    fn test_collisions_normalize() {
197        assert!(rankless_route_collisions(&["/hello/", "/hello"]));
198        assert!(rankless_route_collisions(&["//hello/", "/hello"]));
199        assert!(rankless_route_collisions(&["//hello/", "/hello//"]));
200        assert!(rankless_route_collisions(&["/<a>", "/hello//"]));
201        assert!(rankless_route_collisions(&["/<a>", "/hello///"]));
202        assert!(rankless_route_collisions(&["/hello///bob", "/hello/<b>"]));
203        assert!(rankless_route_collisions(&["/<a..>//", "/a//<a..>"]));
204        assert!(rankless_route_collisions(&["/a/<a..>//", "/a/<a..>"]));
205        assert!(rankless_route_collisions(&["/a/<a..>//", "/a/b//c//d/"]));
206        assert!(rankless_route_collisions(&["/a/<a..>/", "/a/bd/e/"]));
207        assert!(rankless_route_collisions(&["/<a..>/", "/a/bd/e/"]));
208        assert!(rankless_route_collisions(&["//", "/<foo..>"]));
209        assert!(rankless_route_collisions(&["/a/<a..>//", "/a/b//c//d/e/"]));
210        assert!(rankless_route_collisions(&["/a//<a..>//", "/a/b//c//d/e/"]));
211        assert!(rankless_route_collisions(&["///<_>", "/<_>"]));
212        assert!(rankless_route_collisions(&["/a/<_>", "///a//b"]));
213        assert!(rankless_route_collisions(&["//a///<_>", "/a//<b>"]));
214        assert!(rankless_route_collisions(&["//<_..>", "/a/b"]));
215        assert!(rankless_route_collisions(&["//<_..>", "/<_>"]));
216        assert!(rankless_route_collisions(&["///<a>/", "/a/<a..>"]));
217        assert!(rankless_route_collisions(&["///<a..>/", "/a/<a..>"]));
218        assert!(rankless_route_collisions(&["/<a..>", "/hello"]));
219    }
220
221    #[test]
222    fn test_collisions_query() {
223        // Query shouldn't affect things when rankless.
224        assert!(rankless_route_collisions(&["/hello?<foo>", "/hello"]));
225        assert!(rankless_route_collisions(&["/<a>?foo=bar", "/hello?foo=bar&cat=fat"]));
226        assert!(rankless_route_collisions(&["/<a>?foo=bar", "/hello?foo=bar&cat=fat"]));
227        assert!(rankless_route_collisions(&["/<a>", "/<b>?<foo>"]));
228        assert!(rankless_route_collisions(&["/hello/bob?a=b", "/hello/<b>?d=e"]));
229        assert!(rankless_route_collisions(&["/<foo>?a=b", "/foo?d=e"]));
230        assert!(rankless_route_collisions(&["/<foo>?a=b&<c>", "/<foo>?d=e&<c>"]));
231        assert!(rankless_route_collisions(&["/<foo>?a=b&<c>", "/<foo>?d=e"]));
232    }
233
234    #[test]
235    fn test_no_collisions() {
236        assert!(!rankless_route_collisions(&["/a/b", "/a/b/c"]));
237        assert!(!rankless_route_collisions(&["/a/b/c/d", "/a/b/c/<d>/e"]));
238        assert!(!rankless_route_collisions(&["/a/d/<b..>", "/a/b/c"]));
239        assert!(!rankless_route_collisions(&["/<_>", "/"]));
240        assert!(!rankless_route_collisions(&["/a/<_>", "/a"]));
241        assert!(!rankless_route_collisions(&["/a/<_>", "/<_>"]));
242    }
243
244    #[test]
245    fn test_no_collision_when_ranked() {
246        assert!(!default_rank_route_collisions(&["/<a>", "/hello"]));
247        assert!(!default_rank_route_collisions(&["/hello/bob", "/hello/<b>"]));
248        assert!(!default_rank_route_collisions(&["/a/b/c/d", "/<a>/<b>/c/d"]));
249        assert!(!default_rank_route_collisions(&["/hi", "/<hi>"]));
250        assert!(!default_rank_route_collisions(&["/a", "/a/<path..>"]));
251        assert!(!default_rank_route_collisions(&["/", "/<path..>"]));
252        assert!(!default_rank_route_collisions(&["/a/b", "/a/b/<c..>"]));
253        assert!(!default_rank_route_collisions(&["/<_>", "/static"]));
254        assert!(!default_rank_route_collisions(&["/<_..>", "/static"]));
255        assert!(!default_rank_route_collisions(&["/<path..>", "/"]));
256        assert!(!default_rank_route_collisions(&["/<_>/<_>", "/foo/bar"]));
257        assert!(!default_rank_route_collisions(&["/foo/<_>", "/foo/bar"]));
258
259        assert!(!default_rank_route_collisions(&["/<a>/<b>", "/hello/<b>"]));
260        assert!(!default_rank_route_collisions(&["/<a>/<b..>", "/hello/<b>"]));
261        assert!(!default_rank_route_collisions(&["/<a..>", "/hello/<b>"]));
262        assert!(!default_rank_route_collisions(&["/<a..>", "/hello"]));
263        assert!(!default_rank_route_collisions(&["/<a>", "/a/<path..>"]));
264        assert!(!default_rank_route_collisions(&["/a/<b>/c", "/<d>/<c..>"]));
265    }
266
267    #[test]
268    fn test_collision_when_ranked() {
269        assert!(default_rank_route_collisions(&["/a/<b>/<c..>", "/a/<c>"]));
270        assert!(default_rank_route_collisions(&["/<a>/b", "/a/<b>"]));
271    }
272
273    #[test]
274    fn test_collision_when_ranked_query() {
275        assert!(default_rank_route_collisions(&["/a?a=b", "/a?c=d"]));
276        assert!(default_rank_route_collisions(&["/a?a=b&<b>", "/a?<c>&c=d"]));
277        assert!(default_rank_route_collisions(&["/a?a=b&<b..>", "/a?<c>&c=d"]));
278    }
279
280    #[test]
281    fn test_no_collision_when_ranked_query() {
282        assert!(!default_rank_route_collisions(&["/", "/?<c..>"]));
283        assert!(!default_rank_route_collisions(&["/hi", "/hi?<c>"]));
284        assert!(!default_rank_route_collisions(&["/hi", "/hi?c"]));
285        assert!(!default_rank_route_collisions(&["/hi?<c>", "/hi?c"]));
286        assert!(!default_rank_route_collisions(&["/<foo>?a=b", "/<foo>?c=d&<d>"]));
287    }
288
289    fn matches<'a>(router: &'a Router, method: Method, uri: &'a str) -> Vec<&'a Route> {
290        let client = Client::debug_with(vec![]).expect("client");
291        let request = client.req(method, Origin::parse(uri).unwrap());
292        router.route(&request).collect()
293    }
294
295    fn route<'a>(router: &'a Router, method: Method, uri: &'a str) -> Option<&'a Route> {
296        matches(router, method, uri).into_iter().next()
297    }
298
299    #[test]
300    fn test_ok_routing() {
301        let router = router_with_routes(&["/hello"]);
302        assert!(route(&router, Get, "/hello").is_some());
303
304        let router = router_with_routes(&["/<a>"]);
305        assert!(route(&router, Get, "/hello").is_some());
306        assert!(route(&router, Get, "/hi").is_some());
307        assert!(route(&router, Get, "/bobbbbbbbbbby").is_some());
308        assert!(route(&router, Get, "/dsfhjasdf").is_some());
309
310        let router = router_with_routes(&["/<a>/<b>"]);
311        assert!(route(&router, Get, "/hello/hi").is_some());
312        assert!(route(&router, Get, "/a/b/").is_some());
313        assert!(route(&router, Get, "/i/a").is_some());
314        assert!(route(&router, Get, "/jdlk/asdij").is_some());
315
316        let mut router = Router::new();
317        router.add_route(Route::new(Put, "/hello", dummy_handler));
318        router.add_route(Route::new(Post, "/hello", dummy_handler));
319        router.add_route(Route::new(Delete, "/hello", dummy_handler));
320        assert!(route(&router, Put, "/hello").is_some());
321        assert!(route(&router, Post, "/hello").is_some());
322        assert!(route(&router, Delete, "/hello").is_some());
323
324        let router = router_with_routes(&["/<a..>"]);
325        assert!(route(&router, Get, "/").is_some());
326        assert!(route(&router, Get, "//").is_some());
327        assert!(route(&router, Get, "/hi").is_some());
328        assert!(route(&router, Get, "/hello/hi").is_some());
329        assert!(route(&router, Get, "/a/b/").is_some());
330        assert!(route(&router, Get, "/i/a").is_some());
331        assert!(route(&router, Get, "/a/b/c/d/e/f").is_some());
332
333        let router = router_with_routes(&["/foo/<a..>"]);
334        assert!(route(&router, Get, "/foo").is_some());
335        assert!(route(&router, Get, "/foo/").is_some());
336        assert!(route(&router, Get, "/foo///bar").is_some());
337    }
338
339    #[test]
340    fn test_err_routing() {
341        let router = router_with_routes(&["/hello"]);
342        assert!(route(&router, Put, "/hello").is_none());
343        assert!(route(&router, Post, "/hello").is_none());
344        assert!(route(&router, Options, "/hello").is_none());
345        assert!(route(&router, Get, "/hell").is_none());
346        assert!(route(&router, Get, "/hi").is_none());
347        assert!(route(&router, Get, "/hello/there").is_none());
348        assert!(route(&router, Get, "/hello/i").is_none());
349        assert!(route(&router, Get, "/hillo").is_none());
350
351        let router = router_with_routes(&["/<a>"]);
352        assert!(route(&router, Put, "/hello").is_none());
353        assert!(route(&router, Post, "/hello").is_none());
354        assert!(route(&router, Options, "/hello").is_none());
355        assert!(route(&router, Get, "/hello/there").is_none());
356        assert!(route(&router, Get, "/hello/i").is_none());
357
358        let router = router_with_routes(&["/<a>/<b>"]);
359        assert!(route(&router, Get, "/a/b/c").is_none());
360        assert!(route(&router, Get, "/a").is_none());
361        assert!(route(&router, Get, "/a/").is_none());
362        assert!(route(&router, Get, "/a/b/c/d").is_none());
363        assert!(route(&router, Put, "/hello/hi").is_none());
364        assert!(route(&router, Put, "/a/b").is_none());
365        assert!(route(&router, Put, "/a/b").is_none());
366
367        let router = router_with_routes(&["/prefix/<a..>"]);
368        assert!(route(&router, Get, "/").is_none());
369        assert!(route(&router, Get, "/prefi/").is_none());
370    }
371
372    macro_rules! assert_ranked_match {
373        ($routes:expr, $to:expr => $want:expr) => ({
374            let router = router_with_routes($routes);
375            assert!(!router.has_collisions());
376            let route_path = route(&router, Get, $to).unwrap().uri.to_string();
377            assert_eq!(route_path, $want.to_string());
378        })
379    }
380
381    #[test]
382    fn test_default_ranking() {
383        assert_ranked_match!(&["/hello", "/<name>"], "/hello" => "/hello");
384        assert_ranked_match!(&["/<name>", "/hello"], "/hello" => "/hello");
385        assert_ranked_match!(&["/<a>", "/hi", "/hi/<b>"], "/hi" => "/hi");
386        assert_ranked_match!(&["/<a>/b", "/hi/c"], "/hi/c" => "/hi/c");
387        assert_ranked_match!(&["/<a>/<b>", "/hi/a"], "/hi/c" => "/<a>/<b>");
388        assert_ranked_match!(&["/hi/a", "/hi/<c>"], "/hi/c" => "/hi/<c>");
389        assert_ranked_match!(&["/a", "/a?<b>"], "/a?b=c" => "/a?<b>");
390        assert_ranked_match!(&["/a", "/a?<b>"], "/a" => "/a?<b>");
391        assert_ranked_match!(&["/a", "/<a>", "/a?<b>", "/<a>?<b>"], "/a" => "/a?<b>");
392        assert_ranked_match!(&["/a", "/<a>", "/a?<b>", "/<a>?<b>"], "/b" => "/<a>?<b>");
393        assert_ranked_match!(&["/a", "/<a>", "/a?<b>", "/<a>?<b>"], "/b?v=1" => "/<a>?<b>");
394        assert_ranked_match!(&["/a", "/<a>", "/a?<b>", "/<a>?<b>"], "/a?b=c" => "/a?<b>");
395        assert_ranked_match!(&["/a", "/a?b"], "/a?b" => "/a?b");
396        assert_ranked_match!(&["/<a>", "/a?b"], "/a?b" => "/a?b");
397        assert_ranked_match!(&["/a", "/<a>?b"], "/a?b" => "/a");
398        assert_ranked_match!(&["/a?<c>&b", "/a?<b>"], "/a" => "/a?<b>");
399        assert_ranked_match!(&["/a?<c>&b", "/a?<b>"], "/a?b" => "/a?<c>&b");
400        assert_ranked_match!(&["/a?<c>&b", "/a?<b>"], "/a?c" => "/a?<b>");
401        assert_ranked_match!(&["/", "/<foo..>"], "/" => "/");
402        assert_ranked_match!(&["/", "/<foo..>"], "/hi" => "/<foo..>");
403        assert_ranked_match!(&["/hi", "/<foo..>"], "/hi" => "/hi");
404    }
405
406    fn ranked_collisions(routes: &[(isize, &'static str)]) -> bool {
407        let router = router_with_ranked_routes(routes);
408        router.has_collisions()
409    }
410
411    #[test]
412    fn test_no_manual_ranked_collisions() {
413        assert!(!ranked_collisions(&[(1, "/a/<b>"), (2, "/a/<b>")]));
414        assert!(!ranked_collisions(&[(0, "/a/<b>"), (2, "/a/<b>")]));
415        assert!(!ranked_collisions(&[(5, "/a/<b>"), (2, "/a/<b>")]));
416        assert!(!ranked_collisions(&[(1, "/a/<b>"), (1, "/b/<b>")]));
417        assert!(!ranked_collisions(&[(1, "/a/<b..>"), (2, "/a/<b..>")]));
418        assert!(!ranked_collisions(&[(0, "/a/<b..>"), (2, "/a/<b..>")]));
419        assert!(!ranked_collisions(&[(5, "/a/<b..>"), (2, "/a/<b..>")]));
420        assert!(!ranked_collisions(&[(1, "/<a..>"), (2, "/<a..>")]));
421    }
422
423    #[test]
424    fn test_ranked_collisions() {
425        assert!(ranked_collisions(&[(2, "/a/<b..>"), (2, "/a/<b..>")]));
426        assert!(ranked_collisions(&[(2, "/a/c/<b..>"), (2, "/a/<b..>")]));
427        assert!(ranked_collisions(&[(2, "/<b..>"), (2, "/a/<b..>")]));
428    }
429
430    macro_rules! assert_ranked_routing {
431        (to: $to:expr, with: $routes:expr, expect: $($want:expr),+) => ({
432            let router = router_with_ranked_routes(&$routes);
433            let routed_to = matches(&router, Get, $to);
434            let expected = &[$($want),+];
435            assert!(routed_to.len() == expected.len());
436            for (got, expected) in routed_to.iter().zip(expected.iter()) {
437                assert_eq!(got.rank, expected.0);
438                assert_eq!(got.uri.to_string(), expected.1.to_string());
439            }
440        })
441    }
442
443    #[test]
444    fn test_ranked_routing() {
445        assert_ranked_routing!(
446            to: "/a/b",
447            with: [(1, "/a/<b>"), (2, "/a/<b>")],
448            expect: (1, "/a/<b>"), (2, "/a/<b>")
449        );
450
451        assert_ranked_routing!(
452            to: "/b/b",
453            with: [(1, "/a/<b>"), (2, "/b/<b>"), (3, "/b/b")],
454            expect: (2, "/b/<b>"), (3, "/b/b")
455        );
456
457        assert_ranked_routing!(
458            to: "/b/b",
459            with: [(2, "/b/<b>"), (1, "/a/<b>"), (3, "/b/b")],
460            expect: (2, "/b/<b>"), (3, "/b/b")
461        );
462
463        assert_ranked_routing!(
464            to: "/b/b",
465            with: [(3, "/b/b"), (2, "/b/<b>"), (1, "/a/<b>")],
466            expect: (2, "/b/<b>"), (3, "/b/b")
467        );
468
469        assert_ranked_routing!(
470            to: "/b/b",
471            with: [(1, "/a/<b>"), (2, "/b/<b>"), (0, "/b/b")],
472            expect: (0, "/b/b"), (2, "/b/<b>")
473        );
474
475        assert_ranked_routing!(
476            to: "/profile/sergio/edit",
477            with: [(1, "/<a>/<b>/edit"), (2, "/profile/<d>"), (0, "/<a>/<b>/<c>")],
478            expect: (0, "/<a>/<b>/<c>"), (1, "/<a>/<b>/edit")
479        );
480
481        assert_ranked_routing!(
482            to: "/profile/sergio/edit",
483            with: [(0, "/<a>/<b>/edit"), (2, "/profile/<d>"), (5, "/<a>/<b>/<c>")],
484            expect: (0, "/<a>/<b>/edit"), (5, "/<a>/<b>/<c>")
485        );
486
487        assert_ranked_routing!(
488            to: "/a/b",
489            with: [(0, "/a/b"), (1, "/a/<b..>")],
490            expect: (0, "/a/b"), (1, "/a/<b..>")
491        );
492
493        assert_ranked_routing!(
494            to: "/a/b/c/d/e/f",
495            with: [(1, "/a/<b..>"), (2, "/a/b/<c..>")],
496            expect: (1, "/a/<b..>"), (2, "/a/b/<c..>")
497        );
498
499        assert_ranked_routing!(
500            to: "/hi",
501            with: [(1, "/hi/<foo..>"), (0, "/hi/<foo>")],
502            expect: (1, "/hi/<foo..>")
503        );
504    }
505
506    macro_rules! assert_default_ranked_routing {
507        (to: $to:expr, with: $routes:expr, expect: $($want:expr),+) => ({
508            let router = router_with_routes(&$routes);
509            let routed_to = matches(&router, Get, $to);
510            let expected = &[$($want),+];
511            assert!(routed_to.len() == expected.len());
512            for (got, expected) in routed_to.iter().zip(expected.iter()) {
513                assert_eq!(got.uri.to_string(), expected.to_string());
514            }
515        })
516    }
517
518    #[test]
519    fn test_default_ranked_routing() {
520        assert_default_ranked_routing!(
521            to: "/a/b?v=1",
522            with: ["/a/<b>", "/a/b"],
523            expect: "/a/b", "/a/<b>"
524        );
525
526        assert_default_ranked_routing!(
527            to: "/a/b?v=1",
528            with: ["/a/<b>", "/a/b", "/a/b?<v>"],
529            expect: "/a/b?<v>", "/a/b", "/a/<b>"
530        );
531
532        assert_default_ranked_routing!(
533            to: "/a/b?v=1",
534            with: ["/a/<b>", "/a/b", "/a/b?<v>", "/a/<b>?<v>"],
535            expect: "/a/b?<v>", "/a/b", "/a/<b>?<v>", "/a/<b>"
536        );
537
538        assert_default_ranked_routing!(
539            to: "/a/b",
540            with: ["/a/<b>", "/a/b", "/a/b?<v>", "/a/<b>?<v>"],
541            expect: "/a/b?<v>", "/a/b", "/a/<b>?<v>", "/a/<b>"
542        );
543
544        assert_default_ranked_routing!(
545            to: "/a/b?c",
546            with: ["/a/b", "/a/b?<c>", "/a/b?c", "/a/<b>?c", "/a/<b>?<c>", "/<a>/<b>"],
547            expect: "/a/b?c", "/a/b?<c>", "/a/b", "/a/<b>?c", "/a/<b>?<c>", "/<a>/<b>"
548        );
549    }
550
551    fn router_with_catchers(catchers: &[(Option<u16>, &str)]) -> Router {
552        let mut router = Router::new();
553        for (code, base) in catchers {
554            let catcher = Catcher::new(*code, crate::catcher::dummy_handler);
555            router.add_catcher(catcher.map_base(|_| base.to_string()).unwrap());
556        }
557
558        router
559    }
560
561    fn catcher<'a>(router: &'a Router, status: Status, uri: &str) -> Option<&'a Catcher> {
562        let client = Client::debug_with(vec![]).expect("client");
563        let request = client.get(Origin::parse(uri).unwrap());
564        router.catch(status, &request)
565    }
566
567    macro_rules! assert_catcher_routing {
568        (
569            catch: [$(($code:expr, $uri:expr)),+],
570            reqs: [$($r:expr),+],
571            with: [$(($ecode:expr, $euri:expr)),+]
572        ) => ({
573            let catchers = vec![$(($code.into(), $uri)),+];
574            let requests = vec![$($r),+];
575            let expected = vec![$(($ecode.into(), $euri)),+];
576
577            let router = router_with_catchers(&catchers);
578            for (req, expected) in requests.iter().zip(expected.iter()) {
579                let req_status = Status::from_code(req.0).expect("valid status");
580                let catcher = catcher(&router, req_status, req.1).expect("some catcher");
581                assert_eq!(catcher.code, expected.0, "<- got, expected ->");
582                assert_eq!(catcher.base.path(), expected.1, "<- got, expected ->");
583            }
584        })
585    }
586
587    #[test]
588    fn test_catcher_routing() {
589        // Check that the default `/` catcher catches everything.
590        assert_catcher_routing! {
591            catch: [(None, "/")],
592            reqs: [(404, "/a/b/c"), (500, "/a/b"), (415, "/a/b/d"), (422, "/a/b/c/d?foo")],
593            with: [(None, "/"), (None, "/"), (None, "/"), (None, "/")]
594        }
595
596        // Check prefixes when they're exact.
597        assert_catcher_routing! {
598            catch: [(None, "/"), (None, "/a"), (None, "/a/b")],
599            reqs: [
600                (404, "/"), (500, "/"),
601                (404, "/a"), (500, "/a"),
602                (404, "/a/b"), (500, "/a/b")
603            ],
604            with: [
605                (None, "/"), (None, "/"),
606                (None, "/a"), (None, "/a"),
607                (None, "/a/b"), (None, "/a/b")
608            ]
609        }
610
611        // Check prefixes when they're not exact.
612        assert_catcher_routing! {
613            catch: [(None, "/"), (None, "/a"), (None, "/a/b")],
614            reqs: [
615                (404, "/foo"), (500, "/bar"), (422, "/baz/bar"), (418, "/poodle?yes"),
616                (404, "/a/foo"), (500, "/a/bar/baz"), (510, "/a/c"), (423, "/a/c/b"),
617                (404, "/a/b/c"), (500, "/a/b/c/d"), (500, "/a/b?foo"), (400, "/a/b/yes")
618            ],
619            with: [
620                (None, "/"), (None, "/"), (None, "/"), (None, "/"),
621                (None, "/a"), (None, "/a"), (None, "/a"), (None, "/a"),
622                (None, "/a/b"), (None, "/a/b"), (None, "/a/b"), (None, "/a/b")
623            ]
624        }
625
626        // Check that we prefer specific to default.
627        assert_catcher_routing! {
628            catch: [(400, "/"), (404, "/"), (None, "/")],
629            reqs: [
630                (400, "/"), (400, "/bar"), (400, "/foo/bar"),
631                (404, "/"), (404, "/bar"), (404, "/foo/bar"),
632                (405, "/"), (405, "/bar"), (406, "/foo/bar")
633            ],
634            with: [
635                (400, "/"), (400, "/"), (400, "/"),
636                (404, "/"), (404, "/"), (404, "/"),
637                (None, "/"), (None, "/"), (None, "/")
638            ]
639        }
640
641        // Check that we prefer longer prefixes over specific.
642        assert_catcher_routing! {
643            catch: [(None, "/a/b"), (404, "/a"), (422, "/a")],
644            reqs: [
645                (404, "/a/b"), (404, "/a/b/c"), (422, "/a/b/c"),
646                (404, "/a"), (404, "/a/c"), (404, "/a/cat/bar"),
647                (422, "/a"), (422, "/a/c"), (422, "/a/cat/bar")
648            ],
649            with: [
650                (None, "/a/b"), (None, "/a/b"), (None, "/a/b"),
651                (404, "/a"), (404, "/a"), (404, "/a"),
652                (422, "/a"), (422, "/a"), (422, "/a")
653            ]
654        }
655
656        // Just a fun one.
657        assert_catcher_routing! {
658            catch: [(None, "/"), (None, "/a/b"), (500, "/a/b/c"), (500, "/a/b")],
659            reqs: [(404, "/a/b/c"), (500, "/a/b"), (400, "/a/b/d"), (500, "/a/b/c/d?foo")],
660            with: [(None, "/a/b"), (500, "/a/b"), (None, "/a/b"), (500, "/a/b/c")]
661        }
662    }
663}