feat: migrate mihomo to use kode-bridge IPC on Windows
This commit implements a comprehensive migration from legacy service IPC to the kode-bridge library for Windows IPC communication. Key changes include: Replace service_ipc with kode-bridge IpcManager for all mihomo communications Simplify proxy commands using new caching mechanism with ProxyRequestCache Add Windows named pipe (\.\pipe\mihomo) and Unix socket IPC endpoint configuration Update Tauri permissions and dependencies (dashmap, tauri-plugin-notification) Add IPC logging support and improve error handling Fix Windows IPC path handling in directory utilities This migration enables better cross-platform IPC support and improved performance for mihomo proxy core communication.
This commit is contained in:
86
src-tauri/Cargo.lock
generated
86
src-tauri/Cargo.lock
generated
@@ -375,6 +375,25 @@ dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-process"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cde3f4e40e6021d7acffc90095cbd6dc54cb593903d1de5832f435eb274b85dc"
|
||||
dependencies = [
|
||||
"async-channel 2.5.0",
|
||||
"async-io 2.4.1",
|
||||
"async-lock 3.4.0",
|
||||
"async-signal",
|
||||
"async-task",
|
||||
"blocking",
|
||||
"cfg-if",
|
||||
"event-listener 5.3.0",
|
||||
"futures-lite 2.6.0",
|
||||
"rustix 1.0.7",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-recursion"
|
||||
version = "1.1.1"
|
||||
@@ -1106,6 +1125,7 @@ dependencies = [
|
||||
"boa_engine",
|
||||
"chrono",
|
||||
"criterion",
|
||||
"dashmap 6.1.0",
|
||||
"deelevate",
|
||||
"delay_timer",
|
||||
"dirs 6.0.0",
|
||||
@@ -1149,6 +1169,7 @@ dependencies = [
|
||||
"tauri-plugin-dialog",
|
||||
"tauri-plugin-fs",
|
||||
"tauri-plugin-global-shortcut",
|
||||
"tauri-plugin-notification",
|
||||
"tauri-plugin-process",
|
||||
"tauri-plugin-shell",
|
||||
"tauri-plugin-updater",
|
||||
@@ -3980,6 +4001,18 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
|
||||
|
||||
[[package]]
|
||||
name = "mac-notification-sys"
|
||||
version = "0.6.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "119c8490084af61b44c9eda9d626475847a186737c0378c85e32d77c33a01cd4"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"objc2 0.6.1",
|
||||
"objc2-foundation 0.3.1",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "malloc_buf"
|
||||
version = "0.0.6"
|
||||
@@ -4331,6 +4364,20 @@ version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8"
|
||||
|
||||
[[package]]
|
||||
name = "notify-rust"
|
||||
version = "4.11.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6442248665a5aa2514e794af3b39661a8e73033b1cc5e59899e1276117ee4400"
|
||||
dependencies = [
|
||||
"futures-lite 2.6.0",
|
||||
"log",
|
||||
"mac-notification-sys",
|
||||
"serde",
|
||||
"tauri-winrt-notification",
|
||||
"zbus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ntapi"
|
||||
version = "0.4.1"
|
||||
@@ -6670,7 +6717,7 @@ dependencies = [
|
||||
"async-io 1.13.0",
|
||||
"async-lock 2.8.0",
|
||||
"async-net",
|
||||
"async-process",
|
||||
"async-process 1.8.1",
|
||||
"blocking",
|
||||
"futures-lite 1.13.0",
|
||||
]
|
||||
@@ -7272,6 +7319,25 @@ dependencies = [
|
||||
"thiserror 2.0.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-notification"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfe06ed89cff6d0ec06ff4f544fb961e4718348a33309f56ccb2086e77bc9116"
|
||||
dependencies = [
|
||||
"log",
|
||||
"notify-rust",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_repr",
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"thiserror 2.0.12",
|
||||
"time",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-process"
|
||||
version = "2.3.0"
|
||||
@@ -7449,6 +7515,18 @@ dependencies = [
|
||||
"toml 0.8.23",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-winrt-notification"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b1e66e07de489fe43a46678dd0b8df65e0c973909df1b60ba33874e297ba9b9"
|
||||
dependencies = [
|
||||
"quick-xml 0.37.5",
|
||||
"thiserror 2.0.12",
|
||||
"windows 0.61.3",
|
||||
"windows-version",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.20.0"
|
||||
@@ -9593,8 +9671,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "597f45e98bc7e6f0988276012797855613cd8269e23b5be62cc4e5d28b7e515d"
|
||||
dependencies = [
|
||||
"async-broadcast",
|
||||
"async-executor",
|
||||
"async-io 2.4.1",
|
||||
"async-lock 3.4.0",
|
||||
"async-process 2.3.1",
|
||||
"async-recursion",
|
||||
"async-task",
|
||||
"async-trait",
|
||||
"blocking",
|
||||
"enumflags2",
|
||||
"event-listener 5.3.0",
|
||||
"futures-core",
|
||||
|
||||
@@ -79,6 +79,8 @@ sha2 = "0.10.9"
|
||||
hex = "0.4.3"
|
||||
scopeguard = "1.2.0"
|
||||
kode-bridge = { git = "https://github.com/KodeBarinn/kode-bridge", branch = "dev" }
|
||||
dashmap = "6.1.0"
|
||||
tauri-plugin-notification = "2.3.0"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
runas = "=1.2.0"
|
||||
@@ -140,4 +142,4 @@ crate-type = ["staticlib", "cdylib", "rlib"]
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = "0.6.0"
|
||||
tempfile = "3.20.0"
|
||||
tempfile = "3.20.0"
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
"autostart:allow-enable",
|
||||
"autostart:allow-disable",
|
||||
"autostart:allow-is-enabled",
|
||||
"core:window:allow-set-theme"
|
||||
"core:window:allow-set-theme",
|
||||
"notification:default"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,99 +1,41 @@
|
||||
use super::CmdResult;
|
||||
use crate::{core::handle, module::mihomo::MihomoManager, state::proxy::CmdProxyState};
|
||||
use std::{
|
||||
sync::Mutex,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use tauri::Manager;
|
||||
use crate::{ipc::IpcManager, state::proxy::ProxyRequestCache};
|
||||
use std::time::Duration;
|
||||
|
||||
const PROXIES_REFRESH_INTERVAL: Duration = Duration::from_secs(60);
|
||||
const PROVIDERS_REFRESH_INTERVAL: Duration = Duration::from_secs(60);
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_proxies() -> CmdResult<serde_json::Value> {
|
||||
let manager = MihomoManager::global();
|
||||
|
||||
let app_handle = handle::Handle::global().app_handle().unwrap();
|
||||
let cmd_proxy_state = app_handle.state::<Mutex<CmdProxyState>>();
|
||||
|
||||
let should_refresh = {
|
||||
let mut state = cmd_proxy_state.lock().unwrap();
|
||||
let now = Instant::now();
|
||||
if now.duration_since(state.last_refresh_time) > PROXIES_REFRESH_INTERVAL {
|
||||
state.need_refresh = true;
|
||||
state.last_refresh_time = now;
|
||||
}
|
||||
state.need_refresh
|
||||
};
|
||||
|
||||
if should_refresh {
|
||||
let proxies = manager.get_refresh_proxies().await?;
|
||||
{
|
||||
let mut state = cmd_proxy_state.lock().unwrap();
|
||||
state.proxies = Box::new(proxies);
|
||||
state.need_refresh = false;
|
||||
}
|
||||
log::debug!(target: "app", "proxies刷新成功");
|
||||
}
|
||||
|
||||
let proxies = {
|
||||
let state = cmd_proxy_state.lock().unwrap();
|
||||
state.proxies.clone()
|
||||
};
|
||||
Ok(*proxies)
|
||||
let manager = IpcManager::global();
|
||||
let cache = ProxyRequestCache::global();
|
||||
let key = ProxyRequestCache::make_key("proxies", "default");
|
||||
let value = cache
|
||||
.get_or_fetch(key, PROXIES_REFRESH_INTERVAL, || async {
|
||||
manager.get_refresh_proxies().await.expect("fetch failed")
|
||||
})
|
||||
.await;
|
||||
Ok((*value).clone())
|
||||
}
|
||||
|
||||
/// 强制刷新代理缓存用于profile切换
|
||||
#[tauri::command]
|
||||
pub async fn force_refresh_proxies() -> CmdResult<serde_json::Value> {
|
||||
let manager = MihomoManager::global();
|
||||
let app_handle = handle::Handle::global().app_handle().unwrap();
|
||||
let cmd_proxy_state = app_handle.state::<Mutex<CmdProxyState>>();
|
||||
|
||||
log::debug!(target: "app", "强制刷新代理缓存");
|
||||
|
||||
let proxies = manager.get_refresh_proxies().await?;
|
||||
|
||||
{
|
||||
let mut state = cmd_proxy_state.lock().unwrap();
|
||||
state.proxies = Box::new(proxies.clone());
|
||||
state.need_refresh = false;
|
||||
state.last_refresh_time = Instant::now();
|
||||
}
|
||||
|
||||
log::debug!(target: "app", "强制刷新代理缓存完成");
|
||||
Ok(proxies)
|
||||
let cache = ProxyRequestCache::global();
|
||||
let key = ProxyRequestCache::make_key("proxies", "default");
|
||||
cache.map.remove(&key);
|
||||
get_proxies().await
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_providers_proxies() -> CmdResult<serde_json::Value> {
|
||||
let app_handle = handle::Handle::global().app_handle().unwrap();
|
||||
let cmd_proxy_state = app_handle.state::<Mutex<CmdProxyState>>();
|
||||
|
||||
let should_refresh = {
|
||||
let mut state = cmd_proxy_state.lock().unwrap();
|
||||
let now = Instant::now();
|
||||
if now.duration_since(state.last_refresh_time) > PROVIDERS_REFRESH_INTERVAL {
|
||||
state.need_refresh = true;
|
||||
state.last_refresh_time = now;
|
||||
}
|
||||
state.need_refresh
|
||||
};
|
||||
|
||||
if should_refresh {
|
||||
let manager = MihomoManager::global();
|
||||
let providers = manager.get_providers_proxies().await?;
|
||||
{
|
||||
let mut state = cmd_proxy_state.lock().unwrap();
|
||||
state.providers_proxies = Box::new(providers);
|
||||
state.need_refresh = false;
|
||||
}
|
||||
log::debug!(target: "app", "providers_proxies刷新成功");
|
||||
}
|
||||
|
||||
let providers_proxies = {
|
||||
let state = cmd_proxy_state.lock().unwrap();
|
||||
state.providers_proxies.clone()
|
||||
};
|
||||
Ok(*providers_proxies)
|
||||
let manager = IpcManager::global();
|
||||
let cache = ProxyRequestCache::global();
|
||||
let key = ProxyRequestCache::make_key("providers", "default");
|
||||
let value = cache
|
||||
.get_or_fetch(key, PROVIDERS_REFRESH_INTERVAL, || async {
|
||||
manager.get_providers_proxies().await.expect("fetch failed")
|
||||
})
|
||||
.await;
|
||||
Ok((*value).clone())
|
||||
}
|
||||
|
||||
@@ -57,6 +57,10 @@ impl IClashTemp {
|
||||
map.insert("ipv6".into(), true.into());
|
||||
map.insert("mode".into(), "rule".into());
|
||||
map.insert("external-controller".into(), "127.0.0.1:9097".into());
|
||||
#[cfg(unix)]
|
||||
map.insert("external-controller-unix".into(), "mihomo.sock".into());
|
||||
#[cfg(windows)]
|
||||
map.insert("external-controller-pipe".into(), r"\\.\pipe\mihomo".into());
|
||||
cors_map.insert("allow-private-network".into(), true.into());
|
||||
cors_map.insert(
|
||||
"allow-origins".into(),
|
||||
@@ -88,6 +92,11 @@ impl IClashTemp {
|
||||
let socks_port = Self::guard_socks_port(&config);
|
||||
let port = Self::guard_port(&config);
|
||||
let ctrl = Self::guard_server_ctrl(&config);
|
||||
#[cfg(unix)]
|
||||
let external_controller_unix = Self::guard_external_controller_unix(&config);
|
||||
#[cfg(windows)]
|
||||
let external_controller_pipe = Self::guard_external_controller_pipe(&config);
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
config.insert("redir-port".into(), redir_port.into());
|
||||
#[cfg(target_os = "linux")]
|
||||
@@ -96,7 +105,16 @@ impl IClashTemp {
|
||||
config.insert("socks-port".into(), socks_port.into());
|
||||
config.insert("port".into(), port.into());
|
||||
config.insert("external-controller".into(), ctrl.into());
|
||||
|
||||
#[cfg(unix)]
|
||||
config.insert(
|
||||
"external-controller-unix".into(),
|
||||
external_controller_unix.into(),
|
||||
);
|
||||
#[cfg(windows)]
|
||||
config.insert(
|
||||
"external-controller-pipe".into(),
|
||||
external_controller_pipe.into(),
|
||||
);
|
||||
config
|
||||
}
|
||||
|
||||
@@ -257,6 +275,26 @@ impl IClashTemp {
|
||||
Err(_) => "127.0.0.1:9097".into(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
pub fn guard_external_controller_unix(config: &Mapping) -> String {
|
||||
config
|
||||
.get("external-controller-unix")
|
||||
.and_then(|value| value.as_str())
|
||||
.map(|s| s.trim().to_string())
|
||||
.filter(|s| !s.is_empty())
|
||||
.unwrap_or_else(|| "mihomo.sock".to_string())
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
pub fn guard_external_controller_pipe(config: &Mapping) -> String {
|
||||
config
|
||||
.get("external-controller-pipe")
|
||||
.and_then(|value| value.as_str())
|
||||
.map(|s| s.trim().to_string())
|
||||
.filter(|s| !s.is_empty())
|
||||
.unwrap_or_else(|| r"\\.\pipe\mihomo".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
|
||||
|
||||
@@ -2,7 +2,6 @@ use once_cell::sync::OnceCell;
|
||||
use tauri::tray::TrayIconBuilder;
|
||||
#[cfg(target_os = "macos")]
|
||||
pub mod speed_rate;
|
||||
#[cfg(target_os = "macos")]
|
||||
use crate::ipc::Rate;
|
||||
use crate::{
|
||||
cmd,
|
||||
|
||||
@@ -6,7 +6,10 @@ use kode_bridge::{
|
||||
use serde_json::json;
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use crate::utils::dirs::ipc_path;
|
||||
use crate::{
|
||||
logging,
|
||||
utils::{dirs::ipc_path, logging::Type},
|
||||
};
|
||||
|
||||
pub struct IpcManager {
|
||||
ipc_path: String,
|
||||
@@ -26,6 +29,13 @@ impl IpcManager {
|
||||
"IpcManager initialized with IPC path: {}",
|
||||
instance.ipc_path
|
||||
);
|
||||
logging!(
|
||||
info,
|
||||
Type::IPC,
|
||||
true,
|
||||
"IpcManager initialized with IPC path: {}",
|
||||
instance.ipc_path
|
||||
);
|
||||
instance
|
||||
})
|
||||
}
|
||||
@@ -91,6 +101,7 @@ impl IpcManager {
|
||||
}
|
||||
|
||||
impl IpcManager {
|
||||
#[allow(dead_code)]
|
||||
pub async fn is_mihomo_running(&self) -> Result<(), AnyError> {
|
||||
let url = "/version";
|
||||
let _response = self.send_request("GET", url, None).await?;
|
||||
|
||||
@@ -257,6 +257,5 @@ pub fn ipc_path() -> Result<PathBuf> {
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn ipc_path() -> Result<PathBuf> {
|
||||
let res_dir = app_home_dir()?;
|
||||
Ok(res_dir.join(r"\\.\pipe\mihomo"))
|
||||
Ok(PathBuf::from(r"\\.\pipe\mihomo"))
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ pub enum Type {
|
||||
Lightweight,
|
||||
Network,
|
||||
ProxyMode,
|
||||
IPC,
|
||||
}
|
||||
|
||||
impl fmt::Display for Type {
|
||||
@@ -37,6 +38,7 @@ impl fmt::Display for Type {
|
||||
Type::Lightweight => write!(f, "[Lightweight]"),
|
||||
Type::Network => write!(f, "[Network]"),
|
||||
Type::ProxyMode => write!(f, "[ProxMode]"),
|
||||
Type::IPC => write!(f, "[IPC]"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user