Skip to main content

axum_governor/
error.rs

1//! Error and reason types shared across the middleware.
2
3use std::sync::Arc;
4use std::time::Duration;
5
6/// Failure modes when extracting a rate-limit key from a request.
7#[derive(Debug)]
8pub enum ExtractionError {
9	MissingConnectInfo,
10	MissingHeader(&'static str),
11	MalformedHeader(&'static str),
12	UntrustedProxy,
13	Other(Box<dyn std::error::Error + Send + Sync>),
14}
15
16impl std::fmt::Display for ExtractionError {
17	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
18		match self {
19			Self::MissingConnectInfo => write!(f, "connect info extension is absent"),
20			Self::MissingHeader(name) => write!(f, "required header '{}' is missing", name),
21			Self::MalformedHeader(name) => write!(f, "header '{}' contains invalid data", name),
22			Self::UntrustedProxy => write!(f, "request originated from an untrusted proxy"),
23			Self::Other(e) => e.fmt(f),
24		}
25	}
26}
27
28impl std::error::Error for ExtractionError {
29	fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
30		match self {
31			Self::Other(e) => Some(e.as_ref()),
32			_ => None,
33		}
34	}
35}
36
37/// Failure modes surfaced by `GovernorConfigBuilder::finish`.
38#[derive(Debug)]
39pub enum ConfigError {
40	ZeroBurst,
41	EmptyChain,
42	ContradictoryWhitelist,
43	NoExtractor,
44	MissingConnectInfoAcknowledgement,
45}
46
47impl std::fmt::Display for ConfigError {
48	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49		match self {
50			Self::ZeroBurst => write!(f, "burst capacity must be non-zero"),
51			Self::EmptyChain => write!(f, "stacked limiter chain has no entries"),
52			Self::ContradictoryWhitelist => {
53				write!(f, "whitelist contradicts the configured extractor")
54			}
55			Self::NoExtractor => write!(f, "no key extractor was configured"),
56			Self::MissingConnectInfoAcknowledgement => {
57				write!(f, "PeerIp or SmartIp requires expect_connect_info() before finish()")
58			}
59		}
60	}
61}
62
63impl std::error::Error for ConfigError {}
64
65/// The reason the middleware rejected or could not process a request.
66///
67/// Passed to `error_handler` so callers can distinguish quota failure from extraction failure
68/// without status-code sniffing.
69#[derive(Debug)]
70pub enum RejectionReason {
71	QuotaExceeded {
72		wait: Duration,
73		snapshot: governor::middleware::StateSnapshot,
74		key: Box<dyn std::any::Any + Send>,
75		/// Name of the policy that triggered the rejection. Owned (`Arc<str>`) because
76		/// stack entries created via `quotas("name", ..)` carry dynamic labels such as
77		/// `"name:1s"` and would otherwise require leaking memory to outlive the layer.
78		policy_name: Arc<str>,
79	},
80	KeyExtractionFailed(ExtractionError),
81}