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}