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::header::Header;
51use crate::server::ConnectionInfo;
52
53/// Built-in middleware that enforces the process-wide rate limit
54/// (configured via `RWS_CONFIG_RATE_LIMIT_MAX_REQUESTS` and
55/// `RWS_CONFIG_RATE_LIMIT_WINDOW_SECS`).
56///
57/// Returns `429 Too Many Requests` when the sliding-window budget for the
58/// client IP is exhausted; otherwise passes the request to the next layer.
59///
60/// # Example
61///
62/// ```rust,no_run
63/// use rust_web_server::app::App;
64/// use rust_web_server::middleware::{WithMiddleware, RateLimitLayer};
65/// use rust_web_server::core::New;
66///
67/// let app = App::new().wrap(RateLimitLayer);
68/// ```
69pub struct RateLimitLayer;
70
71impl Middleware for RateLimitLayer {
72 /// Headers only: a streamed body passes through untouched.
73 fn accepts_streamed_body(&self, _request: &Request, _connection: &ConnectionInfo) -> bool {
74 true
75 }
76
77
78 fn handle(
79 &self,
80 request: &Request,
81 connection: &ConnectionInfo,
82 next: &dyn Application,
83 ) -> Result<Response, String> {
84 use crate::error::{AppError, IntoResponse};
85 if crate::rate_limit::global().check(&connection.client.ip) {
86 next.execute(request, connection)
87 } else {
88 let mut response = AppError::TooManyRequests.into_response();
89 let retry_after = crate::rate_limit::global().retry_after(&connection.client.ip);
90 response.headers.push(Header {
91 name: Header::_RETRY_AFTER.into(),
92 value: retry_after.to_string().into(),
93 });
94 Ok(response)
95 }
96 }
97
98 /// In-memory only: safe on the event loop.
99 fn runs_inline(&self, _request: &Request, _connection: &ConnectionInfo) -> bool {
100 true
101 }
102}
103
104/// A single middleware layer.
105///
106/// Receive the request, call `next.execute(request, connection)` to pass
107/// control to the next layer (or the inner application), and optionally
108/// inspect or transform the resulting response.
109///
110/// Short-circuit by returning a `Response` (or `Err`) without calling `next`.
111pub trait Middleware: Send + Sync {
112 fn handle(
113 &self,
114 request: &Request,
115 connection: &ConnectionInfo,
116 next: &dyn Application,
117 ) -> Result<Response, String>;
118
119 /// `true` when this layer's own work for `request` never blocks (in-memory
120 /// state, atomics, short mutexes) — see [`Application::runs_inline`]. A
121 /// [`WithMiddleware`] stack runs inline only if every layer and the inner
122 /// application agree. Default `false`.
123 fn runs_inline(&self, _request: &Request, _connection: &ConnectionInfo) -> bool {
124 false
125 }
126
127 /// `true` when `handle` would do nothing for `request` but call `next`
128 /// (e.g. a rate limit that isn't configured). A [`WithMiddleware`] stack
129 /// offers the inner application's `execute_async` only if every layer
130 /// passes the request through. Default `false`.
131 fn is_passthrough(&self, _request: &Request, _connection: &ConnectionInfo) -> bool {
132 false
133 }
134
135 /// `true` when this layer never reads `request.body`, so the inner
136 /// application may receive it as a stream ([`Application::streams_body`]).
137 /// A layer that inspects the body (CSRF tokens in a form, request
138 /// signatures, rewriting) must keep the default `false`: it would see an
139 /// empty body.
140 fn accepts_streamed_body(&self, _request: &Request, _connection: &ConnectionInfo) -> bool {
141 false
142 }
143
144 /// `true` when this layer answers `request` itself — it doesn't call
145 /// `next` — and reads its body as a stream (a proxy forwarding it as it
146 /// arrives). The body is then streamed whatever the layers after it and
147 /// the application would say. Requires `accepts_streamed_body`. Since
148 /// 18.3.0.
149 fn answers_streamed_body(&self, _request: &Request, _connection: &ConnectionInfo) -> bool {
150 false
151 }
152}
153
154/// An [`Application`] that applies a stack of [`Middleware`] layers before
155/// dispatching to an inner application.
156///
157/// Layers are applied in registration order: the first `.wrap()`ed middleware
158/// runs first on the request path and last on the response path.
159pub struct WithMiddleware<A> {
160 inner: A,
161 /// Built once; shared by every clone (the servers clone the app per
162 /// request, so a `Vec` here was an allocation per request).
163 layers: Arc<[Arc<dyn Middleware>]>,
164}
165
166impl<A: Application> WithMiddleware<A> {
167 /// Wrap `app` with no initial middleware.
168 pub fn new(app: A) -> Self {
169 WithMiddleware { inner: app, layers: Arc::new([]) }
170 }
171
172 /// Add a middleware layer. Layers run in registration order.
173 pub fn wrap(mut self, layer: impl Middleware + 'static) -> Self {
174 let mut layers = self.layers.to_vec();
175 layers.push(Arc::new(layer));
176 self.layers = layers.into();
177 self
178 }
179}
180
181impl<A: Application + Send + Sync + Clone + 'static> WithMiddleware<A> {
182 /// Attach an MCP server that handles `POST /mcp`; all other requests fall
183 /// through to this middleware stack.
184 pub fn mcp(self, name: impl Into<String>, version: impl Into<String>) -> crate::mcp::McpServer {
185 crate::mcp::McpServer::new(name, version).wrap(self)
186 }
187}
188
189impl<A: Clone> Clone for WithMiddleware<A> {
190 fn clone(&self) -> Self {
191 WithMiddleware { inner: self.inner.clone(), layers: Arc::clone(&self.layers) }
192 }
193}
194
195impl<A: Application> Application for WithMiddleware<A> {
196 fn execute(&self, request: &Request, connection: &ConnectionInfo) -> Result<Response, String> {
197 Chain { app: &self.inner, layers: &self.layers, index: 0 }.execute(request, connection)
198 }
199
200 fn runs_inline(&self, request: &Request, connection: &ConnectionInfo) -> bool {
201 self.layers.iter().all(|l| l.runs_inline(request, connection)) && self.inner.runs_inline(request, connection)
202 }
203
204 #[cfg(feature = "http2")]
205 fn execute_async<'a>(&'a self, request: &'a Request, connection: &'a ConnectionInfo) -> Option<crate::application::AsyncResponse<'a>> {
206 if self.layers.iter().all(|l| l.is_passthrough(request, connection)) {
207 self.inner.execute_async(request, connection)
208 } else {
209 None
210 }
211 }
212 fn streams_body(&self, request: &Request, connection: &ConnectionInfo) -> bool {
213 for layer in self.layers.iter() {
214 if !layer.accepts_streamed_body(request, connection) {
215 return false;
216 }
217 if layer.answers_streamed_body(request, connection) {
218 return true;
219 }
220 }
221 self.inner.streams_body(request, connection)
222 }
223}
224
225// Internal chain that carries the remaining layers and the final app.
226struct Chain<'a> {
227 app: &'a dyn Application,
228 layers: &'a [Arc<dyn Middleware>],
229 index: usize,
230}
231
232impl<'a> Application for Chain<'a> {
233 fn execute(&self, request: &Request, connection: &ConnectionInfo) -> Result<Response, String> {
234 if self.index < self.layers.len() {
235 let next = Chain { app: self.app, layers: self.layers, index: self.index + 1 };
236 self.layers[self.index].handle(request, connection, &next)
237 } else {
238 self.app.execute(request, connection)
239 }
240 }
241}