mirror of
https://github.com/zhom/donutbrowser.git
synced 2026-05-12 20:52:21 +02:00
refactor: nodecar cleanup
This commit is contained in:
Vendored
+1
@@ -33,6 +33,7 @@
|
||||
"devedition",
|
||||
"doctest",
|
||||
"doesn",
|
||||
"domcontentloaded",
|
||||
"donutbrowser",
|
||||
"dpkg",
|
||||
"dtolnay",
|
||||
|
||||
@@ -87,14 +87,13 @@ export async function startCamoufoxProcess(
|
||||
if (line.trim()) {
|
||||
try {
|
||||
const parsed = JSON.parse(line.trim());
|
||||
if (parsed.success && parsed.id === id && parsed.port) {
|
||||
if (parsed.success && parsed.id === id && parsed.processId) {
|
||||
if (!resolved) {
|
||||
resolved = true;
|
||||
clearTimeout(timeout);
|
||||
// Update config with server details
|
||||
config.port = parsed.port;
|
||||
config.wsEndpoint = parsed.wsEndpoint;
|
||||
config.processId = parsed.processId;
|
||||
saveCamoufoxConfig(config);
|
||||
|
||||
// Unref immediately after success to detach properly
|
||||
child.unref();
|
||||
resolve(config);
|
||||
@@ -177,25 +176,21 @@ export async function stopCamoufoxProcess(id: string): Promise<boolean> {
|
||||
}
|
||||
|
||||
try {
|
||||
// Try to find and kill the worker process using multiple methods
|
||||
const { spawn } = await import("node:child_process");
|
||||
|
||||
// Method 1: Kill by process pattern
|
||||
const killByPattern = spawn("pkill", ["-f", `camoufox-worker.*${id}`], {
|
||||
stdio: "ignore",
|
||||
});
|
||||
|
||||
// Method 2: If we have a port (which is actually the process PID), kill by PID
|
||||
if (config.port) {
|
||||
// Method 2: If we have a process ID, kill by PID
|
||||
if (config.processId) {
|
||||
try {
|
||||
process.kill(config.port, "SIGTERM");
|
||||
process.kill(config.processId, "SIGTERM");
|
||||
|
||||
// Give it a moment to terminate gracefully
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||
|
||||
// Force kill if still running
|
||||
try {
|
||||
process.kill(config.port, "SIGKILL");
|
||||
process.kill(config.processId, "SIGKILL");
|
||||
} catch {
|
||||
// Process already terminated
|
||||
}
|
||||
|
||||
@@ -8,8 +8,7 @@ export interface CamoufoxConfig {
|
||||
options: LaunchOptions;
|
||||
profilePath?: string;
|
||||
url?: string;
|
||||
port?: number;
|
||||
wsEndpoint?: string;
|
||||
processId?: number;
|
||||
}
|
||||
|
||||
const STORAGE_DIR = path.join(tmp.tmpdir, "donutbrowser", "camoufox");
|
||||
@@ -126,23 +125,6 @@ export function updateCamoufoxConfig(config: CamoufoxConfig): boolean {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a Camoufox server is running
|
||||
* @param port The port to check
|
||||
* @returns True if running, false otherwise
|
||||
*/
|
||||
export async function isServerRunning(port: number): Promise<boolean> {
|
||||
try {
|
||||
const response = await fetch(`http://localhost:${port}/json/version`, {
|
||||
method: "GET",
|
||||
signal: AbortSignal.timeout(1000),
|
||||
});
|
||||
return response.ok;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a unique ID for a Camoufox instance
|
||||
* @returns A unique ID string
|
||||
|
||||
@@ -27,16 +27,14 @@ export async function runCamoufoxWorker(id: string): Promise<void> {
|
||||
JSON.stringify({
|
||||
success: true,
|
||||
id: id,
|
||||
port: processId,
|
||||
wsEndpoint: `ws://localhost:0/camoufox-${id}`,
|
||||
processId,
|
||||
profilePath: config.profilePath,
|
||||
message: "Camoufox worker started successfully",
|
||||
}),
|
||||
);
|
||||
|
||||
// Update config with process details
|
||||
config.port = processId;
|
||||
config.wsEndpoint = `ws://localhost:0/camoufox-${id}`;
|
||||
config.processId = processId;
|
||||
saveCamoufoxConfig(config);
|
||||
|
||||
// Handle process termination gracefully
|
||||
@@ -60,11 +58,16 @@ export async function runCamoufoxWorker(id: string): Promise<void> {
|
||||
camoufoxOptions.user_data_dir = config.profilePath;
|
||||
}
|
||||
|
||||
// Theming
|
||||
// Remove custom properties before passing to Camoufox
|
||||
camoufoxOptions.disableTheming = true;
|
||||
camoufoxOptions.showcursor = false;
|
||||
|
||||
// Default to headless for tests
|
||||
// Set Firefox preferences for theming
|
||||
if (!camoufoxOptions.firefox_user_prefs) {
|
||||
camoufoxOptions.firefox_user_prefs = {};
|
||||
}
|
||||
|
||||
// Default to non-headless for visibility
|
||||
if (camoufoxOptions.headless === undefined) {
|
||||
camoufoxOptions.headless = false;
|
||||
}
|
||||
@@ -72,17 +75,6 @@ export async function runCamoufoxWorker(id: string): Promise<void> {
|
||||
const browser = await Camoufox(camoufoxOptions);
|
||||
const context = await browser.newContext();
|
||||
|
||||
// Update config with actual browser details
|
||||
let wsEndpoint: string | undefined;
|
||||
try {
|
||||
const browserWithWs = browser as any;
|
||||
wsEndpoint =
|
||||
browserWithWs.wsEndpoint?.() || `ws://localhost:0/camoufox-${id}`;
|
||||
} catch {
|
||||
wsEndpoint = `ws://localhost:0/camoufox-${id}`;
|
||||
}
|
||||
|
||||
config.wsEndpoint = wsEndpoint;
|
||||
saveCamoufoxConfig(config);
|
||||
|
||||
// Handle URL opening if provided
|
||||
|
||||
@@ -396,9 +396,9 @@ program
|
||||
}
|
||||
}
|
||||
|
||||
// Theming
|
||||
// Theming and cursor - these are custom properties for camoufox-js
|
||||
if (options.disableTheming) camoufoxOptions.disableTheming = true;
|
||||
if (options.noShowcursor) camoufoxOptions.showcursor = false;
|
||||
if (options.showcursor === false) camoufoxOptions.showcursor = false;
|
||||
|
||||
// Use the launcher to start Camoufox properly
|
||||
const config = await startCamoufoxProcess(
|
||||
@@ -413,8 +413,7 @@ program
|
||||
console.log(
|
||||
JSON.stringify({
|
||||
id: config.id,
|
||||
port: config.port,
|
||||
wsEndpoint: config.wsEndpoint,
|
||||
processId: config.processId,
|
||||
profilePath: config.profilePath,
|
||||
url: config.url,
|
||||
}),
|
||||
|
||||
@@ -239,8 +239,8 @@ impl BrowserRunner {
|
||||
format!("Failed to launch camoufox via nodecar: {e}").into()
|
||||
})?;
|
||||
|
||||
// For server-based Camoufox, we use the port as a unique identifier (which is actually the PID)
|
||||
let process_id = camoufox_result.port.unwrap_or(0);
|
||||
// For server-based Camoufox, we use the process_id
|
||||
let process_id = camoufox_result.processId.unwrap_or(0);
|
||||
println!("Camoufox launched successfully with PID: {process_id}");
|
||||
|
||||
// Update profile with the process info from camoufox result
|
||||
@@ -808,7 +808,7 @@ impl BrowserRunner {
|
||||
Ok(Some(camoufox_process)) => {
|
||||
println!(
|
||||
"Found Camoufox process: {} (PID: {:?})",
|
||||
camoufox_process.id, camoufox_process.port
|
||||
camoufox_process.id, camoufox_process.processId
|
||||
);
|
||||
|
||||
match camoufox_launcher
|
||||
@@ -819,12 +819,12 @@ impl BrowserRunner {
|
||||
if stopped {
|
||||
println!(
|
||||
"Successfully stopped Camoufox process: {} (PID: {:?})",
|
||||
camoufox_process.id, camoufox_process.port
|
||||
camoufox_process.id, camoufox_process.processId
|
||||
);
|
||||
} else {
|
||||
println!(
|
||||
"Failed to stop Camoufox process: {} (PID: {:?})",
|
||||
camoufox_process.id, camoufox_process.port
|
||||
camoufox_process.id, camoufox_process.processId
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -967,8 +967,8 @@ impl BrowserVersionService {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use wiremock::matchers::{method, path};
|
||||
use wiremock::{Mock, MockServer, ResponseTemplate};
|
||||
|
||||
use wiremock::MockServer;
|
||||
|
||||
async fn setup_mock_server() -> MockServer {
|
||||
MockServer::start().await
|
||||
|
||||
+21
-22
@@ -96,9 +96,8 @@ impl Default for CamoufoxConfig {
|
||||
#[allow(non_snake_case)]
|
||||
pub struct CamoufoxLaunchResult {
|
||||
pub id: String,
|
||||
pub port: Option<u32>,
|
||||
#[serde(alias = "ws_endpoint")]
|
||||
pub wsEndpoint: Option<String>,
|
||||
#[serde(alias = "process_id")]
|
||||
pub processId: Option<u32>,
|
||||
#[serde(alias = "profile_path")]
|
||||
pub profilePath: Option<String>,
|
||||
pub url: Option<String>,
|
||||
@@ -108,8 +107,7 @@ pub struct CamoufoxLaunchResult {
|
||||
struct CamoufoxInstance {
|
||||
#[allow(dead_code)]
|
||||
id: String,
|
||||
port: Option<u32>,
|
||||
ws_endpoint: Option<String>,
|
||||
process_id: Option<u32>,
|
||||
profile_path: Option<String>,
|
||||
url: Option<String>,
|
||||
}
|
||||
@@ -190,6 +188,8 @@ impl CamoufoxNodecarLauncher {
|
||||
debug: Some(true),
|
||||
enable_cache: Some(true),
|
||||
headless: Some(false), // Not headless for testing
|
||||
disable_theming: Some(true),
|
||||
showcursor: Some(false),
|
||||
|
||||
..Default::default()
|
||||
}
|
||||
@@ -422,7 +422,9 @@ impl CamoufoxNodecarLauncher {
|
||||
}
|
||||
|
||||
if let Some(showcursor) = config.showcursor {
|
||||
if !showcursor {
|
||||
if showcursor {
|
||||
args.push("--showcursor".to_string());
|
||||
} else {
|
||||
args.push("--no-showcursor".to_string());
|
||||
}
|
||||
}
|
||||
@@ -456,8 +458,7 @@ impl CamoufoxNodecarLauncher {
|
||||
// Store the instance
|
||||
let instance = CamoufoxInstance {
|
||||
id: launch_result.id.clone(),
|
||||
port: launch_result.port,
|
||||
ws_endpoint: launch_result.wsEndpoint.clone(),
|
||||
process_id: launch_result.processId,
|
||||
profile_path: launch_result.profilePath.clone(),
|
||||
url: launch_result.url.clone(),
|
||||
};
|
||||
@@ -533,13 +534,12 @@ impl CamoufoxNodecarLauncher {
|
||||
|
||||
if instance_path == target_path {
|
||||
// Verify the server is actually running by checking the process
|
||||
if let Some(port) = instance.port {
|
||||
if self.is_server_running(port).await {
|
||||
if let Some(process_id) = instance.process_id {
|
||||
if self.is_server_running(process_id).await {
|
||||
println!("Found running Camoufox instance for profile: {profile_path}");
|
||||
return Ok(Some(CamoufoxLaunchResult {
|
||||
id: id.clone(),
|
||||
port: instance.port,
|
||||
wsEndpoint: instance.ws_endpoint.clone(),
|
||||
processId: instance.process_id,
|
||||
profilePath: instance.profile_path.clone(),
|
||||
url: instance.url.clone(),
|
||||
}));
|
||||
@@ -566,16 +566,16 @@ impl CamoufoxNodecarLauncher {
|
||||
let inner = self.inner.lock().await;
|
||||
|
||||
for (id, instance) in inner.instances.iter() {
|
||||
if let Some(port) = instance.port {
|
||||
// Check if the process is still alive (port is actually PID)
|
||||
if !self.is_server_running(port).await {
|
||||
if let Some(process_id) = instance.process_id {
|
||||
// Check if the process is still alive
|
||||
if !self.is_server_running(process_id).await {
|
||||
// Process is dead
|
||||
println!("Camoufox instance {id} (PID: {port}) is no longer running");
|
||||
println!("Camoufox instance {id} (PID: {process_id}) is no longer running");
|
||||
dead_instances.push(id.clone());
|
||||
instances_to_remove.push(id.clone());
|
||||
}
|
||||
} else {
|
||||
// No port/PID means it's likely a dead instance
|
||||
// No process_id means it's likely a dead instance
|
||||
println!("Camoufox instance {id} has no PID, marking as dead");
|
||||
dead_instances.push(id.clone());
|
||||
instances_to_remove.push(id.clone());
|
||||
@@ -595,14 +595,13 @@ impl CamoufoxNodecarLauncher {
|
||||
Ok(dead_instances)
|
||||
}
|
||||
|
||||
/// Check if a Camoufox server is running on the given port (which is actually a PID)
|
||||
async fn is_server_running(&self, port: u32) -> bool {
|
||||
// For Camoufox, the "port" is actually the process PID
|
||||
/// Check if a Camoufox server is running with the given process ID
|
||||
async fn is_server_running(&self, process_id: u32) -> bool {
|
||||
// Check if the process is still running
|
||||
use sysinfo::{Pid, System};
|
||||
|
||||
let system = System::new_all();
|
||||
if let Some(process) = system.process(Pid::from(port as usize)) {
|
||||
if let Some(process) = system.process(Pid::from(process_id as usize)) {
|
||||
// Check if this is actually a Camoufox process by looking at the command line
|
||||
let cmd = process.cmd();
|
||||
let is_camoufox = cmd.iter().any(|arg| {
|
||||
@@ -611,7 +610,7 @@ impl CamoufoxNodecarLauncher {
|
||||
});
|
||||
|
||||
if is_camoufox {
|
||||
println!("Found running Camoufox process with PID: {port}");
|
||||
println!("Found running Camoufox process with PID: {process_id}");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -683,7 +683,7 @@ impl ProfileManager {
|
||||
Ok(Some(camoufox_process)) => {
|
||||
// Found a running instance, update profile with process info
|
||||
let mut updated_profile = profile.clone();
|
||||
updated_profile.process_id = camoufox_process.port;
|
||||
updated_profile.process_id = camoufox_process.processId;
|
||||
if let Err(e) = self.save_profile(&updated_profile) {
|
||||
println!("Warning: Failed to update Camoufox profile with process info: {e}");
|
||||
}
|
||||
@@ -695,7 +695,7 @@ impl ProfileManager {
|
||||
|
||||
println!(
|
||||
"Camoufox profile '{}' is running with PID: {:?}",
|
||||
profile.name, camoufox_process.port
|
||||
profile.name, camoufox_process.processId
|
||||
);
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
+1
-2
@@ -110,8 +110,7 @@ export interface CamoufoxConfig {
|
||||
|
||||
export interface CamoufoxLaunchResult {
|
||||
id: string;
|
||||
port?: number;
|
||||
wsEndpoint?: string;
|
||||
processId?: number;
|
||||
profilePath?: string;
|
||||
url?: string;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user