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 ChromeShell,
102 FingerprintChromium,
103 Edge,
104 Brave,
105 Lightpanda,
106 Camoufox,
110}
111
112impl std::str::FromStr for BrowserChoice {
113 type Err = String;
114
115 fn from_str(s: &str) -> Result<Self, Self::Err> {
116 Ok(match s {
117 "auto" => Self::Auto,
118 "chromium" => Self::Chromium,
119 "chrome" => Self::Chrome,
120 "chrome_shell" | "chrome-headless-shell" => Self::ChromeShell,
121 "fingerprint_chromium" | "fingerprint-chromium" => Self::FingerprintChromium,
122 "edge" => Self::Edge,
123 "brave" => Self::Brave,
124 "lightpanda" => Self::Lightpanda,
125 "camoufox" => Self::Camoufox,
126 other => return Err(format!("unknown {other:?}")),
127 })
128 }
129}
130
131impl BrowserChoice {
132 #[must_use]
134 pub fn profile_backend_key(&self) -> &'static str {
135 match self {
136 Self::Auto | Self::Chromium => "chromium",
137 Self::Chrome => "chrome",
138 Self::ChromeShell => "chrome-headless-shell",
139 Self::FingerprintChromium => "fingerprint-chromium",
140 Self::Edge => "edge",
141 Self::Brave => "brave",
142 Self::Lightpanda => "lightpanda",
143 Self::Camoufox => "camoufox",
144 }
145 }
146}
147
148#[derive(Debug, Clone, Copy, PartialEq, Eq)]
149pub enum HealthPublic {
150 Off,
151 Minimal,
152}
153
154pub fn install_rustls_provider() {
155 let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();
156}
157
158pub async fn run(args: HostArgs) -> Result<(), Error> {
161 install_rustls_provider();
162 let state = AppState::launch(&args).await?;
163 state.serve(&args.listen).await
164}
165
166#[cfg(test)]
167mod tests {
168 use super::*;
169
170 #[test]
171 fn browser_choice_default_is_auto() {
172 assert_eq!(BrowserChoice::default(), BrowserChoice::Auto);
173 }
174
175 #[test]
176 fn browser_choice_parses_every_variant_and_aliases() {
177 assert_eq!(
178 "auto".parse::<BrowserChoice>().unwrap(),
179 BrowserChoice::Auto
180 );
181 assert_eq!(
182 "chromium".parse::<BrowserChoice>().unwrap(),
183 BrowserChoice::Chromium
184 );
185 assert_eq!(
186 "chrome".parse::<BrowserChoice>().unwrap(),
187 BrowserChoice::Chrome
188 );
189 assert_eq!(
191 "chrome_shell".parse::<BrowserChoice>().unwrap(),
192 BrowserChoice::ChromeShell
193 );
194 assert_eq!(
195 "chrome-headless-shell".parse::<BrowserChoice>().unwrap(),
196 BrowserChoice::ChromeShell
197 );
198 assert_eq!(
199 "fingerprint_chromium".parse::<BrowserChoice>().unwrap(),
200 BrowserChoice::FingerprintChromium
201 );
202 assert_eq!(
203 "fingerprint-chromium".parse::<BrowserChoice>().unwrap(),
204 BrowserChoice::FingerprintChromium
205 );
206 assert_eq!(
207 "edge".parse::<BrowserChoice>().unwrap(),
208 BrowserChoice::Edge
209 );
210 assert_eq!(
211 "brave".parse::<BrowserChoice>().unwrap(),
212 BrowserChoice::Brave
213 );
214 assert_eq!(
215 "lightpanda".parse::<BrowserChoice>().unwrap(),
216 BrowserChoice::Lightpanda
217 );
218 assert_eq!(
219 "camoufox".parse::<BrowserChoice>().unwrap(),
220 BrowserChoice::Camoufox
221 );
222 }
223
224 #[test]
225 fn browser_choice_rejects_unknown() {
226 let err = "netscape".parse::<BrowserChoice>().unwrap_err();
227 assert!(err.contains("netscape"), "error was {err:?}");
228 }
229}