1#![expect(
2 clippy::allow_attributes,
3 reason = "macro-generated `#[allow]` attributes whose underlying lints fire only for some expansions"
4)]
5
6use std::{cmp::Reverse, convert::Infallible, error::Error, fmt};
7
8use rama_core::{
9 Layer,
10 extensions::{Extensions, ExtensionsRef},
11 layer::MapResult,
12 matcher::Matcher,
13 service::{BoxService, Service},
14 telemetry::tracing,
15};
16use rama_http_types::Method;
17use rama_http_types::{Body, OriginalRouterUri, StatusCode};
18use rama_net::uri::{
19 PathPattern, PathPatternSegmentKind, PathPatternSegmentSpecificity, PathRouter,
20};
21use rama_utils::collections::NonEmptySmallVec;
22
23use crate::{
24 Request, Response,
25 headers::Allow,
26 matcher::path::{compile_pattern, match_pattern},
27 matcher::{HttpMatcher, MethodMatcher, UriParams},
28 service::web::{
29 IntoEndpointService, IntoEndpointServiceWithState,
30 response::{ErrorResponse, Headers, IntoResponse},
31 },
32};
33
34#[derive(Debug, Clone, Default)]
41#[non_exhaustive]
42pub struct DefaultEndpointLayer;
43
44impl<S> Layer<S> for DefaultEndpointLayer
45where
46 S: Service<Request, Output: IntoResponse, Error: Into<ErrorResponse>>,
47{
48 type Service = MapResult<S, fn(Result<S::Output, S::Error>) -> Result<Response, RouterError>>;
49
50 fn layer(&self, inner: S) -> Self::Service {
51 MapResult::new(inner, |res| match res {
52 Ok(v) => Ok(v.into_response()),
53 Err(err) => Ok(err.into().into_response()),
54 })
55 }
56}
57
58#[allow(unused)]
64pub struct Router<State = (), Layer = DefaultEndpointLayer, O = Response, E = RouterError> {
65 routes: Vec<RouteEntry<O, E>>,
66 sub_services: Option<PathRouter<SubService<O, E>>>,
67 not_found: Option<BoxService<Request, O, E>>,
68 layer: Layer,
69 state: State,
70}
71
72struct RouteEntry<O, E> {
76 pattern: PathPattern,
77 specificity: Vec<SegmentSpecificityRank>,
80 handlers: Vec<(HttpMatcher<Body>, BoxService<Request, O, E>)>,
81}
82
83#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
87struct SegmentSpecificityRank {
88 kind: u8,
89 literal_bytes: usize,
90 fewer_dynamic_parts: Reverse<usize>,
91 fewer_optional_parts: Reverse<usize>,
92}
93
94fn rank(spec: PathPatternSegmentSpecificity) -> SegmentSpecificityRank {
95 let kind = match spec.kind {
96 PathPatternSegmentKind::Literal => 2,
97 PathPatternSegmentKind::Dynamic => 1,
98 PathPatternSegmentKind::CatchAll => 0,
99 };
100 SegmentSpecificityRank {
101 kind,
102 literal_bytes: spec.literal_bytes,
103 fewer_dynamic_parts: Reverse(spec.dynamic_parts),
104 fewer_optional_parts: Reverse(spec.optional_parts),
105 }
106}
107
108fn specificity_of(pattern: &PathPattern) -> Vec<SegmentSpecificityRank> {
111 pattern.segment_specificity().map(rank).collect()
112}
113
114impl<S, L, O, E> std::fmt::Debug for Router<S, L, O, E> {
115 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
116 f.debug_struct("Router").finish()
117 }
118}
119
120impl<O, E> Router<(), DefaultEndpointLayer, O, E> {
121 #[must_use]
123 pub fn new() -> Self {
124 Self::new_with_state(())
125 }
126}
127
128impl<State, O, E> Router<State, DefaultEndpointLayer, O, E>
129where
130 State: Send + Sync + Clone + 'static,
131{
132 #[must_use]
133 pub fn new_with_state(state: State) -> Self {
135 Self {
136 routes: Vec::new(),
137 sub_services: None,
138 not_found: None,
139 layer: Default::default(),
140 state,
141 }
142 }
143}
144
145impl<State, L, O, E> Router<State, L, O, E>
146where
147 State: Send + Sync + Clone + 'static,
148{
149 #[inline]
151 pub fn state(&self) -> &State {
152 &self.state
153 }
154
155 pub fn with_endpoint_layer<N>(self, layer: N) -> Router<State, N, O, E> {
159 Router {
160 routes: self.routes,
161 sub_services: self.sub_services,
162 not_found: self.not_found,
163 layer,
164 state: self.state,
165 }
166 }
167
168 pub fn with_default_endpoint_layer(self) -> Router<State, DefaultEndpointLayer, O, E> {
172 Router {
173 routes: self.routes,
174 sub_services: self.sub_services,
175 not_found: self.not_found,
176 layer: DefaultEndpointLayer,
177 state: self.state,
178 }
179 }
180
181 #[must_use]
185 #[inline]
186 pub fn with_get<I, T>(self, path: impl AsRef<str>, service: I) -> Self
187 where
188 I: IntoEndpointServiceWithState<T, State>,
189 L: Layer<I::Service, Service: Service<Request, Output = O, Error = E>>,
190 {
191 let matcher = HttpMatcher::method_get();
192 self.with_match_route(path, matcher, service)
193 }
194
195 #[inline]
199 pub fn set_get<I, T>(&mut self, path: impl AsRef<str>, service: I) -> &mut Self
200 where
201 I: IntoEndpointServiceWithState<T, State>,
202 L: Layer<I::Service, Service: Service<Request, Output = O, Error = E>>,
203 {
204 let matcher = HttpMatcher::method_get();
205 self.set_match_route(path, matcher, service)
206 }
207
208 #[must_use]
210 #[inline]
211 pub fn with_post<I, T>(self, path: impl AsRef<str>, service: I) -> Self
212 where
213 I: IntoEndpointServiceWithState<T, State>,
214 L: Layer<I::Service, Service: Service<Request, Output = O, Error = E>>,
215 {
216 let matcher = HttpMatcher::method_post();
217 self.with_match_route(path, matcher, service)
218 }
219
220 #[inline]
222 pub fn set_post<I, T>(&mut self, path: impl AsRef<str>, service: I) -> &mut Self
223 where
224 I: IntoEndpointServiceWithState<T, State>,
225 L: Layer<I::Service, Service: Service<Request, Output = O, Error = E>>,
226 {
227 let matcher = HttpMatcher::method_post();
228 self.set_match_route(path, matcher, service)
229 }
230
231 #[must_use]
233 #[inline]
234 pub fn with_put<I, T>(self, path: impl AsRef<str>, service: I) -> Self
235 where
236 I: IntoEndpointServiceWithState<T, State>,
237 L: Layer<I::Service, Service: Service<Request, Output = O, Error = E>>,
238 {
239 let matcher = HttpMatcher::method_put();
240 self.with_match_route(path, matcher, service)
241 }
242
243 #[inline]
245 pub fn set_put<I, T>(&mut self, path: impl AsRef<str>, service: I) -> &mut Self
246 where
247 I: IntoEndpointServiceWithState<T, State>,
248 L: Layer<I::Service, Service: Service<Request, Output = O, Error = E>>,
249 {
250 let matcher = HttpMatcher::method_put();
251 self.set_match_route(path, matcher, service)
252 }
253
254 #[must_use]
256 #[inline]
257 pub fn with_delete<I, T>(self, path: impl AsRef<str>, service: I) -> Self
258 where
259 I: IntoEndpointServiceWithState<T, State>,
260 L: Layer<I::Service, Service: Service<Request, Output = O, Error = E>>,
261 {
262 let matcher = HttpMatcher::method_delete();
263 self.with_match_route(path, matcher, service)
264 }
265
266 #[inline]
268 pub fn set_delete<I, T>(&mut self, path: impl AsRef<str>, service: I) -> &mut Self
269 where
270 I: IntoEndpointServiceWithState<T, State>,
271 L: Layer<I::Service, Service: Service<Request, Output = O, Error = E>>,
272 {
273 let matcher = HttpMatcher::method_delete();
274 self.set_match_route(path, matcher, service)
275 }
276
277 #[must_use]
279 #[inline]
280 pub fn with_patch<I, T>(self, path: impl AsRef<str>, service: I) -> Self
281 where
282 I: IntoEndpointServiceWithState<T, State>,
283 L: Layer<I::Service, Service: Service<Request, Output = O, Error = E>>,
284 {
285 let matcher = HttpMatcher::method_patch();
286 self.with_match_route(path, matcher, service)
287 }
288
289 #[inline]
291 pub fn set_patch<I, T>(&mut self, path: impl AsRef<str>, service: I) -> &mut Self
292 where
293 I: IntoEndpointServiceWithState<T, State>,
294 L: Layer<I::Service, Service: Service<Request, Output = O, Error = E>>,
295 {
296 let matcher = HttpMatcher::method_patch();
297 self.set_match_route(path, matcher, service)
298 }
299
300 #[must_use]
302 #[inline]
303 pub fn with_head<I, T>(self, path: impl AsRef<str>, service: I) -> Self
304 where
305 I: IntoEndpointServiceWithState<T, State>,
306 L: Layer<I::Service, Service: Service<Request, Output = O, Error = E>>,
307 {
308 let matcher = HttpMatcher::method_head();
309 self.with_match_route(path, matcher, service)
310 }
311
312 #[inline]
314 pub fn set_head<I, T>(&mut self, path: impl AsRef<str>, service: I) -> &mut Self
315 where
316 I: IntoEndpointServiceWithState<T, State>,
317 L: Layer<I::Service, Service: Service<Request, Output = O, Error = E>>,
318 {
319 let matcher = HttpMatcher::method_head();
320 self.set_match_route(path, matcher, service)
321 }
322
323 #[must_use]
325 #[inline]
326 pub fn with_options<I, T>(self, path: impl AsRef<str>, service: I) -> Self
327 where
328 I: IntoEndpointServiceWithState<T, State>,
329 L: Layer<I::Service, Service: Service<Request, Output = O, Error = E>>,
330 {
331 let matcher = HttpMatcher::method_options();
332 self.with_match_route(path, matcher, service)
333 }
334
335 #[inline]
337 pub fn set_options<I, T>(&mut self, path: impl AsRef<str>, service: I) -> &mut Self
338 where
339 I: IntoEndpointServiceWithState<T, State>,
340 L: Layer<I::Service, Service: Service<Request, Output = O, Error = E>>,
341 {
342 let matcher = HttpMatcher::method_options();
343 self.set_match_route(path, matcher, service)
344 }
345
346 #[must_use]
348 #[inline]
349 pub fn with_trace<I, T>(self, path: impl AsRef<str>, service: I) -> Self
350 where
351 I: IntoEndpointServiceWithState<T, State>,
352 L: Layer<I::Service, Service: Service<Request, Output = O, Error = E>>,
353 {
354 let matcher = HttpMatcher::method_trace();
355 self.with_match_route(path, matcher, service)
356 }
357
358 #[inline]
360 pub fn set_trace<I, T>(&mut self, path: impl AsRef<str>, service: I) -> &mut Self
361 where
362 I: IntoEndpointServiceWithState<T, State>,
363 L: Layer<I::Service, Service: Service<Request, Output = O, Error = E>>,
364 {
365 let matcher = HttpMatcher::method_trace();
366 self.set_match_route(path, matcher, service)
367 }
368
369 #[must_use]
371 #[inline]
372 pub fn with_connect<I, T>(self, path: impl AsRef<str>, service: I) -> Self
373 where
374 I: IntoEndpointServiceWithState<T, State>,
375 L: Layer<I::Service, Service: Service<Request, Output = O, Error = E>>,
376 {
377 let matcher = HttpMatcher::method_connect();
378 self.with_match_route(path, matcher, service)
379 }
380
381 #[inline]
383 pub fn set_connect<I, T>(&mut self, path: impl AsRef<str>, service: I) -> &mut Self
384 where
385 I: IntoEndpointServiceWithState<T, State>,
386 L: Layer<I::Service, Service: Service<Request, Output = O, Error = E>>,
387 {
388 let matcher = HttpMatcher::method_connect();
389 self.set_match_route(path, matcher, service)
390 }
391
392 #[must_use]
397 #[inline]
398 pub fn with_query<I, T>(self, path: impl AsRef<str>, service: I) -> Self
399 where
400 I: IntoEndpointServiceWithState<T, State>,
401 L: Layer<I::Service, Service: Service<Request, Output = O, Error = E>>,
402 {
403 let matcher = HttpMatcher::method_query();
404 self.with_match_route(path, matcher, service)
405 }
406
407 #[inline]
409 pub fn set_query<I, T>(&mut self, path: impl AsRef<str>, service: I) -> &mut Self
410 where
411 I: IntoEndpointServiceWithState<T, State>,
412 L: Layer<I::Service, Service: Service<Request, Output = O, Error = E>>,
413 {
414 let matcher = HttpMatcher::method_query();
415 self.set_match_route(path, matcher, service)
416 }
417
418 #[must_use]
424 #[inline]
425 pub fn with_sub_router_make_fn<Layer>(
426 mut self,
427 prefix: impl AsRef<str>,
428 configure_router: impl FnOnce(Self) -> Router<State, Layer, O, E>,
429 ) -> Self
430 where
431 L: Clone,
432 Router<State, Layer, O, E>: Service<Request, Output = O, Error = E>,
433 {
434 self.set_sub_router_make_fn(prefix, configure_router);
435 self
436 }
437
438 pub fn set_sub_router_make_fn<Layer>(
444 &mut self,
445 prefix: impl AsRef<str>,
446 configure_router: impl FnOnce(Self) -> Router<State, Layer, O, E>,
447 ) -> &mut Self
448 where
449 L: Clone,
450 Router<State, Layer, O, E>: Service<Request, Output = O, Error = E>,
451 {
452 let router = Self {
453 routes: Vec::new(),
454 sub_services: None,
455 not_found: None,
456 layer: self.layer.clone(),
457 state: self.state.clone(),
458 };
459 let router = configure_router(router);
460 let nested = router.boxed();
461 self.set_sub_service_inner(prefix, nested)
462 }
463
464 #[must_use]
473 #[inline]
474 pub fn with_endpoint_service<I, T>(mut self, prefix: impl AsRef<str>, service: I) -> Self
475 where
476 I: IntoEndpointService<T>,
477 L: Layer<I::Service, Service: Service<Request, Output = O, Error = E>>,
478 {
479 self.set_endpoint_service(prefix, service);
480 self
481 }
482
483 #[inline]
492 pub fn set_endpoint_service<I, T>(&mut self, prefix: impl AsRef<str>, service: I) -> &mut Self
493 where
494 I: IntoEndpointService<T>,
495 L: Layer<I::Service, Service: Service<Request, Output = O, Error = E>>,
496 {
497 let nested = self.layer.layer(service.into_endpoint_service()).boxed();
498 self.set_sub_service_inner(prefix, nested)
499 }
500
501 #[must_use]
510 #[inline]
511 pub fn with_sub_service<S>(mut self, prefix: impl AsRef<str>, service: S) -> Self
512 where
513 S: Service<Request, Output = O, Error = E>,
514 {
515 self.set_sub_service(prefix, service);
516 self
517 }
518
519 #[inline]
528 pub fn set_sub_service<S>(&mut self, prefix: impl AsRef<str>, service: S) -> &mut Self
529 where
530 S: Service<Request, Output = O, Error = E>,
531 {
532 let nested = service.into_endpoint_service().boxed();
533 self.set_sub_service_inner(prefix, nested)
534 }
535
536 fn set_sub_service_inner(
537 &mut self,
538 prefix: impl AsRef<str>,
539 nested: BoxService<Request, O, E>,
540 ) -> &mut Self {
541 let router = self.sub_services.get_or_insert_default();
542 router.insert_prefix_with_opts(
543 prefix.as_ref().trim(),
544 crate::matcher::path::HTTP_PATH_OPTS,
545 SubService { svc: nested },
546 );
547
548 self
549 }
550
551 #[inline(always)]
553 #[must_use]
554 pub fn with_match_route<I, T>(
555 mut self,
556 path: impl AsRef<str>,
557 matcher: HttpMatcher<Body>,
558 service: I,
559 ) -> Self
560 where
561 I: IntoEndpointServiceWithState<T, State>,
562 L: Layer<I::Service, Service: Service<Request, Output = O, Error = E>>,
563 {
564 self.set_match_route(path, matcher, service);
565 self
566 }
567
568 pub fn set_match_route<I, T>(
570 &mut self,
571 path: impl AsRef<str>,
572 matcher: HttpMatcher<Body>,
573 service: I,
574 ) -> &mut Self
575 where
576 I: IntoEndpointServiceWithState<T, State>,
577 L: Layer<I::Service, Service: Service<Request, Output = O, Error = E>>,
578 {
579 let service = self
580 .layer
581 .layer(service.into_endpoint_service_with_state(self.state.clone()))
582 .boxed();
583
584 let pattern = compile_pattern(path.as_ref());
585
586 if let Some(entry) = self.routes.iter_mut().find(|e| e.pattern == pattern) {
587 entry.handlers.push((matcher, service));
588 } else {
589 let specificity = specificity_of(&pattern);
590 let pos = self
592 .routes
593 .partition_point(|e| e.specificity >= specificity);
594 self.routes.insert(
595 pos,
596 RouteEntry {
597 pattern,
598 specificity,
599 handlers: vec![(matcher, service)],
600 },
601 );
602 }
603
604 self
605 }
606
607 #[inline(always)]
609 #[must_use]
610 pub fn with_not_found<I, T>(mut self, service: I) -> Self
611 where
612 I: IntoEndpointServiceWithState<T, State>,
613 L: Layer<I::Service, Service: Service<Request, Output = O, Error = E>>,
614 {
615 self.set_not_found(service);
616 self
617 }
618
619 pub fn set_not_found<I, T>(&mut self, service: I) -> &mut Self
621 where
622 I: IntoEndpointServiceWithState<T, State>,
623 L: Layer<I::Service, Service: Service<Request, Output = O, Error = E>>,
624 {
625 self.not_found = Some(
626 self.layer
627 .layer(service.into_endpoint_service_with_state(self.state.clone()))
628 .boxed(),
629 );
630 self
631 }
632}
633
634impl Default for Router {
635 fn default() -> Self {
636 Self::new()
637 }
638}
639
640#[derive(Debug, Clone)]
641pub enum RouterError {
642 Internal,
644 MethodNotAllowed(Box<NonEmptySmallVec<7, Method>>),
645 NotFound,
646}
647
648impl fmt::Display for RouterError {
649 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
650 fmt::Debug::fmt(self, f)
651 }
652}
653
654impl Error for RouterError {}
655
656impl IntoResponse for RouterError {
657 fn into_response(self) -> Response {
658 match self {
659 Self::Internal => StatusCode::INTERNAL_SERVER_ERROR.into_response(),
660 Self::MethodNotAllowed(allowed) => (
661 Headers::single(Allow(*allowed)),
662 StatusCode::METHOD_NOT_ALLOWED,
663 )
664 .into_response(),
665 Self::NotFound => StatusCode::NOT_FOUND.into_response(),
666 }
667 }
668}
669
670impl From<Infallible> for RouterError {
671 fn from(infallible: Infallible) -> Self {
672 match infallible {}
673 }
674}
675
676struct SubService<O, E> {
677 svc: BoxService<Request, O, E>,
678}
679
680impl<State, L, O, E> Service<Request> for Router<State, L, O, E>
681where
682 O: Send + 'static,
683 E: Send + From<RouterError> + 'static,
684 L: Send + Sync + 'static,
685 State: Send + Sync + Clone + 'static,
686{
687 type Output = O;
688 type Error = E;
689
690 async fn serve(&self, req: Request) -> Result<Self::Output, Self::Error> {
691 let path = req.uri().path_ref_or_root();
692
693 let mut allowed_methods: Option<MethodMatcher> = None;
697
698 for entry in self.routes.iter() {
701 let ext = Extensions::new();
702 if !match_pattern(&entry.pattern, Some(&ext), path) {
703 continue;
704 }
705
706 let captured = ext.get_ref::<UriParams>().cloned().unwrap_or_default();
708 let params = match req.extensions().get_ref::<UriParams>() {
709 Some(existing) => {
710 let mut params = existing.clone();
711 params.extend(captured.iter());
712 params
713 }
714 None => captured,
715 };
716 req.extensions().insert(params);
717
718 for (matcher, service) in entry.handlers.iter() {
719 let mext = Extensions::new();
720 if matcher.matches(Some(&mext), &req) {
721 req.extensions().extend(&mext);
722 return service.serve(req).await;
723 }
724 }
725
726 for (matcher, _) in entry.handlers.iter() {
729 if let Some(m) = matcher.allowed_methods() {
730 allowed_methods = Some(allowed_methods.map_or(m, |acc| acc.or_method(m)));
731 }
732 }
733 break;
734 }
735
736 let (mut parts, body) = req.into_parts();
737
738 let sub_match = self.sub_services.as_ref().and_then(|router| {
739 router
740 .match_prefix(parts.uri.path_ref_or_root())
741 .map(|matched| {
742 let (sub_svc, matched_segment_count, captures) = matched.into_parts();
743 (
744 sub_svc,
745 matched_segment_count,
746 UriParams::from_captures(&captures),
747 )
748 })
749 });
750
751 if let Some((sub_svc, matched_segment_count, captured)) = sub_match {
752 let mut modified_uri = parts.uri.clone();
753 if !modified_uri
754 .path_mut()
755 .strip_prefix_segments(matched_segment_count)
756 {
757 tracing::warn!(
758 "failed to strip {matched_segment_count} matched path segments from Uri (bug??)",
759 );
760 return Err(RouterError::Internal.into());
761 }
762
763 if !captured.is_empty() {
764 let params = match parts.extensions.get_ref::<UriParams>() {
765 Some(existing) => {
766 let mut params = existing.clone();
767 params.extend(captured.iter());
768 params
769 }
770 None => captured,
771 };
772 parts.extensions.insert(params);
773 }
774
775 if !parts.extensions.contains::<OriginalRouterUri>() {
776 parts.extensions.insert(OriginalRouterUri(parts.uri));
777 }
778 parts.uri = modified_uri;
779
780 tracing::trace!(
781 "svc request using sub service of router with {matched_segment_count} matched path segments removed from path; new uri: {}",
782 parts.uri,
783 );
784 let req = Request::from_parts(parts, body);
785 return sub_svc.svc.serve(req).await;
786 }
787
788 if let Some(matcher) = allowed_methods
791 && let Some(methods) = NonEmptySmallVec::collect(matcher.iter())
792 {
793 return Err(RouterError::MethodNotAllowed(Box::new(methods)).into());
794 }
795
796 if let Some(not_found) = &self.not_found {
797 let req = Request::from_parts(parts, body);
798 not_found.serve(req).await
799 } else {
800 Err(RouterError::NotFound.into())
801 }
802 }
803}
804
805#[cfg(test)]
806mod tests {
807 use rama_core::{extensions::ExtensionsRef, service::service_fn};
808 use rama_http_types::{Body, Method, Request, StatusCode, body::util::BodyExt, header};
809
810 use super::*;
811 use crate::{
812 layer::error_handling::ErrorHandlerLayer, matcher::UriParams, service::web::extract::State,
813 };
814
815 fn root_service() -> impl Service<Request, Output = Response, Error = Infallible> {
816 service_fn(|_req| async {
817 Ok(Response::builder()
818 .status(200)
819 .body(Body::from("Hello, World!"))
820 .unwrap())
821 })
822 }
823
824 fn create_user_service() -> impl Service<Request, Output = Response, Error = Infallible> {
825 service_fn(|_req| async {
826 Ok(Response::builder()
827 .status(200)
828 .body(Body::from("Create User"))
829 .unwrap())
830 })
831 }
832
833 fn get_users_service() -> impl Service<Request, Output = Response, Error = Infallible> {
834 service_fn(|_req| async {
835 Ok(Response::builder()
836 .status(200)
837 .body(Body::from("List Users"))
838 .unwrap())
839 })
840 }
841
842 fn get_user_service() -> impl Service<Request, Output = Response, Error = Infallible> {
843 service_fn(|req: Request| async move {
844 let uri_params = req.extensions().get_ref::<UriParams>().unwrap();
845 let id = uri_params.get("user_id").unwrap();
846 Ok(Response::builder()
847 .status(200)
848 .body(Body::from(format!("Get User: {id}")))
849 .unwrap())
850 })
851 }
852
853 fn delete_user_service() -> impl Service<Request, Output = Response, Error = Infallible> {
854 service_fn(|req: Request| async move {
855 let uri_params = req.extensions().get_ref::<UriParams>().unwrap();
856 let id = uri_params.get("user_id").unwrap();
857 Ok(Response::builder()
858 .status(200)
859 .body(Body::from(format!("Delete User: {id}")))
860 .unwrap())
861 })
862 }
863
864 fn serve_assets_service() -> impl Service<Request, Output = Response, Error = Infallible> {
865 service_fn(|req: Request| async move {
866 let uri_params = req.extensions().get_ref::<UriParams>().unwrap();
867 let path = uri_params.get("path").unwrap();
868 Ok(Response::builder()
869 .status(200)
870 .body(Body::from(format!("Serve Assets: /{path}")))
871 .unwrap())
872 })
873 }
874
875 fn not_found_service() -> impl Service<Request, Output = Response, Error = Infallible> {
876 service_fn(|_req| async {
877 Ok(Response::builder()
878 .status(StatusCode::NOT_FOUND)
879 .body(Body::from("Not Found"))
880 .unwrap())
881 })
882 }
883
884 fn get_user_order_service() -> impl Service<Request, Output = Response, Error = Infallible> {
885 service_fn(|req: Request| async move {
886 let uri_params = req.extensions().get_ref::<UriParams>().unwrap();
887 let user_id = uri_params.get("user_id").unwrap();
888 let order_id = uri_params.get("order_id").unwrap();
889 Ok(Response::builder()
890 .status(200)
891 .body(Body::from(format!(
892 "Get Order: {order_id} for User: {user_id}",
893 )))
894 .unwrap())
895 })
896 }
897
898 fn query_echo_service() -> impl Service<Request, Output = Response, Error = Infallible> {
900 service_fn(|req: Request| async move {
901 let body = req.into_body().collect().await.unwrap().to_bytes();
902 Ok(Response::builder()
903 .status(200)
904 .body(Body::from(format!(
905 "query: {}",
906 String::from_utf8_lossy(&body)
907 )))
908 .unwrap())
909 })
910 }
911
912 #[tokio::test]
913 async fn test_router() {
914 let cases = vec![
915 (Method::GET, "/", "Hello, World!", StatusCode::OK),
916 (Method::GET, "/users", "List Users", StatusCode::OK),
917 (Method::POST, "/users", "Create User", StatusCode::OK),
918 (Method::GET, "/users/123", "Get User: 123", StatusCode::OK),
919 (
920 Method::DELETE,
921 "/users/123",
922 "Delete User: 123",
923 StatusCode::OK,
924 ),
925 (
926 Method::GET,
927 "/users/123/orders/456",
928 "Get Order: 456 for User: 123",
929 StatusCode::OK,
930 ),
931 (
932 Method::PUT,
933 "/users/123",
934 "",
935 StatusCode::METHOD_NOT_ALLOWED,
936 ),
937 (
938 Method::GET,
939 "/assets/css/style.css",
940 "Serve Assets: /css/style.css",
941 StatusCode::OK,
942 ),
943 (
944 Method::GET,
945 "/not-found",
946 "Not Found",
947 StatusCode::NOT_FOUND,
948 ),
949 ];
950
951 for prefix in ["/", ""] {
952 let router = Router::new()
953 .with_get(prefix, root_service())
954 .with_get(format!("{prefix}users"), get_users_service())
955 .with_post(format!("{prefix}users"), create_user_service())
956 .with_get(format!("{prefix}users/{{user_id}}"), get_user_service())
957 .with_delete(format!("{prefix}users/{{user_id}}"), delete_user_service())
958 .with_get(
959 format!("{prefix}users/{{user_id}}/orders/{{order_id}}"),
960 get_user_order_service(),
961 )
962 .with_get(format!("{prefix}assets/{{*path}}"), serve_assets_service())
963 .with_not_found(not_found_service());
964
965 let router = ErrorHandlerLayer::new().layer(router);
966
967 for (method, path, expected_body, expected_status) in cases.iter() {
968 let req = match *method {
969 Method::GET => Request::get(*path),
970 Method::POST => Request::post(*path),
971 Method::PUT => Request::put(*path),
972 Method::DELETE => Request::delete(*path),
973 _ => panic!("Unsupported HTTP method"),
974 }
975 .body(Body::empty())
976 .unwrap();
977
978 let res = router.serve(req).await.unwrap();
979 assert_eq!(
980 res.status(),
981 *expected_status,
982 "method: {method} ; path = {path}; prefix = {prefix}"
983 );
984 let body = res.into_body().collect().await.unwrap().to_bytes();
985 assert_eq!(
986 body, expected_body,
987 "method: {method} ; path = {path}; prefix = {prefix}"
988 );
989 }
990 }
991 }
992
993 #[tokio::test]
994 async fn test_router_query_method() {
995 let router = Router::new()
996 .with_query("/search", query_echo_service())
997 .with_get("/search", get_users_service())
998 .with_not_found(not_found_service());
999
1000 let router = ErrorHandlerLayer::new().layer(router);
1001
1002 let req = Request::query("/search")
1004 .header(header::CONTENT_TYPE, "application/sql")
1005 .body(Body::from("SELECT 1"))
1006 .unwrap();
1007 let res = router.serve(req).await.unwrap();
1008 assert_eq!(res.status(), StatusCode::OK);
1009 let body = res.into_body().collect().await.unwrap().to_bytes();
1010 assert_eq!(body, "query: SELECT 1");
1011
1012 let req = Request::get("/search").body(Body::empty()).unwrap();
1014 let res = router.serve(req).await.unwrap();
1015 assert_eq!(res.status(), StatusCode::OK);
1016 let body = res.into_body().collect().await.unwrap().to_bytes();
1017 assert_eq!(body, "List Users");
1018
1019 let req = Request::post("/search").body(Body::empty()).unwrap();
1021 let res = router.serve(req).await.unwrap();
1022 assert_eq!(res.status(), StatusCode::METHOD_NOT_ALLOWED);
1023 assert_eq!(
1024 res.headers()
1025 .get(header::ALLOW)
1026 .expect("Allow header must be present on 405")
1027 .to_str()
1028 .unwrap(),
1029 "GET, QUERY"
1030 );
1031 }
1032
1033 #[tokio::test]
1034 async fn test_router_merges_case_insensitive_pattern_registrations() {
1035 let router = Router::new()
1036 .with_get("/Users/{user_id}", get_user_service())
1037 .with_post("/users/{user_id}", create_user_service());
1038
1039 let router = ErrorHandlerLayer::new().layer(router);
1040
1041 let req = Request::post("/USERS/123").body(Body::empty()).unwrap();
1042 let res = router.serve(req).await.unwrap();
1043 assert_eq!(res.status(), StatusCode::OK);
1044 let body = res.into_body().collect().await.unwrap().to_bytes();
1045 assert_eq!(body, "Create User");
1046
1047 let req = Request::put("/users/123").body(Body::empty()).unwrap();
1048 let res = router.serve(req).await.unwrap();
1049 assert_eq!(res.status(), StatusCode::METHOD_NOT_ALLOWED);
1050 assert_eq!(
1051 res.headers().get(header::ALLOW).unwrap().to_str().unwrap(),
1052 "GET, POST"
1053 );
1054 }
1055
1056 #[tokio::test]
1057 async fn test_router_method_not_allowed() {
1058 let router = Router::new()
1059 .with_get("/users", get_users_service())
1060 .with_post("/users", create_user_service())
1061 .with_get("/users/{user_id}", get_user_service())
1062 .with_delete("/users/{user_id}", delete_user_service())
1063 .with_not_found(not_found_service());
1064
1065 let router = ErrorHandlerLayer::new().layer(router);
1066
1067 let req = Request::put("/users/123").body(Body::empty()).unwrap();
1069 let res = router.serve(req).await.unwrap();
1070 assert_eq!(res.status(), StatusCode::METHOD_NOT_ALLOWED);
1071 assert_eq!(
1072 res.headers()
1073 .get(header::ALLOW)
1074 .expect("Allow header must be present on 405")
1075 .to_str()
1076 .unwrap(),
1077 "DELETE, GET"
1078 );
1079 let body = res.into_body().collect().await.unwrap().to_bytes();
1080 assert!(
1081 body.is_empty(),
1082 "405 body must be empty, not from not_found service"
1083 );
1084
1085 let req = Request::delete("/users").body(Body::empty()).unwrap();
1087 let res = router.serve(req).await.unwrap();
1088 assert_eq!(res.status(), StatusCode::METHOD_NOT_ALLOWED);
1089 assert_eq!(
1090 res.headers().get(header::ALLOW).unwrap().to_str().unwrap(),
1091 "GET, POST"
1092 );
1093
1094 let req = Request::get("/nonexistent").body(Body::empty()).unwrap();
1096 let res = router.serve(req).await.unwrap();
1097 assert_eq!(res.status(), StatusCode::NOT_FOUND);
1098 assert!(
1099 res.headers().get(header::ALLOW).is_none(),
1100 "404 must not carry Allow header"
1101 );
1102 }
1103
1104 #[tokio::test]
1105 async fn test_router_method_not_allowed_no_not_found_service() {
1106 let router = Router::new()
1108 .with_get("/users/{user_id}", get_user_service())
1109 .with_delete("/users/{user_id}", delete_user_service());
1110
1111 let router = ErrorHandlerLayer::new().layer(router);
1112
1113 let req = Request::put("/users/123").body(Body::empty()).unwrap();
1114 let res = router.serve(req).await.unwrap();
1115 assert_eq!(res.status(), StatusCode::METHOD_NOT_ALLOWED);
1116 assert_eq!(
1117 res.headers().get(header::ALLOW).unwrap().to_str().unwrap(),
1118 "DELETE, GET"
1119 );
1120
1121 let req = Request::get("/nonexistent").body(Body::empty()).unwrap();
1123 let res = router.serve(req).await.unwrap();
1124 assert_eq!(res.status(), StatusCode::NOT_FOUND);
1125 assert!(res.headers().get(header::ALLOW).is_none());
1126 }
1127
1128 #[test]
1129 fn specificity_reflects_segment_kinds() {
1130 let spec = |p: &str| {
1131 specificity_of(&compile_pattern(p))
1132 .into_iter()
1133 .map(|rank| rank.kind)
1134 .collect::<Vec<_>>()
1135 };
1136 assert_eq!(spec("/a/b/c"), vec![2, 2, 2]); assert_eq!(spec("/users/{id}"), vec![2, 1]); assert_eq!(spec("/files/{}.json"), vec![2, 1]); assert_eq!(spec("/assets/{*path}"), vec![2, 0]); assert_eq!(spec("/api/{*bad name}"), vec![2, 2]);
1143 }
1144
1145 #[test]
1146 fn specificity_breaks_dynamic_ties_with_literal_weight() {
1147 let plain = specificity_of(&compile_pattern("/files/{name}"));
1148 let json = specificity_of(&compile_pattern("/files/{name}.json"));
1149 let wildcard_json = specificity_of(&compile_pattern("/files/{}.json"));
1150
1151 assert!(json > plain);
1152 assert!(wildcard_json > plain);
1153 }
1154
1155 #[tokio::test]
1156 async fn test_router_capture_beats_catch_all_either_order() {
1157 fn name_svc() -> impl Service<Request, Output = Response, Error = Infallible> {
1158 service_fn(|req: Request| async move {
1159 let p = req.extensions().get_ref::<UriParams>().unwrap();
1160 Ok(Response::builder()
1161 .status(200)
1162 .body(Body::from(format!("name:{}", p.get("name").unwrap())))
1163 .unwrap())
1164 })
1165 }
1166 fn rest_svc() -> impl Service<Request, Output = Response, Error = Infallible> {
1167 service_fn(|req: Request| async move {
1168 let p = req.extensions().get_ref::<UriParams>().unwrap();
1169 Ok(Response::builder()
1170 .status(200)
1171 .body(Body::from(format!("rest:{}", p.get("rest").unwrap())))
1172 .unwrap())
1173 })
1174 }
1175
1176 let routers = [
1179 Router::new()
1180 .with_get("/files/{name}", name_svc())
1181 .with_get("/files/{*rest}", rest_svc()),
1182 Router::new()
1183 .with_get("/files/{*rest}", rest_svc())
1184 .with_get("/files/{name}", name_svc()),
1185 ];
1186 for router in routers {
1187 let router = ErrorHandlerLayer::new().layer(router);
1188 let req = Request::get("/files/x").body(Body::empty()).unwrap();
1189 let res = router.serve(req).await.unwrap();
1190 let body = res.into_body().collect().await.unwrap().to_bytes();
1191 assert_eq!(body, "name:x");
1192 }
1193
1194 let router = ErrorHandlerLayer::new().layer(
1196 Router::new()
1197 .with_get("/files/{name}", name_svc())
1198 .with_get("/files/{*rest}", rest_svc()),
1199 );
1200 let req = Request::get("/files/a/b").body(Body::empty()).unwrap();
1201 let res = router.serve(req).await.unwrap();
1202 let body = res.into_body().collect().await.unwrap().to_bytes();
1203 assert_eq!(body, "rest:a/b");
1204 }
1205
1206 #[tokio::test]
1207 async fn test_router_affixed_dynamic_beats_plain_dynamic_either_order() {
1208 fn plain_svc() -> impl Service<Request, Output = Response, Error = Infallible> {
1209 service_fn(|req: Request| async move {
1210 let p = req.extensions().get_ref::<UriParams>().unwrap();
1211 Ok(Response::builder()
1212 .status(200)
1213 .body(Body::from(format!("plain:{}", p.get("name").unwrap())))
1214 .unwrap())
1215 })
1216 }
1217 fn json_svc() -> impl Service<Request, Output = Response, Error = Infallible> {
1218 service_fn(|req: Request| async move {
1219 let p = req.extensions().get_ref::<UriParams>().unwrap();
1220 Ok(Response::builder()
1221 .status(200)
1222 .body(Body::from(format!("json:{}", p.get("name").unwrap())))
1223 .unwrap())
1224 })
1225 }
1226
1227 let routers = [
1228 Router::new()
1229 .with_get("/files/{name}", plain_svc())
1230 .with_get("/files/{name}.json", json_svc()),
1231 Router::new()
1232 .with_get("/files/{name}.json", json_svc())
1233 .with_get("/files/{name}", plain_svc()),
1234 ];
1235 for router in routers {
1236 let router = ErrorHandlerLayer::new().layer(router);
1237 let req = Request::get("/files/readme.json")
1238 .body(Body::empty())
1239 .unwrap();
1240 let res = router.serve(req).await.unwrap();
1241 let body = res.into_body().collect().await.unwrap().to_bytes();
1242 assert_eq!(body, "json:readme");
1243 }
1244 }
1245
1246 #[tokio::test]
1247 async fn test_router_equal_specificity_preserves_registration_order() {
1248 fn svc(
1249 label: &'static str,
1250 ) -> impl Service<Request, Output = Response, Error = Infallible> {
1251 service_fn(move |_req: Request| async move {
1252 Ok(Response::builder()
1253 .status(200)
1254 .body(Body::from(label))
1255 .unwrap())
1256 })
1257 }
1258
1259 let router = ErrorHandlerLayer::new().layer(
1260 Router::new()
1261 .with_get("/files/{a}", svc("first"))
1262 .with_get("/files/{b}", svc("second")),
1263 );
1264 let req = Request::get("/files/readme").body(Body::empty()).unwrap();
1265 let res = router.serve(req).await.unwrap();
1266 let body = res.into_body().collect().await.unwrap().to_bytes();
1267 assert_eq!(body, "first");
1268 }
1269
1270 #[tokio::test]
1271 async fn test_router_anonymous_glob_surfaces_via_uri_params() {
1272 fn glob_svc() -> impl Service<Request, Output = Response, Error = Infallible> {
1273 service_fn(|req: Request| async move {
1274 let p = req.extensions().get_ref::<UriParams>().unwrap();
1275 assert!(p.get("rest").is_none());
1277 Ok(Response::builder()
1278 .status(200)
1279 .body(Body::from(format!("glob:{}", p.glob().unwrap())))
1280 .unwrap())
1281 })
1282 }
1283 let router =
1284 ErrorHandlerLayer::new().layer(Router::new().with_get("/assets/{*}", glob_svc()));
1285 let req = Request::get("/assets/css/app.css")
1286 .body(Body::empty())
1287 .unwrap();
1288 let res = router.serve(req).await.unwrap();
1289 let body = res.into_body().collect().await.unwrap().to_bytes();
1290 assert_eq!(body, "glob:/css/app.css");
1292 }
1293
1294 #[tokio::test]
1295 async fn test_router_invalid_catch_all_mount_is_literal() {
1296 let app = Router::new()
1300 .with_sub_service(
1301 "/api/{*bad name}",
1302 Router::new().with_get("/", root_service()),
1303 )
1304 .with_not_found(not_found_service());
1305 let app = ErrorHandlerLayer::new().layer(app);
1306
1307 let req = Request::get("/api/users").body(Body::empty()).unwrap();
1308 let res = app.serve(req).await.unwrap();
1309 assert_eq!(
1310 res.status(),
1311 StatusCode::NOT_FOUND,
1312 "invalid catch-all must not mount the sub-service at /api"
1313 );
1314 }
1315
1316 #[tokio::test]
1317 async fn test_sub_service_mount_respects_segment_boundaries() {
1318 for prefix in ["/api", "/api/{id}"] {
1319 let app = Router::new()
1320 .with_sub_service(prefix, Router::new().with_get("/", root_service()))
1321 .with_not_found(not_found_service());
1322 let app = ErrorHandlerLayer::new().layer(app);
1323
1324 let req = Request::get("/apix/123").body(Body::empty()).unwrap();
1325 let res = app.serve(req).await.unwrap();
1326 assert_eq!(
1327 res.status(),
1328 StatusCode::NOT_FOUND,
1329 "mount prefix {prefix:?} must not match inside a path segment",
1330 );
1331 }
1332 }
1333
1334 #[tokio::test]
1335 #[tracing_test::traced_test]
1336 async fn test_router_nest() {
1337 let cases = [
1338 (Method::GET, "/", "Hello, World!", StatusCode::OK),
1339 (Method::GET, "/api/users", "List Users", StatusCode::OK),
1340 (Method::POST, "/api/users", "Create User", StatusCode::OK),
1341 (
1342 Method::DELETE,
1343 "/api/users/123",
1344 "Delete User: 123",
1345 StatusCode::OK,
1346 ),
1347 (
1348 Method::GET,
1349 "/api/users/123",
1350 "Get User: 123",
1351 StatusCode::OK,
1352 ),
1353 (
1354 Method::GET,
1355 "/api/users/123/orders/456",
1356 "Get Order: 456 for User: 123",
1357 StatusCode::OK,
1358 ),
1359 (Method::GET, "/api/state", "state", StatusCode::OK),
1360 (Method::GET, "/api/users/123/state", "state", StatusCode::OK),
1361 (Method::GET, "/Api/USERS/123/State", "state", StatusCode::OK),
1363 ];
1364
1365 let state = "state".to_owned();
1366
1367 for prefix in ["/", ""] {
1368 let api_router = Router::new_with_state(state.clone())
1369 .with_get(format!("{prefix}users"), get_users_service())
1370 .with_get(
1371 format!("{prefix}state"),
1372 async |State(state): State<String>| state,
1373 )
1374 .with_post(format!("{prefix}users"), create_user_service())
1375 .with_delete(format!("{prefix}users/{{user_id}}"), delete_user_service())
1376 .with_sub_router_make_fn(
1377 format!("{prefix}users/{{user_id}}/{{*}}"), |router| {
1379 router
1380 .with_get(prefix, get_user_service())
1381 .with_get(
1382 format!("{prefix}orders/{{order_id}}"),
1383 get_user_order_service(),
1384 )
1385 .with_get(
1386 format!("{prefix}/state"),
1387 async |State(state): State<String>| state,
1388 )
1389 },
1390 );
1391
1392 let app = Router::new()
1393 .with_sub_service(format!("{prefix}api"), api_router)
1394 .with_get(prefix, root_service());
1395
1396 let app = ErrorHandlerLayer::new().layer(app);
1397
1398 for (method, path, expected_body, expected_status) in cases.iter() {
1399 let req = match *method {
1400 Method::GET => Request::get(*path),
1401 Method::POST => Request::post(*path),
1402 Method::DELETE => Request::delete(*path),
1403 _ => panic!("Unsupported HTTP method"),
1404 }
1405 .body(Body::empty())
1406 .unwrap();
1407
1408 let res = app.serve(req).await.unwrap();
1409 assert_eq!(
1410 res.status(),
1411 *expected_status,
1412 "method: {method} ; path = {path}; prefix = {prefix}"
1413 );
1414 let body = res.into_body().collect().await.unwrap().to_bytes();
1415 assert_eq!(
1416 body, expected_body,
1417 "method: {method} ; path = {path}; prefix = {prefix}"
1418 );
1419 }
1420
1421 let req = Request::put("/api/users/123").body(Body::empty()).unwrap();
1426 let res = app.serve(req).await.unwrap();
1427 assert_eq!(
1428 res.status(),
1429 StatusCode::METHOD_NOT_ALLOWED,
1430 "nested router: PUT /api/users/123 must be 405; prefix = {prefix}"
1431 );
1432 assert_eq!(
1433 res.headers().get(header::ALLOW).unwrap().to_str().unwrap(),
1434 "GET",
1435 "nested router: Allow header reflects sub-router's registered methods; prefix = {prefix}"
1436 );
1437 }
1438 }
1439}