Skip to main content

typhoon/settings/
structure.rs

1//! Core Settings struct for type-safe configuration access.
2
3#[cfg(test)]
4#[path = "../../tests/settings/assertions.rs"]
5mod tests;
6
7use std::env::var;
8
9use log::warn;
10
11use super::builder::SettingsBuilder;
12use super::error::SettingsError;
13use super::override_map::{Key, OverrideMap, SettingType};
14use super::statics::keys;
15use crate::bytes::BytePool;
16use crate::utils::sync::AsyncExecutor;
17
18/// Try to read an environment variable and parse it as type T.
19/// Returns None if the variable is not set or cannot be parsed.
20fn try_env_override<T: SettingType>(key: &Key<T>) -> Option<T> {
21    let env_str = var(key.name).ok()?;
22    T::try_parse(&env_str).or_else(|| {
23        warn!("Environment variable '{}' set to '{}' cannot be parsed, using default", key.name, env_str);
24        None
25    })
26}
27
28/// Configuration settings with type-safe access.
29///
30/// Values are resolved in this order:
31/// 1. Explicit overrides set via SettingsBuilder
32/// 2. Environment variables (if not disabled)
33/// 3. Default value from the Key definition
34pub struct Settings<AE: AsyncExecutor> {
35    overrides: OverrideMap,
36    executor: AE,
37    pool: BytePool,
38    mtu: usize,
39}
40
41impl<AE: AsyncExecutor> Settings<AE> {
42    /// Create a new Settings instance from its components.
43    pub(super) fn new(overrides: OverrideMap, executor: AE, pool: BytePool, mtu: usize) -> Self {
44        Self {
45            overrides,
46            executor,
47            pool,
48            mtu,
49        }
50    }
51
52    /// Get a setting value with compile-time type safety.
53    ///
54    /// Resolution order: override -> environment -> default
55    #[inline]
56    pub fn get<T: SettingType + Copy>(&self, key: &Key<T>) -> T {
57        if let Some(value) = self.overrides.get(key.name) {
58            return T::from_value(*value);
59        }
60
61        if let Some(value) = try_env_override(key) {
62            return value;
63        }
64
65        key.default
66    }
67
68    /// Set a setting value with compile-time type safety.
69    #[inline]
70    pub fn set<T: SettingType>(&mut self, key: &Key<T>, value: T) {
71        self.overrides.insert(key.name, value.to_value());
72    }
73
74    /// Get a reference to the byte pool.
75    #[inline]
76    pub fn pool(&self) -> &BytePool {
77        &self.pool
78    }
79
80    /// Get the MTU (max packet size) for flow managers.
81    #[inline]
82    pub fn mtu(&self) -> usize {
83        self.mtu
84    }
85
86    /// Get a reference to the async executor.
87    #[inline]
88    pub fn executor(&self) -> &AE {
89        &self.executor
90    }
91
92    /// Validate that all settings satisfy protocol invariants.
93    pub(super) fn assert(&self) -> Result<(), SettingsError> {
94        // Helper to check min <= max for u64 key pairs.
95        let assert_min_max_u64 = |min_key: &Key<u64>, max_key: &Key<u64>| -> Result<(), SettingsError> {
96            let min_val = self.get(min_key);
97            let max_val = self.get(max_key);
98            if min_val > max_val {
99                return Err(SettingsError::AssertionFailed {
100                    message: format!("{} ({}) must be <= {} ({})", min_key.name, min_val, max_key.name, max_val),
101                });
102            }
103            Ok(())
104        };
105
106        // Helper to check min <= max for f64 key pairs.
107        let assert_min_max_f64 = |min_key: &Key<f64>, max_key: &Key<f64>| -> Result<(), SettingsError> {
108            let min_val = self.get(min_key);
109            let max_val = self.get(max_key);
110            if min_val > max_val {
111                return Err(SettingsError::AssertionFailed {
112                    message: format!("{} ({}) must be <= {} ({})", min_key.name, min_val, max_key.name, max_val),
113                });
114            }
115            Ok(())
116        };
117
118        // Helper to check a u64 default is within [min, max].
119        let assert_default_in_range = |min_key: &Key<u64>, default_key: &Key<u64>, max_key: &Key<u64>| -> Result<(), SettingsError> {
120            let min_val = self.get(min_key);
121            let default_val = self.get(default_key);
122            let max_val = self.get(max_key);
123            if default_val < min_val || default_val > max_val {
124                return Err(SettingsError::AssertionFailed {
125                    message: format!("{} ({}) must be within [{} ({}), {} ({})]", default_key.name, default_val, min_key.name, min_val, max_key.name, max_val),
126                });
127            }
128            Ok(())
129        };
130
131        // Helper to check f64 is in exclusive range (0, 1).
132        let assert_unit_exclusive = |key: &Key<f64>| -> Result<(), SettingsError> {
133            let val = self.get(key);
134            if val <= 0.0 || val >= 1.0 {
135                return Err(SettingsError::AssertionFailed {
136                    message: format!("{} ({}) must be in (0, 1)", key.name, val),
137                });
138            }
139            Ok(())
140        };
141
142        // Helper to check f64 is in inclusive range [0, 1].
143        let assert_unit_inclusive = |key: &Key<f64>| -> Result<(), SettingsError> {
144            let val = self.get(key);
145            if !(0.0..=1.0).contains(&val) {
146                return Err(SettingsError::AssertionFailed {
147                    message: format!("{} ({}) must be in [0, 1]", key.name, val),
148                });
149            }
150            Ok(())
151        };
152
153        // Helper to check f64 is positive.
154        let assert_float_positive = |key: &Key<f64>| -> Result<(), SettingsError> {
155            let val = self.get(key);
156            if val <= 0.0 {
157                return Err(SettingsError::AssertionFailed {
158                    message: format!("{} ({}) must be positive", key.name, val),
159                });
160            }
161            Ok(())
162        };
163
164        // Helper to check u64 is positive.
165        let assert_int_positive = |key: &Key<u64>| -> Result<(), SettingsError> {
166            let val = self.get(key);
167            if val == 0 {
168                return Err(SettingsError::AssertionFailed {
169                    message: format!("{} ({}) must be greater than zero", key.name, val),
170                });
171            }
172            Ok(())
173        };
174
175        // Min <= Max pairs
176        assert_min_max_u64(&keys::FAKE_BODY_LENGTH_MIN, &keys::FAKE_BODY_LENGTH_MAX)?;
177        assert_min_max_u64(&keys::FAKE_BODY_CONSTANT_LENGTH_MIN, &keys::FAKE_BODY_CONSTANT_LENGTH_MAX)?;
178        assert_min_max_f64(&keys::DECOY_FALLTHROUGH_PACKETS_MIN, &keys::DECOY_FALLTHROUGH_PACKETS_MAX)?;
179        assert_min_max_u64(&keys::FAKE_HEADER_LENGTH_MIN, &keys::FAKE_HEADER_LENGTH_MAX)?;
180        assert_min_max_u64(&keys::HEALTH_CHECK_NEXT_IN_MIN, &keys::HEALTH_CHECK_NEXT_IN_MAX)?;
181        assert_min_max_u64(&keys::TIMEOUT_MIN, &keys::TIMEOUT_MAX)?;
182        assert_min_max_u64(&keys::RTT_MIN, &keys::RTT_MAX)?;
183        assert_min_max_u64(&keys::DECOY_LENGTH_MIN, &keys::DECOY_LENGTH_MAX)?;
184        assert_min_max_u64(&keys::DECOY_NOISY_DECOY_LENGTH_MIN, &keys::DECOY_NOISY_LENGTH_MAX)?;
185        assert_min_max_u64(&keys::DECOY_HEAVY_LENGTH_MIN, &keys::DECOY_LENGTH_MAX)?;
186        assert_min_max_u64(&keys::DECOY_HEAVY_DELAY_MIN, &keys::DECOY_HEAVY_DELAY_MAX)?;
187        assert_min_max_u64(&keys::DECOY_NOISY_DELAY_MIN, &keys::DECOY_NOISY_DELAY_MAX)?;
188        assert_min_max_u64(&keys::DECOY_SPARSE_DELAY_MIN, &keys::DECOY_SPARSE_DELAY_MAX)?;
189        assert_min_max_u64(&keys::DECOY_SPARSE_LENGTH_MIN, &keys::DECOY_SPARSE_LENGTH_MAX)?;
190        assert_min_max_u64(&keys::DECOY_SMOOTH_DELAY_MIN, &keys::DECOY_SMOOTH_DELAY_MAX)?;
191        assert_min_max_u64(&keys::DECOY_SMOOTH_LENGTH_MIN, &keys::DECOY_SMOOTH_LENGTH_MAX)?;
192        assert_min_max_u64(&keys::DECOY_MAINTENANCE_LENGTH_MIN, &keys::DECOY_MAINTENANCE_LENGTH_MAX)?;
193        assert_min_max_u64(&keys::DECOY_MAINTENANCE_DELAY_MIN, &keys::DECOY_MAINTENANCE_DELAY_MAX)?;
194        assert_min_max_u64(&keys::DECOY_REPLICATION_DELAY_MIN, &keys::DECOY_REPLICATION_DELAY_MAX)?;
195        assert_min_max_u64(&keys::DECOY_SUBHEADER_LENGTH_MIN, &keys::DECOY_SUBHEADER_LENGTH_MAX)?;
196        assert_min_max_f64(&keys::DECOY_REPLICATION_PROBABILITY_MIN, &keys::DECOY_REPLICATION_PROBABILITY_MAX)?;
197        assert_min_max_f64(&keys::FAKE_HEADER_VOLATILE_CHANGE_PROB_MIN, &keys::FAKE_HEADER_VOLATILE_CHANGE_PROB_MAX)?;
198        assert_min_max_u64(&keys::FAKE_HEADER_SWITCHING_TIMEOUT_MIN_MS, &keys::FAKE_HEADER_SWITCHING_TIMEOUT_MAX_MS)?;
199
200        // Defaults within bounds
201        assert_default_in_range(&keys::RTT_MIN, &keys::RTT_DEFAULT, &keys::RTT_MAX)?;
202        assert_default_in_range(&keys::TIMEOUT_MIN, &keys::TIMEOUT_DEFAULT, &keys::TIMEOUT_MAX)?;
203        assert_default_in_range(&keys::DECOY_HEAVY_DELAY_MIN, &keys::DECOY_HEAVY_DELAY_DEFAULT, &keys::DECOY_HEAVY_DELAY_MAX)?;
204        assert_default_in_range(&keys::DECOY_NOISY_DELAY_MIN, &keys::DECOY_NOISY_DELAY_DEFAULT, &keys::DECOY_NOISY_DELAY_MAX)?;
205        assert_default_in_range(&keys::DECOY_SPARSE_DELAY_MIN, &keys::DECOY_SPARSE_DELAY_DEFAULT, &keys::DECOY_SPARSE_DELAY_MAX)?;
206        assert_default_in_range(&keys::DECOY_SMOOTH_DELAY_MIN, &keys::DECOY_SMOOTH_DELAY_DEFAULT, &keys::DECOY_SMOOTH_DELAY_MAX)?;
207
208        // Next-in must be greater than timeout (README: "next in should be always greater than timeout")
209        let next_in_min = self.get(&keys::HEALTH_CHECK_NEXT_IN_MIN);
210        let timeout_max = self.get(&keys::TIMEOUT_MAX);
211        if next_in_min <= timeout_max {
212            return Err(SettingsError::AssertionFailed {
213                message: format!("{} ({}) must be > {} ({})", keys::HEALTH_CHECK_NEXT_IN_MIN.name, next_in_min, keys::TIMEOUT_MAX.name, timeout_max),
214            });
215        }
216
217        // EWMA alpha/beta values must be in (0, 1)
218        assert_unit_exclusive(&keys::RTT_ALPHA)?;
219        assert_unit_exclusive(&keys::RTT_BETA)?;
220        assert_unit_exclusive(&keys::DECOY_CURRENT_ALPHA)?;
221        assert_unit_exclusive(&keys::DECOY_REFERENCE_ALPHA)?;
222
223        // Probability must be in [0, 1]
224        assert_unit_inclusive(&keys::FAKE_HEADER_PROBABILITY)?;
225        assert_unit_inclusive(&keys::SEND_BYTES_JITTER)?;
226        assert_unit_inclusive(&keys::DECOY_FALLTHROUGH_PACKETS_MIN)?;
227        assert_unit_inclusive(&keys::DECOY_FALLTHROUGH_PACKETS_MAX)?;
228        assert_unit_inclusive(&keys::FAKE_HEADER_VOLATILE_CHANGE_PROB_MIN)?;
229        assert_unit_inclusive(&keys::FAKE_HEADER_VOLATILE_CHANGE_PROB_MAX)?;
230
231        // SEND_BYTES_CHUNK must fit inside the MTU.
232        // Zero is the "use max_user_payload" sentinel and bypasses the check.
233        let chunk = self.get(&keys::SEND_BYTES_CHUNK);
234        if chunk != 0 && (chunk as usize) > self.mtu() {
235            return Err(SettingsError::AssertionFailed {
236                message: format!("{} ({}) must be ≤ MTU ({}) or 0 (sentinel)", keys::SEND_BYTES_CHUNK.name, chunk, self.mtu()),
237            });
238        }
239
240        // Positive multipliers
241        assert_float_positive(&keys::TIMEOUT_RTT_FACTOR)?;
242        assert_float_positive(&keys::HANDSHAKE_NEXT_IN_FACTOR)?;
243        assert_float_positive(&keys::DECOY_BYTE_RATE_CAP)?;
244        assert_float_positive(&keys::DECOY_BYTE_RATE_FACTOR)?;
245
246        // Weight modifiers
247        assert_int_positive(&keys::FAKE_BODY_WEIGHT_EMPTY)?;
248        assert_int_positive(&keys::FAKE_BODY_WEIGHT_RANDOM)?;
249        assert_int_positive(&keys::FAKE_BODY_WEIGHT_CONSTANT)?;
250        assert_int_positive(&keys::FAKE_BODY_WEIGHT_SERVICE)?;
251        assert_int_positive(&keys::FAKE_HEADER_FIELD_WEIGHT_RANDOM)?;
252        assert_int_positive(&keys::FAKE_HEADER_FIELD_WEIGHT_CONSTANT)?;
253        assert_int_positive(&keys::FAKE_HEADER_FIELD_WEIGHT_VOLATILE)?;
254        assert_int_positive(&keys::FAKE_HEADER_FIELD_WEIGHT_SWITCHING)?;
255        assert_int_positive(&keys::FAKE_HEADER_FIELD_WEIGHT_INCREMENTAL)?;
256        assert_int_positive(&keys::DECOY_MAINTENANCE_WEIGHT_NONE)?;
257        assert_int_positive(&keys::DECOY_MAINTENANCE_WEIGHT_RANDOM)?;
258        assert_int_positive(&keys::DECOY_MAINTENANCE_WEIGHT_TIMED)?;
259        assert_int_positive(&keys::DECOY_MAINTENANCE_WEIGHT_SIZED)?;
260        assert_int_positive(&keys::DECOY_MAINTENANCE_WEIGHT_BOTH)?;
261        assert_int_positive(&keys::DECOY_REPLICATION_WEIGHT_NONE)?;
262        assert_int_positive(&keys::DECOY_REPLICATION_WEIGHT_MAINTENANCE)?;
263        assert_int_positive(&keys::DECOY_REPLICATION_WEIGHT_ALL)?;
264        assert_int_positive(&keys::DECOY_SUBHEADER_WEIGHT_NONE)?;
265        assert_int_positive(&keys::DECOY_SUBHEADER_WEIGHT_MAINTENANCE)?;
266        assert_int_positive(&keys::DECOY_SUBHEADER_WEIGHT_ALL)?;
267        assert_int_positive(&keys::DECOY_PROVIDER_WEIGHT_SIMPLE)?;
268        assert_int_positive(&keys::DECOY_PROVIDER_WEIGHT_SPARSE)?;
269        assert_int_positive(&keys::DECOY_PROVIDER_WEIGHT_NOISY)?;
270        assert_int_positive(&keys::DECOY_PROVIDER_WEIGHT_SMOOTH)?;
271        assert_int_positive(&keys::DECOY_PROVIDER_WEIGHT_HEAVY)?;
272        assert_int_positive(&keys::FAKE_HEADER_SWITCHING_TIMEOUT_MIN_MS)?;
273
274        Ok(())
275    }
276}
277
278impl<AE: AsyncExecutor> Default for Settings<AE> {
279    #[inline]
280    fn default() -> Self {
281        SettingsBuilder::new().build().expect("default settings must be valid")
282    }
283}