use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use axum::body::Body;
use axum::http::Request;
use limiteron::config::{GlobalConfig, Matcher, Rule};
use limiteron::storage::{MemoryBanStorage, MemoryStorage};
use limiteron::{
ActionConfig, BanStorage, Decision, FlowControlConfig, Governor, LimiterConfig, RequestContext,
Storage,
};
use super::{RateLimitError, RateLimiter};
pub struct LimiteronAdapter {
governor: Arc<Governor>,
}
fn default_config() -> FlowControlConfig {
FlowControlConfig {
version: "0.1.0".to_string(),
global: GlobalConfig::default(),
rules: vec![Rule {
id: "sdforge-default".to_string(),
name: "sdforge default rate limit".to_string(),
priority: 100,
matchers: vec![Matcher::Ip {
ip_ranges: vec!["0.0.0.0/0".to_string()],
}],
limiters: vec![LimiterConfig::TokenBucket {
capacity: 100,
refill_rate: 10,
}],
action: ActionConfig::default(),
}],
}
}
fn extract_identifier(req: &Request<Body>) -> String {
crate::security::ip_util::extract_client_ip_core(req)
.unwrap_or_else(|| "unknown".to_string())
}
impl LimiteronAdapter {
pub async fn new() -> Self {
let storage: Arc<dyn Storage> = Arc::new(MemoryStorage::new());
let ban_storage: Arc<dyn BanStorage> = Arc::new(MemoryBanStorage::new());
let governor = Governor::builder()
.with_config(default_config())
.with_storage(storage)
.with_ban_storage(ban_storage)
.build()
.await
.expect("default_config() must produce a valid FlowControlConfig");
Self {
governor: Arc::new(governor),
}
}
pub async fn default() -> Self {
Self::new().await
}
pub fn builder() -> LimiteronAdapterBuilder {
LimiteronAdapterBuilder::default()
}
pub fn with_dependencies(governor: Arc<Governor>) -> Self {
Self { governor }
}
}
impl RateLimiter for LimiteronAdapter {
fn check<'a>(
&'a self,
identifier: &'a str,
) -> Pin<Box<dyn Future<Output = Result<(), RateLimitError>> + Send + 'a>> {
Box::pin(async move {
let mut ctx = RequestContext::new();
ctx.client_ip = Some(identifier.to_string());
let decision = self.governor.check(&ctx).await?;
match decision {
Decision::Allowed(_) => Ok(()),
Decision::Rejected(meta) => Err(RateLimitError::Exceeded {
limit: meta.limit,
window_seconds: meta.retry_after,
}),
Decision::Banned(info) => Err(RateLimitError::Banned {
reason: info.reason().to_string(),
}),
}
})
}
fn check_request<'a>(
&'a self,
req: &'a Request<Body>,
) -> Pin<Box<dyn Future<Output = Result<(), RateLimitError>> + Send + 'a>> {
let identifier = extract_identifier(req);
Box::pin(async move { self.check(&identifier).await })
}
}
#[derive(Default)]
pub struct LimiteronAdapterBuilder {
config: Option<FlowControlConfig>,
}
impl LimiteronAdapterBuilder {
pub fn with_config(mut self, config: FlowControlConfig) -> Self {
self.config = Some(config);
self
}
pub async fn build(self) -> Result<LimiteronAdapter, RateLimitError> {
let config = self.config.unwrap_or_else(default_config);
let storage: Arc<dyn Storage> = Arc::new(MemoryStorage::new());
let ban_storage: Arc<dyn BanStorage> = Arc::new(MemoryBanStorage::new());
let governor = Governor::builder()
.with_config(config)
.with_storage(storage)
.with_ban_storage(ban_storage)
.build()
.await?;
Ok(LimiteronAdapter::with_dependencies(Arc::new(governor)))
}
}