Skip to main content

tonic/service/
router.rs

1/*
2 *
3 * Copyright 2025 gRPC authors.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to
7 * deal in the Software without restriction, including without limitation the
8 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9 * sell copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 *
23 */
24
25use crate::{Status, body::Body, server::NamedService};
26use http::{Request, Response};
27use std::{
28    convert::Infallible,
29    fmt,
30    future::Future,
31    pin::Pin,
32    task::{Context, Poll},
33};
34use tower::{Service, ServiceExt};
35
36/// A [`Service`] router.
37#[derive(Debug, Clone)]
38pub struct Routes {
39    router: axum::Router,
40}
41
42#[derive(Debug, Default, Clone)]
43/// Allows adding new services to routes by passing a mutable reference to this builder.
44pub struct RoutesBuilder {
45    routes: Option<Routes>,
46}
47
48impl RoutesBuilder {
49    /// Add a new service.
50    pub fn add_service<S>(&mut self, svc: S) -> &mut Self
51    where
52        S: Service<Request<Body>, Error = Infallible>
53            + NamedService
54            + Clone
55            + Send
56            + Sync
57            + 'static,
58        S::Response: axum::response::IntoResponse,
59        S::Future: Send + 'static,
60    {
61        let routes = self.routes.take().unwrap_or_default();
62        self.routes.replace(routes.add_service(svc));
63        self
64    }
65
66    /// Returns the routes with added services or empty [`Routes`] if no service was added
67    pub fn routes(self) -> Routes {
68        self.routes.unwrap_or_default()
69    }
70}
71
72impl Default for Routes {
73    fn default() -> Self {
74        Self {
75            router: axum::Router::new().fallback(unimplemented),
76        }
77    }
78}
79
80impl Routes {
81    /// Create a new routes with `svc` already added to it.
82    pub fn new<S>(svc: S) -> Self
83    where
84        S: Service<Request<Body>, Error = Infallible>
85            + NamedService
86            + Clone
87            + Send
88            + Sync
89            + 'static,
90        S::Response: axum::response::IntoResponse,
91        S::Future: Send + 'static,
92    {
93        Self::default().add_service(svc)
94    }
95
96    /// Create a new empty builder.
97    pub fn builder() -> RoutesBuilder {
98        RoutesBuilder::default()
99    }
100
101    /// Add a new service.
102    pub fn add_service<S>(mut self, svc: S) -> Self
103    where
104        S: Service<Request<Body>, Error = Infallible>
105            + NamedService
106            + Clone
107            + Send
108            + Sync
109            + 'static,
110        S::Response: axum::response::IntoResponse,
111        S::Future: Send + 'static,
112    {
113        self.router = self.router.route_service(
114            &format!("/{}/{{*rest}}", S::NAME),
115            svc.map_request(|req: Request<axum::body::Body>| req.map(Body::new)),
116        );
117        self
118    }
119
120    /// This makes axum perform update some internals of the router that improves perf.
121    ///
122    /// See <https://docs.rs/axum/latest/axum/routing/struct.Router.html#a-note-about-performance>
123    pub fn prepare(self) -> Self {
124        Self {
125            router: self.router.with_state(()),
126        }
127    }
128
129    /// Convert this `Routes` into an [`axum::Router`].
130    pub fn into_axum_router(self) -> axum::Router {
131        self.router
132    }
133
134    /// Get a mutable reference to the [`axum::Router`].
135    pub fn axum_router_mut(&mut self) -> &mut axum::Router {
136        &mut self.router
137    }
138}
139
140impl From<Routes> for RoutesBuilder {
141    fn from(routes: Routes) -> Self {
142        Self {
143            routes: Some(routes),
144        }
145    }
146}
147
148impl From<axum::Router> for RoutesBuilder {
149    fn from(router: axum::Router) -> Self {
150        Self {
151            routes: Some(router.into()),
152        }
153    }
154}
155
156impl From<axum::Router> for Routes {
157    fn from(router: axum::Router) -> Self {
158        Self { router }
159    }
160}
161
162async fn unimplemented() -> Response<Body> {
163    let (parts, ()) = Status::unimplemented("").into_http::<()>().into_parts();
164    Response::from_parts(parts, Body::empty())
165}
166
167impl<B> Service<Request<B>> for Routes
168where
169    B: http_body::Body<Data = bytes::Bytes> + Send + 'static,
170    B::Error: Into<crate::BoxError>,
171{
172    type Response = Response<Body>;
173    type Error = Infallible;
174    type Future = RoutesFuture;
175
176    #[inline]
177    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
178        Service::<Request<B>>::poll_ready(&mut self.router, cx)
179    }
180
181    fn call(&mut self, req: Request<B>) -> Self::Future {
182        RoutesFuture(self.router.call(req))
183    }
184}
185
186pub struct RoutesFuture(axum::routing::future::RouteFuture<Infallible>);
187
188impl fmt::Debug for RoutesFuture {
189    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
190        f.debug_tuple("RoutesFuture").finish()
191    }
192}
193
194impl Future for RoutesFuture {
195    type Output = Result<Response<Body>, Infallible>;
196
197    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
198        Pin::new(&mut self.as_mut().0)
199            .poll(cx)
200            .map_ok(|res| res.map(Body::new))
201    }
202}