rocket/router/
router.rs

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