#[cfg(any(feature = "expect", feature = "interception", feature = "monitor"))]
use std::collections::HashMap;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use zendriver::Browser;
#[cfg(feature = "expect")]
pub type ExpectationId = String;
#[cfg(feature = "interception")]
pub type RuleId = String;
#[cfg(feature = "monitor")]
pub type MonitorId = String;
#[cfg(feature = "monitor")]
pub const MONITOR_BUFFER_CAP: usize = 4096;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum StealthProfileChoice {
#[default]
Auto,
Native,
SpoofMacos,
SpoofLinux,
SpoofWindows,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum StealthPlatformChoice {
Win32,
MacIntel,
LinuxX86_64,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct StealthOverrides {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub platform: Option<StealthPlatformChoice>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub locale: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub timezone: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub memory_gb: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cpu_count: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub chrome_version: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub user_agent: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub bypass_csp: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub geo_country: Option<String>,
}
pub struct SessionState {
pub browser: Option<Browser>,
pub current_tab_id: Option<String>,
pub stealth_profile_choice: StealthProfileChoice,
pub stealth_overrides: StealthOverrides,
#[cfg(feature = "expect")]
pub expectations: HashMap<ExpectationId, ExpectationHandle>,
#[cfg(feature = "interception")]
pub rules: HashMap<RuleId, InterceptRuleHandle>,
#[cfg(feature = "monitor")]
pub monitors: HashMap<MonitorId, std::sync::Arc<tokio::sync::Mutex<MonitorState>>>,
}
#[cfg(feature = "expect")]
pub struct ExpectationHandle {
pub kind: &'static str,
pub task: tokio::task::JoinHandle<()>,
pub rx: tokio::sync::oneshot::Receiver<Result<serde_json::Value, String>>,
}
#[cfg(feature = "interception")]
pub struct InterceptRuleHandle {
pub pattern: String,
pub action_kind: &'static str,
pub _handle: zendriver::InterceptHandle,
}
#[cfg(feature = "monitor")]
#[derive(Debug, Clone, Serialize, JsonSchema, PartialEq, Eq)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum MonitorEvent {
Http {
url: String,
method: String,
status: Option<u16>,
error: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
body: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
body_base64: Option<String>,
},
WebSocketOpen {
request_id: String,
url: String,
},
WebSocketFrame {
request_id: String,
direction: String,
opcode: u8,
payload: String,
},
WebSocketClose {
request_id: String,
},
EventSourceMessage {
request_id: String,
event_name: String,
event_id: String,
data: String,
},
}
#[cfg(feature = "monitor")]
#[derive(Debug, Default)]
pub struct MonitorBuffer {
pub events: std::collections::VecDeque<MonitorEvent>,
pub dropped: usize,
}
#[cfg(feature = "monitor")]
pub struct MonitorState {
pub buffer: std::sync::Arc<tokio::sync::Mutex<MonitorBuffer>>,
pub cancel: tokio_util::sync::CancellationToken,
pub task: tokio::task::JoinHandle<()>,
}
#[cfg(feature = "monitor")]
impl Drop for MonitorState {
fn drop(&mut self) {
self.cancel.cancel();
self.task.abort();
}
}
impl SessionState {
pub fn new() -> Self {
Self {
browser: None,
current_tab_id: None,
stealth_profile_choice: StealthProfileChoice::default(),
stealth_overrides: StealthOverrides::default(),
#[cfg(feature = "expect")]
expectations: HashMap::new(),
#[cfg(feature = "interception")]
rules: HashMap::new(),
#[cfg(feature = "monitor")]
monitors: HashMap::new(),
}
}
}
impl Default for SessionState {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_state_is_empty() {
let s = SessionState::new();
assert!(s.browser.is_none());
assert!(s.current_tab_id.is_none());
assert_eq!(s.stealth_profile_choice, StealthProfileChoice::Auto);
#[cfg(feature = "expect")]
assert!(s.expectations.is_empty());
#[cfg(feature = "interception")]
assert!(s.rules.is_empty());
#[cfg(feature = "monitor")]
assert!(s.monitors.is_empty());
}
}