• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use crate::{
2     body::{boxed, BoxBody},
3     server::NamedService,
4 };
5 use http::{Request, Response};
6 use hyper::Body;
7 use pin_project::pin_project;
8 use std::{
9     convert::Infallible,
10     fmt,
11     future::Future,
12     pin::Pin,
13     task::{ready, Context, Poll},
14 };
15 use tower::ServiceExt;
16 use tower_service::Service;
17 
18 /// A [`Service`] router.
19 #[derive(Debug, Default, Clone)]
20 pub struct Routes {
21     router: axum::Router,
22 }
23 
24 #[derive(Debug, Default, Clone)]
25 /// Allows adding new services to routes by passing a mutable reference to this builder.
26 pub struct RoutesBuilder {
27     routes: Option<Routes>,
28 }
29 
30 impl RoutesBuilder {
31     /// Add a new service.
add_service<S>(&mut self, svc: S) -> &mut Self where S: Service<Request<Body>, Response = Response<BoxBody>, Error = Infallible> + NamedService + Clone + Send + 'static, S::Future: Send + 'static, S::Error: Into<crate::Error> + Send,32     pub fn add_service<S>(&mut self, svc: S) -> &mut Self
33     where
34         S: Service<Request<Body>, Response = Response<BoxBody>, Error = Infallible>
35             + NamedService
36             + Clone
37             + Send
38             + 'static,
39         S::Future: Send + 'static,
40         S::Error: Into<crate::Error> + Send,
41     {
42         let routes = self.routes.take().unwrap_or_default();
43         self.routes.replace(routes.add_service(svc));
44         self
45     }
46 
47     /// Returns the routes with added services or empty [`Routes`] if no service was added
routes(self) -> Routes48     pub fn routes(self) -> Routes {
49         self.routes.unwrap_or_default()
50     }
51 }
52 impl Routes {
53     /// Create a new routes with `svc` already added to it.
new<S>(svc: S) -> Self where S: Service<Request<Body>, Response = Response<BoxBody>, Error = Infallible> + NamedService + Clone + Send + 'static, S::Future: Send + 'static, S::Error: Into<crate::Error> + Send,54     pub fn new<S>(svc: S) -> Self
55     where
56         S: Service<Request<Body>, Response = Response<BoxBody>, Error = Infallible>
57             + NamedService
58             + Clone
59             + Send
60             + 'static,
61         S::Future: Send + 'static,
62         S::Error: Into<crate::Error> + Send,
63     {
64         let router = axum::Router::new().fallback(unimplemented);
65         Self { router }.add_service(svc)
66     }
67 
68     /// Add a new service.
add_service<S>(mut self, svc: S) -> Self where S: Service<Request<Body>, Response = Response<BoxBody>, Error = Infallible> + NamedService + Clone + Send + 'static, S::Future: Send + 'static, S::Error: Into<crate::Error> + Send,69     pub fn add_service<S>(mut self, svc: S) -> Self
70     where
71         S: Service<Request<Body>, Response = Response<BoxBody>, Error = Infallible>
72             + NamedService
73             + Clone
74             + Send
75             + 'static,
76         S::Future: Send + 'static,
77         S::Error: Into<crate::Error> + Send,
78     {
79         let svc = svc.map_response(|res| res.map(axum::body::boxed));
80         self.router = self
81             .router
82             .route_service(&format!("/{}/*rest", S::NAME), svc);
83         self
84     }
85 
prepare(self) -> Self86     pub(crate) fn prepare(self) -> Self {
87         Self {
88             // this makes axum perform update some internals of the router that improves perf
89             // see https://docs.rs/axum/latest/axum/routing/struct.Router.html#a-note-about-performance
90             router: self.router.with_state(()),
91         }
92     }
93 
94     /// Convert this `Routes` into an [`axum::Router`].
into_router(self) -> axum::Router95     pub fn into_router(self) -> axum::Router {
96         self.router
97     }
98 }
99 
unimplemented() -> impl axum::response::IntoResponse100 async fn unimplemented() -> impl axum::response::IntoResponse {
101     let status = http::StatusCode::OK;
102     let headers = [("grpc-status", "12"), ("content-type", "application/grpc")];
103     (status, headers)
104 }
105 
106 impl Service<Request<Body>> for Routes {
107     type Response = Response<BoxBody>;
108     type Error = crate::Error;
109     type Future = RoutesFuture;
110 
111     #[inline]
poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>>112     fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
113         Poll::Ready(Ok(()))
114     }
115 
call(&mut self, req: Request<Body>) -> Self::Future116     fn call(&mut self, req: Request<Body>) -> Self::Future {
117         RoutesFuture(self.router.call(req))
118     }
119 }
120 
121 #[pin_project]
122 pub struct RoutesFuture(#[pin] axum::routing::future::RouteFuture<Body, Infallible>);
123 
124 impl fmt::Debug for RoutesFuture {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result125     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
126         f.debug_tuple("RoutesFuture").finish()
127     }
128 }
129 
130 impl Future for RoutesFuture {
131     type Output = Result<Response<BoxBody>, crate::Error>;
132 
poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>133     fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
134         match ready!(self.project().0.poll(cx)) {
135             Ok(res) => Ok(res.map(boxed)).into(),
136             Err(err) => match err {},
137         }
138     }
139 }
140