actix_service/
transform.rs

1use alloc::{rc::Rc, sync::Arc};
2use core::{
3    future::Future,
4    marker::PhantomData,
5    pin::Pin,
6    task::{Context, Poll},
7};
8
9use futures_core::ready;
10use pin_project_lite::pin_project;
11
12use crate::{IntoServiceFactory, Service, ServiceFactory};
13
14/// Apply a [`Transform`] to a [`Service`].
15pub fn apply<T, S, I, Req>(t: T, factory: I) -> ApplyTransform<T, S, Req>
16where
17    I: IntoServiceFactory<S, Req>,
18    S: ServiceFactory<Req>,
19    T: Transform<S::Service, Req, InitError = S::InitError>,
20{
21    ApplyTransform::new(t, factory.into_factory())
22}
23
24/// Defines the interface of a service factory that wraps inner service during construction.
25///
26/// Transformers wrap an inner service and runs during inbound and/or outbound processing in the
27/// service lifecycle. It may modify request and/or response.
28///
29/// For example, a timeout service wrapper:
30///
31/// ```ignore
32/// pub struct Timeout<S> {
33///     service: S,
34///     timeout: Duration,
35/// }
36///
37/// impl<S: Service<Req>, Req> Service<Req> for Timeout<S> {
38///     type Response = S::Response;
39///     type Error = TimeoutError<S::Error>;
40///     type Future = TimeoutServiceResponse<S>;
41///
42///     actix_service::forward_ready!(service);
43///
44///     fn call(&self, req: Req) -> Self::Future {
45///         TimeoutServiceResponse {
46///             fut: self.service.call(req),
47///             sleep: Sleep::new(clock::now() + self.timeout),
48///         }
49///     }
50/// }
51/// ```
52///
53/// This wrapper service is decoupled from the underlying service implementation and could be
54/// applied to any service.
55///
56/// The `Transform` trait defines the interface of a service wrapper. `Transform` is often
57/// implemented for middleware, defining how to construct a middleware Service. A Service that is
58/// constructed by the factory takes the Service that follows it during execution as a parameter,
59/// assuming ownership of the next Service.
60///
61/// A transform for the `Timeout` middleware could look like this:
62///
63/// ```ignore
64/// pub struct TimeoutTransform {
65///     timeout: Duration,
66/// }
67///
68/// impl<S: Service<Req>, Req> Transform<S, Req> for TimeoutTransform {
69///     type Response = S::Response;
70///     type Error = TimeoutError<S::Error>;
71///     type InitError = S::Error;
72///     type Transform = Timeout<S>;
73///     type Future = Ready<Result<Self::Transform, Self::InitError>>;
74///
75///     fn new_transform(&self, service: S) -> Self::Future {
76///         ready(Ok(Timeout {
77///             service,
78///             timeout: self.timeout,
79///         }))
80///     }
81/// }
82/// ```
83pub trait Transform<S, Req> {
84    /// Responses produced by the service.
85    type Response;
86
87    /// Errors produced by the service.
88    type Error;
89
90    /// The `TransformService` value created by this factory
91    type Transform: Service<Req, Response = Self::Response, Error = Self::Error>;
92
93    /// Errors produced while building a transform service.
94    type InitError;
95
96    /// The future response value.
97    type Future: Future<Output = Result<Self::Transform, Self::InitError>>;
98
99    /// Creates and returns a new Transform component, asynchronously
100    fn new_transform(&self, service: S) -> Self::Future;
101}
102
103impl<T, S, Req> Transform<S, Req> for Rc<T>
104where
105    T: Transform<S, Req>,
106{
107    type Response = T::Response;
108    type Error = T::Error;
109    type Transform = T::Transform;
110    type InitError = T::InitError;
111    type Future = T::Future;
112
113    fn new_transform(&self, service: S) -> T::Future {
114        self.as_ref().new_transform(service)
115    }
116}
117
118impl<T, S, Req> Transform<S, Req> for Arc<T>
119where
120    T: Transform<S, Req>,
121{
122    type Response = T::Response;
123    type Error = T::Error;
124    type Transform = T::Transform;
125    type InitError = T::InitError;
126    type Future = T::Future;
127
128    fn new_transform(&self, service: S) -> T::Future {
129        self.as_ref().new_transform(service)
130    }
131}
132
133/// Apply a [`Transform`] to a [`Service`].
134pub struct ApplyTransform<T, S, Req>(Rc<(T, S)>, PhantomData<Req>);
135
136impl<T, S, Req> ApplyTransform<T, S, Req>
137where
138    S: ServiceFactory<Req>,
139    T: Transform<S::Service, Req, InitError = S::InitError>,
140{
141    /// Create new `ApplyTransform` new service instance
142    fn new(t: T, service: S) -> Self {
143        Self(Rc::new((t, service)), PhantomData)
144    }
145}
146
147impl<T, S, Req> Clone for ApplyTransform<T, S, Req> {
148    fn clone(&self) -> Self {
149        ApplyTransform(self.0.clone(), PhantomData)
150    }
151}
152
153impl<T, S, Req> ServiceFactory<Req> for ApplyTransform<T, S, Req>
154where
155    S: ServiceFactory<Req>,
156    T: Transform<S::Service, Req, InitError = S::InitError>,
157{
158    type Response = T::Response;
159    type Error = T::Error;
160
161    type Config = S::Config;
162    type Service = T::Transform;
163    type InitError = T::InitError;
164    type Future = ApplyTransformFuture<T, S, Req>;
165
166    fn new_service(&self, cfg: S::Config) -> Self::Future {
167        ApplyTransformFuture {
168            store: self.0.clone(),
169            state: ApplyTransformFutureState::A {
170                fut: self.0.as_ref().1.new_service(cfg),
171            },
172        }
173    }
174}
175
176pin_project! {
177    pub struct ApplyTransformFuture<T, S, Req>
178    where
179        S: ServiceFactory<Req>,
180        T: Transform<S::Service, Req, InitError = S::InitError>,
181    {
182        store: Rc<(T, S)>,
183        #[pin]
184        state: ApplyTransformFutureState<T, S, Req>,
185    }
186}
187
188pin_project! {
189    #[project = ApplyTransformFutureStateProj]
190    pub enum ApplyTransformFutureState<T, S, Req>
191    where
192        S: ServiceFactory<Req>,
193        T: Transform<S::Service, Req, InitError = S::InitError>,
194    {
195        A { #[pin] fut: S::Future },
196        B { #[pin] fut: T::Future },
197    }
198}
199
200impl<T, S, Req> Future for ApplyTransformFuture<T, S, Req>
201where
202    S: ServiceFactory<Req>,
203    T: Transform<S::Service, Req, InitError = S::InitError>,
204{
205    type Output = Result<T::Transform, T::InitError>;
206
207    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
208        let mut this = self.as_mut().project();
209
210        match this.state.as_mut().project() {
211            ApplyTransformFutureStateProj::A { fut } => {
212                let srv = ready!(fut.poll(cx))?;
213                let fut = this.store.0.new_transform(srv);
214                this.state.set(ApplyTransformFutureState::B { fut });
215                self.poll(cx)
216            }
217            ApplyTransformFutureStateProj::B { fut } => fut.poll(cx),
218        }
219    }
220}
221
222#[cfg(test)]
223mod tests {
224    use core::time::Duration;
225
226    use actix_utils::future::{ready, Ready};
227
228    use super::*;
229
230    // pseudo-doctest for Transform trait
231    #[allow(unused)]
232    pub struct TimeoutTransform {
233        timeout: Duration,
234    }
235
236    // pseudo-doctest for Transform trait
237    impl<S: Service<Req>, Req> Transform<S, Req> for TimeoutTransform {
238        type Response = S::Response;
239        type Error = S::Error;
240        type InitError = S::Error;
241        type Transform = Timeout<S>;
242        type Future = Ready<Result<Self::Transform, Self::InitError>>;
243
244        fn new_transform(&self, service: S) -> Self::Future {
245            ready(Ok(Timeout {
246                service,
247                _timeout: self.timeout,
248            }))
249        }
250    }
251
252    // pseudo-doctest for Transform trait
253    #[allow(unused)]
254    pub struct Timeout<S> {
255        service: S,
256        _timeout: Duration,
257    }
258
259    // pseudo-doctest for Transform trait
260    impl<S: Service<Req>, Req> Service<Req> for Timeout<S> {
261        type Response = S::Response;
262        type Error = S::Error;
263        type Future = S::Future;
264
265        crate::forward_ready!(service);
266
267        fn call(&self, req: Req) -> Self::Future {
268            self.service.call(req)
269        }
270    }
271}