1use std::{collections::HashMap, convert::Infallible};
11
12use faststr::FastStr;
13use http::status::StatusCode;
14use metainfo::METAINFO;
15use motore::{ServiceExt, layer::Layer, service::Service};
16use volo::context::Context;
17
18use super::{
19 Fallback, Route,
20 method_router::MethodRouter,
21 utils::{Matcher, NEST_CATCH_PARAM, RouteId, StripPrefixLayer},
22};
23use crate::{
24 body::Body,
25 context::ServerContext,
26 request::Request,
27 response::Response,
28 server::{IntoResponse, handler::Handler},
29};
30
31#[must_use]
33pub struct Router<B = Body, E = Infallible> {
34 matcher: Matcher,
35 routes: HashMap<RouteId, (Endpoint<B, E>, FastStr)>,
38 fallback: Fallback<B, E>,
39 is_default_fallback: bool,
40}
41
42impl<B, E> Default for Router<B, E>
43where
44 B: Send + 'static,
45 E: 'static,
46{
47 fn default() -> Self {
48 Self::new()
49 }
50}
51
52impl<B, E> Router<B, E>
53where
54 B: Send + 'static,
55 E: 'static,
56{
57 pub fn new() -> Self
59 where
60 E: 'static,
61 {
62 Self {
63 matcher: Default::default(),
64 routes: Default::default(),
65 fallback: Fallback::from_status_code(StatusCode::NOT_FOUND),
66 is_default_fallback: true,
67 }
68 }
69
70 pub fn route<S>(mut self, uri: S, method_router: MethodRouter<B, E>) -> Self
169 where
170 S: AsRef<str>,
171 {
172 let route_id = self
173 .matcher
174 .insert(uri.as_ref())
175 .expect("Insert routing rule failed");
176
177 self.routes.insert(
178 route_id,
179 (
180 Endpoint::MethodRouter(method_router),
181 FastStr::new(uri.as_ref()),
182 ),
183 );
184
185 self
186 }
187
188 pub fn nest<U>(self, uri: U, router: Router<B, E>) -> Self
229 where
230 U: AsRef<str>,
231 {
232 self.nest_route(uri.as_ref().to_owned(), Route::new(router))
233 }
234
235 pub fn nest_service<U, S>(self, uri: U, service: S) -> Self
240 where
241 U: AsRef<str>,
242 S: Service<ServerContext, Request<B>, Error = E> + Send + Sync + 'static,
243 S::Response: IntoResponse,
244 {
245 self.nest_route(
246 uri.as_ref().to_owned(),
247 Route::new(service.map_response(IntoResponse::into_response)),
248 )
249 }
250
251 fn nest_route(mut self, prefix: String, route: Route<B, E>) -> Self {
252 let uri = if prefix.ends_with('/') {
253 format!("{prefix}{NEST_CATCH_PARAM}")
254 } else {
255 format!("{prefix}/{NEST_CATCH_PARAM}")
256 };
257
258 let route_id = self
261 .matcher
262 .insert(prefix.clone())
263 .expect("Insert routing rule failed");
264 let pattern = FastStr::new(prefix.trim_end_matches('/'));
265
266 if !prefix.ends_with('/') {
270 let prefix_with_slash = prefix + "/";
271 self.matcher
272 .insert_with_id(prefix_with_slash, route_id)
273 .expect("Insert routing rule failed");
274 }
275
276 self.matcher
277 .insert_with_id(uri, route_id)
278 .expect("Insert routing rule failed");
279
280 self.routes.insert(
281 route_id,
282 (
283 Endpoint::Service(Route::new(StripPrefixLayer.layer(route))),
284 pattern,
285 ),
286 );
287
288 self
289 }
290
291 pub fn fallback<H, T>(mut self, handler: H) -> Self
297 where
298 for<'a> H: Handler<T, B, E> + Clone + Send + Sync + 'a,
299 T: 'static,
300 E: 'static,
301 {
302 self.fallback = Fallback::from_handler(handler);
303 self
304 }
305
306 pub fn fallback_service<S>(mut self, service: S) -> Self
312 where
313 for<'a> S: Service<ServerContext, Request<B>, Error = E> + Send + Sync + 'a,
314 S::Response: IntoResponse,
315 {
316 self.fallback = Fallback::from_service(service);
317 self
318 }
319
320 pub fn merge(mut self, other: Self) -> Self {
359 let Router {
360 mut matcher,
361 mut routes,
362 fallback,
363 is_default_fallback,
364 } = other;
365
366 for (path, route_id) in matcher.drain() {
367 self.matcher
368 .insert_with_id(path, route_id)
369 .expect("Insert routing rule failed during merging router");
370 }
371 for (route_id, route) in routes.drain() {
372 if self.routes.insert(route_id, route).is_some() {
373 unreachable!()
374 }
375 }
376
377 match (self.is_default_fallback, is_default_fallback) {
378 (_, true) => {}
379 (true, false) => {
380 self.fallback = fallback;
381 self.is_default_fallback = false;
382 }
383 (false, false) => {
384 panic!("Merge `Router` failed because both `Router` have customized `fallback`")
385 }
386 }
387
388 self
389 }
390
391 pub fn layer<L, B2, E2>(self, l: L) -> Router<B2, E2>
395 where
396 L: Layer<Route<B, E>> + Clone + Send + Sync + 'static,
397 L::Service: Service<ServerContext, Request<B2>, Error = E2> + Send + Sync + 'static,
398 <L::Service as Service<ServerContext, Request<B2>>>::Response: IntoResponse,
399 B2: 'static,
400 {
401 let routes = self
402 .routes
403 .into_iter()
404 .map(|(id, (route, pattern))| {
405 let route = route.layer(l.clone());
406 (id, (route, pattern))
407 })
408 .collect();
409
410 let fallback = self.fallback.layer(l.clone());
411
412 Router {
413 matcher: self.matcher,
414 routes,
415 fallback,
416 is_default_fallback: self.is_default_fallback,
417 }
418 }
419}
420
421impl<B, E> Service<ServerContext, Request<B>> for Router<B, E>
422where
423 B: Send + 'static,
424 E: 'static,
425{
426 type Response = Response;
427 type Error = E;
428
429 async fn call(
430 &self,
431 cx: &mut ServerContext,
432 req: Request<B>,
433 ) -> Result<Self::Response, Self::Error> {
434 if let Ok(matched) = self.matcher.at(req.uri().clone().path()) {
435 if let Some((route, pattern)) = self.routes.get(matched.value) {
436 if !matched.params.is_empty() {
437 cx.params_mut().extend(matched.params);
438 }
439 MatchedPath::record(cx, pattern);
440 return route.call(cx, req).await;
441 }
442 }
443
444 cx.extensions_mut().remove::<MatchedPath>();
447 let _ = METAINFO.try_with(|mi| mi.borrow_mut().remove_faststr::<MatchedPath>());
448
449 self.fallback.call(cx, req).await
450 }
451}
452
453#[derive(Clone, Debug, PartialEq, Eq)]
479pub struct MatchedPath(FastStr);
480
481impl MatchedPath {
482 pub fn as_str(&self) -> &str {
484 &self.0
485 }
486
487 pub fn into_inner(self) -> FastStr {
489 self.0
490 }
491
492 fn record(cx: &mut ServerContext, pattern: &FastStr) {
493 let extensions = cx.extensions_mut();
494 let matched = match extensions.get_mut::<MatchedPath>() {
495 Some(outer) => {
497 outer.0 = FastStr::from_string(outer.0.to_string() + pattern);
498 outer.0.clone()
499 }
500 None => {
501 extensions.insert(MatchedPath(pattern.clone()));
502 pattern.clone()
503 }
504 };
505 let _ = METAINFO.try_with(|mi| mi.borrow_mut().insert_faststr::<MatchedPath>(matched));
506 }
507}
508
509impl std::ops::Deref for MatchedPath {
510 type Target = str;
511
512 fn deref(&self) -> &Self::Target {
513 &self.0
514 }
515}
516
517impl AsRef<str> for MatchedPath {
518 fn as_ref(&self) -> &str {
519 &self.0
520 }
521}
522
523#[allow(clippy::large_enum_variant)]
524enum Endpoint<B = Body, E = Infallible> {
525 MethodRouter(MethodRouter<B, E>),
526 Service(Route<B, E>),
527}
528
529impl<B, E> Service<ServerContext, Request<B>> for Endpoint<B, E>
530where
531 B: Send + 'static,
532 E: 'static,
533{
534 type Response = Response;
535 type Error = E;
536
537 async fn call(
538 &self,
539 cx: &mut ServerContext,
540 req: Request<B>,
541 ) -> Result<Self::Response, Self::Error> {
542 match self {
543 Self::MethodRouter(mr) => mr.call(cx, req).await,
544 Self::Service(service) => service.call(cx, req).await,
545 }
546 }
547}
548
549impl<B, E> Default for Endpoint<B, E>
550where
551 B: Send + 'static,
552 E: 'static,
553{
554 fn default() -> Self {
555 Self::MethodRouter(Default::default())
556 }
557}
558
559impl<B, E> Endpoint<B, E>
560where
561 B: Send + 'static,
562 E: 'static,
563{
564 fn layer<L, B2, E2>(self, l: L) -> Endpoint<B2, E2>
565 where
566 L: Layer<Route<B, E>> + Clone + Send + Sync + 'static,
567 L::Service: Service<ServerContext, Request<B2>, Error = E2> + Send + Sync,
568 <L::Service as Service<ServerContext, Request<B2>>>::Response: IntoResponse,
569 B2: 'static,
570 {
571 match self {
572 Self::MethodRouter(mr) => Endpoint::MethodRouter(mr.layer(l)),
573 Self::Service(s) => Endpoint::Service(Route::new(
574 l.layer(s).map_response(IntoResponse::into_response),
575 )),
576 }
577 }
578}
579
580#[cfg(test)]
581mod router_tests {
582 use faststr::FastStr;
583 use http::{method::Method, status::StatusCode, uri::Uri};
584 use motore::service::Service;
585 use volo::context::Context;
586
587 use super::{MatchedPath, Router};
588 use crate::{
589 body::{Body, BodyConversion},
590 request::Request,
591 server::{
592 Server,
593 param::PathParamsVec,
594 route::method_router::any,
595 test_helpers::{TestServer, empty_cx},
596 },
597 };
598
599 async fn always_ok() {}
600 async fn teapot() -> StatusCode {
601 StatusCode::IM_A_TEAPOT
602 }
603
604 #[tokio::test]
605 async fn url_match() {
606 async fn is_ok(server: &TestServer<Router<Option<Body>>, Option<Body>>, uri: &str) -> bool {
607 server.call_route(Method::GET, uri, None).await.status() == StatusCode::OK
608 }
609 let router: Router<Option<Body>> = Router::new()
610 .route("/", any(always_ok))
611 .route("/catch/{id}", any(always_ok))
612 .route("/catch/{id}/another", any(always_ok))
613 .route("/catch/{id}/another/{uid}", any(always_ok))
614 .route("/catch/{id}/another/{uid}/again", any(always_ok))
615 .route("/catch/{id}/another/{uid}/again/{tid}", any(always_ok))
616 .route("/catch_all/{*all}", any(always_ok));
617 let server = Server::new(router).into_test_server();
618
619 assert!(is_ok(&server, "/").await);
620 assert!(is_ok(&server, "/catch/114").await);
621 assert!(is_ok(&server, "/catch/514").await);
622 assert!(is_ok(&server, "/catch/ll45l4").await);
623 assert!(is_ok(&server, "/catch/ll45l4/another").await);
624 assert!(is_ok(&server, "/catch/ll45l4/another/1919").await);
625 assert!(is_ok(&server, "/catch/ll45l4/another/1919/again").await);
626 assert!(is_ok(&server, "/catch/ll45l4/another/1919/again/810").await);
627 assert!(is_ok(&server, "/catch_all/114").await);
628 assert!(is_ok(&server, "/catch_all/114/514/1919/810").await);
629
630 assert!(!is_ok(&server, "/catch").await);
631 assert!(!is_ok(&server, "/catch/114/").await);
632 assert!(!is_ok(&server, "/catch/114/another/514/").await);
633 assert!(!is_ok(&server, "/catch/11/another/45/again/14/").await);
634 assert!(!is_ok(&server, "/catch_all").await);
635 assert!(!is_ok(&server, "/catch_all/").await);
636 }
637
638 #[tokio::test]
639 async fn matched_path() {
640 async fn matched_path_of(router: &Router<Option<Body>>, uri: &str) -> Option<String> {
641 let mut cx = empty_cx();
642 let req = Request::builder()
643 .method(Method::GET)
644 .uri(uri)
645 .body(None)
646 .unwrap();
647 let _ = router.call(&mut cx, req).await;
648 cx.extensions()
649 .get::<MatchedPath>()
650 .map(|path| path.as_str().to_owned())
651 }
652
653 let router: Router<Option<Body>> = Router::new()
654 .route("/", any(always_ok))
655 .route("/users/{id}", any(always_ok))
656 .route("/files/{*path}", any(always_ok))
657 .nest(
658 "/api/{version}",
659 Router::new().route("/items/{id}", any(always_ok)),
660 )
661 .nest("/legacy/", Router::new().route("/", any(always_ok)))
662 .nest(
663 "/v/{ver}",
664 Router::new().nest("/deep", Router::new().route("/leaf/{id}", any(always_ok))),
665 )
666 .merge(Router::new().route("/merged/{id}", any(always_ok)));
667
668 assert_eq!(matched_path_of(&router, "/").await.as_deref(), Some("/"));
669 assert_eq!(
671 matched_path_of(&router, "/users/42").await.as_deref(),
672 Some("/users/{id}")
673 );
674 assert_eq!(
675 matched_path_of(&router, "/users/43").await.as_deref(),
676 Some("/users/{id}")
677 );
678 assert_eq!(
679 matched_path_of(&router, "/files/a/b/c").await.as_deref(),
680 Some("/files/{*path}")
681 );
682 assert_eq!(
684 matched_path_of(&router, "/api/v2/items/7").await.as_deref(),
685 Some("/api/{version}/items/{id}")
686 );
687 assert_eq!(
688 matched_path_of(&router, "/legacy/").await.as_deref(),
689 Some("/legacy/")
690 );
691 assert_eq!(
693 matched_path_of(&router, "/v/3/deep/leaf/9")
694 .await
695 .as_deref(),
696 Some("/v/{ver}/deep/leaf/{id}")
697 );
698 assert_eq!(
700 matched_path_of(&router, "/merged/1").await.as_deref(),
701 Some("/merged/{id}")
702 );
703 assert_eq!(matched_path_of(&router, "/nope").await, None);
705 assert_eq!(matched_path_of(&router, "/api/v2/nope").await, None);
706 }
707
708 #[tokio::test]
709 async fn matched_path_is_mirrored_into_metainfo() {
710 use std::cell::RefCell;
711
712 use metainfo::{METAINFO, MetaInfo};
713
714 async fn both(
716 router: &Router<Option<Body>>,
717 uri: &str,
718 ) -> (Option<String>, Option<String>) {
719 METAINFO
720 .scope(RefCell::new(MetaInfo::default()), async {
721 let mut cx = empty_cx();
722 let req = Request::builder()
723 .method(Method::GET)
724 .uri(uri)
725 .body(None)
726 .unwrap();
727 let _ = router.call(&mut cx, req).await;
728 let from_cx = cx
729 .extensions()
730 .get::<MatchedPath>()
731 .map(|p| p.as_str().to_owned());
732 let from_mi = METAINFO.with(|mi| {
733 mi.borrow()
734 .get_faststr::<MatchedPath>()
735 .map(|p| p.as_str().to_owned())
736 });
737 (from_cx, from_mi)
738 })
739 .await
740 }
741
742 let router: Router<Option<Body>> = Router::new().route("/users/{id}", any(always_ok)).nest(
743 "/api/{version}",
744 Router::new().route("/items/{id}", any(always_ok)),
745 );
746
747 let (cx, mi) = both(&router, "/users/42").await;
748 assert_eq!(cx.as_deref(), Some("/users/{id}"));
749 assert_eq!(mi, cx);
750
751 let (cx, mi) = both(&router, "/api/v2/items/7").await;
753 assert_eq!(cx.as_deref(), Some("/api/{version}/items/{id}"));
754 assert_eq!(mi, cx);
755
756 assert_eq!(both(&router, "/nope").await, (None, None));
758 assert_eq!(both(&router, "/api/v2/nope").await, (None, None));
759 }
760
761 #[tokio::test]
762 async fn matched_path_is_visible_mid_request() {
763 use std::cell::RefCell;
764
765 use metainfo::{METAINFO, MetaInfo};
766
767 use crate::{
768 context::ServerContext,
769 response::Response,
770 server::{
771 IntoResponse,
772 middleware::{Next, from_fn},
773 },
774 };
775
776 fn current() -> Option<String> {
777 METAINFO.with(|mi| {
778 mi.borrow()
779 .get_faststr::<MatchedPath>()
780 .map(|p| p.as_str().to_owned())
781 })
782 }
783
784 async fn echo_matched_path() -> String {
786 current().unwrap_or_default()
787 }
788
789 struct Seen {
791 start: Option<String>,
792 end: Option<String>,
793 }
794
795 async fn probe(
796 cx: &mut ServerContext,
797 req: Request<Option<Body>>,
798 next: Next<Option<Body>>,
799 ) -> Response {
800 let start = current();
801 let resp = next.run(cx, req).await.into_response();
802 cx.extensions_mut().insert(Seen {
803 start,
804 end: current(),
805 });
806 resp
807 }
808
809 async fn run(router: &Router<Option<Body>>, uri: &str) -> (String, Seen) {
810 METAINFO
811 .scope(RefCell::new(MetaInfo::default()), async {
812 let mut cx = empty_cx();
813 let req = Request::builder()
814 .method(Method::GET)
815 .uri(uri)
816 .body(None)
817 .unwrap();
818 let resp = router.call(&mut cx, req).await.unwrap();
819 let body = resp.into_body().into_string().await.unwrap();
820 let seen = cx.extensions_mut().remove::<Seen>().unwrap();
821 (body, seen)
822 })
823 .await
824 }
825
826 let router: Router<Option<Body>> = Router::new()
827 .route("/users/{id}", any(echo_matched_path))
828 .nest(
829 "/api/{version}",
830 Router::new().route("/items/{id}", any(echo_matched_path)),
831 )
832 .layer(from_fn(probe));
833
834 let (body, seen) = run(&router, "/users/42").await;
836 assert_eq!(body, "/users/{id}");
837 assert_eq!(seen.start.as_deref(), Some("/users/{id}"));
838 assert_eq!(seen.end.as_deref(), Some("/users/{id}"));
839
840 let (body, seen) = run(&router, "/api/v2/items/7").await;
843 assert_eq!(body, "/api/{version}/items/{id}");
844 assert_eq!(seen.start.as_deref(), Some("/api/{version}"));
845 assert_eq!(seen.end.as_deref(), Some("/api/{version}/items/{id}"));
846 }
847
848 #[tokio::test]
849 async fn router_fallback() {
850 async fn is_teapot(
851 server: &TestServer<Router<Option<Body>>, Option<Body>>,
852 uri: &str,
853 ) -> bool {
854 server.call_route(Method::GET, uri, None).await.status() == StatusCode::IM_A_TEAPOT
855 }
856 let router: Router<Option<Body>> = Router::new()
857 .route("/", any(always_ok))
858 .route("/catch/{id}", any(always_ok))
859 .route("/catch_all/{*all}", any(always_ok))
860 .fallback(teapot);
861 let server = Server::new(router).into_test_server();
862
863 assert!(is_teapot(&server, "//").await);
864 assert!(is_teapot(&server, "/catch/").await);
865 assert!(is_teapot(&server, "/catch_all/").await);
866
867 assert!(!is_teapot(&server, "/catch/114").await);
868 assert!(!is_teapot(&server, "/catch_all/514").await);
869 assert!(!is_teapot(&server, "/catch_all/114/514/1919/810").await);
870 }
871
872 #[tokio::test]
873 async fn nest_router() {
874 async fn uri_and_params(uri: Uri, params: PathParamsVec) -> String {
875 let mut v = vec![FastStr::from_string(uri.to_string())];
876 v.extend(params.into_iter().map(|(_, v)| v));
877 v.join("\n")
878 }
879 async fn get_res(
880 server: &TestServer<Router<Option<Body>>, Option<Body>>,
881 uri: &str,
882 ) -> String {
883 server
884 .call_route(Method::GET, uri, None)
885 .await
886 .into_string()
887 .await
888 .unwrap()
889 }
890
891 let router: Router<Option<Body>> = Router::new()
892 .nest(
893 "/test-1",
895 Router::new()
896 .route("/", any(uri_and_params))
897 .route("/id/{id}", any(uri_and_params))
898 .route("/catch/{*content}", any(uri_and_params)),
899 )
900 .nest(
901 "/test-2/",
903 Router::new()
904 .route("/", any(uri_and_params))
905 .route("/id/{id}", any(uri_and_params))
906 .route("/catch/{*content}", any(uri_and_params)),
907 )
908 .nest(
909 "/test-3/{catch}",
911 Router::new()
912 .route("/", any(uri_and_params))
913 .route("/id/{id}", any(uri_and_params))
914 .route("/catch/{*content}", any(uri_and_params)),
915 )
916 .nest(
917 "/test-4/{catch}/",
919 Router::new()
920 .route("/", any(uri_and_params))
921 .route("/id/{id}", any(uri_and_params))
922 .route("/catch/{*content}", any(uri_and_params)),
923 );
924 let server = Server::new(router).into_test_server();
925
926 assert_eq!(get_res(&server, "/test-1").await, "/");
928 assert_eq!(get_res(&server, "/test-1/").await, "/");
929 assert_eq!(get_res(&server, "/test-1/id/114").await, "/id/114\n114");
930 assert_eq!(
931 get_res(&server, "/test-1/catch/114/514/1919/810").await,
932 "/catch/114/514/1919/810\n114/514/1919/810",
933 );
934
935 assert!(get_res(&server, "/test-2").await.is_empty());
937 assert_eq!(get_res(&server, "/test-2/").await, "/");
938 assert_eq!(get_res(&server, "/test-2/id/114").await, "/id/114\n114");
939 assert_eq!(
940 get_res(&server, "/test-2/catch/114/514/1919/810").await,
941 "/catch/114/514/1919/810\n114/514/1919/810",
942 );
943
944 assert_eq!(get_res(&server, "/test-3/114").await, "/\n114");
946 assert_eq!(get_res(&server, "/test-3/114/").await, "/\n114");
947 assert_eq!(
948 get_res(&server, "/test-3/114/id/514").await,
949 "/id/514\n114\n514",
950 );
951 assert_eq!(
952 get_res(&server, "/test-3/114/catch/514/1919/810").await,
953 "/catch/514/1919/810\n114\n514/1919/810",
954 );
955
956 assert!(get_res(&server, "/test-4/114").await.is_empty());
958 assert_eq!(get_res(&server, "/test-4/114/").await, "/\n114");
959 assert_eq!(
960 get_res(&server, "/test-4/114/id/514").await,
961 "/id/514\n114\n514",
962 );
963 assert_eq!(
964 get_res(&server, "/test-4/114/catch/514/1919/810").await,
965 "/catch/514/1919/810\n114\n514/1919/810",
966 );
967 }
968
969 #[tokio::test]
970 async fn deep_nest_router() {
971 async fn uri_and_params(uri: Uri, params: PathParamsVec) -> String {
972 let mut v = vec![FastStr::from_string(uri.to_string())];
973 v.extend(params.into_iter().map(|(_, v)| v));
974 v.join("\n")
975 }
976 async fn get_res(
977 server: &TestServer<Router<Option<Body>>, Option<Body>>,
978 uri: &str,
979 ) -> String {
980 server
981 .call_route(Method::GET, uri, None)
982 .await
983 .into_string()
984 .await
985 .unwrap()
986 }
987
988 let router: Router<Option<Body>> = Router::new().nest(
990 "/test-1/{catch1}",
991 Router::new().nest(
992 "/test-2/{catch2}/",
993 Router::new().nest(
994 "/test-3",
995 Router::new()
996 .route("/", any(uri_and_params))
997 .route("/id/{id}", any(uri_and_params))
998 .route("/catch/{*content}", any(uri_and_params)),
999 ),
1000 ),
1001 );
1002 let server = Server::new(router).into_test_server();
1003
1004 assert_eq!(
1008 get_res(&server, "/test-1/114/test-2/514/test-3/").await,
1009 "/\n114\n514",
1010 );
1011 assert_eq!(
1016 get_res(&server, "/test-1/114/test-2/514/test-3/id/1919").await,
1017 "/id/1919\n114\n514\n1919",
1018 );
1019 assert_eq!(
1024 get_res(&server, "/test-1/114/test-2/514/test-3/catch/1919/810").await,
1025 "/catch/1919/810\n114\n514\n1919/810",
1026 );
1027 }
1028
1029 #[tokio::test]
1030 async fn nest_router_with_query() {
1031 async fn get_query(uri: Uri) -> Result<String, StatusCode> {
1032 if let Some(query) = uri.query() {
1033 Ok(query.to_owned())
1034 } else {
1035 Err(StatusCode::BAD_REQUEST)
1036 }
1037 }
1038 async fn get_res(
1039 server: &TestServer<Router<Option<Body>>, Option<Body>>,
1040 uri: &str,
1041 ) -> Result<String, StatusCode> {
1042 let resp = server.call_route(Method::GET, uri, None).await;
1043 if resp.status().is_success() {
1044 Ok(resp
1045 .into_string()
1046 .await
1047 .expect("response is not a valid string"))
1048 } else {
1049 Err(resp.status())
1050 }
1051 }
1052
1053 let router: Router<Option<Body>> =
1054 Router::new().nest("/nest", Router::new().route("/query", any(get_query)));
1055 let server = Server::new(router).into_test_server();
1056
1057 assert_eq!(
1058 get_res(&server, "/nest/query?foo=bar").await.unwrap(),
1059 "foo=bar",
1060 );
1061 assert_eq!(get_res(&server, "/nest/query?foo").await.unwrap(), "foo");
1062 assert_eq!(get_res(&server, "/nest/query?").await.unwrap(), "");
1063 assert!(get_res(&server, "/nest/query").await.is_err());
1064 }
1065
1066 #[tokio::test]
1067 async fn deep_nest_router_with_query() {
1068 async fn get_query(uri: Uri) -> Result<String, StatusCode> {
1069 if let Some(query) = uri.query() {
1070 Ok(query.to_owned())
1071 } else {
1072 Err(StatusCode::BAD_REQUEST)
1073 }
1074 }
1075 async fn get_res(
1076 server: &TestServer<Router<Option<Body>>, Option<Body>>,
1077 uri: &str,
1078 ) -> Result<String, StatusCode> {
1079 let resp = server.call_route(Method::GET, uri, None).await;
1080 if resp.status().is_success() {
1081 Ok(resp
1082 .into_string()
1083 .await
1084 .expect("response is not a valid string"))
1085 } else {
1086 Err(resp.status())
1087 }
1088 }
1089
1090 let router: Router<Option<Body>> = Router::new().nest(
1091 "/nest-1",
1092 Router::new().nest(
1093 "/nest-2",
1094 Router::new().nest("/nest-3", Router::new().route("/query", any(get_query))),
1095 ),
1096 );
1097 let server = Server::new(router).into_test_server();
1098
1099 assert_eq!(
1100 get_res(&server, "/nest-1/nest-2/nest-3/query?foo=bar")
1101 .await
1102 .unwrap(),
1103 "foo=bar",
1104 );
1105 assert_eq!(
1106 get_res(&server, "/nest-1/nest-2/nest-3/query?foo")
1107 .await
1108 .unwrap(),
1109 "foo",
1110 );
1111 assert_eq!(
1112 get_res(&server, "/nest-1/nest-2/nest-3/query?")
1113 .await
1114 .unwrap(),
1115 "",
1116 );
1117 assert!(
1118 get_res(&server, "/nest-1/nest-2/nest-3/query")
1119 .await
1120 .is_err()
1121 );
1122 }
1123}