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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
//! The layers between a socket and a handler, and the order they run in.
//!
//! Everything here is adapter: it reads headers, consults state, and turns the
//! result into a status. The rules live in `domain::transport` and in
//! `transport::origin`; this module's only job is to make sure a request that
//! must be refused never reaches a handler.
//!
//! ## Order, and why it is this order
//!
//! ```text
//! concurrency limit — shed load before doing any work
//! request timeout — a shed request must not hold a slot forever
//! origin check — 403 before authentication, so a rebinding page is
//! refused without this server revealing anything about
//! the credential it sent
//! rate limit — the only check that must consult mutable state
//! handler
//! ```
//!
//! Origin sits ahead of authentication on purpose. Refusing a foreign
//! `Origin` *after* authenticating would tell a page that is trying to rebind
//! whether the token it guessed was any good — which is precisely the oracle
//! DNS rebinding is after. Refusing first answers the same way whether the
//! credential was valid, invalid, or absent.
//!
//! Every check before the rate limiter is free, and the rate limiter spends a
//! lock and a map lookup. A limiter that paid that to refuse a request an
//! earlier check would have refused is a limiter doing work for nothing, so it
//! goes last.
//!
//! ## What the rate limit keys on
//!
//! `AuthContext` would be the ideal subject and is not available here: axum
//! middleware runs *before* the handler, and the authenticator runs inside it.
//! So the key is what a pre-handler layer can actually see —
//!
//! 1. `Mcp-Session-Id`, for a stateful client, because a session is stable
//! across requests and is the natural unit of "this caller".
//! 2. Otherwise a single shared `"anonymous"` allowance.
//!
//! (2) is a deliberate choice rather than a fallback of convenience: an
//! unauthenticated flood must not be able to mint itself a fresh allowance by
//! inventing a session id, and it cannot be given per-client isolation it has
//! not proved any right to. A caller that wants isolation establishes a
//! session, which is what sessions are for.
//!
//! ## What is deliberately absent
//!
//! No body limit. `RouterState` already refuses an oversized body with
//! [`Refusal::BodyTooLarge`] before reading it, and a second `DefaultBodyLimit`
//! here would be a second `413` path with its own idea of the ceiling — two
//! limits that agree today and will not after the first one is reconfigured.
use ;
use HeaderValue;
use Next;
use ;
use Arc;
use RateLimiter;
use Refusal;
use RouterState;
use crateVerdict;
/// The key used when a request carries no session id.
pub const ANONYMOUS_SUBJECT: &str = "anonymous";
/// Refuses a request whose `Origin` is not one this server serves.
pub async
/// Refuses a request whose `Host` is not one this server serves.
///
/// The pair with [`enforce_origin`], and the one that catches what the other
/// cannot: `Origin` is absent from every non-browser client, so a defence
/// built only on it answers "allowed" to exactly the requests a rebinding
/// attack is shaped to look like. `Host` is present on every HTTP/1.1 request
/// and is what a browser sets from the URL it dialled, so a rebound request
/// naming `evil.example` is refused here.
pub async
/// Counts the request against its subject's allowance, refusing with `429`.
///
/// The refusal carries `Retry-After` because a client told only "429" is a
/// client that retries immediately, which is the one behaviour a rate limit
/// cannot survive.
pub async
/// The key this request is counted against.
///
/// Private to the module because the choice of key is part of the threat
/// model, not a detail: a caller who could pick their own key could pick a
/// fresh one per request, and the limit would measure nothing.
/// Drops a request whose handler has not answered in time.
///
/// Written as `from_fn` rather than as `tower::timeout::TimeoutLayer` because
/// axum's `Router` has `Error = Infallible`, and `TimeoutLayer` requires its
/// inner service's error to absorb `BoxError`. The type gymnastics that gets
/// around that are worse than thirty lines of the obvious thing.
pub async
/// Wraps `router` in the concurrency ceiling.
///
/// Applied as one stack so the order is stated once and cannot drift between
/// the transports that mount it. The body limit is deliberately not here, and
/// neither is the deadline — see the module docs and `enforce_deadline`.
///
/// No type parameters: by the time this runs the router's state has already
/// been applied, so there is nothing here to infer `S` or `H` from.