Skip to main content

ntex_service/
lib.rs

1//! Asynchronous services, factories, middleware, and execution pipelines.
2//!
3//! The [`Service`] trait is the crate's central abstraction. A service
4//! asynchronously transforms a request into a response, while
5//! [`ServiceFactory`] constructs services and [`Pipeline`] manages readiness,
6//! calls, and shutdown.
7#![deny(clippy::pedantic)]
8#![allow(
9    clippy::cast_possible_truncation,
10    clippy::missing_fields_in_debug,
11    clippy::missing_errors_doc,
12    clippy::missing_panics_doc,
13    clippy::must_use_candidate,
14    clippy::type_complexity,
15    clippy::unused_async,
16    clippy::unused_async_trait_impl
17)]
18use std::rc::Rc;
19
20mod and_then;
21mod apply;
22pub mod boxed;
23pub mod cfg;
24mod chain;
25mod ctx;
26mod fn_ready;
27mod fn_service;
28mod fn_shutdown;
29mod macros;
30mod map;
31mod map_err;
32mod map_init_err;
33mod map_state;
34mod middleware;
35pub mod state;
36mod then;
37mod util;
38
39pub mod pipeline;
40mod pl_factory;
41mod pl_inner;
42mod pl_state;
43
44pub use crate::apply::{apply_fn, apply_fn_factory};
45pub use crate::chain::{ServiceChain, ServiceChainFactory, factory, service};
46pub use crate::ctx::Ctx;
47pub use crate::fn_service::{fn_factory, fn_service, fn_service_st};
48pub use crate::map_state::{map_state, map_state_factory};
49pub use crate::middleware::{Identity, Middleware, Stack, apply, fn_layer};
50pub use crate::pipeline::Pipeline;
51pub use crate::state::{RequestState, State};
52
53#[allow(unused_variables)]
54/// An asynchronous operation from a request to a response.
55///
56/// A service receives requests and asynchronously produces responses.
57/// Conceptually, it is similar to:
58///
59/// ```rust,ignore
60/// async fn(Request) -> Result<Response, Error>
61/// ```
62///
63/// The request and pipeline-state types are generic parameters. The response
64/// and error types are associated types, allowing one service type to implement
65/// `Service` for multiple request types.
66///
67/// Methods take `&self`, so implementations that mutate internal state must use
68/// interior mutability such as `Cell`, `RefCell`, or a synchronization
69/// primitive when appropriate.
70///
71/// The same abstraction can represent client- and server-side operations.
72/// Services focus on transformation, making them straightforward to test and
73/// compose.
74///
75/// A service call requires a [`Ctx`] and therefore runs through a [`Pipeline`]
76/// or from another service. The pipeline coordinates readiness across a
77/// composed service chain before dispatching a request.
78///
79/// ```rust
80/// # use std::convert::Infallible;
81/// #
82/// # use ntex_service::{Service, Ctx};
83///
84/// struct MyService;
85///
86/// impl Service<(), u8> for MyService {
87///     type Res = u64;
88///     type Error = Infallible;
89///
90///     async fn call(&self, req: u8, ctx: Ctx<'_, Self>) -> Result<Self::Res, Self::Error> {
91///         Ok(req as u64)
92///     }
93/// }
94/// ```
95///
96/// Simple services do not need a manual trait implementation. The example
97/// above can be expressed with [`fn_service`]:
98///
99/// ```rust
100/// # use std::convert::Infallible;
101/// # use ntex_service::{Pipeline, fn_service};
102/// #
103/// # async fn run() -> Result<(), Infallible> {
104/// let service = fn_service(|req: u8| async move {
105///     Ok::<_, Infallible>(u64::from(req))
106/// });
107/// let pipeline = Pipeline::new((), service);
108///
109/// assert_eq!(pipeline.call(10).await?, 10);
110/// # Ok(())
111/// # }
112/// ```
113pub trait Service<St, Req> {
114    /// Response produced by the service.
115    type Res;
116
117    /// Error produced while checking readiness or processing a request.
118    type Error;
119
120    /// Processes a request and asynchronously returns the response.
121    ///
122    /// The enclosing pipeline checks readiness before invoking this method.
123    /// Implementations should not call their own `ready` method. A composed
124    /// service can use `ctx` to call an inner service.
125    async fn call(&self, req: Req, ctx: Ctx<'_, Self, St>) -> Result<Self::Res, Self::Error>;
126
127    #[inline]
128    /// Waits until the service is ready to process a request.
129    ///
130    /// If the service is at capacity, the returned future remains pending until
131    /// capacity becomes available.
132    ///
133    /// Pipeline readiness is coordinated across all services in a composed
134    /// chain. A request is dispatched only when the chain is ready.
135    async fn ready(&self, ctx: Ctx<'_, Self, St>) -> Result<(), Self::Error> {
136        Ok(())
137    }
138
139    #[inline]
140    /// Shuts down the service.
141    ///
142    /// Returns when the service has been properly shut down.
143    async fn shutdown(&self, ctx: Ctx<'_, Self, St>) {}
144
145    #[inline]
146    /// Maps this service's output to a different type, returning a new service.
147    ///
148    /// This is similar to `Option::map` or `Iterator::map`, changing the
149    /// output type of the underlying service.
150    ///
151    /// This function consumes the original service and returns a wrapped version,
152    /// following the pattern of standard library `map` methods.
153    fn map<F, Res>(self, f: F) -> ServiceChain<dev::Map<F, Self, Res>, St, Req>
154    where
155        Self: Sized,
156        F: Fn(Self::Res) -> Res,
157    {
158        service(dev::Map::new(f, self))
159    }
160
161    #[inline]
162    /// Maps this service's error to a different type, returning a new service.
163    ///
164    /// This is similar to `Result::map_err`, changing the error type of the
165    /// underlying service. It is useful, for example, to ensure multiple
166    /// services have the same error type.
167    ///
168    /// This function consumes the original service and returns a wrapped version.
169    fn map_err<F, E>(self, f: F) -> ServiceChain<dev::MapErr<F, Self, E>, St, Req>
170    where
171        Self: Sized,
172        F: Fn(Self::Error) -> E,
173    {
174        service(dev::MapErr::new(f, self))
175    }
176
177    #[inline]
178    /// Calls another service after this service completes successfully.
179    ///
180    /// The first service's response becomes the second service's request. If
181    /// the first service returns an error, the second service is not called.
182    fn and_then<Next, F>(self, f: F) -> ServiceChain<dev::AndThen<Self, Next>, St, Req>
183    where
184        Self: Sized,
185        Next: Service<St, Self::Res, Error = Self::Error>,
186        F: IntoService<Next, St, Self::Res>,
187    {
188        service(dev::AndThen::new(self, f.into_service()))
189    }
190
191    #[inline]
192    /// Wraps this service and its state in a [`Pipeline`].
193    fn pipeline(self, st: St) -> Pipeline<Req, Self::Res, Self::Error>
194    where
195        Self: Sized + 'static,
196        St: 'static,
197        Req: 'static,
198    {
199        Pipeline::new(st, self)
200    }
201}
202
203/// A factory for asynchronously creating [`Service`] values.
204///
205/// This is useful when new `Service`s must be produced dynamically. For example,
206/// a TCP server listener accepts new connections, constructs a new `Service` for
207/// each connection using the `ServiceFactory` trait, and uses that service to
208/// handle inbound requests.
209///
210/// `St` is the state type shared by the factory and its services.
211///
212/// Simple factories can often use [`fn_factory`] to reduce boilerplate.
213pub trait ServiceFactory<St, Req> {
214    /// Response produced by the created services.
215    type Res;
216
217    /// Error produced by the created services.
218    type Error;
219
220    /// The type of `Service` produced by this factory.
221    type Service: Service<St, Req, Res = Self::Res, Error = Self::Error>;
222
223    /// Error that can occur while constructing a service.
224    type InitError;
225
226    /// Asynchronously creates a service using the supplied state.
227    async fn create(&self, cfg: &St) -> Result<Self::Service, Self::InitError>;
228
229    #[inline]
230    /// Creates a service and wraps it with its state in a [`Pipeline`].
231    async fn pipeline(
232        &self,
233        st: St,
234    ) -> Result<Pipeline<Req, Self::Res, Self::Error>, Self::InitError>
235    where
236        Self: 'static,
237        St: 'static,
238        Req: 'static,
239    {
240        let svc = self.create(&st).await?;
241        Ok(Pipeline::new(st, svc))
242    }
243
244    #[inline]
245    /// Returns a factory whose services map responses to a different type.
246    fn map<F, Res>(self, f: F) -> ServiceChainFactory<dev::MapFactory<F, Self, Res>, St, Req>
247    where
248        Self: Sized,
249        F: Fn(Self::Res) -> Res + Clone,
250    {
251        factory(dev::MapFactory::new(f, self))
252    }
253
254    #[inline]
255    /// Returns a factory whose services map errors to a different type.
256    fn map_err<F, E>(self, f: F) -> ServiceChainFactory<dev::MapErrFactory<F, Self, E>, St, Req>
257    where
258        Self: Sized,
259        F: Fn(Self::Error) -> E + Clone,
260    {
261        factory(dev::MapErrFactory::new(f, self))
262    }
263
264    #[inline]
265    /// Maps this factory's initialization error to a different error,
266    /// returning a new service factory.
267    fn map_init_err<F, E>(self, f: F) -> ServiceChainFactory<dev::MapInitErr<F, Self, E>, St, Req>
268    where
269        Self: Sized,
270        F: Fn(Self::InitError) -> E + Clone,
271    {
272        factory(dev::MapInitErr::new(f, self))
273    }
274
275    /// Chains another factory after this factory's services.
276    ///
277    /// Each response from the first service becomes a request to the second
278    /// service. The second service is not called when the first returns an
279    /// error.
280    fn and_then<U, F>(self, f: F) -> ServiceChainFactory<dev::AndThenFactory<Self, U>, St, Req>
281    where
282        Self: Sized,
283        U: ServiceFactory<St, Self::Res, Error = Self::Error, InitError = Self::InitError>,
284        F: IntoServiceFactory<U, St, Self::Res>,
285    {
286        factory(dev::AndThenFactory::new(self, f.into_factory()))
287    }
288
289    /// Creates a boxed service factory.
290    fn boxed(self) -> boxed::BoxServiceFactory<St, Req, Self::Res, Self::Error, Self::InitError>
291    where
292        St: 'static,
293        Req: 'static,
294        Self: Sized + 'static,
295    {
296        boxed::factory(self)
297    }
298}
299
300impl<S, St, Req> Service<St, Req> for &S
301where
302    S: Service<St, Req>,
303{
304    type Res = S::Res;
305    type Error = S::Error;
306
307    #[inline]
308    async fn ready(&self, ctx: Ctx<'_, Self, St>) -> Result<(), S::Error> {
309        ctx.ready(&**self).await
310    }
311
312    #[inline]
313    async fn call(&self, req: Req, ctx: Ctx<'_, Self, St>) -> Result<S::Res, S::Error> {
314        ctx.call_nowait(&**self, req).await
315    }
316
317    #[inline]
318    async fn shutdown(&self, ctx: Ctx<'_, Self, St>) {
319        ctx.shutdown(&**self).await;
320    }
321}
322
323impl<S, St, Req> Service<St, Req> for Box<S>
324where
325    S: Service<St, Req>,
326{
327    type Res = S::Res;
328    type Error = S::Error;
329
330    #[inline]
331    async fn ready(&self, ctx: Ctx<'_, Self, St>) -> Result<(), S::Error> {
332        ctx.ready(&**self).await
333    }
334
335    #[inline]
336    async fn call(&self, req: Req, ctx: Ctx<'_, Self, St>) -> Result<S::Res, S::Error> {
337        ctx.call_nowait(&**self, req).await
338    }
339
340    #[inline]
341    async fn shutdown(&self, ctx: Ctx<'_, Self, St>) {
342        ctx.shutdown(&**self).await;
343    }
344}
345
346impl<S, St, Req> Service<St, Req> for Rc<S>
347where
348    S: Service<St, Req>,
349{
350    type Res = S::Res;
351    type Error = S::Error;
352
353    #[inline]
354    async fn ready(&self, ctx: Ctx<'_, Self, St>) -> Result<(), S::Error> {
355        ctx.ready(&**self).await
356    }
357
358    #[inline]
359    async fn call(&self, req: Req, ctx: Ctx<'_, Self, St>) -> Result<S::Res, S::Error> {
360        ctx.call_nowait(&**self, req).await
361    }
362
363    #[inline]
364    async fn shutdown(&self, ctx: Ctx<'_, Self, St>) {
365        ctx.shutdown(&**self).await;
366    }
367}
368
369impl<Sf, St, Req> ServiceFactory<St, Req> for Rc<Sf>
370where
371    Sf: ServiceFactory<St, Req>,
372{
373    type Res = Sf::Res;
374    type Error = Sf::Error;
375    type Service = Sf::Service;
376    type InitError = Sf::InitError;
377
378    async fn create(&self, cfg: &St) -> Result<Self::Service, Self::InitError> {
379        self.as_ref().create(cfg).await
380    }
381}
382
383/// A common interface for values that can call a service.
384pub trait ServiceCaller<Req, Res, Err> {
385    /// Waits for readiness, then calls the service.
386    async fn call_service(&self, req: Req) -> Result<Res, Err>;
387}
388
389/// Conversion into a [`Service`].
390pub trait IntoService<S, St, Req>
391where
392    S: Service<St, Req>,
393{
394    /// Converts this value into a service.
395    fn into_service(self) -> S;
396}
397
398/// Conversion into a [`ServiceFactory`].
399pub trait IntoServiceFactory<Sf, St, Req>
400where
401    Sf: ServiceFactory<St, Req>,
402{
403    /// Converts this value into a service factory.
404    fn into_factory(self) -> Sf;
405}
406
407impl<S, St, Req> IntoService<S, St, Req> for S
408where
409    S: Service<St, Req>,
410{
411    #[inline]
412    fn into_service(self) -> S {
413        self
414    }
415}
416
417impl<Sf, St, Req> IntoServiceFactory<Sf, St, Req> for Sf
418where
419    Sf: ServiceFactory<St, Req>,
420{
421    #[inline]
422    fn into_factory(self) -> Sf {
423        self
424    }
425}
426
427pub mod dev {
428    pub use crate::and_then::{AndThen, AndThenFactory};
429    pub use crate::apply::{Apply, ApplyCtx, ApplyFactory};
430    pub use crate::chain::{ServiceChain, ServiceChainFactory};
431    pub use crate::fn_ready::FnReadiness;
432    pub use crate::fn_service::{FnFactory, FnService, FnServiceSt, FnServiceStFactory};
433    pub use crate::fn_shutdown::FnShutdown;
434    pub use crate::map::{Map, MapFactory};
435    pub use crate::map_err::{MapErr, MapErrFactory};
436    pub use crate::map_init_err::MapInitErr;
437    pub use crate::map_state::{MapState, MapStateFactory};
438    pub use crate::middleware::{ApplyMiddleware, FnMiddleware};
439    pub use crate::then::{Then, ThenFactory};
440}