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