antithesis_intake/http/
state.rs

1//! Shared state carried by each HTTP router.
2
3use std::path::Path;
4use std::sync::{Arc, OnceLock};
5
6use crate::capture;
7use crate::context_pool::Pool;
8use crate::sut_config::SutConfig;
9
10/// Per-router state: the shared recorder handle, the lane this router writes to, and the shared
11/// context pool the drivers draw from.
12#[derive(Clone, Debug)]
13pub struct AppState {
14    pub(crate) recorder: capture::State,
15    pub(crate) target: capture::Target,
16    /// First non-empty host resolved on this lane, set once. Pyld17 requires every series across all
17    /// inbound traffic on the lane to resolve to this same host.
18    pub(crate) established_host: Arc<OnceLock<String>>,
19    /// The shared context pool served by `GET /contexts`. One pool backs every lane, so the drivers
20    /// draw recurring identities across lanes.
21    pub(crate) pool: Arc<Pool>,
22    /// Directory holding the timeline's sampled `datadog.yaml`.
23    config_dir: Arc<Path>,
24    /// The sampled config, read on the first request that finds the file written.
25    sut_config: Arc<OnceLock<SutConfig>>,
26}
27
28impl AppState {
29    /// Creates router state for Datadog Agent intake.
30    #[must_use]
31    pub fn agent(recorder: &capture::State, pool: Arc<Pool>, config_dir: &Path) -> Self {
32        Self::new(recorder, capture::Target::Agent, pool, config_dir)
33    }
34
35    /// Creates router state for ADP intake.
36    #[must_use]
37    pub fn adp(recorder: &capture::State, pool: Arc<Pool>, config_dir: &Path) -> Self {
38        Self::new(recorder, capture::Target::Adp, pool, config_dir)
39    }
40
41    fn new(recorder: &capture::State, target: capture::Target, pool: Arc<Pool>, config_dir: &Path) -> Self {
42        Self {
43            recorder: recorder.clone(),
44            target,
45            established_host: Arc::default(),
46            pool,
47            config_dir: Arc::from(config_dir),
48            sut_config: Arc::default(),
49        }
50    }
51
52    /// The sampled config, or `None` while the file is still absent or unparseable. Retried per
53    /// request until it reads, since the intake binds before `first_sample_config` runs.
54    pub(crate) fn sut_config(&self) -> Option<&SutConfig> {
55        if let Some(config) = self.sut_config.get() {
56            return Some(config);
57        }
58        let config = SutConfig::load(&self.config_dir)?;
59        Some(self.sut_config.get_or_init(|| config))
60    }
61}