160 lines
6.2 KiB
Rust
160 lines
6.2 KiB
Rust
mod tests {
|
|
use super::*;
|
|
#[test]
|
|
fn access_modes_are_fail_closed_for_allow_lists() {
|
|
let mut settings = Settings::default();
|
|
settings.country_access.mode = CountryMode::Allow;
|
|
settings.country_access.countries = vec!["pl".into()];
|
|
settings.validate().unwrap();
|
|
assert!(settings.country_allowed(Some("PL")));
|
|
assert!(!settings.country_allowed(Some("DE")));
|
|
assert!(!settings.country_allowed(None));
|
|
settings.country_access.mode = CountryMode::Block;
|
|
assert!(!settings.country_allowed(Some("PL")));
|
|
assert!(settings.country_allowed(None));
|
|
}
|
|
#[test]
|
|
fn country_limit_overrides_base_limit() {
|
|
let mut settings = Settings {
|
|
enabled: true,
|
|
requests: 10,
|
|
burst: 0,
|
|
country_limits: vec![CountryLimit {
|
|
country: "PL".into(),
|
|
requests: 1,
|
|
window_seconds: 60,
|
|
burst: 0,
|
|
}],
|
|
..Default::default()
|
|
};
|
|
settings.validate().unwrap();
|
|
let limiter = Limiter::new();
|
|
let ip = "192.0.2.1".parse().unwrap();
|
|
assert!(limiter.check(1, "site", ip, Some("PL"), &settings).is_ok());
|
|
assert!(limiter.check(1, "site", ip, Some("PL"), &settings).is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn expired_client_buckets_are_reclaimed_without_waiting_for_capacity_pressure() {
|
|
let settings = Settings {
|
|
enabled: true,
|
|
requests: 10,
|
|
window_seconds: 1,
|
|
burst: 0,
|
|
..Default::default()
|
|
};
|
|
let limiter = Limiter::new();
|
|
let first = "192.0.2.1".parse().unwrap();
|
|
let second = "192.0.2.2".parse().unwrap();
|
|
assert!(limiter.check(7, "site", first, None, &settings).is_ok());
|
|
{
|
|
let mut state = lock(&limiter.state);
|
|
let bucket = state.buckets.get_mut("site").unwrap().get_mut(&first).unwrap();
|
|
bucket.started = Instant::now()
|
|
.checked_sub(Duration::from_secs(2))
|
|
.unwrap();
|
|
state.last_sweep = None;
|
|
}
|
|
assert!(limiter.check(7, "site", second, None, &settings).is_ok());
|
|
let state = lock(&limiter.state);
|
|
assert_eq!(state.bucket_count, 1);
|
|
assert!(!state.buckets.get("site").unwrap().contains_key(&first));
|
|
assert!(state.buckets.get("site").unwrap().contains_key(&second));
|
|
}
|
|
|
|
#[test]
|
|
fn maintenance_reclaims_expired_clients_without_new_traffic() {
|
|
let settings = Settings {
|
|
enabled: true,
|
|
requests: 10,
|
|
window_seconds: 1,
|
|
burst: 0,
|
|
..Default::default()
|
|
};
|
|
let limiter = Limiter::new();
|
|
let ip = "192.0.2.10".parse().unwrap();
|
|
assert!(limiter.check(9, "idle-site", ip, None, &settings).is_ok());
|
|
{
|
|
let mut state = lock(&limiter.state);
|
|
let bucket = state
|
|
.buckets
|
|
.get_mut("idle-site")
|
|
.unwrap()
|
|
.get_mut(&ip)
|
|
.unwrap();
|
|
bucket.started = Instant::now()
|
|
.checked_sub(Duration::from_secs(2))
|
|
.unwrap();
|
|
}
|
|
limiter.maintenance();
|
|
let state = lock(&limiter.state);
|
|
assert_eq!(state.bucket_count, 0);
|
|
assert!(state.buckets.is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn vhost_name_is_stored_once_for_multiple_clients() {
|
|
let settings = Settings {
|
|
enabled: true,
|
|
requests: 10,
|
|
..Default::default()
|
|
};
|
|
let limiter = Limiter::new();
|
|
for ip in ["192.0.2.1", "192.0.2.2"] {
|
|
assert!(limiter
|
|
.check(1, "shared-vhost", ip.parse().unwrap(), None, &settings)
|
|
.is_ok());
|
|
}
|
|
let state = lock(&limiter.state);
|
|
assert_eq!(state.buckets.len(), 1);
|
|
assert_eq!(state.bucket_count, 2);
|
|
}
|
|
#[test]
|
|
fn user_agent_override_is_isolated_from_other_traffic() {
|
|
let mut settings = Settings {
|
|
enabled: true,
|
|
requests: 3,
|
|
burst: 0,
|
|
user_agent_limits: vec![UserAgentLimit {
|
|
pattern: "*BadBot*".into(), requests: 1, window_seconds: 60, burst: 0,
|
|
}],
|
|
..Default::default()
|
|
};
|
|
settings.validate().unwrap();
|
|
let limiter = Limiter::new();
|
|
let ip = "198.51.100.12".parse().unwrap();
|
|
assert!(limiter.check_with_user_agent(7, "a", ip, None, Some("badbot/1.0"), &settings).is_ok());
|
|
assert!(limiter.check_with_user_agent(7, "a", ip, None, Some("BADBOT/2"), &settings).is_err());
|
|
for _ in 0..3 {
|
|
assert!(limiter.check_with_user_agent(7, "a", ip, None, Some("Browser"), &settings).is_ok());
|
|
}
|
|
assert!(limiter.check_with_user_agent(7, "a", ip, None, Some("Browser"), &settings).is_err());
|
|
assert!(limiter.check_with_user_agent(7, "b", ip, None, Some("BadBot"), &settings).is_ok());
|
|
}
|
|
#[test]
|
|
fn user_agent_overrides_work_without_base_rate_or_geoip() {
|
|
let mut settings = Settings {
|
|
enabled: false,
|
|
user_agent_limits: vec![UserAgentLimit { pattern: "scanner".into(), requests: 1, window_seconds: 60, burst: 0 }],
|
|
..Default::default()
|
|
};
|
|
settings.validate().unwrap();
|
|
let limiter = Limiter::new();
|
|
let ip = "192.0.2.100".parse().unwrap();
|
|
assert!(limiter.check_with_user_agent(1,"site",ip,None,Some("ScanNER"),&settings).is_ok());
|
|
assert!(limiter.check_with_user_agent(1,"site",ip,None,Some("scanner"),&settings).is_err());
|
|
assert!(limiter.check_with_user_agent(1,"site",ip,None,None,&settings).is_ok());
|
|
}
|
|
#[test]
|
|
fn user_agent_override_validation_is_bounded_and_unique() {
|
|
let mut settings = Settings { user_agent_limits: vec![
|
|
UserAgentLimit { pattern: "TEST*".into(), ..Default::default() },
|
|
UserAgentLimit { pattern: "test*".into(), ..Default::default() },
|
|
], ..Default::default() };
|
|
assert!(settings.validate().is_err());
|
|
settings.user_agent_limits[1].pattern = "other".into();
|
|
assert!(settings.validate().is_ok());
|
|
}
|
|
|
|
}
|