/ abzu-chameleon / src / static_gen.rs
static_gen.rs
  1  //! Static traffic generator with fixed jitter range
  2  //!
  3  //! This is the default, zero-dependency generator that provides
  4  //! basic traffic obfuscation through random jitter.
  5  
  6  use crate::traits::TrafficGenerator;
  7  use rand::Rng;
  8  use std::time::Duration;
  9  
 10  /// Static generator configuration
 11  #[derive(Debug, Clone)]
 12  pub struct StaticConfig {
 13      /// Minimum delay in milliseconds
 14      pub min_delay_ms: u32,
 15      /// Maximum delay in milliseconds
 16      pub max_delay_ms: u32,
 17      /// Preferred size bucket (0-5)
 18      pub preferred_bucket: usize,
 19  }
 20  
 21  impl Default for StaticConfig {
 22      fn default() -> Self {
 23          Self {
 24              min_delay_ms: 50,
 25              max_delay_ms: 500,
 26              preferred_bucket: 2, // 1024 bytes
 27          }
 28      }
 29  }
 30  
 31  /// Simple generator with fixed random jitter
 32  ///
 33  /// Uses uniform distribution within configured range.
 34  /// No learning, no adaptation, minimal overhead.
 35  #[derive(Debug, Clone)]
 36  pub struct StaticGenerator {
 37      config: StaticConfig,
 38  }
 39  
 40  impl Default for StaticGenerator {
 41      fn default() -> Self {
 42          Self::new(StaticConfig::default())
 43      }
 44  }
 45  
 46  impl StaticGenerator {
 47      /// Create with custom configuration
 48      pub fn new(config: StaticConfig) -> Self {
 49          Self { config }
 50      }
 51  
 52      /// Create with specified jitter range
 53      pub fn with_jitter(min_ms: u32, max_ms: u32) -> Self {
 54          Self::new(StaticConfig {
 55              min_delay_ms: min_ms,
 56              max_delay_ms: max_ms,
 57              ..Default::default()
 58          })
 59      }
 60  }
 61  
 62  impl TrafficGenerator for StaticGenerator {
 63      fn next_delay(&mut self) -> Duration {
 64          let mut rng = rand::thread_rng();
 65          let ms = rng.gen_range(self.config.min_delay_ms..=self.config.max_delay_ms);
 66          Duration::from_millis(ms as u64)
 67      }
 68  
 69      fn next_size_bucket(&mut self) -> usize {
 70          let mut rng = rand::thread_rng();
 71          // Slight variation around preferred bucket
 72          let offset: i32 = rng.gen_range(-1..=1);
 73          let bucket = (self.config.preferred_bucket as i32 + offset).clamp(0, 5);
 74          bucket as usize
 75      }
 76  
 77      fn name(&self) -> &'static str {
 78          "static"
 79      }
 80  }
 81  
 82  #[cfg(test)]
 83  mod tests {
 84      use super::*;
 85  
 86      #[test]
 87      fn test_delay_in_range() {
 88          let mut generator = StaticGenerator::default();
 89          for _ in 0..100 {
 90              let delay = generator.next_delay();
 91              assert!(delay >= Duration::from_millis(50));
 92              assert!(delay <= Duration::from_millis(500));
 93          }
 94      }
 95  
 96      #[test]
 97      fn test_size_bucket_valid() {
 98          let mut generator = StaticGenerator::default();
 99          for _ in 0..100 {
100              let bucket = generator.next_size_bucket();
101              assert!(bucket <= 5);
102          }
103      }
104  
105      #[test]
106      fn test_custom_jitter() {
107          let mut generator = StaticGenerator::with_jitter(10, 20);
108          for _ in 0..100 {
109              let delay = generator.next_delay();
110              assert!(delay >= Duration::from_millis(10));
111              assert!(delay <= Duration::from_millis(20));
112          }
113      }
114  }