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> Application for WithMiddleware<A> {
126 fn execute(&self, request: &Request, connection: &ConnectionInfo) -> Result<Response, String> {
127 Chain { app: &self.inner, layers: &self.layers, index: 0 }.execute(request, connection)
128 }
129}
130
131// Internal chain that carries the remaining layers and the final app.
132struct Chain<'a> {
133 app: &'a dyn Application,
134 layers: &'a [Arc<dyn Middleware>],
135 index: usize,
136}
137
138impl<'a> Application for Chain<'a> {
139 fn execute(&self, request: &Request, connection: &ConnectionInfo) -> Result<Response, String> {
140 if self.index < self.layers.len() {
141 let next = Chain { app: self.app, layers: self.layers, index: self.index + 1 };
142 self.layers[self.index].handle(request, connection, &next)
143 } else {
144 self.app.execute(request, connection)
145 }
146 }
147}