Skip to main content

ntex_service/
lib.rs

1//! See [`Service`] docs for information on this crate's foundational trait.
2#![deny(clippy::pedantic)]
3#![allow(
4    clippy::cast_possible_truncation,
5    clippy::missing_fields_in_debug,
6    clippy::missing_errors_doc,
7    clippy::missing_panics_doc,
8    clippy::must_use_candidate,
9    clippy::type_complexity,
10    clippy::unused_async,
11    clippy::unused_async_trait_impl
12)]
13use std::rc::Rc;
14
15mod and_then;
16mod apply;
17pub mod boxed;
18pub mod cfg;
19mod chain;
20mod ctx;
21mod fn_ready;
22mod fn_service;
23mod fn_shutdown;
24mod macros;
25mod map;
26mod map_err;
27mod map_init_err;
28mod map_state;
29mod middleware;
30pub mod state;
31mod then;
32mod util;
33
34pub mod pipeline;
35mod pl_factory;
36mod pl_inner;
37mod pl_state;
38
39pub use crate::apply::{apply_fn, apply_fn_factory};
40pub use crate::chain::{ServiceChain, ServiceChainFactory, factory, service};
41pub use crate::ctx::Ctx;
42pub use crate::fn_service::{fn_factory, fn_service, fn_service_st};
43pub use crate::map_state::{map_state, map_state_factory};
44pub use crate::middleware::{Identity, Middleware, Stack, apply, fn_layer};
45pub use crate::pipeline::Pipeline;
46pub use crate::state::{RequestState, State};
47
48#[allow(unused_variables)]
49/// An asynchronous function from a `Request` to a `Response`.
50///
51/// The `Service` trait represents a request/response interaction, receiving
52/// requests and returning replies. Conceptually, a service is like a function
53/// with one argument that returns a result asynchronously:
54///
55/// ```rust,ignore
56/// async fn(Request) -> Result<Response, Error>
57/// ```
58///
59/// The `Service` trait generalizes this form. Requests are defined as a generic
60/// type parameter, while responses and other details are defined as associated
61/// types on the trait implementation. This design allows services to accept
62/// many request types and produce a single response type.
63///
64/// Services can also have internal mutable state that influences computation
65/// using `Cell`, `RefCell`, or `Mutex`. Services intentionally do not take
66/// `&mut self` to reduce overhead in common use cases.
67///
68/// `Service` provides a uniform API; the same abstractions can represent both
69/// clients and servers. Services describe only _transformation_ operations,
70/// which encourages simple API surfaces, easier testing, and straightforward
71/// composition.
72///
73/// Services can only be called within a pipeline. The `Pipeline` enforces
74/// shared readiness for all services in the pipeline. To process requests from
75/// one service to another, all services must be ready; otherwise, processing
76/// is paused until that state is achieved.
77///
78/// ```rust
79/// # use std::convert::Infallible;
80/// #
81/// # use ntex_service::{Service, Ctx};
82///
83/// struct MyService;
84///
85/// impl Service<(), u8> for MyService {
86///     type Res = u64;
87///     type Error = Infallible;
88///
89///     async fn call(&self, req: u8, ctx: Ctx<'_, Self>) -> Result<Self::Res, Self::Error> {
90///         Ok(req as u64)
91///     }
92/// }
93/// ```
94///
95/// Sometimes it is not necessary to implement the Service trait. For example, the above service
96/// could be rewritten as a simple function and passed to [`fn_service`](fn_service()).
97///
98/// ```rust,ignore
99/// async fn my_service(req: u8) -> Result<u64, Infallible>;
100/// ```
101///
102/// Service cannot be called directly, it must be wrapped to an instance of [`Pipeline`] or
103/// by using `ctx` argument of the call method in case of chanined services.
104pub trait Service<St, Req> {
105    /// Responses that the service could provide.
106    type Res;
107
108    /// Errors produced by the service while checking readiness or executing a call.
109    type Error;
110
111    /// Processes a request and asynchronously returns the response.
112    ///
113    /// The `call` method can only be invoked within a pipeline, which ensures
114    /// that all services in the pipeline are ready. Implementations of `call`
115    /// must not call `ready`; the `ctx` argument ensures that the service is
116    /// ready before it is invoked.
117    async fn call(&self, req: Req, ctx: Ctx<'_, Self, St>) -> Result<Self::Res, Self::Error>;
118
119    #[inline]
120    /// Returns when the service is ready to process requests.
121    ///
122    /// If the service is at capacity, `ready` will not return immediately. The current
123    /// task is notified when the service becomes ready again. This function should
124    /// be called while executing on a task.
125    ///
126    /// **Note:** Pipeline readiness is maintained across all services in the pipeline.
127    /// The pipeline can process requests only if every service in the pipeline is ready.
128    async fn ready(&self, ctx: Ctx<'_, Self, St>) -> Result<(), Self::Error> {
129        Ok(())
130    }
131
132    #[inline]
133    /// Shuts down the service.
134    ///
135    /// Returns when the service has been properly shut down.
136    async fn shutdown(&self, cfg: Ctx<'_, Self, St>) {}
137
138    #[inline]
139    /// Maps this service's output to a different type, returning a new service.
140    ///
141    /// This is similar to `Option::map` or `Iterator::map`, changing the
142    /// output type of the underlying service.
143    ///
144    /// This function consumes the original service and returns a wrapped version,
145    /// following the pattern of standard library `map` methods.
146    fn map<F, Res>(self, f: F) -> ServiceChain<dev::Map<F, Self, Res>, St, Req>
147    where
148        Self: Sized,
149        F: Fn(Self::Res) -> Res,
150    {
151        service(dev::Map::new(f, self))
152    }
153
154    #[inline]
155    /// Maps this service's error to a different type, returning a new service.
156    ///
157    /// This is similar to `Result::map_err`, changing the error type of the
158    /// underlying service. It is useful, for example, to ensure multiple
159    /// services have the same error type.
160    ///
161    /// This function consumes the original service and returns a wrapped version.
162    fn map_err<F, E>(self, f: F) -> ServiceChain<dev::MapErr<F, Self, E>, St, Req>
163    where
164        Self: Sized,
165        F: Fn(Self::Error) -> E,
166    {
167        service(dev::MapErr::new(f, self))
168    }
169
170    #[inline]
171    /// Call another service after call to this one has resolved successfully.
172    ///
173    /// This function can be used to chain two services together and ensure that
174    /// the second service isn't called until call to the fist service have
175    /// finished. Result of the call to the first service is used as an
176    /// input parameter for the second service's call.
177    ///
178    /// Note that this function consumes the receiving service and returns a
179    /// wrapped version of it.
180    fn and_then<Next, F>(self, f: F) -> ServiceChain<dev::AndThen<Self, Next>, St, Req>
181    where
182        Self: Sized,
183        Next: Service<St, Self::Res, Error = Self::Error>,
184        F: IntoService<Next, St, Self::Res>,
185    {
186        service(dev::AndThen::new(self, f.into_service()))
187    }
188
189    #[inline]
190    /// Wraps it in a container.
191    fn pipeline(self, st: St) -> Pipeline<Req, Self::Res, Self::Error>
192    where
193        Self: Sized + 'static,
194        St: 'static,
195        Req: 'static,
196    {
197        Pipeline::new(st, self)
198    }
199}
200
201/// A factory for creating `Service`s.
202///
203/// This is useful when new `Service`s must be produced dynamically. For example,
204/// a TCP server listener accepts new connections, constructs a new `Service` for
205/// each connection using the `ServiceFactory` trait, and uses that service to
206/// handle inbound requests.
207///
208/// `Config` represents the configuration type for the service factory.
209///
210/// Simple factories can often use [`fn_factory`] or [`fn_factory_with_config`]
211/// to reduce boilerplate.
212pub trait ServiceFactory<St, Req> {
213    /// Responses given by the created services.
214    type Res;
215
216    /// Errors produced by the created services.
217    type Error;
218
219    /// The type of `Service` produced by this factory.
220    type Service: Service<St, Req, Res = Self::Res, Error = Self::Error>;
221
222    /// Possible errors encountered during service construction.
223    type InitError;
224
225    /// Creates a new service asynchronously and returns it.
226    async fn create(&self, cfg: &St) -> Result<Self::Service, Self::InitError>;
227
228    #[inline]
229    /// Asynchronously creates a new service and wraps it in a container.
230    async fn pipeline(
231        &self,
232        st: St,
233    ) -> Result<Pipeline<Req, Self::Res, Self::Error>, Self::InitError>
234    where
235        Self: 'static,
236        St: 'static,
237        Req: 'static,
238    {
239        let svc = self.create(&st).await?;
240        Ok(Pipeline::new(st, svc))
241    }
242
243    #[inline]
244    /// Returns a new service that maps this service's output to a different type.
245    fn map<F, Res>(self, f: F) -> ServiceChainFactory<dev::MapFactory<F, Self, Res>, St, Req>
246    where
247        Self: Sized,
248        F: Fn(Self::Res) -> Res + Clone,
249    {
250        factory(dev::MapFactory::new(f, self))
251    }
252
253    #[inline]
254    /// Transforms this service's error into another error,
255    /// producing a new service.
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    /// Call another service after call to this one has resolved successfully.
276    fn and_then<U, F>(self, f: F) -> ServiceChainFactory<dev::AndThenFactory<Self, U>, St, Req>
277    where
278        Self: Sized,
279        U: ServiceFactory<St, Self::Res, Error = Self::Error, InitError = Self::InitError>,
280        F: IntoServiceFactory<U, St, Self::Res>,
281    {
282        factory(dev::AndThenFactory::new(self, f.into_factory()))
283    }
284
285    /// Creates a boxed service factory.
286    fn boxed(self) -> boxed::BoxServiceFactory<St, Req, Self::Res, Self::Error, Self::InitError>
287    where
288        St: 'static,
289        Req: 'static,
290        Self: Sized + 'static,
291    {
292        boxed::factory(self)
293    }
294}
295
296impl<S, St, Req> Service<St, Req> for &S
297where
298    S: Service<St, Req>,
299{
300    type Res = S::Res;
301    type Error = S::Error;
302
303    #[inline]
304    async fn ready(&self, ctx: Ctx<'_, Self, St>) -> Result<(), S::Error> {
305        ctx.ready(&**self).await
306    }
307
308    #[inline]
309    async fn call(&self, req: Req, ctx: Ctx<'_, Self, St>) -> Result<S::Res, S::Error> {
310        ctx.call_nowait(&**self, req).await
311    }
312
313    #[inline]
314    async fn shutdown(&self, ctx: Ctx<'_, Self, St>) {
315        ctx.shutdown(&**self).await;
316    }
317}
318
319impl<S, St, Req> Service<St, Req> for Box<S>
320where
321    S: Service<St, Req>,
322{
323    type Res = S::Res;
324    type Error = S::Error;
325
326    #[inline]
327    async fn ready(&self, ctx: Ctx<'_, Self, St>) -> Result<(), S::Error> {
328        ctx.ready(&**self).await
329    }
330
331    #[inline]
332    async fn call(&self, req: Req, ctx: Ctx<'_, Self, St>) -> Result<S::Res, S::Error> {
333        ctx.call_nowait(&**self, req).await
334    }
335
336    #[inline]
337    async fn shutdown(&self, ctx: Ctx<'_, Self, St>) {
338        ctx.shutdown(&**self).await;
339    }
340}
341
342impl<S, St, Req> Service<St, Req> for Rc<S>
343where
344    S: Service<St, Req>,
345{
346    type Res = S::Res;
347    type Error = S::Error;
348
349    #[inline]
350    async fn ready(&self, ctx: Ctx<'_, Self, St>) -> Result<(), S::Error> {
351        ctx.ready(&**self).await
352    }
353
354    #[inline]
355    async fn call(&self, req: Req, ctx: Ctx<'_, Self, St>) -> Result<S::Res, S::Error> {
356        ctx.call_nowait(&**self, req).await
357    }
358
359    #[inline]
360    async fn shutdown(&self, ctx: Ctx<'_, Self, St>) {
361        ctx.shutdown(&**self).await;
362    }
363}
364
365impl<Sf, St, Req> ServiceFactory<St, Req> for Rc<Sf>
366where
367    Sf: ServiceFactory<St, Req>,
368{
369    type Res = Sf::Res;
370    type Error = Sf::Error;
371    type Service = Sf::Service;
372    type InitError = Sf::InitError;
373
374    async fn create(&self, cfg: &St) -> Result<Self::Service, Self::InitError> {
375        self.as_ref().create(cfg).await
376    }
377}
378
379/// Trait for types that can be called
380pub trait ServiceCaller<Req, Res, Err> {
381    /// Wait for service readiness and then call service.
382    async fn call_service(&self, req: Req) -> Result<Res, Err>;
383}
384
385/// Trait for types that can be converted to a `Service`
386pub trait IntoService<S, St, Req>
387where
388    S: Service<St, Req>,
389{
390    /// Convert to a `Service`
391    fn into_service(self) -> S;
392}
393
394/// Trait for types that can be converted to a `ServiceFactory`
395pub trait IntoServiceFactory<Sf, St, Req>
396where
397    Sf: ServiceFactory<St, Req>,
398{
399    /// Convert `Self` to a `ServiceFactory`
400    fn into_factory(self) -> Sf;
401}
402
403impl<S, St, Req> IntoService<S, St, Req> for S
404where
405    S: Service<St, Req>,
406{
407    #[inline]
408    fn into_service(self) -> S {
409        self
410    }
411}
412
413impl<Sf, St, Req> IntoServiceFactory<Sf, St, Req> for Sf
414where
415    Sf: ServiceFactory<St, Req>,
416{
417    #[inline]
418    fn into_factory(self) -> Sf {
419        self
420    }
421}
422
423pub mod dev {
424    pub use crate::and_then::{AndThen, AndThenFactory};
425    pub use crate::apply::{Apply, ApplyCtx, ApplyFactory};
426    pub use crate::chain::{ServiceChain, ServiceChainFactory};
427    pub use crate::fn_ready::FnReadiness;
428    pub use crate::fn_service::{FnFactory, FnService, FnServiceSt, FnServiceStFactory};
429    pub use crate::fn_shutdown::FnShutdown;
430    pub use crate::map::{Map, MapFactory};
431    pub use crate::map_err::{MapErr, MapErrFactory};
432    pub use crate::map_init_err::MapInitErr;
433    pub use crate::map_state::{MapState, MapStateFactory};
434    pub use crate::middleware::{ApplyMiddleware, FnMiddleware};
435    pub use crate::then::{Then, ThenFactory};
436}