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