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
// src/rate_limiter/factory.rs
use crate::rate_limiter::RateLimiter;
use std::sync::Arc;
use tracing::{error, info, warn};
// Use the type-safe RedisConfig and CacheDriver from options.rs
use crate::error::Result;
use crate::options::{CacheDriver, RateLimiterConfig, RedisConnection};
use crate::rate_limiter::memory_limiter::MemoryRateLimiter;
use crate::rate_limiter::redis_limiter::RedisRateLimiter;
pub struct RateLimiterFactory;
impl RateLimiterFactory {
pub async fn create(
config: &RateLimiterConfig,
global_redis_conn_details: &RedisConnection, // For Redis URL/nodes if not in RateLimiterConfig.redis.url_override
debug_enabled: bool,
) -> Result<Arc<dyn RateLimiter + Send + Sync>> {
if !config.enabled {
info!(
"{}",
"HTTP API Rate limiting is globally disabled. Returning a permissive limiter."
.to_string(),
);
return Ok(Arc::new(MemoryRateLimiter::new(u32::MAX, 1))); // Allows all
}
info!(
"{}",
format!(
"Initializing HTTP API RateLimiter with driver: {:?}",
config.driver
)
);
match config.driver {
CacheDriver::Redis => {
// Assuming RateLimiter uses CacheDriver enum for its backend
// Determine if this Redis instance should be cluster or standalone
// based on the specific RateLimiterConfig.redis.cluster_mode
if config.redis.cluster_mode {
info!(
"{}",
"RateLimiter: Using Redis Cluster backend.".to_string()
);
if global_redis_conn_details.cluster_nodes.is_empty() {
error!("{}", "RateLimiter: Redis cluster mode enabled, but no cluster_nodes configured.".to_string());
return Err(crate::error::Error::ConfigurationError(
"RateLimiter: Redis cluster nodes not configured.".to_string(),
));
}
let nodes: Vec<String> = global_redis_conn_details
.cluster_nodes
.iter()
.map(|node| format!("redis://{}:{}", node.host, node.port))
.collect();
let prefix = config.redis.prefix.clone().unwrap_or_else(|| {
global_redis_conn_details.key_prefix.clone() + "rl_http:"
});
// Here you would instantiate your RedisClusterRateLimiter
// For now, let's assume it's not implemented and fall back or error
warn!("{}", "RedisClusterRateLimiter not yet implemented. Falling back to MemoryRateLimiter for HTTP API.".to_string());
let limiter = MemoryRateLimiter::new(
config.api_rate_limit.max_requests,
config.api_rate_limit.window_seconds,
);
Ok(Arc::new(limiter))
// Example if it were implemented:
// let limiter = RedisClusterRateLimiter::new(nodes, prefix, config.api_rate_limit.max_requests, config.api_rate_limit.window_seconds).await?;
// Ok(Arc::new(limiter))
} else {
info!(
"{}",
"RateLimiter: Using standalone Redis backend.".to_string()
);
let redis_url = config.redis.url_override.clone().unwrap_or_else(|| {
format!(
"redis://{}:{}",
global_redis_conn_details.host, global_redis_conn_details.port
)
});
let prefix = config.redis.prefix.clone().unwrap_or_else(|| {
global_redis_conn_details.key_prefix.clone() + "rl_http:"
});
let client = redis::Client::open(redis_url.as_str()).map_err(|e| {
crate::error::Error::RedisError(format!(
"Failed to create Redis client for rate limiter: {}",
e
))
})?;
let limiter = RedisRateLimiter::new(
client,
prefix,
config.api_rate_limit.max_requests,
config.api_rate_limit.window_seconds,
)
.await?;
Ok(Arc::new(limiter))
}
}
CacheDriver::RedisCluster => {
// Explicit RedisCluster driver for RateLimiter backend
info!(
"{}",
"RateLimiter: Using Redis Cluster backend (explicitly selected).".to_string(),
);
if global_redis_conn_details.cluster_nodes.is_empty() {
error!("{}", "RateLimiter: Redis cluster driver selected, but no cluster_nodes configured.".to_string());
return Err(crate::error::Error::ConfigurationError("RateLimiter: Redis cluster nodes not configured for explicit cluster driver.".to_string()));
}
// As above, if RedisClusterRateLimiter is implemented:
warn!("{}", "RedisClusterRateLimiter not yet implemented. Falling back to MemoryRateLimiter for HTTP API.".to_string());
let limiter = MemoryRateLimiter::new(
config.api_rate_limit.max_requests,
config.api_rate_limit.window_seconds,
);
Ok(Arc::new(limiter))
}
CacheDriver::Memory | _ => {
// Default to memory for rate limiter if driver is "memory" or unknown
info!("{}", "Using memory rate limiter for HTTP API.".to_string());
let limiter = MemoryRateLimiter::new(
config.api_rate_limit.max_requests,
config.api_rate_limit.window_seconds,
);
Ok(Arc::new(limiter))
}
}
}
}