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;
22pub mod boxed;
23pub mod cfg;
24mod chain;
25mod ctx;
26mod fn_ready;
27mod fn_service;
28mod fn_shutdown;
29mod macros;
30mod map;
31mod map_err;
32mod map_init_err;
33mod map_state;
34mod middleware;
35pub mod state;
36mod then;
37mod util;
38
39pub mod pipeline;
40mod pl_factory;
41mod pl_inner;
42mod pl_state;
43
44pub use crate::apply::{apply_fn, apply_fn_factory};
45pub use crate::chain::{ServiceChain, ServiceChainFactory, factory, service};
46pub use crate::ctx::Ctx;
47pub use crate::fn_service::{fn_factory, fn_service, fn_service_st};
48pub use crate::map_state::{map_state, map_state_factory};
49pub use crate::middleware::{Identity, Middleware, Stack, apply, fn_layer};
50pub use crate::pipeline::Pipeline;
51pub use crate::state::{RequestState, State};
52
53#[allow(unused_variables)]
54/// An asynchronous operation from a request to a response.
55///
56/// A service receives requests and asynchronously produces responses.
57/// Conceptually, it is similar to:
58///
59/// ```rust,ignore
60/// async fn(Request) -> Result<Response, Error>
61/// ```
62///
63/// The request and pipeline-state types are generic parameters. The response
64/// and error types are associated types, allowing one service type to implement
65/// `Service` for multiple request types.
66///
67/// Methods take `&self`, so implementations that mutate internal state must use
68/// interior mutability such as `Cell`, `RefCell`, or a synchronization
69/// primitive when appropriate.
70///
71/// The same abstraction can represent client- and server-side operations.
72/// Services focus on transformation, making them straightforward to test and
73/// compose.
74///
75/// A service call requires a [`Ctx`] and therefore runs through a [`Pipeline`]
76/// or from another service. The pipeline coordinates readiness across a
77/// composed service chain before dispatching a request.
78///
79/// ```rust
80/// # use std::convert::Infallible;
81/// #
82/// # use ntex_service::{Service, Ctx};
83///
84/// struct MyService;
85///
86/// impl Service<(), u8> for MyService {
87///     type Res = u64;
88///     type Error = Infallible;
89///
90///     async fn call(&self, req: u8, ctx: Ctx<'_, Self>) -> Result<Self::Res, Self::Error> {
91///         Ok(req as u64)
92///     }
93/// }
94/// ```
95///
96/// Simple services do not need a manual trait implementation. The example
97/// above can be expressed with [`fn_service`]:
98///
99/// ```rust
100/// # use std::convert::Infallible;
101/// # use ntex_service::{Pipeline, fn_service};
102/// #
103/// # async fn run() -> Result<(), Infallible> {
104/// let service = fn_service(|req: u8| async move {
105///     Ok::<_, Infallible>(u64::from(req))
106/// });
107/// let pipeline = Pipeline::new((), service);
108///
109/// assert_eq!(pipeline.call(10).await?, 10);
110/// # Ok(())
111/// # }
112/// ```
113pub trait Service<St, Req> {
114    /// Response produced by the service.
115    type Res;
116
117    /// Error produced while checking readiness or processing a request.
118    type Error;
119
120    /// Processes a request and asynchronously returns the response.
121    ///
122    /// The enclosing pipeline checks readiness before invoking this method.
123    /// Implementations should not call their own `ready` method. A composed
124    /// service can use `ctx` to call an inner service.
125    async fn call(&self, req: Req, ctx: Ctx<'_, Self, St>) -> Result<Self::Res, Self::Error>;
126
127    #[inline]
128    /// Waits until the service is ready to process a request.
129    ///
130    /// If the service is at capacity, the returned future remains pending until
131    /// capacity becomes available.
132    ///
133    /// Pipeline readiness is coordinated across all services in a composed
134    /// chain. A request is dispatched only when the chain is ready.
135    async fn ready(&self, ctx: Ctx<'_, Self, St>) -> Result<(), Self::Error> {
136        Ok(())
137    }
138
139    #[inline]
140    /// Shuts down the service.
141    ///
142    /// Returns when the service has been properly shut down.
143    async fn shutdown(&self, ctx: Ctx<'_, Self, St>) {}
144
145    #[inline]
146    /// Maps this service's output to a different type, returning a new service.
147    ///
148    /// This is similar to `Option::map` or `Iterator::map`, changing the
149    /// output type of the underlying service.
150    ///
151    /// This function consumes the original service and returns a wrapped version,
152    /// following the pattern of standard library `map` methods.
153    fn map<F, Res>(self, f: F) -> ServiceChain<dev::Map<F, Self, Res>, St, Req>
154    where
155        Self: Sized,
156        F: Fn(Self::Res) -> Res,
157    {
158        service(dev::Map::new(f, self))
159    }
160
161    #[inline]
162    /// Maps this service's error to a different type, returning a new service.
163    ///
164    /// This is similar to `Result::map_err`, changing the error type of the
165    /// underlying service. It is useful, for example, to ensure multiple
166    /// services have the same error type.
167    ///
168    /// This function consumes the original service and returns a wrapped version.
169    fn map_err<F, E>(self, f: F) -> ServiceChain<dev::MapErr<F, Self, E>, St, Req>
170    where
171        Self: Sized,
172        F: Fn(Self::Error) -> E,
173    {
174        service(dev::MapErr::new(f, self))
175    }
176
177    #[inline]
178    /// Calls another service after this service completes successfully.
179    ///
180    /// The first service's response becomes the second service's request. If
181    /// the first service returns an error, the second service is not called.
182    fn and_then<Next, F>(self, f: F) -> ServiceChain<dev::AndThen<Self, Next>, St, Req>
183    where
184        Self: Sized,
185        Next: Service<St, Self::Res, Error = Self::Error>,
186        F: IntoService<Next, St, Self::Res>,
187    {
188        service(dev::AndThen::new(self, f.into_service()))
189    }
190
191    #[inline]
192    /// Wraps this service and its state in a [`Pipeline`].
193    fn pipeline(self, st: St) -> Pipeline<Req, Self::Res, Self::Error>
194    where
195        Self: Sized + 'static,
196        St: 'static,
197        Req: 'static,
198    {
199        Pipeline::new(st, self)
200    }
201}
202
203/// A factory for asynchronously creating [`Service`] values.
204///
205/// This is useful when new `Service`s must be produced dynamically. For example,
206/// a TCP server listener accepts new connections, constructs a new `Service` for
207/// each connection using the `ServiceFactory` trait, and uses that service to
208/// handle inbound requests.
209///
210/// `St` is the state type shared by the factory and its services.
211///
212/// Simple factories can often use [`fn_factory`] to reduce boilerplate.
213pub trait ServiceFactory<St, Req> {
214    /// Response produced by the created services.
215    type Res;
216
217    /// Error produced by the created services.
218    type Error;
219
220    /// The type of `Service` produced by this factory.
221    type Service: Service<St, Req, Res = Self::Res, Error = Self::Error>;
222
223    /// Error that can occur while constructing a service.
224    type InitError;
225
226    /// Asynchronously creates a service using the supplied state.
227    async fn create(&self, st: &St) -> Result<Self::Service, Self::InitError>;
228
229    #[inline]
230    /// Creates a service and wraps it with its state in a [`Pipeline`].
231    async fn pipeline(
232        &self,
233        st: St,
234    ) -> Result<Pipeline<Req, Self::Res, Self::Error>, Self::InitError>
235    where
236        Self: 'static,
237        St: 'static,
238        Req: 'static,
239    {
240        let svc = self.create(&st).await?;
241        Ok(Pipeline::new(st, svc))
242    }
243
244    #[inline]
245    /// Returns a factory whose services map responses to a different type.
246    fn map<F, Res>(self, f: F) -> ServiceChainFactory<dev::MapFactory<F, Self, Res>, St, Req>
247    where
248        Self: Sized,
249        F: Fn(Self::Res) -> Res + Clone,
250    {
251        factory(dev::MapFactory::new(f, self))
252    }
253
254    #[inline]
255    /// Returns a factory whose services map errors to a different type.
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    /// Chains another factory after this factory's services.
276    ///
277    /// Each response from the first service becomes a request to the second
278    /// service. The second service is not called when the first returns an
279    /// error.
280    fn and_then<U, F>(self, f: F) -> ServiceChainFactory<dev::AndThenFactory<Self, U>, St, Req>
281    where
282        Self: Sized,
283        U: ServiceFactory<St, Self::Res, Error = Self::Error, InitError = Self::InitError>,
284        F: IntoServiceFactory<U, St, Self::Res>,
285    {
286        factory(dev::AndThenFactory::new(self, f.into_factory()))
287    }
288
289    /// Creates a boxed service factory.
290    fn boxed(self) -> boxed::BoxServiceFactory<St, Req, Self::Res, Self::Error, Self::InitError>
291    where
292        St: 'static,
293        Req: 'static,
294        Self: Sized + 'static,
295    {
296        boxed::factory(self)
297    }
298}
299
300impl<S, St, Req> Service<St, Req> for &S
301where
302    S: Service<St, Req>,
303{
304    type Res = S::Res;
305    type Error = S::Error;
306
307    #[inline]
308    async fn ready(&self, ctx: Ctx<'_, Self, St>) -> Result<(), S::Error> {
309        ctx.ready(&**self).await
310    }
311
312    #[inline]
313    async fn call(&self, req: Req, ctx: Ctx<'_, Self, St>) -> Result<S::Res, S::Error> {
314        ctx.call_nowait(&**self, req).await
315    }
316
317    #[inline]
318    async fn shutdown(&self, ctx: Ctx<'_, Self, St>) {
319        ctx.shutdown(&**self).await;
320    }
321}
322
323impl<S, St, Req> Service<St, Req> for Box<S>
324where
325    S: Service<St, Req>,
326{
327    type Res = S::Res;
328    type Error = S::Error;
329
330    #[inline]
331    async fn ready(&self, ctx: Ctx<'_, Self, St>) -> Result<(), S::Error> {
332        ctx.ready(&**self).await
333    }
334
335    #[inline]
336    async fn call(&self, req: Req, ctx: Ctx<'_, Self, St>) -> Result<S::Res, S::Error> {
337        ctx.call_nowait(&**self, req).await
338    }
339
340    #[inline]
341    async fn shutdown(&self, ctx: Ctx<'_, Self, St>) {
342        ctx.shutdown(&**self).await;
343    }
344}
345
346impl<S, St, Req> Service<St, Req> for Rc<S>
347where
348    S: Service<St, Req>,
349{
350    type Res = S::Res;
351    type Error = S::Error;
352
353    #[inline]
354    async fn ready(&self, ctx: Ctx<'_, Self, St>) -> Result<(), S::Error> {
355        ctx.ready(&**self).await
356    }
357
358    #[inline]
359    async fn call(&self, req: Req, ctx: Ctx<'_, Self, St>) -> Result<S::Res, S::Error> {
360        ctx.call_nowait(&**self, req).await
361    }
362
363    #[inline]
364    async fn shutdown(&self, ctx: Ctx<'_, Self, St>) {
365        ctx.shutdown(&**self).await;
366    }
367}
368
369impl<Sf, St, Req> ServiceFactory<St, Req> for Rc<Sf>
370where
371    Sf: ServiceFactory<St, Req>,
372{
373    type Res = Sf::Res;
374    type Error = Sf::Error;
375    type Service = Sf::Service;
376    type InitError = Sf::InitError;
377
378    async fn create(&self, st: &St) -> Result<Self::Service, Self::InitError> {
379        self.as_ref().create(st).await
380    }
381}
382
383/// A common interface for values that can call a service.
384pub trait ServiceCaller<Req, Res, Err> {
385    /// Waits for readiness, then calls the service.
386    async fn call_service(&self, req: Req) -> Result<Res, Err>;
387}
388
389/// Conversion into a [`Service`].
390pub trait IntoService<S, St, Req>
391where
392    S: Service<St, Req>,
393{
394    /// Converts this value into a service.
395    fn into_service(self) -> S;
396}
397
398/// Conversion into a [`ServiceFactory`].
399pub trait IntoServiceFactory<Sf, St, Req>
400where
401    Sf: ServiceFactory<St, Req>,
402{
403    /// Converts this value into a service factory.
404    fn into_factory(self) -> Sf;
405}
406
407impl<S, St, Req> IntoService<S, St, Req> for S
408where
409    S: Service<St, Req>,
410{
411    #[inline]
412    fn into_service(self) -> S {
413        self
414    }
415}
416
417impl<Sf, St, Req> IntoServiceFactory<Sf, St, Req> for Sf
418where
419    Sf: ServiceFactory<St, Req>,
420{
421    #[inline]
422    fn into_factory(self) -> Sf {
423        self
424    }
425}
426
427/// Combinator and helper types used by the public API.
428pub mod dev {
429    pub use crate::and_then::{AndThen, AndThenFactory};
430    pub use crate::apply::{Apply, ApplyCtx, ApplyFactory};
431    pub use crate::chain::{ServiceChain, ServiceChainFactory};
432    pub use crate::fn_ready::FnReadiness;
433    pub use crate::fn_service::{FnFactory, FnService, FnServiceSt, FnServiceStFactory};
434    pub use crate::fn_shutdown::FnShutdown;
435    pub use crate::map::{Map, MapFactory};
436    pub use crate::map_err::{MapErr, MapErrFactory};
437    pub use crate::map_init_err::MapInitErr;
438    pub use crate::map_state::{MapState, MapStateFactory};
439    pub use crate::middleware::{ApplyMiddleware, FnMiddleware};
440    pub use crate::then::{Then, ThenFactory};
441}
442
443#[cfg(test)]
444mod tests {
445    use std::{cell::Cell, task::Poll};
446
447    use super::*;
448    use crate::dev::FnServiceSt;
449    use crate::pipeline::PipelineFactory;
450
451    #[derive(Clone, Debug, Default)]
452    struct Srv(Rc<Cell<usize>>);
453
454    impl Service<usize, usize> for Srv {
455        type Res = usize;
456        type Error = &'static str;
457
458        async fn ready(&self, ctx: Ctx<'_, Self, usize>) -> Result<(), Self::Error> {
459            // waker of the current readiness owner is available
460            assert!(ctx.poll_once(|_| true));
461            self.0.set(self.0.get() + 1);
462            Ok(())
463        }
464
465        async fn call(&self, req: usize, ctx: Ctx<'_, Self, usize>) -> Result<usize, Self::Error> {
466            let one = ctx.poll_once(|_| 1);
467            let two = ctx.poll_fn(|_| Poll::Ready(2)).await;
468            assert_eq!(one + two, 3);
469
470            if req == 0 { Err("zero") } else { Ok(req + *ctx) }
471        }
472
473        async fn shutdown(&self, _: Ctx<'_, Self, usize>) {
474            self.0.set(self.0.get() + 100);
475        }
476    }
477
478    struct Fwd {
479        inner: Srv,
480        pl: Pipeline<usize, usize, &'static str>,
481    }
482
483    impl Service<usize, usize> for Fwd {
484        type Res = usize;
485        type Error = usize;
486
487        crate::forward_ready!(usize, inner, str::len);
488
489        async fn call(&self, req: usize, ctx: Ctx<'_, Self, usize>) -> Result<usize, usize> {
490            let res = ctx.call(&self.inner, req).await.map_err(str::len)?;
491            self.pl.call(res).await.map_err(str::len)
492        }
493
494        crate::forward_shutdown!(usize, inner);
495    }
496
497    struct FwdPl {
498        pl: Pipeline<usize, usize, &'static str>,
499    }
500
501    impl Service<usize, usize> for FwdPl {
502        type Res = usize;
503        type Error = &'static str;
504
505        crate::forward_pl_ready!(usize, pl);
506        crate::forward_pl_shutdown!(usize, pl);
507
508        async fn call(&self, req: usize, _: Ctx<'_, Self, usize>) -> Result<usize, &'static str> {
509            self.pl.call(req).await
510        }
511    }
512
513    struct FwdPlErr {
514        pl: Pipeline<usize, usize, &'static str>,
515    }
516
517    impl Service<usize, usize> for FwdPlErr {
518        type Res = usize;
519        type Error = usize;
520
521        crate::forward_pl_ready!(usize, pl, str::len);
522
523        async fn call(&self, req: usize, _: Ctx<'_, Self, usize>) -> Result<usize, usize> {
524            self.pl.call(req).await.map_err(str::len)
525        }
526    }
527
528    #[ntex::test]
529    async fn service_wrappers() {
530        let cnt = Rc::new(Cell::new(0));
531
532        let srv: &'static Srv = Box::leak(Box::new(Srv(cnt.clone())));
533        let pl = Pipeline::new(1, srv);
534        assert_eq!(pl.call(1).await, Ok(2));
535        assert_eq!(pl.call(0).await, Err("zero"));
536        pl.shutdown().await;
537        assert_eq!(cnt.get(), 102);
538
539        let pl = Pipeline::new(2, Box::new(Srv(cnt.clone())));
540        assert_eq!(pl.call(1).await, Ok(3));
541        pl.shutdown().await;
542        assert_eq!(cnt.get(), 203);
543
544        let pl = Pipeline::new(3, Rc::new(Srv(cnt.clone())));
545        assert_eq!(pl.call(1).await, Ok(4));
546        pl.shutdown().await;
547        assert_eq!(cnt.get(), 304);
548
549        let pl = Pipeline::new(
550            1,
551            Fwd {
552                inner: Srv(cnt.clone()),
553                pl: Pipeline::new(10, Srv(cnt.clone())),
554            },
555        );
556        assert_eq!(pl.call(1).await, Ok(12));
557        assert_eq!(pl.call(0).await, Err(4));
558        pl.shutdown().await;
559        assert_eq!(cnt.get(), 409);
560
561        let pl = Pipeline::new(
562            1,
563            FwdPl {
564                pl: Pipeline::new(10, Srv(cnt.clone())),
565            },
566        );
567        assert_eq!(pl.call(1).await, Ok(11));
568        pl.shutdown().await;
569        assert_eq!(cnt.get(), 511);
570
571        let pl = Pipeline::new(
572            1,
573            FwdPlErr {
574                pl: Pipeline::new(10, Srv(cnt.clone())),
575            },
576        );
577        assert_eq!(pl.call(0).await, Err(4));
578        pl.shutdown().await;
579        assert_eq!(cnt.get(), 513);
580    }
581
582    #[ntex::test]
583    async fn pipeline_calls() {
584        let cnt = Rc::new(Cell::new(0));
585        let pl = Srv(cnt.clone()).pipeline(1);
586
587        assert_eq!(pl.call_static(1).await, Ok(2));
588        assert_eq!(pl.call_nowait(2).await, Ok(3));
589        assert_eq!(ServiceCaller::call_service(&pl, 3).await, Ok(4));
590        assert_eq!(cnt.get(), 2);
591
592        let b = pl.bind();
593        assert!(format!("{b:?}").contains("PipelineBinding"));
594        assert_eq!(b.call_static(4).await, Ok(5));
595        assert_eq!(cnt.get(), 3);
596
597        let svc = apply_fn(
598            Srv(cnt.clone()),
599            async |req: usize, svc: &dev::ApplyCtx<'_, Srv, usize, usize>| {
600                svc.call_service(req * 2).await
601            },
602        );
603        let pl = Pipeline::new(1, svc);
604        assert_eq!(pl.call(2).await, Ok(5));
605        assert_eq!(cnt.get(), 6);
606    }
607
608    #[ntex::test]
609    async fn fn_conversions() {
610        let pl = Pipeline::new(5, async |st: &usize, req: usize| Ok::<_, ()>(st + req));
611        assert_eq!(pl.call(1).await, Ok(6));
612
613        let _: FnServiceSt<_, usize, usize, usize, ()> =
614            IntoService::into_service(async |st: &usize, req: usize| Ok::<_, ()>(st + req));
615
616        let f = factory(async |st: &usize| Ok::<_, ()>(Srv(Rc::new(Cell::new(*st)))));
617        let pl = f.pipeline(1).await.unwrap();
618        assert_eq!(pl.call(1).await, Ok(2));
619    }
620
621    #[ntex::test]
622    async fn factory_combinators() {
623        let cnt = Rc::new(Cell::new(0));
624        let c = cnt.clone();
625        let f = fn_factory(async move |st: &usize| {
626            if *st == 0 {
627                Err(())
628            } else {
629                Ok::<_, ()>(Srv(c.clone()))
630            }
631        });
632
633        let rc = Rc::new(f.clone());
634        let pl = rc.pipeline(1).await.unwrap();
635        assert_eq!(pl.call(1).await, Ok(2));
636        assert!(rc.pipeline(0).await.is_err());
637
638        let f2 = f.clone().map_init_err(|()| "init");
639        assert_eq!(f2.create(&0).await.err(), Some("init"));
640
641        let f2 = f.clone().and_then(f.clone());
642        let pl = f2.pipeline(1).await.unwrap();
643        assert_eq!(pl.call(1).await, Ok(3));
644
645        let f2 = factory(f.clone()).map(|r| r * 10).map_err(|_| ());
646        let pl = f2.pipeline(1).await.unwrap();
647        assert_eq!(pl.call(1).await, Ok(20));
648        assert_eq!(pl.call(0).await, Err(()));
649
650        let pf = PipelineFactory::new(f.clone());
651        let pf2 = pf.clone();
652        assert!(format!("{pf2:?}").contains("PipelineFactory"));
653        let pl = pf2.create(2).await.unwrap();
654        assert_eq!(pl.call(1).await, Ok(3));
655        assert!(pf.create(0).await.is_err());
656    }
657
658    #[ntex::test]
659    async fn boxed_and_debug() {
660        let cnt = Rc::new(Cell::new(0));
661
662        let svc = boxed::service(Srv(cnt.clone()));
663        let pl = Pipeline::new(1, svc.clone());
664        assert_eq!(pl.call(1).await, Ok(2));
665
666        let f = boxed::factory(fn_factory(async |_: &usize| Ok::<_, ()>(Srv::default())));
667        let pl = f.clone().pipeline(1).await.unwrap();
668        assert_eq!(pl.call(1).await, Ok(2));
669
670        let s = format!("{:?}", service(Srv::default()).map(|r| r).map_err(|e| e));
671        assert!(s.contains("Map") && s.contains("MapErr"));
672    }
673
674    #[test]
675    fn request_state() {
676        let st = State {
677            req: 1,
678            state: "st",
679        };
680        assert_eq!(st.unpack(), ("st", 1));
681        assert_eq!(("st", 2).unpack(), ("st", 2));
682    }
683}