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