Skip to main content

rust_web_server/middleware/
mod.rs

1//! Composable middleware pipeline.
2//!
3//! [`Middleware`] is a single layer that wraps request dispatch. Implement it
4//! to add cross-cutting behaviour — logging, authentication, rate limiting,
5//! header injection — without editing the inner application.
6//!
7//! [`WithMiddleware`] stacks one or more middleware layers around any
8//! [`Application`]. Layers run in registration order on the way in and in
9//! reverse order on the way out.
10//!
11//! # Example
12//!
13//! ```rust,no_run
14//! use rust_web_server::middleware::{Middleware, WithMiddleware};
15//! use rust_web_server::application::Application;
16//! use rust_web_server::request::Request;
17//! use rust_web_server::response::Response;
18//! use rust_web_server::server::ConnectionInfo;
19//! use rust_web_server::app::App;
20//! use rust_web_server::core::New;
21//!
22//! pub struct LoggingMiddleware;
23//!
24//! impl Middleware for LoggingMiddleware {
25//!     fn handle(
26//!         &self,
27//!         request: &Request,
28//!         connection: &ConnectionInfo,
29//!         next: &dyn Application,
30//!     ) -> Result<Response, String> {
31//!         println!("{} {}", request.method, request.request_uri);
32//!         let response = next.execute(request, connection)?;
33//!         println!("  → {}", response.status_code);
34//!         Ok(response)
35//!     }
36//! }
37//!
38//! let app = WithMiddleware::new(App::new())
39//!     .wrap(LoggingMiddleware);
40//! ```
41
42#[cfg(test)]
43mod tests;
44
45use std::sync::Arc;
46
47use crate::application::Application;
48use crate::request::Request;
49use crate::response::Response;
50use crate::server::ConnectionInfo;
51
52/// A single middleware layer.
53///
54/// Receive the request, call `next.execute(request, connection)` to pass
55/// control to the next layer (or the inner application), and optionally
56/// inspect or transform the resulting response.
57///
58/// Short-circuit by returning a `Response` (or `Err`) without calling `next`.
59pub trait Middleware: Send + Sync {
60    fn handle(
61        &self,
62        request: &Request,
63        connection: &ConnectionInfo,
64        next: &dyn Application,
65    ) -> Result<Response, String>;
66}
67
68/// An [`Application`] that applies a stack of [`Middleware`] layers before
69/// dispatching to an inner application.
70///
71/// Layers are applied in registration order: the first `.wrap()`ed middleware
72/// runs first on the request path and last on the response path.
73pub struct WithMiddleware<A> {
74    inner: A,
75    layers: Vec<Arc<dyn Middleware>>,
76}
77
78impl<A: Application> WithMiddleware<A> {
79    /// Wrap `app` with no initial middleware.
80    pub fn new(app: A) -> Self {
81        WithMiddleware { inner: app, layers: Vec::new() }
82    }
83
84    /// Add a middleware layer. Layers run in registration order.
85    pub fn wrap(mut self, layer: impl Middleware + 'static) -> Self {
86        self.layers.push(Arc::new(layer));
87        self
88    }
89}
90
91impl<A: Application> Application for WithMiddleware<A> {
92    fn execute(&self, request: &Request, connection: &ConnectionInfo) -> Result<Response, String> {
93        Chain { app: &self.inner, layers: &self.layers, index: 0 }.execute(request, connection)
94    }
95}
96
97// Internal chain that carries the remaining layers and the final app.
98struct Chain<'a> {
99    app: &'a dyn Application,
100    layers: &'a [Arc<dyn Middleware>],
101    index: usize,
102}
103
104impl<'a> Application for Chain<'a> {
105    fn execute(&self, request: &Request, connection: &ConnectionInfo) -> Result<Response, String> {
106        if self.index < self.layers.len() {
107            let next = Chain { app: self.app, layers: self.layers, index: self.index + 1 };
108            self.layers[self.index].handle(request, connection, &next)
109        } else {
110            self.app.execute(request, connection)
111        }
112    }
113}