Skip to main content

siderite_core/middleware/
adapt.rs

1//! Plumbing shared by all middleware: the boxed service type, `from_fn`,
2//! and adapters between siderite bodies and the engine's bodies.
3
4use crate::body::Body;
5use crate::extract::Request;
6use crate::response::Response;
7use crate::service::RouterService;
8use axum::BoxError;
9use axum::body::HttpBody;
10use bytes::Bytes;
11use std::convert::Infallible;
12use std::future::Future;
13use std::pin::Pin;
14use std::task::{Context, Poll};
15use tower::util::BoxCloneSyncService;
16use tower::{Layer, Service, ServiceExt};
17
18type BoxFuture<T> = Pin<Box<dyn Future<Output = T> + Send>>;
19
20/// The service type middleware layers wrap: the rest of the application.
21pub type BoxService = BoxCloneSyncService<Request, Response, Infallible>;
22
23/// The rest of the middleware chain, handed to [`from_fn`] closures.
24#[derive(Clone)]
25pub struct Next {
26    inner: BoxService,
27}
28
29impl Next {
30    /// Forward `req` to the inner service and await its response.
31    pub async fn run(self, req: Request) -> Response {
32        match self.inner.oneshot(req).await {
33            Ok(response) => response,
34            Err(never) => match never {},
35        }
36    }
37}
38
39/// Build a middleware layer from an async function `(Request, Next) -> Response`.
40pub fn from_fn<F, Fut>(f: F) -> FromFnLayer<F>
41where
42    F: Fn(Request, Next) -> Fut + Clone + Send + Sync + 'static,
43    Fut: Future<Output = Response> + Send + 'static,
44{
45    FromFnLayer { f }
46}
47
48/// Layer returned by [`from_fn`].
49#[derive(Clone)]
50pub struct FromFnLayer<F> {
51    f: F,
52}
53
54impl<F, Fut> Layer<BoxService> for FromFnLayer<F>
55where
56    F: Fn(Request, Next) -> Fut + Clone + Send + Sync + 'static,
57    Fut: Future<Output = Response> + Send + 'static,
58{
59    type Service = BoxService;
60
61    fn layer(&self, inner: BoxService) -> BoxService {
62        BoxService::new(FromFnService {
63            inner,
64            f: self.f.clone(),
65        })
66    }
67}
68
69#[derive(Clone)]
70struct FromFnService<F> {
71    inner: BoxService,
72    f: F,
73}
74
75impl<F, Fut> Service<Request> for FromFnService<F>
76where
77    F: Fn(Request, Next) -> Fut + Clone + Send + Sync + 'static,
78    Fut: Future<Output = Response> + Send + 'static,
79{
80    type Response = Response;
81    type Error = Infallible;
82    type Future = BoxFuture<Result<Response, Infallible>>;
83
84    fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Infallible>> {
85        Poll::Ready(Ok(()))
86    }
87
88    fn call(&mut self, req: Request) -> Self::Future {
89        let fut = (self.f)(
90            req,
91            Next {
92                inner: self.inner.clone(),
93            },
94        );
95        Box::pin(async move { Ok(fut.await) })
96    }
97}
98
99/// Implement `tower::Layer<BoxService>` for types with an inherent
100/// `fn wrap(&self, BoxService) -> BoxService`.
101macro_rules! impl_layer {
102    ($($ty:ty),+ $(,)?) => {$(
103        impl tower::Layer<$crate::middleware::BoxService> for $ty {
104            type Service = $crate::middleware::BoxService;
105
106            fn layer(&self, inner: $crate::middleware::BoxService) -> Self::Service {
107                self.wrap(inner)
108            }
109        }
110    )+};
111}
112pub(crate) use impl_layer;
113
114/// Wrap `router` in a user layer, producing a router that delegates to it.
115pub(crate) fn apply_layer<L>(layer: &L, router: axum::Router) -> axum::Router
116where
117    L: Layer<BoxService>,
118    L::Service:
119        Service<Request, Response = Response, Error = Infallible> + Clone + Send + Sync + 'static,
120    <L::Service as Service<Request>>::Future: Send + 'static,
121{
122    let service = layer.layer(BoxService::new(RouterService::new(router)));
123    axum::Router::new().fallback_service(ToEngine { inner: service })
124}
125
126/// Engine-facing adapter: engine bodies in, engine bodies out.
127#[derive(Clone)]
128struct ToEngine<S> {
129    inner: S,
130}
131
132impl<S> Service<axum::extract::Request> for ToEngine<S>
133where
134    S: Service<Request, Response = Response, Error = Infallible> + Clone + Send + 'static,
135    S::Future: Send + 'static,
136{
137    type Response = axum::response::Response;
138    type Error = Infallible;
139    type Future = BoxFuture<Result<Self::Response, Infallible>>;
140
141    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Infallible>> {
142        self.inner.poll_ready(cx)
143    }
144
145    fn call(&mut self, req: axum::extract::Request) -> Self::Future {
146        let fut = self.inner.call(req.map(Body::from_inner));
147        Box::pin(async move { fut.await.map(|r| r.map(Body::into_inner)) })
148    }
149}
150
151/// Presents a [`BoxService`] to a `tower-http` layer.
152#[derive(Clone)]
153pub(crate) struct EngineIn(BoxService);
154
155impl<B> Service<http::Request<B>> for EngineIn
156where
157    B: HttpBody<Data = Bytes> + Send + 'static,
158    B::Error: Into<BoxError>,
159{
160    type Response = http::Response<axum::body::Body>;
161    type Error = Infallible;
162    type Future = BoxFuture<Result<Self::Response, Infallible>>;
163
164    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Infallible>> {
165        self.0.poll_ready(cx)
166    }
167
168    fn call(&mut self, req: http::Request<B>) -> Self::Future {
169        let fut = self
170            .0
171            .call(req.map(|b| Body::from_inner(axum::body::Body::new(b))));
172        Box::pin(async move { fut.await.map(|r| r.map(Body::into_inner)) })
173    }
174}
175
176#[derive(Clone)]
177struct EngineOut<S>(S);
178
179impl<S, B> Service<Request> for EngineOut<S>
180where
181    S: Service<http::Request<axum::body::Body>, Response = http::Response<B>, Error = Infallible>,
182    S::Future: Send + 'static,
183    B: HttpBody<Data = Bytes> + Send + 'static,
184    B::Error: Into<BoxError>,
185{
186    type Response = Response;
187    type Error = Infallible;
188    type Future = BoxFuture<Result<Response, Infallible>>;
189
190    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Infallible>> {
191        self.0.poll_ready(cx)
192    }
193
194    fn call(&mut self, req: Request) -> Self::Future {
195        let fut = self.0.call(req.map(Body::into_inner));
196        Box::pin(async move {
197            fut.await
198                .map(|r| r.map(|b| Body::from_inner(axum::body::Body::new(b))))
199        })
200    }
201}
202
203/// Apply a `tower-http` style layer (generic over body types) to `inner`.
204pub(crate) fn wrap_engine<L, B>(layer: &L, inner: BoxService) -> BoxService
205where
206    L: Layer<EngineIn>,
207    L::Service: Service<http::Request<axum::body::Body>, Response = http::Response<B>, Error = Infallible>
208        + Clone
209        + Send
210        + Sync
211        + 'static,
212    <L::Service as Service<http::Request<axum::body::Body>>>::Future: Send + 'static,
213    B: HttpBody<Data = Bytes> + Send + 'static,
214    B::Error: Into<BoxError>,
215{
216    BoxService::new(EngineOut(layer.layer(EngineIn(inner))))
217}