#![allow(deprecated)]
use crate::connection::{Message, WebSocketConnection};
use crate::middleware::{
ConnectionContext, ConnectionMiddleware, MessageMiddleware, MiddlewareError, MiddlewareResult,
};
use async_trait::async_trait;
use std::collections::HashMap;
use std::collections::VecDeque;
use std::sync::Arc;
use std::time::{Duration, Instant};
use tokio::sync::RwLock;
#[derive(Debug, thiserror::Error)]
pub enum ThrottleError {
#[error("Rate limit exceeded")]
RateLimitExceeded(String),
#[error("Too many connections")]
TooManyConnections(String),
#[error("Connection rate exceeded")]
ConnectionRateExceeded(String),
}
pub type ThrottleResult<T> = Result<T, ThrottleError>;
#[derive(Debug, Clone)]
pub struct RateLimitConfig {
max_requests: usize,
window: Duration,
}
impl RateLimitConfig {
pub fn new(max_requests: usize, window: Duration) -> Self {
Self {
max_requests,
window,
}
}
pub fn max_requests(&self) -> usize {
self.max_requests
}
pub fn window(&self) -> Duration {
self.window
}
pub fn permissive() -> Self {
Self::new(10000, Duration::from_secs(60))
}
pub fn strict() -> Self {
Self::new(10, Duration::from_secs(60))
}
}
#[derive(Debug)]
struct RequestCounter {
count: usize,
window_start: Instant,
}
impl RequestCounter {
fn new() -> Self {
Self {
count: 0,
window_start: Instant::now(),
}
}
fn increment(&mut self, config: &RateLimitConfig) -> ThrottleResult<()> {
let elapsed = self.window_start.elapsed();
if elapsed >= config.window {
self.count = 1;
self.window_start = Instant::now();
Ok(())
} else if self.count < config.max_requests {
self.count += 1;
Ok(())
} else {
Err(ThrottleError::RateLimitExceeded(format!(
"Exceeded {} requests per {:?}",
config.max_requests, config.window
)))
}
}
fn reset(&mut self) {
self.count = 0;
self.window_start = Instant::now();
}
}
pub struct RateLimiter {
config: RateLimitConfig,
counters: Arc<RwLock<HashMap<String, RequestCounter>>>,
}
impl RateLimiter {
pub fn new(config: RateLimitConfig) -> Self {
Self {
config,
counters: Arc::new(RwLock::new(HashMap::new())),
}
}
pub async fn check_rate_limit(&self, client_id: &str) -> ThrottleResult<()> {
let mut counters = self.counters.write().await;
let counter = counters
.entry(client_id.to_string())
.or_insert_with(RequestCounter::new);
counter.increment(&self.config)
}
pub async fn reset_client(&self, client_id: &str) {
let mut counters = self.counters.write().await;
if let Some(counter) = counters.get_mut(client_id) {
counter.reset();
}
}
pub async fn clear_all(&self) {
let mut counters = self.counters.write().await;
counters.clear();
}
pub async fn get_count(&self, client_id: &str) -> usize {
let counters = self.counters.read().await;
counters
.get(client_id)
.map(|counter| counter.count)
.unwrap_or(0)
}
}
pub struct ConnectionThrottler {
max_connections_per_ip: usize,
connections: Arc<RwLock<HashMap<String, usize>>>,
}
impl ConnectionThrottler {
pub fn new(max_connections_per_ip: usize) -> Self {
Self {
max_connections_per_ip,
connections: Arc::new(RwLock::new(HashMap::new())),
}
}
pub async fn acquire_connection(&self, ip: &str) -> ThrottleResult<()> {
let mut connections = self.connections.write().await;
let count = connections.entry(ip.to_string()).or_insert(0);
if *count >= self.max_connections_per_ip {
Err(ThrottleError::TooManyConnections(ip.to_string()))
} else {
*count += 1;
Ok(())
}
}
pub async fn release_connection(&self, ip: &str) {
let mut connections = self.connections.write().await;
if let Some(count) = connections.get_mut(ip) {
if *count > 0 {
*count -= 1;
}
if *count == 0 {
connections.remove(ip);
}
}
}
pub async fn get_connection_count(&self, ip: &str) -> usize {
let connections = self.connections.read().await;
connections.get(ip).copied().unwrap_or(0)
}
pub async fn clear_all(&self) {
let mut connections = self.connections.write().await;
connections.clear();
}
}
pub struct CombinedThrottler {
rate_limiter: RateLimiter,
connection_throttler: ConnectionThrottler,
}
impl CombinedThrottler {
pub fn new(rate_config: RateLimitConfig, max_connections_per_ip: usize) -> Self {
Self {
rate_limiter: RateLimiter::new(rate_config),
connection_throttler: ConnectionThrottler::new(max_connections_per_ip),
}
}
pub async fn check_connection(&self, ip: &str) -> ThrottleResult<()> {
self.connection_throttler.acquire_connection(ip).await
}
pub async fn release_connection(&self, ip: &str) {
self.connection_throttler.release_connection(ip).await
}
pub async fn check_message_rate(&self, client_id: &str) -> ThrottleResult<()> {
self.rate_limiter.check_rate_limit(client_id).await
}
pub async fn reset_client_rate(&self, client_id: &str) {
self.rate_limiter.reset_client(client_id).await
}
}
pub struct ConnectionRateLimiter {
max_connections_per_window: usize,
window: Duration,
timestamps: Arc<RwLock<HashMap<String, VecDeque<Instant>>>>,
}
impl ConnectionRateLimiter {
pub fn new(max_connections_per_window: usize, window: Duration) -> Self {
Self {
max_connections_per_window,
window,
timestamps: Arc::new(RwLock::new(HashMap::new())),
}
}
pub async fn check_connection_rate(&self, ip: &str) -> ThrottleResult<()> {
let mut timestamps = self.timestamps.write().await;
let now = Instant::now();
let entries = timestamps
.entry(ip.to_string())
.or_insert_with(VecDeque::new);
while let Some(&front) = entries.front() {
if now.duration_since(front) > self.window {
entries.pop_front();
} else {
break;
}
}
let result = if entries.len() >= self.max_connections_per_window {
Err(ThrottleError::ConnectionRateExceeded(format!(
"{} (exceeded {} connections per {:?})",
ip, self.max_connections_per_window, self.window
)))
} else {
entries.push_back(now);
Ok(())
};
timestamps.retain(|_, v| !v.is_empty());
result
}
pub async fn get_current_count(&self, ip: &str) -> usize {
let timestamps = self.timestamps.read().await;
let now = Instant::now();
timestamps
.get(ip)
.map(|entries| {
entries
.iter()
.filter(|&&ts| now.duration_since(ts) <= self.window)
.count()
})
.unwrap_or(0)
}
pub async fn clear_all(&self) {
let mut timestamps = self.timestamps.write().await;
timestamps.clear();
}
pub fn max_connections_per_window(&self) -> usize {
self.max_connections_per_window
}
pub fn window(&self) -> Duration {
self.window
}
}
#[deprecated(
since = "0.2.0",
note = "Use `RateLimitSettings` with the `#[settings]` macro instead."
)]
#[derive(Debug, Clone)]
pub struct WebSocketRateLimitConfig {
max_connections_per_window: usize,
connection_window: Duration,
max_concurrent_connections_per_ip: usize,
max_messages_per_window: usize,
message_window: Duration,
}
impl Default for WebSocketRateLimitConfig {
fn default() -> Self {
Self {
max_connections_per_window: 20,
connection_window: Duration::from_secs(60),
max_concurrent_connections_per_ip: 10,
max_messages_per_window: 100,
message_window: Duration::from_secs(60),
}
}
}
impl WebSocketRateLimitConfig {
pub fn strict() -> Self {
Self {
max_connections_per_window: 5,
connection_window: Duration::from_secs(60),
max_concurrent_connections_per_ip: 3,
max_messages_per_window: 30,
message_window: Duration::from_secs(60),
}
}
pub fn permissive() -> Self {
Self {
max_connections_per_window: 100,
connection_window: Duration::from_secs(60),
max_concurrent_connections_per_ip: 50,
max_messages_per_window: 1000,
message_window: Duration::from_secs(60),
}
}
pub fn with_max_connections_per_window(mut self, max: usize) -> Self {
self.max_connections_per_window = max;
self
}
pub fn with_connection_window(mut self, window: Duration) -> Self {
self.connection_window = window;
self
}
pub fn with_max_concurrent_connections_per_ip(mut self, max: usize) -> Self {
self.max_concurrent_connections_per_ip = max;
self
}
pub fn with_max_messages_per_window(mut self, max: usize) -> Self {
self.max_messages_per_window = max;
self
}
pub fn with_message_window(mut self, window: Duration) -> Self {
self.message_window = window;
self
}
pub fn max_connections_per_window(&self) -> usize {
self.max_connections_per_window
}
pub fn connection_window(&self) -> Duration {
self.connection_window
}
pub fn max_concurrent_connections_per_ip(&self) -> usize {
self.max_concurrent_connections_per_ip
}
pub fn max_messages_per_window(&self) -> usize {
self.max_messages_per_window
}
pub fn message_window(&self) -> Duration {
self.message_window
}
}
pub struct RateLimitMiddleware {
connection_rate_limiter: ConnectionRateLimiter,
connection_throttler: ConnectionThrottler,
message_rate_limiter: RateLimiter,
connection_ips: Arc<RwLock<HashMap<String, String>>>,
}
impl RateLimitMiddleware {
pub fn new(config: WebSocketRateLimitConfig) -> Self {
Self {
connection_rate_limiter: ConnectionRateLimiter::new(
config.max_connections_per_window,
config.connection_window,
),
connection_throttler: ConnectionThrottler::new(
config.max_concurrent_connections_per_ip,
),
message_rate_limiter: RateLimiter::new(RateLimitConfig::new(
config.max_messages_per_window,
config.message_window,
)),
connection_ips: Arc::new(RwLock::new(HashMap::new())),
}
}
pub fn with_defaults() -> Self {
Self::new(WebSocketRateLimitConfig::default())
}
pub async fn release_connection(&self, ip: &str) {
self.connection_throttler.release_connection(ip).await;
}
pub fn connection_rate_limiter(&self) -> &ConnectionRateLimiter {
&self.connection_rate_limiter
}
pub fn connection_throttler(&self) -> &ConnectionThrottler {
&self.connection_throttler
}
pub fn message_rate_limiter(&self) -> &RateLimiter {
&self.message_rate_limiter
}
}
#[async_trait]
impl ConnectionMiddleware for RateLimitMiddleware {
async fn on_connect(&self, context: &mut ConnectionContext) -> MiddlewareResult<()> {
let ip = &context.ip;
self.connection_rate_limiter
.check_connection_rate(ip)
.await
.map_err(|e| MiddlewareError::ConnectionRejected(e.to_string()))?;
self.connection_throttler
.acquire_connection(ip)
.await
.map_err(|e| MiddlewareError::ConnectionRejected(e.to_string()))?;
if let Some(conn_id) = &context.connection_id {
self.connection_ips
.write()
.await
.insert(conn_id.clone(), context.ip.clone());
}
Ok(())
}
async fn on_disconnect(&self, connection: &Arc<WebSocketConnection>) -> MiddlewareResult<()> {
let ip = self.connection_ips.write().await.remove(connection.id());
if let Some(ip) = ip {
self.connection_throttler.release_connection(&ip).await;
}
Ok(())
}
}
#[async_trait]
impl MessageMiddleware for RateLimitMiddleware {
async fn on_message(
&self,
connection: &Arc<WebSocketConnection>,
message: Message,
) -> MiddlewareResult<Message> {
self.message_rate_limiter
.check_rate_limit(connection.id())
.await
.map_err(|e| MiddlewareError::MessageRejected(e.to_string()))?;
Ok(message)
}
}
#[cfg(test)]
mod tests {
use super::*;
use rstest::rstest;
use tokio::sync::mpsc;
#[rstest]
fn test_rate_limit_config_new() {
let config = RateLimitConfig::new(100, Duration::from_secs(60));
assert_eq!(config.max_requests(), 100);
assert_eq!(config.window(), Duration::from_secs(60));
}
#[rstest]
fn test_rate_limit_config_presets() {
let permissive = RateLimitConfig::permissive();
let strict = RateLimitConfig::strict();
assert_eq!(permissive.max_requests(), 10000);
assert_eq!(permissive.window(), Duration::from_secs(60));
assert_eq!(strict.max_requests(), 10);
assert_eq!(strict.window(), Duration::from_secs(60));
}
#[rstest]
#[tokio::test]
async fn test_rate_limiter_within_limit() {
let config = RateLimitConfig::new(5, Duration::from_secs(10));
let limiter = RateLimiter::new(config);
for _ in 0..5 {
assert!(limiter.check_rate_limit("user_1").await.is_ok());
}
}
#[rstest]
#[tokio::test]
async fn test_rate_limiter_exceeds_limit() {
let config = RateLimitConfig::new(3, Duration::from_secs(10));
let limiter = RateLimiter::new(config);
for _ in 0..3 {
limiter.check_rate_limit("user_1").await.unwrap();
}
let result = limiter.check_rate_limit("user_1").await;
assert!(result.is_err());
assert!(matches!(
result.unwrap_err(),
ThrottleError::RateLimitExceeded(_)
));
}
#[rstest]
#[tokio::test]
async fn test_rate_limiter_reset() {
let config = RateLimitConfig::new(2, Duration::from_secs(10));
let limiter = RateLimiter::new(config);
limiter.check_rate_limit("user_1").await.unwrap();
limiter.check_rate_limit("user_1").await.unwrap();
assert!(limiter.check_rate_limit("user_1").await.is_err());
limiter.reset_client("user_1").await;
assert!(limiter.check_rate_limit("user_1").await.is_ok());
}
#[rstest]
#[tokio::test]
async fn test_rate_limiter_get_count() {
let config = RateLimitConfig::new(10, Duration::from_secs(10));
let limiter = RateLimiter::new(config);
assert_eq!(limiter.get_count("user_1").await, 0);
limiter.check_rate_limit("user_1").await.unwrap();
limiter.check_rate_limit("user_1").await.unwrap();
assert_eq!(limiter.get_count("user_1").await, 2);
}
#[rstest]
#[tokio::test]
async fn test_rate_limiter_independent_clients() {
let config = RateLimitConfig::new(2, Duration::from_secs(10));
let limiter = RateLimiter::new(config);
limiter.check_rate_limit("user_1").await.unwrap();
limiter.check_rate_limit("user_1").await.unwrap();
assert!(limiter.check_rate_limit("user_1").await.is_err());
assert!(limiter.check_rate_limit("user_2").await.is_ok());
}
#[rstest]
#[tokio::test]
async fn test_connection_throttler_within_limit() {
let throttler = ConnectionThrottler::new(3);
assert!(throttler.acquire_connection("192.168.1.1").await.is_ok());
assert!(throttler.acquire_connection("192.168.1.1").await.is_ok());
assert!(throttler.acquire_connection("192.168.1.1").await.is_ok());
}
#[rstest]
#[tokio::test]
async fn test_connection_throttler_exceeds_limit() {
let throttler = ConnectionThrottler::new(2);
throttler.acquire_connection("192.168.1.1").await.unwrap();
throttler.acquire_connection("192.168.1.1").await.unwrap();
let result = throttler.acquire_connection("192.168.1.1").await;
assert!(result.is_err());
assert!(matches!(
result.unwrap_err(),
ThrottleError::TooManyConnections(_)
));
}
#[rstest]
#[tokio::test]
async fn test_connection_throttler_release() {
let throttler = ConnectionThrottler::new(2);
throttler.acquire_connection("192.168.1.1").await.unwrap();
throttler.acquire_connection("192.168.1.1").await.unwrap();
assert!(throttler.acquire_connection("192.168.1.1").await.is_err());
throttler.release_connection("192.168.1.1").await;
assert!(throttler.acquire_connection("192.168.1.1").await.is_ok());
}
#[rstest]
#[tokio::test]
async fn test_connection_throttler_get_count() {
let throttler = ConnectionThrottler::new(10);
assert_eq!(throttler.get_connection_count("192.168.1.1").await, 0);
throttler.acquire_connection("192.168.1.1").await.unwrap();
throttler.acquire_connection("192.168.1.1").await.unwrap();
assert_eq!(throttler.get_connection_count("192.168.1.1").await, 2);
}
#[rstest]
#[tokio::test]
async fn test_connection_throttler_independent_ips() {
let throttler = ConnectionThrottler::new(1);
throttler.acquire_connection("192.168.1.1").await.unwrap();
assert!(throttler.acquire_connection("192.168.1.1").await.is_err());
assert!(throttler.acquire_connection("10.0.0.1").await.is_ok());
}
#[rstest]
#[tokio::test]
async fn test_combined_throttler() {
let config = RateLimitConfig::new(10, Duration::from_secs(10));
let throttler = CombinedThrottler::new(config, 5);
assert!(throttler.check_connection("192.168.1.1").await.is_ok());
assert!(throttler.check_message_rate("user_1").await.is_ok());
throttler.release_connection("192.168.1.1").await;
throttler.reset_client_rate("user_1").await;
}
#[rstest]
#[tokio::test]
async fn test_connection_rate_limiter_within_limit() {
let limiter = ConnectionRateLimiter::new(3, Duration::from_secs(60));
assert!(limiter.check_connection_rate("192.168.1.1").await.is_ok());
assert!(limiter.check_connection_rate("192.168.1.1").await.is_ok());
assert!(limiter.check_connection_rate("192.168.1.1").await.is_ok());
}
#[rstest]
#[tokio::test]
async fn test_connection_rate_limiter_exceeds_limit() {
let limiter = ConnectionRateLimiter::new(2, Duration::from_secs(60));
limiter.check_connection_rate("192.168.1.1").await.unwrap();
limiter.check_connection_rate("192.168.1.1").await.unwrap();
let result = limiter.check_connection_rate("192.168.1.1").await;
assert!(result.is_err());
assert!(matches!(
result.unwrap_err(),
ThrottleError::ConnectionRateExceeded(_)
));
}
#[rstest]
#[tokio::test]
async fn test_connection_rate_limiter_independent_ips() {
let limiter = ConnectionRateLimiter::new(1, Duration::from_secs(60));
limiter.check_connection_rate("192.168.1.1").await.unwrap();
assert!(limiter.check_connection_rate("192.168.1.1").await.is_err());
assert!(limiter.check_connection_rate("10.0.0.1").await.is_ok());
}
#[rstest]
#[tokio::test]
async fn test_connection_rate_limiter_window_expiry() {
let limiter = ConnectionRateLimiter::new(1, Duration::from_millis(50));
limiter.check_connection_rate("192.168.1.1").await.unwrap();
assert!(limiter.check_connection_rate("192.168.1.1").await.is_err());
tokio::time::sleep(Duration::from_millis(60)).await;
assert!(limiter.check_connection_rate("192.168.1.1").await.is_ok());
}
#[rstest]
#[tokio::test]
async fn test_connection_rate_limiter_get_current_count() {
let limiter = ConnectionRateLimiter::new(10, Duration::from_secs(60));
assert_eq!(limiter.get_current_count("192.168.1.1").await, 0);
limiter.check_connection_rate("192.168.1.1").await.unwrap();
limiter.check_connection_rate("192.168.1.1").await.unwrap();
assert_eq!(limiter.get_current_count("192.168.1.1").await, 2);
}
#[rstest]
#[tokio::test]
async fn test_connection_rate_limiter_clear_all() {
let limiter = ConnectionRateLimiter::new(1, Duration::from_secs(60));
limiter.check_connection_rate("192.168.1.1").await.unwrap();
assert!(limiter.check_connection_rate("192.168.1.1").await.is_err());
limiter.clear_all().await;
assert!(limiter.check_connection_rate("192.168.1.1").await.is_ok());
}
#[rstest]
fn test_connection_rate_limiter_accessors() {
let limiter = ConnectionRateLimiter::new(5, Duration::from_secs(30));
assert_eq!(limiter.max_connections_per_window(), 5);
assert_eq!(limiter.window(), Duration::from_secs(30));
}
#[rstest]
fn test_websocket_rate_limit_config_default() {
let config = WebSocketRateLimitConfig::default();
assert_eq!(config.max_connections_per_window(), 20);
assert_eq!(config.connection_window(), Duration::from_secs(60));
assert_eq!(config.max_concurrent_connections_per_ip(), 10);
assert_eq!(config.max_messages_per_window(), 100);
assert_eq!(config.message_window(), Duration::from_secs(60));
}
#[rstest]
fn test_websocket_rate_limit_config_strict() {
let config = WebSocketRateLimitConfig::strict();
assert_eq!(config.max_connections_per_window(), 5);
assert_eq!(config.max_concurrent_connections_per_ip(), 3);
assert_eq!(config.max_messages_per_window(), 30);
}
#[rstest]
fn test_websocket_rate_limit_config_permissive() {
let config = WebSocketRateLimitConfig::permissive();
assert_eq!(config.max_connections_per_window(), 100);
assert_eq!(config.max_concurrent_connections_per_ip(), 50);
assert_eq!(config.max_messages_per_window(), 1000);
}
#[rstest]
fn test_websocket_rate_limit_config_builder() {
let config = WebSocketRateLimitConfig::default()
.with_max_connections_per_window(50)
.with_connection_window(Duration::from_secs(120))
.with_max_concurrent_connections_per_ip(25)
.with_max_messages_per_window(500)
.with_message_window(Duration::from_secs(30));
assert_eq!(config.max_connections_per_window(), 50);
assert_eq!(config.connection_window(), Duration::from_secs(120));
assert_eq!(config.max_concurrent_connections_per_ip(), 25);
assert_eq!(config.max_messages_per_window(), 500);
assert_eq!(config.message_window(), Duration::from_secs(30));
}
#[rstest]
#[tokio::test]
async fn test_rate_limit_middleware_allows_connection() {
let config = WebSocketRateLimitConfig::default();
let middleware = RateLimitMiddleware::new(config);
let mut context = ConnectionContext::new("192.168.1.1".to_string());
let result = middleware.on_connect(&mut context).await;
assert!(result.is_ok());
}
#[rstest]
#[tokio::test]
async fn test_rate_limit_middleware_rejects_connection_rate_exceeded() {
let config = WebSocketRateLimitConfig::default().with_max_connections_per_window(2);
let middleware = RateLimitMiddleware::new(config);
let mut ctx1 = ConnectionContext::new("192.168.1.1".to_string());
let mut ctx2 = ConnectionContext::new("192.168.1.1".to_string());
let mut ctx3 = ConnectionContext::new("192.168.1.1".to_string());
middleware.on_connect(&mut ctx1).await.unwrap();
middleware.on_connect(&mut ctx2).await.unwrap();
let result = middleware.on_connect(&mut ctx3).await;
assert!(result.is_err());
assert!(matches!(
result.unwrap_err(),
MiddlewareError::ConnectionRejected(_)
));
}
#[rstest]
#[tokio::test]
async fn test_rate_limit_middleware_rejects_concurrent_exceeded() {
let config = WebSocketRateLimitConfig::default()
.with_max_connections_per_window(100)
.with_max_concurrent_connections_per_ip(1);
let middleware = RateLimitMiddleware::new(config);
let mut ctx1 = ConnectionContext::new("192.168.1.1".to_string());
let mut ctx2 = ConnectionContext::new("192.168.1.1".to_string());
middleware.on_connect(&mut ctx1).await.unwrap();
let result = middleware.on_connect(&mut ctx2).await;
assert!(result.is_err());
assert!(matches!(
result.unwrap_err(),
MiddlewareError::ConnectionRejected(_)
));
}
#[rstest]
#[tokio::test]
async fn test_rate_limit_middleware_allows_message_within_limit() {
let config = WebSocketRateLimitConfig::default();
let middleware = RateLimitMiddleware::new(config);
let (tx, _rx) = mpsc::unbounded_channel();
let conn = Arc::new(WebSocketConnection::new("test_conn".to_string(), tx));
let message = Message::text("hello".to_string());
let result = middleware.on_message(&conn, message).await;
assert!(result.is_ok());
}
#[rstest]
#[tokio::test]
async fn test_rate_limit_middleware_rejects_message_rate_exceeded() {
let config = WebSocketRateLimitConfig::default().with_max_messages_per_window(3);
let middleware = RateLimitMiddleware::new(config);
let (tx, _rx) = mpsc::unbounded_channel();
let conn = Arc::new(WebSocketConnection::new("test_conn".to_string(), tx));
for _ in 0..3 {
let msg = Message::text("hello".to_string());
middleware.on_message(&conn, msg).await.unwrap();
}
let result = middleware
.on_message(&conn, Message::text("overflow".to_string()))
.await;
assert!(result.is_err());
assert!(matches!(
result.unwrap_err(),
MiddlewareError::MessageRejected(_)
));
}
#[rstest]
#[tokio::test]
async fn test_rate_limit_middleware_with_defaults() {
let middleware = RateLimitMiddleware::with_defaults();
let mut context = ConnectionContext::new("10.0.0.1".to_string());
let result = middleware.on_connect(&mut context).await;
assert!(result.is_ok());
}
#[rstest]
#[tokio::test]
async fn test_rate_limit_middleware_release_connection() {
let config = WebSocketRateLimitConfig::default()
.with_max_connections_per_window(100)
.with_max_concurrent_connections_per_ip(1);
let middleware = RateLimitMiddleware::new(config);
let mut ctx1 = ConnectionContext::new("192.168.1.1".to_string());
middleware.on_connect(&mut ctx1).await.unwrap();
let mut ctx2 = ConnectionContext::new("192.168.1.1".to_string());
assert!(middleware.on_connect(&mut ctx2).await.is_err());
middleware.release_connection("192.168.1.1").await;
let mut ctx3 = ConnectionContext::new("192.168.1.1".to_string());
assert!(middleware.on_connect(&mut ctx3).await.is_ok());
}
#[rstest]
#[tokio::test]
async fn test_rate_limit_middleware_on_disconnect_releases_slot() {
let config = WebSocketRateLimitConfig::default()
.with_max_connections_per_window(100)
.with_max_concurrent_connections_per_ip(1);
let middleware = RateLimitMiddleware::new(config);
let mut ctx1 = ConnectionContext::new("192.168.1.1".to_string());
ctx1.connection_id = Some("conn_1".to_string());
middleware.on_connect(&mut ctx1).await.unwrap();
let mut ctx2 = ConnectionContext::new("192.168.1.1".to_string());
assert!(middleware.on_connect(&mut ctx2).await.is_err());
let (tx, _rx) = tokio::sync::mpsc::unbounded_channel();
let connection = Arc::new(WebSocketConnection::new("conn_1".to_string(), tx));
middleware.on_disconnect(&connection).await.unwrap();
let mut ctx3 = ConnectionContext::new("192.168.1.1".to_string());
assert!(middleware.on_connect(&mut ctx3).await.is_ok());
}
#[rstest]
#[tokio::test]
async fn test_rate_limit_middleware_in_chain() {
use crate::middleware::MiddlewareChain;
let config = WebSocketRateLimitConfig::default().with_max_connections_per_window(2);
let middleware = RateLimitMiddleware::new(config);
let mut chain = MiddlewareChain::new();
chain.add_connection_middleware(Box::new(middleware));
let mut ctx1 = ConnectionContext::new("192.168.1.1".to_string());
assert!(chain.process_connect(&mut ctx1).await.is_ok());
let mut ctx2 = ConnectionContext::new("192.168.1.1".to_string());
assert!(chain.process_connect(&mut ctx2).await.is_ok());
let mut ctx3 = ConnectionContext::new("192.168.1.1".to_string());
assert!(chain.process_connect(&mut ctx3).await.is_err());
}
#[rstest]
#[tokio::test]
async fn test_rate_limit_middleware_message_in_chain() {
use crate::middleware::MiddlewareChain;
let config = WebSocketRateLimitConfig::default().with_max_messages_per_window(1);
let middleware = RateLimitMiddleware::new(config);
let mut chain = MiddlewareChain::new();
chain.add_message_middleware(Box::new(middleware));
let (tx, _rx) = mpsc::unbounded_channel();
let conn = Arc::new(WebSocketConnection::new("test".to_string(), tx));
let msg1 = Message::text("first".to_string());
assert!(chain.process_message(&conn, msg1).await.is_ok());
let msg2 = Message::text("second".to_string());
assert!(chain.process_message(&conn, msg2).await.is_err());
}
#[rstest]
#[tokio::test]
async fn test_rate_limit_middleware_accessors() {
let config = WebSocketRateLimitConfig::default()
.with_max_connections_per_window(15)
.with_max_concurrent_connections_per_ip(7);
let middleware = RateLimitMiddleware::new(config);
assert_eq!(
middleware
.connection_rate_limiter()
.max_connections_per_window(),
15
);
middleware
.connection_throttler()
.acquire_connection("1.2.3.4")
.await
.unwrap();
assert_eq!(
middleware
.connection_throttler()
.get_connection_count("1.2.3.4")
.await,
1
);
}
}