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;
22mod bind;
23mod binding;
24pub mod boxed;
25pub mod cfg;
26mod chain;
27mod ctx;
28mod fn_ready;
29mod fn_service;
30mod fn_shutdown;
31mod macros;
32mod map;
33mod map_err;
34mod map_init_err;
35mod map_state;
36mod middleware;
37mod readiness;
38mod ready;
39pub mod state;
40mod then;
41mod util;
42
43pub mod pipeline;
44mod pl_factory;
45mod pl_inner;
46mod pl_state;
47
48pub use crate::apply::{apply_fn, apply_fn_factory};
49pub use crate::bind::BindCtx;
50pub use crate::binding::{ReadyBinding, ReadyBindingMiddleware};
51pub use crate::chain::{ServiceChain, ServiceChainFactory, factory, service};
52pub use crate::ctx::Ctx;
53pub use crate::fn_service::{fn_factory, fn_service, fn_service_st};
54pub use crate::map_state::{map_state, map_state_factory};
55pub use crate::middleware::{Identity, Middleware, Stack, apply, fn_layer};
56pub use crate::pipeline::Pipeline;
57pub use crate::readiness::{ReadyCallback, ReadyLink, ReadyNotifier};
58pub use crate::ready::{ready, ready_factory};
59pub use crate::state::{RequestState, State};
60
61#[allow(unused_variables)]
62/// An asynchronous operation from a request to a response.
63///
64/// A service receives requests and asynchronously produces responses.
65/// Conceptually, it is similar to:
66///
67/// ```rust,ignore
68/// async fn(Request) -> Result<Response, Error>
69/// ```
70///
71/// The request and pipeline-state types are generic parameters. The response
72/// and error types are associated types, allowing one service type to implement
73/// `Service` for multiple request types.
74///
75/// Methods take `&self`, so implementations that mutate internal state must use
76/// interior mutability such as `Cell`, `RefCell`, or a synchronization
77/// primitive when appropriate.
78///
79/// The same abstraction can represent client- and server-side operations.
80/// Services focus on transformation, making them straightforward to test and
81/// compose.
82///
83/// A service call requires a [`Ctx`] and therefore runs through a [`Pipeline`]
84/// or from another service. The pipeline coordinates readiness across a
85/// composed service chain before dispatching a request.
86///
87/// ```rust
88/// # use std::convert::Infallible;
89/// #
90/// # use ntex_service::{Service, Ctx};
91///
92/// struct MyService;
93///
94/// impl Service<(), u8> for MyService {
95///     type Res = u64;
96///     type Error = Infallible;
97///
98///     async fn call(&self, req: u8, ctx: Ctx<'_, Self>) -> Result<Self::Res, Self::Error> {
99///         Ok(req as u64)
100///     }
101/// }
102/// ```
103///
104/// Simple services do not need a manual trait implementation. The example
105/// above can be expressed with [`fn_service`]:
106///
107/// ```rust
108/// # use std::convert::Infallible;
109/// # use ntex_service::{Pipeline, fn_service};
110/// #
111/// # async fn run() -> Result<(), Infallible> {
112/// let service = fn_service(|req: u8| async move {
113///     Ok::<_, Infallible>(u64::from(req))
114/// });
115/// let pipeline = Pipeline::new((), service);
116///
117/// assert_eq!(pipeline.call(10).await?, 10);
118/// # Ok(())
119/// # }
120/// ```
121///
122/// # Framework guide
123///
124/// - [Service Model](https://docs.ntex.rs/1-service.html)
125/// - [Service Pipelines](https://docs.ntex.rs/2-pipeline.html)
126/// - [Service State](https://docs.ntex.rs/3-state.html)
127#[diagnostic::on_unimplemented(
128    message = "`{Self}` is not a `Service<{St}, {Req}>`",
129    label = "expected a service for `{Req}` requests with `{St}` state",
130    note = "the state type comes first: `impl Service<St, Req> for T`; use `()` for no state",
131    note = "check that `St` and `Req` match the pipeline or parent service exactly",
132    note = "wrap async functions with `fn_service` (`async |req| ..`) or `fn_service_st` (`async |st, req| ..`)"
133)]
134pub trait Service<St, Req> {
135    /// Response produced by the service.
136    type Res;
137
138    /// Error produced while checking readiness or processing a request.
139    type Error;
140
141    /// Processes a request and asynchronously returns the response.
142    ///
143    /// The enclosing pipeline checks readiness before invoking this method.
144    /// Implementations should not call their own `ready` method. A composed
145    /// service can use [`Ctx::call`] to call an inner service using the closest
146    /// upstream readiness cache. A service wrapped with [`ready()`] can be
147    /// called directly to check its own cache instead.
148    async fn call(&self, req: Req, ctx: Ctx<'_, Self, St>) -> Result<Self::Res, Self::Error>;
149
150    #[inline]
151    /// Initializes the service within a [`Pipeline`].
152    ///
153    /// Called during pipeline construction or [`Ctx::create`], before runtime
154    /// operations on the new service.
155    ///
156    /// The default is suitable for leaves without notification sources.
157    /// Composites must bind every existing child, and services with changing
158    /// readiness must subscribe to all their sources here. Lazily created
159    /// children must be initialized through [`Ctx::create`], which binds them
160    /// explicitly before use.
161    /// Register reusable [`ReadyCallback`] handles with [`BindCtx::on_notify`]
162    /// to observe notifications reaching the service's scope.
163    ///
164    /// Binding is synchronous. Shared services can be bound to multiple parents;
165    /// implementations must support repeated binding.
166    /// [`forward!`] forwards to one or several service fields. Register [`Pipeline`] fields with [`Pipeline::bind_to`].
167    /// Ordinary readiness checks, calls, and shutdown do not add dependencies.
168    fn bind(&self, ctx: BindCtx<'_>) {}
169
170    #[inline]
171    /// Waits until the service is ready to process a request.
172    ///
173    /// If the service is at capacity, the returned future remains pending until
174    /// capacity becomes available.
175    ///
176    /// Pipeline readiness is coordinated across all services in a composed
177    /// chain. A request is dispatched only when the chain is ready.
178    ///
179    /// A [`Pipeline`] or [`ReadyBinding`] caches success until a subscribed
180    /// [`ReadyNotifier`] invalidates it; calls do not consume that success.
181    /// Subscribe in [`Self::bind`] and notify on every readiness-changing
182    /// transition, including consuming or releasing capacity and external
183    /// failures, before another caller can use stale cached readiness.
184    /// A task wake schedules a pending check but does not invalidate a cached
185    /// success. Services must not rely on this method being polled on every
186    /// call to drive background progress.
187    ///
188    /// Readiness checks and their wake sources run on the pipeline's thread.
189    /// This local-only requirement does not apply to task wakers supplied to
190    /// [`Self::call`] or [`Self::shutdown`].
191    ///
192    /// Readiness may inspect [`Ctx::st`], but it describes the shared gate, not
193    /// a reservation for one request or state value. State-taking pipeline
194    /// handles share the same cache; independent gates need separate pipelines
195    /// or service bindings.
196    async fn ready(&self, ctx: Ctx<'_, Self, St>) -> Result<(), Self::Error> {
197        Ok(())
198    }
199
200    #[inline]
201    /// Shuts down the service.
202    ///
203    /// Returns when the service has been properly shut down.
204    async fn shutdown(&self, ctx: Ctx<'_, Self, St>) {}
205
206    #[inline]
207    /// Maps this service's output to a different type, returning a new service.
208    ///
209    /// This is similar to `Option::map` or `Iterator::map`, changing the
210    /// output type of the underlying service.
211    ///
212    /// This function consumes the original service and returns a wrapped version,
213    /// following the pattern of standard library `map` methods.
214    fn map<F, Res>(self, f: F) -> ServiceChain<dev::Map<F, Self, Res>, St, Req>
215    where
216        Self: Sized,
217        F: Fn(Self::Res) -> Res,
218    {
219        service(dev::Map::new(f, self))
220    }
221
222    #[inline]
223    /// Maps this service's error to a different type, returning a new service.
224    ///
225    /// This is similar to `Result::map_err`, changing the error type of the
226    /// underlying service. It is useful, for example, to ensure multiple
227    /// services have the same error type.
228    ///
229    /// This function consumes the original service and returns a wrapped version.
230    fn map_err<F, E>(self, f: F) -> ServiceChain<dev::MapErr<F, Self, E>, St, Req>
231    where
232        Self: Sized,
233        F: Fn(Self::Error) -> E,
234    {
235        service(dev::MapErr::new(f, self))
236    }
237
238    #[inline]
239    /// Calls another service after this service completes successfully.
240    ///
241    /// The first service's response becomes the second service's request. If
242    /// the first service returns an error, the second service is not called.
243    fn and_then<Next, F>(self, f: F) -> ServiceChain<dev::AndThen<Self, Next>, St, Req>
244    where
245        Self: Sized,
246        Next: Service<St, Self::Res, Error = Self::Error>,
247        F: IntoService<Next, St, Self::Res>,
248    {
249        service(dev::AndThen::new(self, f.into_service()))
250    }
251
252    #[inline]
253    /// Wraps this service and its state in a [`Pipeline`].
254    fn pipeline(self, st: St) -> Pipeline<Req, Self::Res, Self::Error>
255    where
256        Self: Sized + 'static,
257        St: 'static,
258        Req: 'static,
259    {
260        Pipeline::new(st, self)
261    }
262}
263
264/// A factory for asynchronously creating [`Service`] values.
265///
266/// This is useful when new `Service`s must be produced dynamically. For example,
267/// a TCP server listener accepts new connections, constructs a new `Service` for
268/// each connection using the `ServiceFactory` trait, and uses that service to
269/// handle inbound requests.
270///
271/// `St` is the state type shared by the factory and its services.
272///
273/// Simple factories can often use [`fn_factory`] to reduce boilerplate.
274#[diagnostic::on_unimplemented(
275    message = "`{Self}` is not a `ServiceFactory<{St}, {Req}>`",
276    label = "expected a factory of services for `{Req}` requests with `{St}` state",
277    note = "the state type comes first: `impl ServiceFactory<St, Req> for T`; use `()` for no state",
278    note = "a `Service` is not a factory; use `fn_factory(async |st| Ok(service))` to create it per pipeline",
279    note = "async functions `async |req| ..` and `async |st, req| ..` convert into factories automatically"
280)]
281pub trait ServiceFactory<St, Req> {
282    /// Response produced by the created services.
283    type Res;
284
285    /// Error produced by the created services.
286    type Error;
287
288    /// The type of `Service` produced by this factory.
289    type Service: Service<St, Req, Res = Self::Res, Error = Self::Error>;
290
291    /// Error that can occur while constructing a service.
292    type InitError;
293
294    /// Asynchronously creates a service using the supplied state.
295    async fn create(&self, st: &St) -> Result<Self::Service, Self::InitError>;
296
297    #[inline]
298    /// Creates a service and wraps it with its state in a [`Pipeline`].
299    async fn pipeline(
300        &self,
301        st: St,
302    ) -> Result<Pipeline<Req, Self::Res, Self::Error>, Self::InitError>
303    where
304        Self: 'static,
305        St: 'static,
306        Req: 'static,
307    {
308        let svc = self.create(&st).await?;
309        Ok(Pipeline::new(st, svc))
310    }
311
312    #[inline]
313    /// Returns a factory whose services map responses to a different type.
314    fn map<F, Res>(self, f: F) -> ServiceChainFactory<dev::MapFactory<F, Self, Res>, St, Req>
315    where
316        Self: Sized,
317        F: Fn(Self::Res) -> Res + Clone,
318    {
319        factory(dev::MapFactory::new(f, self))
320    }
321
322    #[inline]
323    /// Returns a factory whose services map errors to a different type.
324    fn map_err<F, E>(self, f: F) -> ServiceChainFactory<dev::MapErrFactory<F, Self, E>, St, Req>
325    where
326        Self: Sized,
327        F: Fn(Self::Error) -> E + Clone,
328    {
329        factory(dev::MapErrFactory::new(f, self))
330    }
331
332    #[inline]
333    /// Maps this factory's initialization error to a different error,
334    /// returning a new service factory.
335    fn map_init_err<F, E>(self, f: F) -> ServiceChainFactory<dev::MapInitErr<F, Self, E>, St, Req>
336    where
337        Self: Sized,
338        F: Fn(Self::InitError) -> E + Clone,
339    {
340        factory(dev::MapInitErr::new(f, self))
341    }
342
343    /// Chains another factory after this factory's services.
344    ///
345    /// Each response from the first service becomes a request to the second
346    /// service. The second service is not called when the first returns an
347    /// error.
348    fn and_then<U, F>(self, f: F) -> ServiceChainFactory<dev::AndThenFactory<Self, U>, St, Req>
349    where
350        Self: Sized,
351        U: ServiceFactory<St, Self::Res, Error = Self::Error, InitError = Self::InitError>,
352        F: IntoServiceFactory<U, St, Self::Res>,
353    {
354        factory(dev::AndThenFactory::new(self, f.into_factory()))
355    }
356
357    /// Creates a boxed service factory.
358    fn boxed(self) -> boxed::BoxServiceFactory<St, Req, Self::Res, Self::Error, Self::InitError>
359    where
360        St: 'static,
361        Req: 'static,
362        Self: Sized + 'static,
363    {
364        boxed::factory(self)
365    }
366}
367
368impl<S, St, Req> Service<St, Req> for &S
369where
370    S: Service<St, Req>,
371{
372    type Res = S::Res;
373    type Error = S::Error;
374
375    fn bind(&self, ctx: BindCtx<'_>) {
376        ctx.bind(&**self);
377    }
378
379    #[inline]
380    async fn ready(&self, ctx: Ctx<'_, Self, St>) -> Result<(), S::Error> {
381        Service::ready(&**self, ctx.cast()).await
382    }
383
384    #[inline]
385    async fn call(&self, req: Req, ctx: Ctx<'_, Self, St>) -> Result<S::Res, S::Error> {
386        Service::call(&**self, req, ctx.cast()).await
387    }
388
389    #[inline]
390    async fn shutdown(&self, ctx: Ctx<'_, Self, St>) {
391        Service::shutdown(&**self, ctx.cast()).await;
392    }
393}
394
395impl<S, St, Req> Service<St, Req> for Box<S>
396where
397    S: Service<St, Req>,
398{
399    type Res = S::Res;
400    type Error = S::Error;
401
402    fn bind(&self, ctx: BindCtx<'_>) {
403        ctx.bind(&**self);
404    }
405
406    #[inline]
407    async fn ready(&self, ctx: Ctx<'_, Self, St>) -> Result<(), S::Error> {
408        Service::ready(&**self, ctx.cast()).await
409    }
410
411    #[inline]
412    async fn call(&self, req: Req, ctx: Ctx<'_, Self, St>) -> Result<S::Res, S::Error> {
413        Service::call(&**self, req, ctx.cast()).await
414    }
415
416    #[inline]
417    async fn shutdown(&self, ctx: Ctx<'_, Self, St>) {
418        Service::shutdown(&**self, ctx.cast()).await;
419    }
420}
421
422impl<S, St, Req> Service<St, Req> for Rc<S>
423where
424    S: Service<St, Req>,
425{
426    type Res = S::Res;
427    type Error = S::Error;
428
429    fn bind(&self, ctx: BindCtx<'_>) {
430        ctx.bind(&**self);
431    }
432
433    #[inline]
434    async fn ready(&self, ctx: Ctx<'_, Self, St>) -> Result<(), S::Error> {
435        Service::ready(&**self, ctx.cast()).await
436    }
437
438    #[inline]
439    async fn call(&self, req: Req, ctx: Ctx<'_, Self, St>) -> Result<S::Res, S::Error> {
440        Service::call(&**self, req, ctx.cast()).await
441    }
442
443    #[inline]
444    async fn shutdown(&self, ctx: Ctx<'_, Self, St>) {
445        Service::shutdown(&**self, ctx.cast()).await;
446    }
447}
448
449impl<Sf, St, Req> ServiceFactory<St, Req> for Rc<Sf>
450where
451    Sf: ServiceFactory<St, Req>,
452{
453    type Res = Sf::Res;
454    type Error = Sf::Error;
455    type Service = Sf::Service;
456    type InitError = Sf::InitError;
457
458    async fn create(&self, st: &St) -> Result<Self::Service, Self::InitError> {
459        self.as_ref().create(st).await
460    }
461}
462
463/// A common interface for values that can call a service.
464pub trait ServiceCaller<Req, Res, Err> {
465    /// Waits for readiness, then calls the service.
466    async fn call_service(&self, req: Req) -> Result<Res, Err>;
467}
468
469/// Conversion into a [`Service`].
470#[diagnostic::on_unimplemented(
471    message = "`{Self}` cannot be converted into a `Service<{St}, {Req}>`",
472    label = "expected a service or an async function for `{Req}` requests",
473    note = "accepted values: a `Service<{St}, {Req}>`, `async |req: {Req}| -> Result<Res, Err>`, or `async |st: &{St}, req: {Req}| -> Result<Res, Err>`",
474    note = "async functions must return `Result`"
475)]
476pub trait IntoService<S, St, Req>
477where
478    S: Service<St, Req>,
479{
480    /// Converts this value into a service.
481    fn into_service(self) -> S;
482}
483
484/// Conversion into a [`ServiceFactory`].
485#[diagnostic::on_unimplemented(
486    message = "`{Self}` cannot be converted into a `ServiceFactory<{St}, {Req}>`",
487    label = "expected a service factory or an async function for `{Req}` requests",
488    note = "accepted values: a `ServiceFactory<{St}, {Req}>`, a `Clone` async function `async |req: {Req}| -> Result<Res, Err>` or `async |st: &{St}, req: {Req}| -> Result<Res, Err>`, or `async |st: &{St}| -> Result<impl Service, Err>`",
489    note = "a `Service` value is not a factory; wrap its construction with `fn_factory`"
490)]
491pub trait IntoServiceFactory<Sf, St, Req>
492where
493    Sf: ServiceFactory<St, Req>,
494{
495    /// Converts this value into a service factory.
496    fn into_factory(self) -> Sf;
497}
498
499impl<S, St, Req> IntoService<S, St, Req> for S
500where
501    S: Service<St, Req>,
502{
503    #[inline]
504    fn into_service(self) -> S {
505        self
506    }
507}
508
509impl<Sf, St, Req> IntoServiceFactory<Sf, St, Req> for Sf
510where
511    Sf: ServiceFactory<St, Req>,
512{
513    #[inline]
514    fn into_factory(self) -> Sf {
515        self
516    }
517}
518
519/// Combinator and helper types used by the public API.
520///
521/// Most services can be built through the crate-level functions and the
522/// [`Service`] and [`ServiceFactory`] methods without naming these types.
523/// This module exposes their concrete return types for type annotations,
524/// reusable wrappers, and custom composition.
525///
526/// - [`ServiceChain`] and [`ServiceChainFactory`] are builders returned by
527///   [`service()`] and [`factory()`].
528/// - [`dev::FnService`], [`dev::FnServiceSt`], and [`dev::FnFactory`] adapt
529///   functions into services and factories.
530/// - [`dev::AndThen`], [`dev::Then`], [`dev::Map`], and [`dev::MapErr`] compose
531///   or transform service results, with corresponding factory adapters.
532/// - [`dev::ApplyCtx`] provides state and inner-service calls to
533///   [`apply_fn`] middleware; [`dev::MapState`] substitutes the inner state.
534/// - [`dev::Ready`] and [`dev::ReadyFactory`] are the wrappers produced by
535///   [`ready()`] and [`ready_factory()`] for independent cached readiness.
536///
537/// # Framework guide
538///
539/// - [Service Pipelines](https://docs.ntex.rs/2-pipeline.html)
540/// - [Service State](https://docs.ntex.rs/3-state.html)
541#[doc(hidden)]
542pub mod __private {
543    pub use crate::readiness::Generation;
544    pub use crate::util::{join, ready_field, ready_join};
545}
546
547pub mod dev {
548    pub use crate::and_then::{AndThen, AndThenFactory};
549    pub use crate::apply::{Apply, ApplyCtx, ApplyFactory};
550    pub use crate::chain::{ServiceChain, ServiceChainFactory};
551    pub use crate::fn_ready::FnReadiness;
552    pub use crate::fn_service::{FnFactory, FnService, FnServiceSt, FnServiceStFactory};
553    pub use crate::fn_shutdown::FnShutdown;
554    pub use crate::map::{Map, MapFactory};
555    pub use crate::map_err::{MapErr, MapErrFactory};
556    pub use crate::map_init_err::MapInitErr;
557    pub use crate::map_state::{MapState, MapStateFactory};
558    pub use crate::middleware::{ApplyMiddleware, FnMiddleware};
559    pub use crate::ready::{Ready, ReadyFactory};
560    pub use crate::then::{Then, ThenFactory};
561}
562
563#[cfg(test)]
564#[path = "../tests/unit/lib.rs"]
565mod tests;