cratestack_axum/ratelimit/
layer.rs1use std::sync::Arc;
2use std::time::Duration;
3
4use axum::extract::Request;
5use cratestack_core::CratestackError;
6use tower::Layer;
7
8use super::budget::RateLimitBucketBudget;
9use super::budget::warn::BudgetWarnings;
10use super::config::RateLimitConfig;
11use super::key_fn::{default_key_fn, default_should_rate_limit_fn};
12use super::policy::{DEFAULT_STORE_TIMEOUT, StoreErrorPolicy, StoreErrorWarnings};
13use super::scope::{KeyDerivation, UnverifiedAuthPolicy};
14use super::service::RateLimitService;
15use super::store::RateLimitStore;
16
17pub(super) type KeyFn =
18 Arc<dyn Fn(&Request) -> Result<KeyDerivation, CratestackError> + Send + Sync>;
19
20#[derive(Clone)]
21pub struct RateLimitLayer {
22 store: Arc<dyn RateLimitStore>,
23 config: RateLimitConfig,
24 key_fn: Option<KeyFn>,
25 should_rate_limit_fn: Arc<dyn Fn(&Request) -> bool + Send + Sync>,
26 store_error_policy: StoreErrorPolicy,
27 store_timeout: Duration,
28 bucket_budget: Option<RateLimitBucketBudget>,
29 unverified_auth_policy: UnverifiedAuthPolicy,
30 warnings: Arc<StoreErrorWarnings>,
31 budget_warnings: Arc<BudgetWarnings>,
32}
33
34impl RateLimitLayer {
35 pub fn new(store: Arc<dyn RateLimitStore>, config: RateLimitConfig) -> Self {
36 Self {
37 store,
38 config,
39 key_fn: None,
40 should_rate_limit_fn: Arc::new(default_should_rate_limit_fn),
41 store_error_policy: StoreErrorPolicy::default(),
42 store_timeout: DEFAULT_STORE_TIMEOUT,
43 bucket_budget: Some(RateLimitBucketBudget::default()),
44 unverified_auth_policy: UnverifiedAuthPolicy::default(),
45 warnings: Arc::new(StoreErrorWarnings::default()),
46 budget_warnings: Arc::new(BudgetWarnings::default()),
47 }
48 }
49
50 pub fn with_store_error_policy(mut self, policy: StoreErrorPolicy) -> Self {
57 self.store_error_policy = policy;
58 self
59 }
60
61 pub fn with_store_timeout(mut self, timeout: Duration) -> Self {
72 self.store_timeout = timeout;
73 self
74 }
75
76 pub fn with_bucket_budget(mut self, budget: RateLimitBucketBudget) -> Self {
79 self.bucket_budget = Some(budget);
80 self
81 }
82
83 pub fn without_bucket_budget(mut self) -> Self {
91 self.bucket_budget = None;
92 self
93 }
94
95 pub fn with_unverified_auth_policy(mut self, policy: UnverifiedAuthPolicy) -> Self {
98 self.unverified_auth_policy = policy;
99 self
100 }
101
102 pub fn with_key_fn(mut self, f: impl Fn(&Request) -> String + Send + Sync + 'static) -> Self {
112 self.key_fn = Some(Arc::new(move |req| Ok(KeyDerivation::unbudgeted(f(req)))));
113 self
114 }
115
116 pub fn with_should_rate_limit_fn(
117 mut self,
118 f: impl Fn(&Request) -> bool + Send + Sync + 'static,
119 ) -> Self {
120 self.should_rate_limit_fn = Arc::new(f);
121 self
122 }
123
124 pub(super) fn _budget_warnings(&self) -> &BudgetWarnings {
129 &self.budget_warnings
130 }
131
132 fn resolved_key_fn(&self) -> KeyFn {
136 if let Some(key_fn) = &self.key_fn {
137 return key_fn.clone();
138 }
139 let budget = self.bucket_budget;
140 let policy = self.unverified_auth_policy;
141 let warnings = self.budget_warnings.clone();
142 Arc::new(move |req| match budget {
143 Some(budget) => default_key_fn(req, budget, policy, &warnings),
144 None => default_key_fn(req, RateLimitBucketBudget::default(), policy, &warnings)
149 .map(|derivation| KeyDerivation::unbudgeted(derivation.key)),
150 })
151 }
152}
153
154impl<S> Layer<S> for RateLimitLayer {
155 type Service = RateLimitService<S>;
156
157 fn layer(&self, inner: S) -> Self::Service {
158 RateLimitService {
159 inner,
160 store: self.store.clone(),
161 config: self.config,
162 key_fn: self.resolved_key_fn(),
163 should_rate_limit_fn: self.should_rate_limit_fn.clone(),
164 store_error_policy: self.store_error_policy,
165 store_timeout: self.store_timeout,
166 warnings: self.warnings.clone(),
167 budget_warnings: self.budget_warnings.clone(),
168 }
169 }
170}