#[cfg(test)]
#[path = "../../tests/settings/assertions.rs"]
mod tests;
use std::env::var;
use log::warn;
use super::builder::SettingsBuilder;
use super::error::SettingsError;
use super::override_map::{Key, OverrideMap, SettingType};
use super::statics::keys;
use crate::bytes::BytePool;
use crate::utils::sync::AsyncExecutor;
fn try_env_override<T: SettingType>(key: &Key<T>) -> Option<T> {
let env_str = var(key.name).ok()?;
T::try_parse(&env_str).or_else(|| {
warn!("Environment variable '{}' set to '{}' cannot be parsed, using default", key.name, env_str);
None
})
}
pub struct Settings<AE: AsyncExecutor> {
overrides: OverrideMap,
executor: AE,
pool: BytePool,
mtu: usize,
}
impl<AE: AsyncExecutor> Settings<AE> {
pub(super) fn new(overrides: OverrideMap, executor: AE, pool: BytePool, mtu: usize) -> Self {
Self {
overrides,
executor,
pool,
mtu,
}
}
#[inline]
pub fn get<T: SettingType + Copy>(&self, key: &Key<T>) -> T {
if let Some(value) = self.overrides.get(key.name) {
return T::from_value(*value);
}
if let Some(value) = try_env_override(key) {
return value;
}
key.default
}
#[inline]
pub fn set<T: SettingType>(&mut self, key: &Key<T>, value: T) {
self.overrides.insert(key.name, value.to_value());
}
#[inline]
pub fn pool(&self) -> &BytePool {
&self.pool
}
#[inline]
pub fn mtu(&self) -> usize {
self.mtu
}
#[inline]
pub fn executor(&self) -> &AE {
&self.executor
}
pub(super) fn assert(&self) -> Result<(), SettingsError> {
let assert_min_max_u64 = |min_key: &Key<u64>, max_key: &Key<u64>| -> Result<(), SettingsError> {
let min_val = self.get(min_key);
let max_val = self.get(max_key);
if min_val > max_val {
return Err(SettingsError::AssertionFailed {
message: format!("{} ({}) must be <= {} ({})", min_key.name, min_val, max_key.name, max_val),
});
}
Ok(())
};
let assert_min_max_f64 = |min_key: &Key<f64>, max_key: &Key<f64>| -> Result<(), SettingsError> {
let min_val = self.get(min_key);
let max_val = self.get(max_key);
if min_val > max_val {
return Err(SettingsError::AssertionFailed {
message: format!("{} ({}) must be <= {} ({})", min_key.name, min_val, max_key.name, max_val),
});
}
Ok(())
};
let assert_default_in_range = |min_key: &Key<u64>, default_key: &Key<u64>, max_key: &Key<u64>| -> Result<(), SettingsError> {
let min_val = self.get(min_key);
let default_val = self.get(default_key);
let max_val = self.get(max_key);
if default_val < min_val || default_val > max_val {
return Err(SettingsError::AssertionFailed {
message: format!("{} ({}) must be within [{} ({}), {} ({})]", default_key.name, default_val, min_key.name, min_val, max_key.name, max_val),
});
}
Ok(())
};
let assert_unit_exclusive = |key: &Key<f64>| -> Result<(), SettingsError> {
let val = self.get(key);
if val <= 0.0 || val >= 1.0 {
return Err(SettingsError::AssertionFailed {
message: format!("{} ({}) must be in (0, 1)", key.name, val),
});
}
Ok(())
};
let assert_unit_inclusive = |key: &Key<f64>| -> Result<(), SettingsError> {
let val = self.get(key);
if !(0.0..=1.0).contains(&val) {
return Err(SettingsError::AssertionFailed {
message: format!("{} ({}) must be in [0, 1]", key.name, val),
});
}
Ok(())
};
let assert_float_positive = |key: &Key<f64>| -> Result<(), SettingsError> {
let val = self.get(key);
if val <= 0.0 {
return Err(SettingsError::AssertionFailed {
message: format!("{} ({}) must be positive", key.name, val),
});
}
Ok(())
};
let assert_int_positive = |key: &Key<u64>| -> Result<(), SettingsError> {
let val = self.get(key);
if val == 0 {
return Err(SettingsError::AssertionFailed {
message: format!("{} ({}) must be greater than zero", key.name, val),
});
}
Ok(())
};
assert_min_max_u64(&keys::FAKE_BODY_LENGTH_MIN, &keys::FAKE_BODY_LENGTH_MAX)?;
assert_min_max_u64(&keys::FAKE_BODY_CONSTANT_LENGTH_MIN, &keys::FAKE_BODY_CONSTANT_LENGTH_MAX)?;
assert_min_max_f64(&keys::DECOY_FALLTHROUGH_PACKETS_MIN, &keys::DECOY_FALLTHROUGH_PACKETS_MAX)?;
assert_min_max_u64(&keys::FAKE_HEADER_LENGTH_MIN, &keys::FAKE_HEADER_LENGTH_MAX)?;
assert_min_max_u64(&keys::HEALTH_CHECK_NEXT_IN_MIN, &keys::HEALTH_CHECK_NEXT_IN_MAX)?;
assert_min_max_u64(&keys::TIMEOUT_MIN, &keys::TIMEOUT_MAX)?;
assert_min_max_u64(&keys::RTT_MIN, &keys::RTT_MAX)?;
assert_min_max_u64(&keys::DECOY_LENGTH_MIN, &keys::DECOY_LENGTH_MAX)?;
assert_min_max_u64(&keys::DECOY_NOISY_DECOY_LENGTH_MIN, &keys::DECOY_NOISY_LENGTH_MAX)?;
assert_min_max_u64(&keys::DECOY_HEAVY_LENGTH_MIN, &keys::DECOY_LENGTH_MAX)?;
assert_min_max_u64(&keys::DECOY_HEAVY_DELAY_MIN, &keys::DECOY_HEAVY_DELAY_MAX)?;
assert_min_max_u64(&keys::DECOY_NOISY_DELAY_MIN, &keys::DECOY_NOISY_DELAY_MAX)?;
assert_min_max_u64(&keys::DECOY_SPARSE_DELAY_MIN, &keys::DECOY_SPARSE_DELAY_MAX)?;
assert_min_max_u64(&keys::DECOY_SPARSE_LENGTH_MIN, &keys::DECOY_SPARSE_LENGTH_MAX)?;
assert_min_max_u64(&keys::DECOY_SMOOTH_DELAY_MIN, &keys::DECOY_SMOOTH_DELAY_MAX)?;
assert_min_max_u64(&keys::DECOY_SMOOTH_LENGTH_MIN, &keys::DECOY_SMOOTH_LENGTH_MAX)?;
assert_min_max_u64(&keys::DECOY_MAINTENANCE_LENGTH_MIN, &keys::DECOY_MAINTENANCE_LENGTH_MAX)?;
assert_min_max_u64(&keys::DECOY_MAINTENANCE_DELAY_MIN, &keys::DECOY_MAINTENANCE_DELAY_MAX)?;
assert_min_max_u64(&keys::DECOY_REPLICATION_DELAY_MIN, &keys::DECOY_REPLICATION_DELAY_MAX)?;
assert_min_max_u64(&keys::DECOY_SUBHEADER_LENGTH_MIN, &keys::DECOY_SUBHEADER_LENGTH_MAX)?;
assert_min_max_f64(&keys::DECOY_REPLICATION_PROBABILITY_MIN, &keys::DECOY_REPLICATION_PROBABILITY_MAX)?;
assert_min_max_f64(&keys::FAKE_HEADER_VOLATILE_CHANGE_PROB_MIN, &keys::FAKE_HEADER_VOLATILE_CHANGE_PROB_MAX)?;
assert_min_max_u64(&keys::FAKE_HEADER_SWITCHING_TIMEOUT_MIN_MS, &keys::FAKE_HEADER_SWITCHING_TIMEOUT_MAX_MS)?;
assert_default_in_range(&keys::RTT_MIN, &keys::RTT_DEFAULT, &keys::RTT_MAX)?;
assert_default_in_range(&keys::TIMEOUT_MIN, &keys::TIMEOUT_DEFAULT, &keys::TIMEOUT_MAX)?;
assert_default_in_range(&keys::DECOY_HEAVY_DELAY_MIN, &keys::DECOY_HEAVY_DELAY_DEFAULT, &keys::DECOY_HEAVY_DELAY_MAX)?;
assert_default_in_range(&keys::DECOY_NOISY_DELAY_MIN, &keys::DECOY_NOISY_DELAY_DEFAULT, &keys::DECOY_NOISY_DELAY_MAX)?;
assert_default_in_range(&keys::DECOY_SPARSE_DELAY_MIN, &keys::DECOY_SPARSE_DELAY_DEFAULT, &keys::DECOY_SPARSE_DELAY_MAX)?;
assert_default_in_range(&keys::DECOY_SMOOTH_DELAY_MIN, &keys::DECOY_SMOOTH_DELAY_DEFAULT, &keys::DECOY_SMOOTH_DELAY_MAX)?;
let next_in_min = self.get(&keys::HEALTH_CHECK_NEXT_IN_MIN);
let timeout_max = self.get(&keys::TIMEOUT_MAX);
if next_in_min <= timeout_max {
return Err(SettingsError::AssertionFailed {
message: format!("{} ({}) must be > {} ({})", keys::HEALTH_CHECK_NEXT_IN_MIN.name, next_in_min, keys::TIMEOUT_MAX.name, timeout_max),
});
}
assert_unit_exclusive(&keys::RTT_ALPHA)?;
assert_unit_exclusive(&keys::RTT_BETA)?;
assert_unit_exclusive(&keys::DECOY_CURRENT_ALPHA)?;
assert_unit_exclusive(&keys::DECOY_REFERENCE_ALPHA)?;
assert_unit_inclusive(&keys::FAKE_HEADER_PROBABILITY)?;
assert_unit_inclusive(&keys::SEND_BYTES_JITTER)?;
assert_unit_inclusive(&keys::DECOY_FALLTHROUGH_PACKETS_MIN)?;
assert_unit_inclusive(&keys::DECOY_FALLTHROUGH_PACKETS_MAX)?;
assert_unit_inclusive(&keys::FAKE_HEADER_VOLATILE_CHANGE_PROB_MIN)?;
assert_unit_inclusive(&keys::FAKE_HEADER_VOLATILE_CHANGE_PROB_MAX)?;
let chunk = self.get(&keys::SEND_BYTES_CHUNK);
if chunk != 0 && (chunk as usize) > self.mtu() {
return Err(SettingsError::AssertionFailed {
message: format!("{} ({}) must be ≤ MTU ({}) or 0 (sentinel)", keys::SEND_BYTES_CHUNK.name, chunk, self.mtu()),
});
}
assert_float_positive(&keys::TIMEOUT_RTT_FACTOR)?;
assert_float_positive(&keys::HANDSHAKE_NEXT_IN_FACTOR)?;
assert_float_positive(&keys::DECOY_BYTE_RATE_CAP)?;
assert_float_positive(&keys::DECOY_BYTE_RATE_FACTOR)?;
assert_int_positive(&keys::FAKE_BODY_WEIGHT_EMPTY)?;
assert_int_positive(&keys::FAKE_BODY_WEIGHT_RANDOM)?;
assert_int_positive(&keys::FAKE_BODY_WEIGHT_CONSTANT)?;
assert_int_positive(&keys::FAKE_BODY_WEIGHT_SERVICE)?;
assert_int_positive(&keys::FAKE_HEADER_FIELD_WEIGHT_RANDOM)?;
assert_int_positive(&keys::FAKE_HEADER_FIELD_WEIGHT_CONSTANT)?;
assert_int_positive(&keys::FAKE_HEADER_FIELD_WEIGHT_VOLATILE)?;
assert_int_positive(&keys::FAKE_HEADER_FIELD_WEIGHT_SWITCHING)?;
assert_int_positive(&keys::FAKE_HEADER_FIELD_WEIGHT_INCREMENTAL)?;
assert_int_positive(&keys::DECOY_MAINTENANCE_WEIGHT_NONE)?;
assert_int_positive(&keys::DECOY_MAINTENANCE_WEIGHT_RANDOM)?;
assert_int_positive(&keys::DECOY_MAINTENANCE_WEIGHT_TIMED)?;
assert_int_positive(&keys::DECOY_MAINTENANCE_WEIGHT_SIZED)?;
assert_int_positive(&keys::DECOY_MAINTENANCE_WEIGHT_BOTH)?;
assert_int_positive(&keys::DECOY_REPLICATION_WEIGHT_NONE)?;
assert_int_positive(&keys::DECOY_REPLICATION_WEIGHT_MAINTENANCE)?;
assert_int_positive(&keys::DECOY_REPLICATION_WEIGHT_ALL)?;
assert_int_positive(&keys::DECOY_SUBHEADER_WEIGHT_NONE)?;
assert_int_positive(&keys::DECOY_SUBHEADER_WEIGHT_MAINTENANCE)?;
assert_int_positive(&keys::DECOY_SUBHEADER_WEIGHT_ALL)?;
assert_int_positive(&keys::DECOY_PROVIDER_WEIGHT_SIMPLE)?;
assert_int_positive(&keys::DECOY_PROVIDER_WEIGHT_SPARSE)?;
assert_int_positive(&keys::DECOY_PROVIDER_WEIGHT_NOISY)?;
assert_int_positive(&keys::DECOY_PROVIDER_WEIGHT_SMOOTH)?;
assert_int_positive(&keys::DECOY_PROVIDER_WEIGHT_HEAVY)?;
assert_int_positive(&keys::FAKE_HEADER_SWITCHING_TIMEOUT_MIN_MS)?;
Ok(())
}
}
impl<AE: AsyncExecutor> Default for Settings<AE> {
#[inline]
fn default() -> Self {
SettingsBuilder::new().build().expect("default settings must be valid")
}
}