1use std::path::PathBuf;
6
7use crate::host::listener::AppState;
8use crate::shared::error::Error;
9
10#[derive(Debug, Clone)]
13pub struct HostArgs {
14 pub listen: String,
15 pub profile: ProfileChoice,
17 pub display: DisplayMode,
18 pub takeover: Takeover,
19 pub display_quality: u8,
23 pub browser: BrowserChoice,
24 pub browser_bin: Option<PathBuf>,
25 pub token: Option<String>,
26 pub takeover_enabled: bool,
27 pub health_enabled: bool,
28 pub health_public: HealthPublic,
29 pub engine_envs: Vec<(String, String)>,
34 pub browser_args: Vec<String>,
41 pub proxy: Option<String>,
48 pub recent_requests_cap: usize,
51}
52
53#[derive(Debug, Clone, PartialEq, Eq)]
54pub enum ProfileChoice {
55 Ephemeral,
56 Persistent(String),
57}
58
59#[derive(Debug, Clone, Copy, PartialEq, Eq)]
60pub enum DisplayMode {
61 Headless,
62 Headful,
63}
64
65#[derive(Debug, Clone, Copy, PartialEq, Eq)]
66pub enum Takeover {
67 Off,
68 On { provider: TakeoverProviderKind },
69}
70
71#[derive(Debug, Clone, Copy, PartialEq, Eq)]
72pub enum TakeoverProviderKind {
73 KasmVnc,
74}
75
76impl TakeoverProviderKind {
77 pub fn as_str(self) -> &'static str {
78 match self {
79 Self::KasmVnc => "kasmvnc",
80 }
81 }
82}
83
84impl std::str::FromStr for TakeoverProviderKind {
85 type Err = String;
86
87 fn from_str(s: &str) -> Result<Self, Self::Err> {
88 Ok(match s {
89 "kasmvnc" => Self::KasmVnc,
90 other => return Err(format!("unknown {other:?}; expected kasmvnc")),
91 })
92 }
93}
94
95#[derive(Debug, Clone, PartialEq, Eq, Default)]
96pub enum BrowserChoice {
97 #[default]
98 Auto,
99 Chromium,
100 Chrome,
101 FingerprintChromium,
102 Edge,
103 Brave,
104 Lightpanda,
105 Camoufox,
109}
110
111impl std::str::FromStr for BrowserChoice {
112 type Err = String;
113
114 fn from_str(s: &str) -> Result<Self, Self::Err> {
115 Ok(match s {
116 "auto" => Self::Auto,
117 "chromium" => Self::Chromium,
118 "chrome" => Self::Chrome,
119 "fingerprint_chromium" | "fingerprint-chromium" => Self::FingerprintChromium,
120 "edge" => Self::Edge,
121 "brave" => Self::Brave,
122 "lightpanda" => Self::Lightpanda,
123 "camoufox" => Self::Camoufox,
124 other => return Err(format!("unknown {other:?}")),
125 })
126 }
127}
128
129impl BrowserChoice {
130 #[must_use]
132 pub fn profile_backend_key(&self) -> &'static str {
133 match self {
134 Self::Auto | Self::Chromium => "chromium",
135 Self::Chrome => "chrome",
136 Self::FingerprintChromium => "fingerprint-chromium",
137 Self::Edge => "edge",
138 Self::Brave => "brave",
139 Self::Lightpanda => "lightpanda",
140 Self::Camoufox => "camoufox",
141 }
142 }
143}
144
145#[derive(Debug, Clone, Copy, PartialEq, Eq)]
146pub enum HealthPublic {
147 Off,
148 Minimal,
149}
150
151pub fn install_rustls_provider() {
152 let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();
153}
154
155pub async fn run(args: HostArgs) -> Result<(), Error> {
158 install_rustls_provider();
159 let state = AppState::launch(&args).await?;
160 state.serve(&args.listen).await
161}
162
163#[cfg(test)]
164mod tests {
165 use super::*;
166
167 #[test]
168 fn browser_choice_default_is_auto() {
169 assert_eq!(BrowserChoice::default(), BrowserChoice::Auto);
170 }
171
172 #[test]
173 fn browser_choice_parses_every_variant_and_aliases() {
174 assert_eq!(
175 "auto".parse::<BrowserChoice>().unwrap(),
176 BrowserChoice::Auto
177 );
178 assert_eq!(
179 "chromium".parse::<BrowserChoice>().unwrap(),
180 BrowserChoice::Chromium
181 );
182 assert_eq!(
183 "chrome".parse::<BrowserChoice>().unwrap(),
184 BrowserChoice::Chrome
185 );
186 assert!("chrome-headless-shell".parse::<BrowserChoice>().is_err());
191 assert!("chrome_shell".parse::<BrowserChoice>().is_err());
192 assert_eq!(
194 "fingerprint_chromium".parse::<BrowserChoice>().unwrap(),
195 BrowserChoice::FingerprintChromium
196 );
197 assert_eq!(
198 "fingerprint-chromium".parse::<BrowserChoice>().unwrap(),
199 BrowserChoice::FingerprintChromium
200 );
201 assert_eq!(
202 "edge".parse::<BrowserChoice>().unwrap(),
203 BrowserChoice::Edge
204 );
205 assert_eq!(
206 "brave".parse::<BrowserChoice>().unwrap(),
207 BrowserChoice::Brave
208 );
209 assert_eq!(
210 "lightpanda".parse::<BrowserChoice>().unwrap(),
211 BrowserChoice::Lightpanda
212 );
213 assert_eq!(
214 "camoufox".parse::<BrowserChoice>().unwrap(),
215 BrowserChoice::Camoufox
216 );
217 }
218
219 #[test]
220 fn browser_choice_rejects_unknown() {
221 let err = "netscape".parse::<BrowserChoice>().unwrap_err();
222 assert!(err.contains("netscape"), "error was {err:?}");
223 }
224}