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