Skip to main content

rust_webx_host/
pipeline.rs

1//! Middleware pipeline —Sequential execution model.
2//!
3//! Middlewares are called in registration order. Each middleware can
4//! inspect or modify the request. The final handler (router) is called
5//! after all middlewares have passed.
6
7use rust_webx_core::error::Result;
8use rust_webx_core::http::IHttpContext;
9use rust_webx_core::middleware::IMiddleware;
10use std::future::Future;
11use std::pin::Pin;
12use std::sync::Arc;
13
14/// Boxed final handler function type.
15pub type HandlerFn = Arc<
16    dyn for<'a> Fn(
17            &'a mut dyn IHttpContext,
18        ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>>
19        + Send
20        + Sync,
21>;
22
23pub struct MiddlewarePipeline {
24    middlewares: Vec<Arc<dyn IMiddleware>>,
25}
26
27impl MiddlewarePipeline {
28    pub fn new() -> Self {
29        Self {
30            middlewares: Vec::new(),
31        }
32    }
33
34    pub fn add_middleware(&mut self, middleware: Arc<dyn IMiddleware>) {
35        self.middlewares.push(middleware);
36    }
37
38    /// Execute middleware onion: invoke forward, final handler, after hooks reverse.
39    ///
40    /// Supports short-circuit: if a middleware's `invoke` returns `ControlFlow::Break(())`,
41    /// remaining middleware and the final handler are skipped. `after` hooks on
42    /// already-executed middleware still run in reverse order.
43    pub async fn execute(
44        &self,
45        ctx: &mut dyn IHttpContext,
46        final_handler: HandlerFn,
47    ) -> Result<()> {
48        use std::ops::ControlFlow;
49
50        let mut executed: Vec<Arc<dyn IMiddleware>> = Vec::new();
51        let mut short_circuited = false;
52
53        // Forward pass: invoke each middleware, track executed for after hooks
54        for middleware in &self.middlewares {
55            match middleware.invoke(ctx).await? {
56                ControlFlow::Continue(()) => executed.push(Arc::clone(middleware)),
57                ControlFlow::Break(()) => {
58                    short_circuited = true;
59                    break;
60                }
61            }
62        }
63
64        // Run the final handler (router) only if not short-circuited
65        if !short_circuited {
66            final_handler(ctx).await?;
67        }
68
69        // Reverse pass: after hooks on executed middleware only
70        for middleware in executed.into_iter().rev() {
71            middleware.after(ctx).await?;
72        }
73
74        Ok(())
75    }
76}
77
78impl Default for MiddlewarePipeline {
79    fn default() -> Self {
80        Self::new()
81    }
82}