mirror of
https://github.com/zhom/donutbrowser.git
synced 2026-04-23 12:26:17 +02:00
refactor: better proxy handling
This commit is contained in:
@@ -57,10 +57,8 @@ program
|
||||
|
||||
// Build upstream URL from individual components if provided
|
||||
if (options.host && options.proxyPort && options.type) {
|
||||
const protocol =
|
||||
options.type === "socks4" || options.type === "socks5"
|
||||
? options.type
|
||||
: "http";
|
||||
// Preserve provided scheme (http, https, socks4, socks5)
|
||||
const protocol = String(options.type).toLowerCase();
|
||||
const auth =
|
||||
options.username && options.password
|
||||
? `${encodeURIComponent(options.username)}:${encodeURIComponent(
|
||||
|
||||
@@ -1491,11 +1491,18 @@ pub async fn launch_browser_profile(
|
||||
// Store the internal proxy settings for passing to launch_browser
|
||||
let mut internal_proxy_settings: Option<ProxySettings> = None;
|
||||
|
||||
// Resolve the most up-to-date profile from disk by name to avoid using stale proxy_id/browser state
|
||||
let profile_for_launch = browser_runner
|
||||
.list_profiles()
|
||||
.map_err(|e| format!("Failed to list profiles: {e}"))?
|
||||
.into_iter()
|
||||
.find(|p| p.name == profile.name)
|
||||
.unwrap_or_else(|| profile.clone());
|
||||
|
||||
// Always start a local proxy before launching (non-Camoufox handled here; Camoufox has its own flow)
|
||||
let profile_for_launch = profile.clone();
|
||||
if profile.browser != "camoufox" {
|
||||
// Determine upstream proxy if configured; otherwise use DIRECT
|
||||
let upstream_proxy = profile
|
||||
let upstream_proxy = profile_for_launch
|
||||
.proxy_id
|
||||
.as_ref()
|
||||
.and_then(|id| PROXY_MANAGER.get_proxy_settings_by_id(id));
|
||||
@@ -1518,11 +1525,13 @@ pub async fn launch_browser_profile(
|
||||
|
||||
// For Firefox-based browsers, apply PAC/user.js to point to the local proxy
|
||||
if matches!(
|
||||
profile.browser.as_str(),
|
||||
profile_for_launch.browser.as_str(),
|
||||
"firefox" | "firefox-developer" | "zen" | "tor-browser" | "mullvad-browser"
|
||||
) {
|
||||
let profiles_dir = browser_runner.get_profiles_dir();
|
||||
let profile_path = profiles_dir.join(profile.id.to_string()).join("profile");
|
||||
let profile_path = profiles_dir
|
||||
.join(profile_for_launch.id.to_string())
|
||||
.join("profile");
|
||||
|
||||
// Provide a dummy upstream (ignored when internal proxy is provided)
|
||||
let dummy_upstream = ProxySettings {
|
||||
@@ -1540,7 +1549,7 @@ pub async fn launch_browser_profile(
|
||||
|
||||
println!(
|
||||
"Local proxy prepared for profile: {} on port: {} (upstream: {})",
|
||||
profile.name,
|
||||
profile_for_launch.name,
|
||||
internal_proxy.port,
|
||||
upstream_proxy
|
||||
.as_ref()
|
||||
|
||||
+107
-13
@@ -18,6 +18,8 @@ pub struct ProxyInfo {
|
||||
pub upstream_port: u16,
|
||||
pub upstream_type: String,
|
||||
pub local_port: u16,
|
||||
// Optional profile name to which this proxy instance is logically tied
|
||||
pub profile_name: Option<String>,
|
||||
}
|
||||
|
||||
// Stored proxy configuration with name and ID for reuse
|
||||
@@ -51,7 +53,9 @@ pub struct ProxyManager {
|
||||
active_proxies: Mutex<HashMap<u32, ProxyInfo>>, // Maps browser process ID to proxy info
|
||||
// Store proxy info by profile name for persistence across browser restarts
|
||||
profile_proxies: Mutex<HashMap<String, ProxySettings>>, // Maps profile name to proxy settings
|
||||
stored_proxies: Mutex<HashMap<String, StoredProxy>>, // Maps proxy ID to stored proxy
|
||||
// Track active proxy IDs by profile name for targeted cleanup
|
||||
profile_active_proxy_ids: Mutex<HashMap<String, String>>, // Maps profile name to proxy id
|
||||
stored_proxies: Mutex<HashMap<String, StoredProxy>>, // Maps proxy ID to stored proxy
|
||||
base_dirs: BaseDirs,
|
||||
}
|
||||
|
||||
@@ -61,6 +65,7 @@ impl ProxyManager {
|
||||
let manager = Self {
|
||||
active_proxies: Mutex::new(HashMap::new()),
|
||||
profile_proxies: Mutex::new(HashMap::new()),
|
||||
profile_active_proxy_ids: Mutex::new(HashMap::new()),
|
||||
stored_proxies: Mutex::new(HashMap::new()),
|
||||
base_dirs,
|
||||
};
|
||||
@@ -257,20 +262,94 @@ impl ProxyManager {
|
||||
browser_pid: u32,
|
||||
profile_name: Option<&str>,
|
||||
) -> Result<ProxySettings, String> {
|
||||
// Check if we already have a proxy for this browser
|
||||
// First, proactively cleanup any dead proxies so we don't accidentally reuse stale ones
|
||||
let _ = self.cleanup_dead_proxies(app_handle.clone()).await;
|
||||
|
||||
// If we have a previous proxy tied to this profile, and the upstream settings are changing,
|
||||
// stop it before starting a new one so the change takes effect immediately.
|
||||
if let Some(name) = profile_name {
|
||||
// Check if we have an active proxy recorded for this profile
|
||||
let maybe_existing_id = {
|
||||
let map = self.profile_active_proxy_ids.lock().unwrap();
|
||||
map.get(name).cloned()
|
||||
};
|
||||
|
||||
if let Some(existing_id) = maybe_existing_id {
|
||||
// Find the existing proxy info
|
||||
let existing_info = {
|
||||
let proxies = self.active_proxies.lock().unwrap();
|
||||
proxies.values().find(|p| p.id == existing_id).cloned()
|
||||
};
|
||||
|
||||
if let Some(existing) = existing_info {
|
||||
let desired_type = proxy_settings
|
||||
.map(|p| p.proxy_type.as_str())
|
||||
.unwrap_or("DIRECT");
|
||||
let desired_host = proxy_settings.map(|p| p.host.as_str()).unwrap_or("DIRECT");
|
||||
let desired_port = proxy_settings.map(|p| p.port).unwrap_or(0);
|
||||
|
||||
let is_same_upstream = existing.upstream_type == desired_type
|
||||
&& existing.upstream_host == desired_host
|
||||
&& existing.upstream_port == desired_port;
|
||||
|
||||
if !is_same_upstream {
|
||||
// Stop the previous proxy tied to this profile (best effort)
|
||||
// We don't know the original PID mapping that created it; iterate to find its key
|
||||
let pid_to_stop = {
|
||||
let proxies = self.active_proxies.lock().unwrap();
|
||||
proxies.iter().find_map(|(pid, info)| {
|
||||
if info.id == existing_id {
|
||||
Some(*pid)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
};
|
||||
if let Some(pid) = pid_to_stop {
|
||||
let _ = self.stop_proxy(app_handle.clone(), pid).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check if we already have a proxy for this browser PID. If it exists but the upstream
|
||||
// settings don't match the newly requested ones, stop it and create a new proxy so that
|
||||
// changes take effect immediately.
|
||||
let mut needs_restart = false;
|
||||
{
|
||||
let proxies = self.active_proxies.lock().unwrap();
|
||||
if let Some(proxy) = proxies.get(&browser_pid) {
|
||||
return Ok(ProxySettings {
|
||||
proxy_type: "http".to_string(),
|
||||
host: "127.0.0.1".to_string(), // Use 127.0.0.1 instead of localhost for better compatibility
|
||||
port: proxy.local_port,
|
||||
username: None,
|
||||
password: None,
|
||||
});
|
||||
if let Some(existing) = proxies.get(&browser_pid) {
|
||||
let desired_type = proxy_settings
|
||||
.map(|p| p.proxy_type.as_str())
|
||||
.unwrap_or("DIRECT");
|
||||
let desired_host = proxy_settings.map(|p| p.host.as_str()).unwrap_or("DIRECT");
|
||||
let desired_port = proxy_settings.map(|p| p.port).unwrap_or(0);
|
||||
|
||||
let is_same_upstream = existing.upstream_type == desired_type
|
||||
&& existing.upstream_host == desired_host
|
||||
&& existing.upstream_port == desired_port;
|
||||
|
||||
if is_same_upstream {
|
||||
// Reuse existing local proxy
|
||||
return Ok(ProxySettings {
|
||||
proxy_type: "http".to_string(),
|
||||
host: "127.0.0.1".to_string(),
|
||||
port: existing.local_port,
|
||||
username: None,
|
||||
password: None,
|
||||
});
|
||||
} else {
|
||||
// Upstream changed; we must restart the local proxy so that traffic is routed correctly
|
||||
needs_restart = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if needs_restart {
|
||||
// Best-effort stop of the old proxy for this PID before starting a new one
|
||||
let _ = self.stop_proxy(app_handle.clone(), browser_pid).await;
|
||||
}
|
||||
|
||||
// Check if we have a preferred port for this profile
|
||||
let preferred_port = if let Some(name) = profile_name {
|
||||
let profile_proxies = self.profile_proxies.lock().unwrap();
|
||||
@@ -367,6 +446,7 @@ impl ProxyManager {
|
||||
.map(|p| p.proxy_type.clone())
|
||||
.unwrap_or_else(|| "DIRECT".to_string()),
|
||||
local_port,
|
||||
profile_name: profile_name.map(|s| s.to_string()),
|
||||
};
|
||||
|
||||
// Store the proxy info
|
||||
@@ -381,6 +461,9 @@ impl ProxyManager {
|
||||
let mut profile_proxies = self.profile_proxies.lock().unwrap();
|
||||
profile_proxies.insert(name.to_string(), proxy_settings.clone());
|
||||
}
|
||||
// Also record the active proxy id for this profile for quick cleanup on changes
|
||||
let mut map = self.profile_active_proxy_ids.lock().unwrap();
|
||||
map.insert(name.to_string(), proxy_info.id.clone());
|
||||
}
|
||||
|
||||
// Return proxy settings for the browser
|
||||
@@ -399,10 +482,10 @@ impl ProxyManager {
|
||||
app_handle: tauri::AppHandle,
|
||||
browser_pid: u32,
|
||||
) -> Result<(), String> {
|
||||
let proxy_id = {
|
||||
let (proxy_id, profile_name): (String, Option<String>) = {
|
||||
let mut proxies = self.active_proxies.lock().unwrap();
|
||||
match proxies.remove(&browser_pid) {
|
||||
Some(proxy) => proxy.id,
|
||||
Some(proxy) => (proxy.id, proxy.profile_name.clone()),
|
||||
None => return Ok(()), // No proxy to stop
|
||||
}
|
||||
};
|
||||
@@ -415,7 +498,7 @@ impl ProxyManager {
|
||||
.arg("proxy")
|
||||
.arg("stop")
|
||||
.arg("--id")
|
||||
.arg(proxy_id);
|
||||
.arg(&proxy_id);
|
||||
|
||||
let output = nodecar.output().await.unwrap();
|
||||
|
||||
@@ -425,6 +508,16 @@ impl ProxyManager {
|
||||
// We still return Ok since we've already removed the proxy from our tracking
|
||||
}
|
||||
|
||||
// Clear profile-to-proxy mapping if it references this proxy
|
||||
if let Some(name) = profile_name {
|
||||
let mut map = self.profile_active_proxy_ids.lock().unwrap();
|
||||
if let Some(current_id) = map.get(&name) {
|
||||
if current_id == &proxy_id {
|
||||
map.remove(&name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -624,6 +717,7 @@ mod tests {
|
||||
upstream_port: 3128,
|
||||
upstream_type: "http".to_string(),
|
||||
local_port: (8000 + i) as u16,
|
||||
profile_name: None,
|
||||
};
|
||||
|
||||
// Add proxy
|
||||
|
||||
@@ -132,8 +132,8 @@ export function ProfilesDataTable({
|
||||
async (profileName: string, proxyId: string | null) => {
|
||||
try {
|
||||
await invoke("update_profile_proxy", {
|
||||
profileName,
|
||||
proxyId,
|
||||
profileName: profileName,
|
||||
proxy_id: proxyId,
|
||||
});
|
||||
setProxyOverrides((prev) => ({ ...prev, [profileName]: proxyId }));
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user