1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
//! # modo::middleware
//!
//! Universal HTTP middleware for the modo web framework.
//!
//! Provides a collection of Tower-compatible middleware layers covering
//! the most common cross-cutting concerns — compression, request IDs,
//! panic recovery, CORS, CSRF, centralised error rendering, security
//! headers, request tracing, and rate limiting. Always available (no
//! feature flag required).
//!
//! ## Relationship to `modo::middlewares`
//!
//! This module ships the framework-universal layers. The virtual
//! [`modo::middlewares`](crate::middlewares) module is a flat
//! wiring-site index that re-exports **both** these universal
//! middlewares **and** domain-specific layers from feature-gated
//! modules (e.g. `tenant`, `auth`, `flash`, `ip`, `tier`,
//! `geolocation`, `template`). Reach for `modo::middlewares` when you
//! want a single namespace at your `.layer(...)` call sites; reach for
//! `modo::middleware` when you only need the universal layers or the
//! supporting configuration and extractor types.
//!
//! ## Provided items
//!
//! | Function / type | Purpose |
//! |---|---|
//! | [`compression`] | Compress responses (gzip, deflate, brotli, zstd) |
//! | [`request_id`] | Set / propagate `x-request-id` header |
//! | [`catch_panic`] | Convert handler panics into 500 responses |
//! | [`cors`] / [`cors_with`] | CORS headers (static or dynamic origins) |
//! | [`CorsConfig`] | CORS configuration |
//! | [`subdomains`] / [`urls`] | CORS origin predicates |
//! | [`csrf`] / [`CsrfConfig`] | Double-submit signed-cookie CSRF protection |
//! | [`CsrfToken`] | CSRF token in request/response extensions |
//! | [`error_handler`] | Centralised error-response rendering |
//! | [`default_error_handler`] | Ready-made responder that translates `locale_key` when a [`Translator`](crate::i18n::Translator) is available |
//! | [`security_headers`] / [`SecurityHeadersConfig`] | Security response headers |
//! | [`tracing`] | HTTP request/response lifecycle spans (`http_request`) |
//! | [`ModoMakeSpan`] | Span maker used by [`tracing`]; pre-declares fields like `tenant_id` |
//! | [`rate_limit`] / [`rate_limit_with`] | Token-bucket rate limiting |
//! | [`RateLimitConfig`] | Rate-limit configuration |
//! | [`RateLimitLayer`] | Tower layer produced by `rate_limit` / `rate_limit_with` |
//! | [`KeyExtractor`] | Trait for custom rate-limit key extraction |
//! | [`PeerIpKeyExtractor`] / [`GlobalKeyExtractor`] | Built-in key extractors |
//! | [`UserAgentLayer`] | Sanitize and bound the inbound `User-Agent` header |
//!
//! ## Layer composition
//!
//! axum applies `.layer(...)` in reverse declaration order — the **last**
//! layer added wraps everything before it and runs **first** on the inbound
//! path. The idiomatic stack for the always-available layers is:
//!
//! 1. `tracing()` — outermost, so every request (including errors produced
//! by inner layers) is observed. The span is built by [`ModoMakeSpan`],
//! which pre-declares `tenant_id = tracing::field::Empty` so that the
//! tenant middleware can fill it later via `tracing::Span::record`.
//! Any new field that downstream middleware needs to record **must** be
//! added to [`ModoMakeSpan`] first — `tracing` drops fields that were
//! not pre-declared at span creation.
//! 2. `catch_panic()` — converts handler/inner-layer panics into 500s
//! before they bubble past the error handler.
//! 3. `request_id()` — sets / propagates `x-request-id` so it appears on
//! every response, including rejected ones from `csrf` / `rate_limit`.
//! 4. `compression()` — close to the handler so compressed bytes flow
//! through the fewest layers.
//! 5. `error_handler(handler)` — innermost of the cross-cutting stack, so
//! errors produced by `csrf`, `rate_limit`, and the handler itself are
//! routed through your custom responder before being wrapped in spans.
//!
//! Translated into `.layer(...)` calls the outer-to-inner declaration order
//! is reversed:
//!
//! ```rust,no_run
//! use axum::{Router, routing::get};
//! use axum::response::IntoResponse;
//! use modo::middleware::*;
//!
//! async fn render_error(err: modo::Error, _parts: http::request::Parts) -> axum::response::Response {
//! (err.status(), err.message().to_string()).into_response()
//! }
//!
//! let app: Router = Router::new()
//! .route("/", get(|| async { "hello" }))
//! .layer(error_handler(render_error))
//! .layer(compression())
//! .layer(request_id())
//! .layer(catch_panic())
//! .layer(tracing());
//! ```
//!
//! ## `.layer` vs `.route_layer`
//!
//! Use `Router::layer(...)` for middleware that should run for every route
//! on the router (including 404s generated by axum). Use
//! `Router::route_layer(...)` for middleware that must only see matched
//! routes — for example, a guard that returns 401 only on real routes and
//! must not turn a 404 into a 401. The layers in this module are all safe
//! to install globally via `.layer(...)`; authorization guards from
//! `auth`, `tier`, and similar domain modules typically want `.route_layer`.
pub use ;
pub use catch_panic;
pub use compression;
pub use ;
pub use ;
pub use ;
pub use ;
pub use request_id;
pub use ;
pub use UserAgentLayer;