Skip to main content

rainier_middleware/
stack.rs

1//! Middleware as values — [`MiddlewareStack`] and [`IntoMiddlewareStack`].
2//!
3//! In a name-keyed kernel a route names its middleware — `middleware("auth")`
4//! — resolved through an alias map at runtime. That map is the only thing
5//! standing between a typo and a route that runs unguarded, and a dynamic
6//! language has no better option.
7//!
8//! Rust does. A route takes the **middleware itself**:
9//!
10//! ```
11//! # use rainier_middleware::{AddHeaders, MiddlewareStack, ThrottleRequests};
12//! let stack = MiddlewareStack::from(AddHeaders::security_defaults())
13//!     .with(ThrottleRequests::per_minute(60));
14//!
15//! assert_eq!(stack.len(), 2);
16//! ```
17//!
18//! There is nothing to misspell, nothing to register, and nothing to look up.
19//! Deleting a middleware breaks every route that used it, in the compiler,
20//! naming each one.
21//!
22//! ## Groups are functions, not names
23//!
24//! Elsewhere, `web` and `api` groups are keys in a map. Here a group is a
25//! function that returns a stack — so it is discoverable by "go to definition",
26//! it can take arguments, and calling one that does not exist is a compile
27//! error:
28//!
29//! ```
30//! # use rainier_middleware::{AddHeaders, HandleCors, MiddlewareStack, ThrottleRequests};
31//! pub fn api(per_minute: u32) -> MiddlewareStack {
32//!     MiddlewareStack::new()
33//!         .with(HandleCors::any_origin())
34//!         .with(ThrottleRequests::per_minute(per_minute))
35//! }
36//!
37//! pub fn web() -> MiddlewareStack {
38//!     MiddlewareStack::new().with(AddHeaders::security_defaults())
39//! }
40//!
41//! assert_eq!(api(60).len(), 2);
42//! ```
43//!
44//! Nesting one group inside another is [`with_stack`](MiddlewareStack::with_stack),
45//! and a cycle is not expressible — a function cannot call itself into a value
46//! without recursing forever, which is a stack overflow the type system already
47//! prevents you from writing by accident, rather than a runtime check the old
48//! registry had to perform.
49//!
50//! ## When the middleware needs the container
51//!
52//! Some middleware cannot be built while routes are being declared: an
53//! authentication guard needs the `AuthManager` the application binds, and the
54//! container is not populated yet. That is the one case the old
55//! name-and-factory indirection genuinely solved.
56//!
57//! [`resolved`](MiddlewareStack::resolved) solves it without a name. The
58//! closure runs when the router **compiles**, which is after the container is
59//! populated and still before the first request:
60//!
61//! ```ignore
62//! MiddlewareStack::new()
63//!     .resolved(|auth: Arc<AuthManager<User>>| Authenticate::new(auth))
64//! ```
65//!
66//! The container arrives as an argument rather than through a global. That is
67//! the difference between a builder you can call in a test and one that panics
68//! unless a process-wide slot happens to be filled — and it is the same reason
69//! the names went: a value handed to you is checkable, a value looked up is
70//! not.
71//!
72//! A failure fails the boot, naming the middleware's type and the route that
73//! wanted it.
74
75use std::sync::Arc;
76
77use rainier_container::Container;
78use rainier_support::{Error, Result};
79
80use crate::pipeline::Middleware;
81
82/// Builds one middleware from the container, when the router compiles.
83type Builder = Arc<dyn Fn(&Container) -> Result<Arc<dyn Middleware>> + Send + Sync>;
84
85/// One entry in a stack: an instance, or a closure that will build one.
86enum Stage {
87    Ready(Arc<dyn Middleware>),
88    /// The `&'static str` is the middleware's type name, captured at
89    /// declaration so a failure can say what failed to build.
90    Deferred(&'static str, Builder),
91}
92
93impl Clone for Stage {
94    fn clone(&self) -> Self {
95        match self {
96            Stage::Ready(middleware) => Stage::Ready(Arc::clone(middleware)),
97            Stage::Deferred(name, build) => Stage::Deferred(name, Arc::clone(build)),
98        }
99    }
100}
101
102/// An ordered list of middleware, outermost first.
103///
104/// What a route, a group, or a controller action attaches. The value that
105/// replaces a name-keyed group entry.
106#[derive(Default, Clone)]
107pub struct MiddlewareStack {
108    stages: Vec<Stage>,
109}
110
111impl MiddlewareStack {
112    /// An empty stack.
113    pub fn new() -> Self {
114        Self::default()
115    }
116
117    /// Append a middleware instance.
118    pub fn with(mut self, middleware: impl Middleware) -> Self {
119        self.stages.push(Stage::Ready(Arc::new(middleware)));
120        self
121    }
122
123    /// Append an already-shared instance.
124    ///
125    /// For middleware built once and attached to several routes — a rate
126    /// limiter, whose counters are the point of sharing it.
127    pub fn with_arc(mut self, middleware: Arc<dyn Middleware>) -> Self {
128        self.stages.push(Stage::Ready(middleware));
129        self
130    }
131
132    /// Append every stage of `other`, keeping its order.
133    ///
134    /// How a group nests inside a group.
135    pub fn with_stack(mut self, other: MiddlewareStack) -> Self {
136        self.stages.extend(other.stages);
137        self
138    }
139
140    /// Append middleware built when the router compiles, from the container.
141    ///
142    /// For anything that cannot exist while routes are being declared: the
143    /// container is populated by then, and the first request has not arrived.
144    /// See [the module docs](self).
145    pub fn deferred<M, F>(mut self, build: F) -> Self
146    where
147        M: Middleware,
148        F: Fn(&Container) -> Result<M> + Send + Sync + 'static,
149    {
150        let name = crate::pipeline::short_type_name(std::any::type_name::<M>());
151        self.stages.push(Stage::Deferred(
152            name,
153            Arc::new(move |container| build(container).map(|m| Arc::new(m) as Arc<dyn Middleware>)),
154        ));
155        self
156    }
157
158    /// Append middleware built from one service in the container.
159    ///
160    /// The common shape of [`deferred`](Self::deferred), with the resolve
161    /// spelled once:
162    ///
163    /// ```ignore
164    /// MiddlewareStack::new()
165    ///     .resolved(|auth: Arc<AuthManager<User>>| Authenticate::new(auth))
166    /// ```
167    ///
168    /// A missing binding fails the boot with the service's type name in the
169    /// message, which is the container's own error.
170    pub fn resolved<S, M, F>(self, build: F) -> Self
171    where
172        S: Send + Sync + 'static,
173        M: Middleware,
174        F: Fn(Arc<S>) -> M + Send + Sync + 'static,
175    {
176        self.deferred(move |container| Ok(build(container.resolve::<S>()?)))
177    }
178
179    /// Prepend every stage of `outer`, so it runs first.
180    ///
181    /// How a group's middleware ends up outside a route's own.
182    pub fn prepend(&mut self, outer: &MiddlewareStack) {
183        let mut combined = outer.stages.clone();
184        combined.append(&mut self.stages);
185        self.stages = combined;
186    }
187
188    /// How many stages are declared.
189    pub fn len(&self) -> usize {
190        self.stages.len()
191    }
192
193    /// Whether nothing is declared.
194    pub fn is_empty(&self) -> bool {
195        self.stages.is_empty()
196    }
197
198    /// Build every stage, running the deferred closures against `container`.
199    ///
200    /// Called once, when the router compiles.
201    pub fn resolve(&self, container: &Container) -> Result<Vec<Arc<dyn Middleware>>> {
202        self.stages
203            .iter()
204            .map(|stage| match stage {
205                Stage::Ready(middleware) => Ok(Arc::clone(middleware)),
206                Stage::Deferred(name, build) => build(container).map_err(|e| {
207                    Error::internal(format!("could not build the `{name}` middleware: {e}"))
208                }),
209            })
210            .collect()
211    }
212
213    /// The label of each stage, for diagnostics before resolution.
214    ///
215    /// A deferred stage reports its declared type, which is the best that can
216    /// be said before it is built.
217    pub fn labels(&self) -> Vec<&'static str> {
218        self.stages
219            .iter()
220            .map(|stage| match stage {
221                Stage::Ready(middleware) => middleware.name(),
222                Stage::Deferred(name, _) => name,
223            })
224            .collect()
225    }
226}
227
228impl std::fmt::Debug for MiddlewareStack {
229    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
230        f.debug_list().entries(self.labels()).finish()
231    }
232}
233
234/// Anything that can be attached where middleware is expected.
235///
236/// Implemented for a single middleware, an `Arc<dyn Middleware>`, a
237/// [`MiddlewareStack`], and tuples of up to eight — so all of these read the
238/// same way:
239///
240/// ```
241/// # use rainier_middleware::{AddHeaders, HandleCors, IntoMiddlewareStack, ThrottleRequests};
242/// let one = AddHeaders::security_defaults().into_middleware_stack();
243/// let several = (HandleCors::any_origin(), ThrottleRequests::per_minute(60))
244///     .into_middleware_stack();
245///
246/// assert_eq!(one.len(), 1);
247/// assert_eq!(several.len(), 2);
248/// ```
249pub trait IntoMiddlewareStack {
250    /// Convert into a stack.
251    fn into_middleware_stack(self) -> MiddlewareStack;
252}
253
254impl<M: Middleware> IntoMiddlewareStack for M {
255    fn into_middleware_stack(self) -> MiddlewareStack {
256        MiddlewareStack::new().with(self)
257    }
258}
259
260impl IntoMiddlewareStack for MiddlewareStack {
261    fn into_middleware_stack(self) -> MiddlewareStack {
262        self
263    }
264}
265
266impl IntoMiddlewareStack for Arc<dyn Middleware> {
267    fn into_middleware_stack(self) -> MiddlewareStack {
268        MiddlewareStack::new().with_arc(self)
269    }
270}
271
272impl IntoMiddlewareStack for Vec<Arc<dyn Middleware>> {
273    fn into_middleware_stack(self) -> MiddlewareStack {
274        self.into_iter().fold(MiddlewareStack::new(), MiddlewareStack::with_arc)
275    }
276}
277
278impl<M: Middleware> From<M> for MiddlewareStack {
279    fn from(middleware: M) -> Self {
280        MiddlewareStack::new().with(middleware)
281    }
282}
283
284/// `impl IntoMiddlewareStack` for tuples, so several stages attach in one call
285/// and keep their order.
286macro_rules! tuple_stacks {
287    ($( ($($name:ident),+) ),+ $(,)?) => {
288        $(
289            #[allow(non_snake_case, reason = "the idiomatic spelling for a tuple impl")]
290            impl<$($name: Middleware),+> IntoMiddlewareStack for ($($name,)+) {
291                fn into_middleware_stack(self) -> MiddlewareStack {
292                    let ($($name,)+) = self;
293                    MiddlewareStack::new()$(.with($name))+
294                }
295            }
296        )+
297    };
298}
299
300tuple_stacks! {
301    (A, B),
302    (A, B, C),
303    (A, B, C, D),
304    (A, B, C, D, E),
305    (A, B, C, D, E, F),
306    (A, B, C, D, E, F, G),
307    (A, B, C, D, E, F, G, H),
308}
309
310#[cfg(test)]
311mod tests {
312    use super::*;
313    use crate::pipeline::Next;
314    use rainier_http::{Request, Response};
315
316    macro_rules! tag {
317        ($name:ident) => {
318            struct $name;
319
320            #[async_trait::async_trait]
321            impl Middleware for $name {
322                async fn handle(&self, request: Request, next: Next) -> Response {
323                    next.run(request).await
324                }
325                fn name(&self) -> &'static str {
326                    stringify!($name)
327                }
328            }
329        };
330    }
331
332    tag!(First);
333    tag!(Second);
334    tag!(Third);
335
336    fn resolved_names(stack: &MiddlewareStack) -> Vec<&'static str> {
337        stack.resolve(&Container::new()).unwrap().iter().map(|m| m.name()).collect()
338    }
339
340    #[test]
341    fn a_stack_keeps_the_order_it_was_built_in() {
342        let stack = MiddlewareStack::new().with(First).with(Second).with(Third);
343        assert_eq!(resolved_names(&stack), vec!["First", "Second", "Third"]);
344    }
345
346    #[test]
347    fn one_middleware_converts_to_a_stack_of_one() {
348        assert_eq!(resolved_names(&First.into_middleware_stack()), vec!["First"]);
349    }
350
351    #[test]
352    fn a_tuple_converts_in_order() {
353        let stack = (First, Second, Third).into_middleware_stack();
354        assert_eq!(resolved_names(&stack), vec!["First", "Second", "Third"]);
355    }
356
357    #[test]
358    fn a_stack_converts_to_itself() {
359        let stack = MiddlewareStack::new().with(First);
360        assert_eq!(resolved_names(&stack.into_middleware_stack()), vec!["First"]);
361    }
362
363    #[test]
364    fn nesting_a_stack_flattens_it_in_place() {
365        // What a group inside a group has to do, and the reason the old
366        // registry needed a cycle check that this cannot want.
367        let inner = MiddlewareStack::new().with(Second).with(Third);
368        let outer = MiddlewareStack::new().with(First).with_stack(inner);
369
370        assert_eq!(resolved_names(&outer), vec!["First", "Second", "Third"]);
371    }
372
373    #[test]
374    fn prepending_puts_the_group_outside_the_routes_own() {
375        let mut route = MiddlewareStack::new().with(Third);
376        route.prepend(&MiddlewareStack::new().with(First).with(Second));
377
378        assert_eq!(resolved_names(&route), vec!["First", "Second", "Third"]);
379    }
380
381    #[test]
382    fn a_deferred_stage_is_built_at_resolution_not_declaration() {
383        use std::sync::atomic::{AtomicUsize, Ordering};
384        static BUILT: AtomicUsize = AtomicUsize::new(0);
385
386        let stack = MiddlewareStack::new().deferred(|_| {
387            BUILT.fetch_add(1, Ordering::SeqCst);
388            Ok(First)
389        });
390
391        assert_eq!(BUILT.load(Ordering::SeqCst), 0, "declaring must not build");
392
393        assert_eq!(resolved_names(&stack), vec!["First"]);
394        assert_eq!(BUILT.load(Ordering::SeqCst), 1);
395    }
396
397    #[test]
398    fn a_deferred_stage_that_fails_names_its_type() {
399        // The container was missing a binding. The message has to say which
400        // middleware wanted it, or the boot failure is a mystery.
401        let stack = MiddlewareStack::new()
402            .deferred(|_| Err::<First, _>(Error::internal("AuthManager is not bound")));
403
404        let err = stack.resolve(&Container::new()).err().expect("resolution should fail");
405        assert!(err.message().contains("`First`"), "{}", err.message());
406        assert!(err.message().contains("AuthManager is not bound"), "{}", err.message());
407    }
408
409    #[test]
410    fn resolved_builds_from_a_binding() {
411        struct Guard(&'static str);
412
413        let container = Container::new();
414        container.instance(Guard("bound"));
415
416        let stack = MiddlewareStack::new().resolved(|guard: Arc<Guard>| {
417            assert_eq!(guard.0, "bound");
418            First
419        });
420
421        // Against *this* container — the binding is the point of the test.
422        let built = stack.resolve(&container).unwrap();
423        assert_eq!(built.iter().map(|m| m.name()).collect::<Vec<_>>(), vec!["First"]);
424    }
425
426    #[test]
427    fn resolved_reports_the_missing_binding_by_type() {
428        // The whole message a developer gets when the kernel and the providers
429        // disagree, so it has to name both the service and the middleware.
430        struct NeverBound;
431
432        let stack = MiddlewareStack::new().resolved(|_: Arc<NeverBound>| First);
433
434        let err = stack.resolve(&Container::new()).err().expect("should fail");
435        assert!(err.message().contains("`First`"), "{}", err.message());
436        assert!(err.message().contains("NeverBound"), "{}", err.message());
437    }
438
439    #[test]
440    fn a_deferred_stage_labels_itself_before_it_is_built() {
441        // `route:list` and a debug print both want this without running the
442        // closure, which needs a container the caller may not have.
443        let stack = MiddlewareStack::new().with(First).deferred(|_| Ok(Second));
444
445        assert_eq!(stack.labels(), vec!["First", "Second"]);
446        assert_eq!(format!("{stack:?}"), r#"["First", "Second"]"#);
447    }
448
449    #[test]
450    fn a_shared_instance_is_not_cloned_per_route() {
451        // The property a rate limiter depends on: two routes attaching the same
452        // `Arc` share its counters.
453        let shared: Arc<dyn Middleware> = Arc::new(First);
454
455        let a = MiddlewareStack::new().with_arc(Arc::clone(&shared));
456        let b = MiddlewareStack::new().with_arc(Arc::clone(&shared));
457
458        let container = Container::new();
459        assert!(Arc::ptr_eq(
460            &a.resolve(&container).unwrap()[0],
461            &b.resolve(&container).unwrap()[0]
462        ));
463    }
464}