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/// Built-in middleware that enforces the process-wide rate limit
53/// (configured via `RWS_CONFIG_RATE_LIMIT_MAX_REQUESTS` and
54/// `RWS_CONFIG_RATE_LIMIT_WINDOW_SECS`).
55///
56/// Returns `429 Too Many Requests` when the sliding-window budget for the
57/// client IP is exhausted; otherwise passes the request to the next layer.
58///
59/// # Example
60///
61/// ```rust,no_run
62/// use rust_web_server::app::App;
63/// use rust_web_server::middleware::{WithMiddleware, RateLimitLayer};
64/// use rust_web_server::core::New;
65///
66/// let app = App::new().wrap(RateLimitLayer);
67/// ```
68pub struct RateLimitLayer;
69
70impl Middleware for RateLimitLayer {
71    fn handle(
72        &self,
73        request: &Request,
74        connection: &ConnectionInfo,
75        next: &dyn Application,
76    ) -> Result<Response, String> {
77        use crate::error::{AppError, IntoResponse};
78        if crate::rate_limit::global().check(&connection.client.ip) {
79            next.execute(request, connection)
80        } else {
81            Ok(AppError::TooManyRequests.into_response())
82        }
83    }
84}
85
86/// A single middleware layer.
87///
88/// Receive the request, call `next.execute(request, connection)` to pass
89/// control to the next layer (or the inner application), and optionally
90/// inspect or transform the resulting response.
91///
92/// Short-circuit by returning a `Response` (or `Err`) without calling `next`.
93pub trait Middleware: Send + Sync {
94    fn handle(
95        &self,
96        request: &Request,
97        connection: &ConnectionInfo,
98        next: &dyn Application,
99    ) -> Result<Response, String>;
100}
101
102/// An [`Application`] that applies a stack of [`Middleware`] layers before
103/// dispatching to an inner application.
104///
105/// Layers are applied in registration order: the first `.wrap()`ed middleware
106/// runs first on the request path and last on the response path.
107pub struct WithMiddleware<A> {
108    inner: A,
109    layers: Vec<Arc<dyn Middleware>>,
110}
111
112impl<A: Application> WithMiddleware<A> {
113    /// Wrap `app` with no initial middleware.
114    pub fn new(app: A) -> Self {
115        WithMiddleware { inner: app, layers: Vec::new() }
116    }
117
118    /// Add a middleware layer. Layers run in registration order.
119    pub fn wrap(mut self, layer: impl Middleware + 'static) -> Self {
120        self.layers.push(Arc::new(layer));
121        self
122    }
123}
124
125impl<A: Application + Send + Sync + Clone + 'static> WithMiddleware<A> {
126    /// Attach an MCP server that handles `POST /mcp`; all other requests fall
127    /// through to this middleware stack.
128    pub fn mcp(self, name: impl Into<String>, version: impl Into<String>) -> crate::mcp::McpServer {
129        crate::mcp::McpServer::new(name, version).wrap(self)
130    }
131}
132
133impl<A: Clone> Clone for WithMiddleware<A> {
134    fn clone(&self) -> Self {
135        WithMiddleware { inner: self.inner.clone(), layers: self.layers.clone() }
136    }
137}
138
139impl<A: Application> Application for WithMiddleware<A> {
140    fn execute(&self, request: &Request, connection: &ConnectionInfo) -> Result<Response, String> {
141        Chain { app: &self.inner, layers: &self.layers, index: 0 }.execute(request, connection)
142    }
143}
144
145// Internal chain that carries the remaining layers and the final app.
146struct Chain<'a> {
147    app: &'a dyn Application,
148    layers: &'a [Arc<dyn Middleware>],
149    index: usize,
150}
151
152impl<'a> Application for Chain<'a> {
153    fn execute(&self, request: &Request, connection: &ConnectionInfo) -> Result<Response, String> {
154        if self.index < self.layers.len() {
155            let next = Chain { app: self.app, layers: self.layers, index: self.index + 1 };
156            self.layers[self.index].handle(request, connection, &next)
157        } else {
158            self.app.execute(request, connection)
159        }
160    }
161}