use std::sync::Arc;
use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
use std::time::{Duration, Instant};
use tokio::sync::Semaphore;
use tokio::time::timeout;
#[derive(Debug, Clone)]
pub struct ResourceLimits {
pub max_request_size: usize,
pub max_concurrent_connections: usize,
pub max_requests_per_second: Option<u32>,
pub connection_timeout: Duration,
pub max_idle_time: Duration,
}
impl Default for ResourceLimits {
fn default() -> Self {
Self {
max_request_size: 1024 * 1024, max_concurrent_connections: 100,
max_requests_per_second: Some(100), connection_timeout: Duration::from_secs(30),
max_idle_time: Duration::from_secs(300), }
}
}
#[derive(Debug)]
pub struct RateLimiter {
permits: Arc<Semaphore>,
refill_rate: u32,
last_refill: Arc<std::sync::Mutex<Instant>>,
}
impl RateLimiter {
pub fn new(requests_per_second: u32) -> Self {
Self {
permits: Arc::new(Semaphore::new(requests_per_second as usize)),
refill_rate: requests_per_second,
last_refill: Arc::new(std::sync::Mutex::new(Instant::now())),
}
}
pub async fn acquire(&self) -> Result<(), RateLimitError> {
self.try_refill();
match timeout(Duration::from_millis(100), self.permits.acquire()).await {
Ok(Ok(_permit)) => Ok(()),
Ok(Err(_)) => Err(RateLimitError::Closed),
Err(_) => Err(RateLimitError::Exceeded),
}
}
fn try_refill(&self) {
let now = Instant::now();
if let Ok(mut last_refill) = self.last_refill.try_lock() {
let elapsed = now.duration_since(*last_refill);
if elapsed >= Duration::from_secs(1) {
let permits_to_add = (elapsed.as_secs() as u32 * self.refill_rate) as usize;
let current_permits = self.permits.available_permits();
let max_permits = self.refill_rate as usize;
if current_permits < max_permits {
let actual_add = std::cmp::min(permits_to_add, max_permits - current_permits);
self.permits.add_permits(actual_add);
}
*last_refill = now;
}
}
}
}
#[derive(Debug, thiserror::Error)]
pub enum RateLimitError {
#[error("Rate limit exceeded")]
Exceeded,
#[error("Rate limiter closed")]
Closed,
}
#[derive(Debug)]
pub struct ConnectionTracker {
active_connections: AtomicUsize,
total_connections: AtomicU64,
connection_semaphore: Arc<Semaphore>,
limits: ResourceLimits,
}
impl ConnectionTracker {
pub fn new(limits: ResourceLimits) -> Self {
Self {
active_connections: AtomicUsize::new(0),
total_connections: AtomicU64::new(0),
connection_semaphore: Arc::new(Semaphore::new(limits.max_concurrent_connections)),
limits,
}
}
pub async fn acquire_connection(&self) -> Result<ConnectionGuard, ConnectionError> {
let permit = timeout(Duration::from_secs(1), self.connection_semaphore.acquire())
.await
.map_err(|_| ConnectionError::Timeout)?
.map_err(|_| ConnectionError::Closed)?;
let active = self.active_connections.fetch_add(1, Ordering::SeqCst) + 1;
let total = self.total_connections.fetch_add(1, Ordering::SeqCst) + 1;
tracing::info!(
active_connections = active,
total_connections = total,
"Connection acquired"
);
Ok(ConnectionGuard {
_permit: permit,
tracker: self,
start_time: Instant::now(),
})
}
pub fn metrics(&self) -> ConnectionMetrics {
ConnectionMetrics {
active_connections: self.active_connections.load(Ordering::SeqCst),
total_connections: self.total_connections.load(Ordering::SeqCst),
available_slots: self.connection_semaphore.available_permits(),
max_connections: self.limits.max_concurrent_connections,
}
}
}
pub struct ConnectionGuard<'a> {
_permit: tokio::sync::SemaphorePermit<'a>,
tracker: &'a ConnectionTracker,
start_time: Instant,
}
impl<'a> Drop for ConnectionGuard<'a> {
fn drop(&mut self) {
let active = self
.tracker
.active_connections
.fetch_sub(1, Ordering::SeqCst)
- 1;
let duration = self.start_time.elapsed();
tracing::info!(
active_connections = active,
connection_duration_ms = duration.as_millis(),
"Connection released"
);
}
}
#[derive(Debug, thiserror::Error)]
pub enum ConnectionError {
#[error("Connection limit reached, timeout waiting for slot")]
Timeout,
#[error("Connection tracker closed")]
Closed,
}
#[derive(Debug, Clone)]
pub struct ConnectionMetrics {
pub active_connections: usize,
pub total_connections: u64,
pub available_slots: usize,
pub max_connections: usize,
}
pub struct SizeValidator {
max_size: usize,
}
impl SizeValidator {
pub fn new(max_size: usize) -> Self {
Self { max_size }
}
pub fn validate_size(&self, size: usize) -> Result<(), SizeError> {
if size > self.max_size {
Err(SizeError::TooLarge {
actual: size,
max: self.max_size,
})
} else {
Ok(())
}
}
pub fn max_size(&self) -> usize {
self.max_size
}
}
#[derive(Debug, thiserror::Error)]
pub enum SizeError {
#[error("Request too large: {actual} bytes, maximum allowed: {max} bytes")]
TooLarge { actual: usize, max: usize },
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_rate_limiter() {
let _limiter = RateLimiter::new(1);
}
#[tokio::test]
async fn test_connection_tracker() {
let limits = ResourceLimits {
max_concurrent_connections: 2,
..Default::default()
};
let tracker = ConnectionTracker::new(limits);
let _guard1 = tracker.acquire_connection().await.unwrap();
let _guard2 = tracker.acquire_connection().await.unwrap();
assert!(matches!(
tracker.acquire_connection().await,
Err(ConnectionError::Timeout)
));
drop(_guard1);
let _guard3 = tracker.acquire_connection().await.unwrap();
}
#[test]
fn test_size_validator() {
let validator = SizeValidator::new(100);
assert!(validator.validate_size(50).is_ok());
assert!(validator.validate_size(100).is_ok());
assert!(validator.validate_size(101).is_err());
}
}