Skip to main content

rust_serv/throttle/
config.rs

1//! Throttle configuration
2
3use std::time::Duration;
4
5/// Bandwidth throttle configuration
6#[derive(Debug, Clone)]
7pub struct ThrottleConfig {
8    /// Global bandwidth limit in bytes per second (0 = unlimited)
9    pub global_limit: u64,
10    /// Per-IP bandwidth limit in bytes per second (0 = unlimited)
11    pub per_ip_limit: u64,
12    /// Bucket capacity (burst size) in bytes
13    pub bucket_capacity: u64,
14    /// Refill interval
15    pub refill_interval: Duration,
16    /// Enabled flag
17    pub enabled: bool,
18}
19
20impl ThrottleConfig {
21    /// Create a new throttle config
22    pub fn new() -> Self {
23        Self {
24            global_limit: 0,
25            per_ip_limit: 0,
26            bucket_capacity: 64 * 1024, // 64KB burst
27            refill_interval: Duration::from_millis(100),
28            enabled: false,
29        }
30    }
31
32    /// Set global bandwidth limit (bytes/sec)
33    pub fn with_global_limit(mut self, limit: u64) -> Self {
34        self.global_limit = limit;
35        self
36    }
37
38    /// Set per-IP bandwidth limit (bytes/sec)
39    pub fn with_per_ip_limit(mut self, limit: u64) -> Self {
40        self.per_ip_limit = limit;
41        self
42    }
43
44    /// Set bucket capacity
45    pub fn with_bucket_capacity(mut self, capacity: u64) -> Self {
46        self.bucket_capacity = capacity;
47        self
48    }
49
50    /// Set refill interval
51    pub fn with_refill_interval(mut self, interval: Duration) -> Self {
52        self.refill_interval = interval;
53        self
54    }
55
56    /// Enable throttling
57    pub fn enable(mut self) -> Self {
58        self.enabled = true;
59        self
60    }
61
62    /// Disable throttling
63    pub fn disable(mut self) -> Self {
64        self.enabled = false;
65        self
66    }
67
68    /// Check if global limit is set
69    pub fn has_global_limit(&self) -> bool {
70        self.enabled && self.global_limit > 0
71    }
72
73    /// Check if per-IP limit is set
74    pub fn has_per_ip_limit(&self) -> bool {
75        self.enabled && self.per_ip_limit > 0
76    }
77
78    /// Check if any limit is set
79    pub fn is_active(&self) -> bool {
80        self.enabled && (self.global_limit > 0 || self.per_ip_limit > 0)
81    }
82
83    /// Calculate tokens to add per interval
84    pub fn tokens_per_interval(&self, limit: u64) -> u64 {
85        let intervals_per_sec = 1000 / self.refill_interval.as_millis() as u64;
86        limit / intervals_per_sec
87    }
88
89    /// Create a human-readable limit string
90    pub fn format_limit(bytes_per_sec: u64) -> String {
91        if bytes_per_sec == 0 {
92            return "unlimited".to_string();
93        }
94        
95        const KB: u64 = 1024;
96        const MB: u64 = 1024 * KB;
97        const GB: u64 = 1024 * MB;
98        
99        if bytes_per_sec >= GB {
100            format!("{:.1} GB/s", bytes_per_sec as f64 / GB as f64)
101        } else if bytes_per_sec >= MB {
102            format!("{:.1} MB/s", bytes_per_sec as f64 / MB as f64)
103        } else if bytes_per_sec >= KB {
104            format!("{:.1} KB/s", bytes_per_sec as f64 / KB as f64)
105        } else {
106            format!("{} B/s", bytes_per_sec)
107        }
108    }
109}
110
111impl Default for ThrottleConfig {
112    fn default() -> Self {
113        Self::new()
114    }
115}
116
117#[cfg(test)]
118mod tests {
119    use super::*;
120
121    #[test]
122    fn test_config_creation() {
123        let config = ThrottleConfig::new();
124        assert_eq!(config.global_limit, 0);
125        assert_eq!(config.per_ip_limit, 0);
126        assert!(!config.enabled);
127    }
128
129    #[test]
130    fn test_config_with_global_limit() {
131        let config = ThrottleConfig::new()
132            .with_global_limit(1024 * 1024); // 1 MB/s
133        
134        assert_eq!(config.global_limit, 1024 * 1024);
135    }
136
137    #[test]
138    fn test_config_with_per_ip_limit() {
139        let config = ThrottleConfig::new()
140            .with_per_ip_limit(512 * 1024); // 512 KB/s
141        
142        assert_eq!(config.per_ip_limit, 512 * 1024);
143    }
144
145    #[test]
146    fn test_config_with_bucket_capacity() {
147        let config = ThrottleConfig::new()
148            .with_bucket_capacity(128 * 1024);
149        
150        assert_eq!(config.bucket_capacity, 128 * 1024);
151    }
152
153    #[test]
154    fn test_config_with_refill_interval() {
155        let config = ThrottleConfig::new()
156            .with_refill_interval(Duration::from_millis(50));
157        
158        assert_eq!(config.refill_interval, Duration::from_millis(50));
159    }
160
161    #[test]
162    fn test_config_enable() {
163        let config = ThrottleConfig::new().enable();
164        assert!(config.enabled);
165    }
166
167    #[test]
168    fn test_config_disable() {
169        let config = ThrottleConfig::new().enable().disable();
170        assert!(!config.enabled);
171    }
172
173    #[test]
174    fn test_has_global_limit() {
175        let config = ThrottleConfig::new()
176            .enable()
177            .with_global_limit(1024);
178        
179        assert!(config.has_global_limit());
180        assert!(!config.has_per_ip_limit());
181    }
182
183    #[test]
184    fn test_has_per_ip_limit() {
185        let config = ThrottleConfig::new()
186            .enable()
187            .with_per_ip_limit(1024);
188        
189        assert!(!config.has_global_limit());
190        assert!(config.has_per_ip_limit());
191    }
192
193    #[test]
194    fn test_has_limit_disabled() {
195        let config = ThrottleConfig::new()
196            .with_global_limit(1024); // Not enabled
197        
198        assert!(!config.has_global_limit());
199    }
200
201    #[test]
202    fn test_is_active() {
203        // Not enabled
204        let config = ThrottleConfig::new()
205            .with_global_limit(1024);
206        assert!(!config.is_active());
207        
208        // Enabled with limit
209        let config = ThrottleConfig::new()
210            .enable()
211            .with_global_limit(1024);
212        assert!(config.is_active());
213        
214        // Enabled but no limit
215        let config = ThrottleConfig::new().enable();
216        assert!(!config.is_active());
217    }
218
219    #[test]
220    fn test_tokens_per_interval() {
221        let config = ThrottleConfig::new()
222            .with_refill_interval(Duration::from_millis(100));
223        
224        // 1000 bytes/sec with 100ms interval = 100 tokens per interval
225        assert_eq!(config.tokens_per_interval(1000), 100);
226        
227        // 10 MB/sec with 100ms interval = 1 MB per interval
228        assert_eq!(config.tokens_per_interval(10 * 1024 * 1024), 1024 * 1024);
229    }
230
231    #[test]
232    fn test_format_limit_unlimited() {
233        assert_eq!(ThrottleConfig::format_limit(0), "unlimited");
234    }
235
236    #[test]
237    fn test_format_limit_bytes() {
238        assert_eq!(ThrottleConfig::format_limit(500), "500 B/s");
239    }
240
241    #[test]
242    fn test_format_limit_kb() {
243        let result = ThrottleConfig::format_limit(1024);
244        assert!(result.contains("KB/s"));
245    }
246
247    #[test]
248    fn test_format_limit_mb() {
249        let result = ThrottleConfig::format_limit(1024 * 1024);
250        assert!(result.contains("MB/s"));
251    }
252
253    #[test]
254    fn test_format_limit_gb() {
255        let result = ThrottleConfig::format_limit(1024 * 1024 * 1024);
256        assert!(result.contains("GB/s"));
257    }
258
259    #[test]
260    fn test_default() {
261        let config = ThrottleConfig::default();
262        assert!(!config.enabled);
263        assert_eq!(config.global_limit, 0);
264    }
265}