ntex_service/middleware.rs
1use std::{fmt, marker::PhantomData, rc::Rc};
2
3use crate::dev::{Apply, ApplyCtx};
4use crate::{IntoServiceFactory, Service, ServiceChainFactory, ServiceFactory};
5
6/// Applies middleware to every service produced by a factory.
7///
8/// Returns a [`ServiceChainFactory`] containing an [`ApplyMiddleware`].
9/// Creation first awaits the inner factory, then calls [`Middleware::create`]
10/// synchronously with the new service and the same state reference. If the
11/// factory fails, its initialization error is returned without invoking the
12/// middleware.
13///
14/// To use the result as a [`ServiceFactory`], the middleware's service must
15/// accept the same state and request types as the inner service. Response and
16/// service-error types may change. The wrapper is responsible for forwarding
17/// calls, binding, readiness, and shutdown as appropriate.
18///
19/// Cloning the resulting factory shares the middleware and inner factory
20/// through [`Rc`]; neither needs to implement [`Clone`].
21///
22/// # Example
23///
24/// ```
25/// use ntex_service::{Identity, apply, fn_factory, fn_service};
26///
27/// # async fn example() -> Result<(), ()> {
28/// let factory = apply(
29/// Identity,
30/// fn_factory(async |offset: &u32| {
31/// let offset = *offset;
32/// Ok::<_, ()>(fn_service(async move |req: u32| Ok::<_, ()>(req + offset)))
33/// }),
34/// );
35///
36/// let pipeline = factory.pipeline(10).await?;
37/// assert_eq!(pipeline.call(2).await?, 12);
38/// # Ok(())
39/// # }
40/// ```
41pub fn apply<Sf, St, Req, M>(
42 mw: M,
43 factory: impl IntoServiceFactory<Sf, St, Req>,
44) -> ServiceChainFactory<ApplyMiddleware<M, Sf>, St, Req>
45where
46 Sf: ServiceFactory<St, Req>,
47 M: Middleware<Sf::Service, St>,
48{
49 ServiceChainFactory {
50 factory: ApplyMiddleware::new(mw, factory.into_factory()),
51 _t: PhantomData,
52 }
53}
54
55/// Wraps an inner service during service construction.
56///
57/// [`Self::create`] takes ownership of an already-created service and returns
58/// a wrapper synchronously and infallibly. The wrapper can run work before
59/// and after inner calls, transform requests, responses, or errors, or avoid
60/// calling the inner service.
61///
62/// Use [`apply()`], [`Self::apply_to`], or [`ServiceChainFactory::apply`] to
63/// install middleware on a factory. These require the wrapper to implement
64/// [`Service`] for the factory's state and request types. Direct use of
65/// [`Self::create`] can also construct wrappers with a different request type.
66///
67/// The state reference is available during construction. A service wrapper
68/// can access the current state on each call through [`crate::Ctx::st`].
69/// [`Rc`] middleware shares one middleware value across its clones, and
70/// [`Stack`] composes multiple wrappers.
71///
72/// # Choosing an approach
73///
74/// - Use [`fn_layer`] for logic that runs before or after the inner call.
75/// Binding, readiness, and shutdown are forwarded automatically.
76/// - Implement this trait with a wrapper [`Service`] when the wrapper owns
77/// its own readiness source, keeps per-service state, or needs a named type.
78/// Forward lifecycle methods with [`crate::forward!`].
79///
80/// # Lifecycle forwarding
81///
82/// Middleware must arrange the resulting service's lifecycle behavior.
83/// Wrappers retaining an inner service should forward [`Service::bind`] so
84/// its readiness notifications reach the enclosing pipeline, and forward or
85/// deliberately replace its readiness and shutdown behavior. These methods
86/// are not forwarded automatically by implementing this trait.
87///
88/// # Example
89///
90/// For example, timeout middleware:
91///
92/// ```rust
93/// use ntex_service::{Ctx, Service};
94/// use ntex::{time::sleep, util::Either, util::select};
95///
96/// pub struct Timeout<S> {
97/// service: S,
98/// timeout: std::time::Duration,
99/// }
100///
101/// pub enum TimeoutError<E> {
102/// Service(E),
103/// Timeout,
104/// }
105///
106/// impl<S, R> Service<(), R> for Timeout<S>
107/// where
108/// S: Service<(), R>,
109/// {
110/// type Res = S::Res;
111/// type Error = TimeoutError<S::Error>;
112///
113/// ntex_service::forward!((), service, map_err = TimeoutError::Service);
114///
115/// async fn call(&self, req: R, ctx: Ctx<'_, Self>) -> Result<Self::Res, Self::Error> {
116/// match select(sleep(self.timeout), ctx.call(&self.service, req)).await {
117/// Either::Left(_) => Err(TimeoutError::Timeout),
118/// Either::Right(res) => res.map_err(TimeoutError::Service),
119/// }
120/// }
121/// }
122/// ```
123///
124/// The timeout service is independent of the wrapped service implementation and
125/// can be applied to any compatible service.
126///
127/// Middleware that constructs `Timeout` could look like this:
128///
129/// ```rust
130/// # use ntex_service::Middleware;
131/// # pub struct Timeout<S> {
132/// # service: S,
133/// # timeout: std::time::Duration,
134/// # }
135/// pub struct TimeoutMiddleware {
136/// timeout: std::time::Duration,
137/// }
138///
139/// impl<S> Middleware<S, ()> for TimeoutMiddleware
140/// {
141/// type Service = Timeout<S>;
142///
143/// fn create(&self, _: &(), service: S) -> Self::Service {
144/// Timeout {
145/// service,
146/// timeout: self.timeout,
147/// }
148/// }
149/// }
150/// ```
151#[diagnostic::on_unimplemented(
152 message = "`{Self}` is not a `Middleware<{S}, {St}>`",
153 label = "expected middleware that wraps `{S}` with `{St}` state",
154 note = "implement it generically over the wrapped service: `impl<S, St> Middleware<S, St> for T`",
155 note = "add only the bounds `create` needs; `Service<St, Req>` bounds belong on the wrapper's `Service` impl",
156 note = "use `fn_layer(async |req, ctx| ctx.call(req).await)` for middleware written as an async function"
157)]
158pub trait Middleware<S, St> {
159 /// Wrapper returned by [`Self::create`].
160 ///
161 /// Factory application requires this type to implement [`Service`] for
162 /// the factory's state and request types. Its response and error types
163 /// may differ from those of the inner service.
164 type Service;
165
166 /// Wraps `service` using the supplied construction state.
167 ///
168 /// This is synchronous and infallible. It does not bind or check readiness
169 /// of the returned service; those steps belong to the enclosing pipeline.
170 fn create(&self, st: &St, service: S) -> Self::Service;
171
172 /// Creates a service factory that instantiates a service and applies
173 /// the current middleware to it.
174 ///
175 /// This is equivalent to [`apply()`] with this middleware and `factory`.
176 /// The factory is awaited before invoking [`Self::create`] with the same
177 /// state reference. Initialization errors bypass the middleware.
178 ///
179 /// The middleware and factory are shared when the returned builder is
180 /// cloned; they do not need to implement [`Clone`].
181 fn apply_to<Sf, Req>(
182 self,
183 factory: Sf,
184 ) -> ServiceChainFactory<ApplyMiddleware<Self, Sf>, St, Req>
185 where
186 Sf: ServiceFactory<St, Req, Service = S>,
187 Self: Sized,
188 Self::Service: Service<St, Req>,
189 {
190 crate::factory(ApplyMiddleware::new(self, factory))
191 }
192}
193
194impl<M, S, St> Middleware<S, St> for Rc<M>
195where
196 M: Middleware<S, St>,
197{
198 type Service = M::Service;
199
200 fn create(&self, st: &St, service: S) -> M::Service {
201 self.as_ref().create(st, service)
202 }
203}
204
205/// A service factory with middleware applied.
206///
207/// Created by [`apply()`], [`Middleware::apply_to`], or
208/// [`ServiceChainFactory::apply`]. Each creation first awaits the inner
209/// factory, then wraps its service through [`Middleware::create`] with the
210/// same state reference.
211///
212/// The wrapper determines response and service-error types and is responsible
213/// for lifecycle forwarding. Inner initialization errors are returned
214/// unchanged without invoking the middleware.
215///
216/// Cloning shares both middleware and factory through [`Rc`] rather than
217/// cloning them or creating a service.
218pub struct ApplyMiddleware<M, Sf>(Rc<(M, Sf)>);
219
220impl<M, Sf> ApplyMiddleware<M, Sf> {
221 /// Creates a new `ApplyMiddleware` factory.
222 pub(crate) fn new(mw: M, sf: Sf) -> Self {
223 Self(Rc::new((mw, sf)))
224 }
225}
226
227impl<M, Sf> Clone for ApplyMiddleware<M, Sf> {
228 fn clone(&self) -> Self {
229 Self(self.0.clone())
230 }
231}
232
233impl<M, Sf> fmt::Debug for ApplyMiddleware<M, Sf>
234where
235 M: fmt::Debug,
236 Sf: fmt::Debug,
237{
238 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
239 f.debug_struct("ApplyMiddleware")
240 .field("factory", &self.0.1)
241 .field("middleware", &self.0.0)
242 .finish()
243 }
244}
245
246impl<M, Sf, St, Req> ServiceFactory<St, Req> for ApplyMiddleware<M, Sf>
247where
248 Sf: ServiceFactory<St, Req>,
249 M: Middleware<Sf::Service, St>,
250 M::Service: Service<St, Req>,
251{
252 type Res = <M::Service as Service<St, Req>>::Res;
253 type Error = <M::Service as Service<St, Req>>::Error;
254
255 type Service = M::Service;
256 type InitError = Sf::InitError;
257
258 #[inline]
259 async fn create(&self, st: &St) -> Result<Self::Service, Self::InitError> {
260 Ok(self.0.0.create(st, self.0.1.create(st).await?))
261 }
262}
263
264/// Middleware that returns the wrapped service unchanged.
265///
266/// Construction ignores state and returns the exact input service without
267/// adding a wrapper, readiness cache, or lifecycle behavior. This is useful as
268/// a default middleware or a no-op component of a [`Stack`].
269#[derive(Debug, Clone, Copy)]
270pub struct Identity;
271
272impl<S, St> Middleware<S, St> for Identity {
273 type Service = S;
274
275 #[inline]
276 fn create(&self, _: &St, service: S) -> Self::Service {
277 service
278 }
279}
280
281/// Two middleware values applied in sequence.
282///
283/// Construction applies `inner` first, then `outer`, with the same state
284/// reference: the result is `outer(inner(service))`. Requests enter the outer
285/// wrapper first; if forwarded, they then pass through the inner wrapper to
286/// the service. Responses return through the inner wrapper before the outer.
287///
288/// The stack only composes construction. Lifecycle forwarding and whether
289/// calls reach the inner service are determined by each wrapper. Cloning
290/// clones both middleware values without constructing a service.
291///
292/// # Example
293///
294/// ```
295/// use ntex_service::{Middleware, Service, Stack, fn_layer, fn_service};
296///
297/// # async fn example() -> Result<(), ()> {
298/// let inner = fn_layer(async |req: u32, ctx| ctx.call(req + 1).await);
299/// let outer = fn_layer(async |req: u32, ctx| ctx.call(req * 2).await);
300/// let service = Stack::new(inner, outer)
301/// .create(&(), fn_service(async |req: u32| Ok::<_, ()>(req)));
302///
303/// let pipeline = service.pipeline(());
304/// assert_eq!(pipeline.call(2).await?, 5);
305/// # Ok(())
306/// # }
307/// ```
308#[derive(Debug, Clone)]
309pub struct Stack<Inner, Outer> {
310 inner: Inner,
311 outer: Outer,
312}
313
314impl<Inner, Outer> Stack<Inner, Outer> {
315 /// Stores two middleware values in inner-to-outer construction order.
316 ///
317 /// No service is created until [`Middleware::create`] is called. The
318 /// resulting outer wrapper receives requests before the inner wrapper.
319 pub fn new(inner: Inner, outer: Outer) -> Self {
320 Stack { inner, outer }
321 }
322}
323
324impl<S, St, Inner, Outer> Middleware<S, St> for Stack<Inner, Outer>
325where
326 Inner: Middleware<S, St>,
327 Outer: Middleware<Inner::Service, St>,
328{
329 type Service = Outer::Service;
330
331 fn create(&self, st: &St, service: S) -> Self::Service {
332 self.outer.create(st, self.inner.create(st, service))
333 }
334}
335
336/// Creates middleware from an asynchronous function.
337///
338/// This is the simplest way to write middleware. The function receives an
339/// input request and an [`ApplyCtx`] that can call the wrapped service with
340/// [`ApplyCtx::call`] or access pipeline state with [`ApplyCtx::st`]. Binding,
341/// readiness, and shutdown of the wrapped service are forwarded automatically.
342///
343/// Each [`Middleware::create`] call clones the function into an [`Apply`]
344/// wrapper; it does not invoke it. Request, response, and error types may
345/// change, while the state type stays the same. The middleware implementation
346/// requires `Err: From<S::Error>` to convert inner readiness errors. See
347/// [`crate::apply_fn`] for lifecycle and call-time readiness semantics.
348///
349/// Annotate the request type, and the response or error type when the
350/// closure does not otherwise constrain them.
351///
352/// # Example
353///
354/// ```
355/// use ntex_service::{Middleware, Service, fn_layer, fn_service};
356///
357/// # async fn example() -> Result<(), ()> {
358/// let log = fn_layer(async |req: u32, ctx| {
359/// println!("request: {req}");
360/// let res: u32 = ctx.call(req).await?;
361/// println!("response: {res}");
362/// Ok::<_, ()>(res)
363/// });
364/// let service = log.create(&(), fn_service(async |req: u32| Ok::<_, ()>(req + 1)));
365///
366/// assert_eq!(service.pipeline(()).call(1).await?, 2);
367/// # Ok(())
368/// # }
369/// ```
370pub fn fn_layer<F, S, St, Req, In, Out, Err>(f: F) -> FnMiddleware<F, S, St, Req, In, Out, Err>
371where
372 F: AsyncFn(In, &ApplyCtx<'_, S, St, Req>) -> Result<Out, Err> + Clone,
373 S: Service<St, Req>,
374{
375 FnMiddleware { f, r: PhantomData }
376}
377
378/// Middleware backed by an asynchronous function.
379///
380/// Created by [`fn_layer`]. Each [`Middleware::create`] call wraps the supplied
381/// service in [`Apply`] with a clone of the function. The construction state
382/// is not captured automatically; the function accesses current pipeline state
383/// through [`ApplyCtx::st`].
384///
385/// The function controls whether and how often the inner service is called.
386/// Binding, readiness, and shutdown follow [`Apply`], including conversion of
387/// inner readiness errors through [`From`]. Cloning this middleware clones only
388/// the function, not an inner service.
389pub struct FnMiddleware<F, S, St, Req, In, Out, Err> {
390 f: F,
391 r: PhantomData<fn(S, St, Req) -> (In, Out, Err)>,
392}
393
394impl<F, S, St, Req, In, Out, Err> Clone for FnMiddleware<F, S, St, Req, In, Out, Err>
395where
396 F: Clone,
397{
398 fn clone(&self) -> Self {
399 FnMiddleware {
400 f: self.f.clone(),
401 r: PhantomData,
402 }
403 }
404}
405
406impl<F, S, St, Req, In, Out, Err> fmt::Debug for FnMiddleware<F, S, St, Req, In, Out, Err> {
407 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
408 f.debug_struct("FnMiddleware")
409 .field("layer", &std::any::type_name::<F>())
410 .finish()
411 }
412}
413
414impl<F, S, St, Req, In, Out, Err> Middleware<S, St> for FnMiddleware<F, S, St, Req, In, Out, Err>
415where
416 S: Service<St, Req>,
417 F: AsyncFn(In, &ApplyCtx<'_, S, St, Req>) -> Result<Out, Err> + Clone,
418 Err: From<S::Error>,
419{
420 type Service = Apply<S, St, Req, F, In, Out, Err>;
421
422 fn create(&self, _: &St, service: S) -> Self::Service {
423 Apply::new(service, self.f.clone())
424 }
425}
426
427#[cfg(test)]
428#[allow(clippy::redundant_clone)]
429#[path = "../tests/unit/middleware.rs"]
430mod tests;