refactor: use of Mihomo API with socket-based communication

This commit is contained in:
Tunglies 2025-04-16 01:54:41 +08:00
parent 2fa90ef29e
commit a58020fb52
15 changed files with 105 additions and 64 deletions

View File

@ -18,6 +18,9 @@
- 外部控制的开关
- 使用 socks 进行内核通信,以解决各种潜在的内核通信异常
#### 重构了:
- Mihomo 内核不再使用 http 交互,而是使用 socket 进行通信
## v2.2.3

53
src-tauri/Cargo.lock generated
View File

@ -1824,6 +1824,12 @@ dependencies = [
"litrs",
]
[[package]]
name = "dotenv"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
[[package]]
name = "downcast-rs"
version = "1.2.1"
@ -2913,7 +2919,7 @@ dependencies = [
"httpdate",
"itoa 1.0.15",
"pin-project-lite",
"socket2 0.5.8",
"socket2 0.4.10",
"tokio",
"tower-service",
"tracing",
@ -2933,6 +2939,7 @@ dependencies = [
"http 1.3.1",
"http-body 1.0.1",
"httparse",
"httpdate",
"itoa 1.0.15",
"pin-project-lite",
"smallvec",
@ -2988,9 +2995,9 @@ dependencies = [
[[package]]
name = "hyper-util"
version = "0.1.10"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4"
checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2"
dependencies = [
"bytes",
"futures-channel",
@ -2998,13 +3005,29 @@ dependencies = [
"http 1.3.1",
"http-body 1.0.1",
"hyper 1.6.0",
"libc",
"pin-project-lite",
"socket2 0.5.8",
"socket2 0.5.9",
"tokio",
"tower-service",
"tracing",
]
[[package]]
name = "hyperlocal"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "986c5ce3b994526b3cd75578e62554abd09f0899d6206de48b3e96ab34ccc8c7"
dependencies = [
"hex",
"http-body-util",
"hyper 1.6.0",
"hyper-util",
"pin-project-lite",
"tokio",
"tower-service",
]
[[package]]
name = "iana-time-zone"
version = "0.1.62"
@ -3852,10 +3875,18 @@ dependencies = [
name = "mihomo_api"
version = "0.0.0"
dependencies = [
"reqwest",
"serde",
"async-trait",
"dotenv",
"futures",
"http-body-util",
"hyper 1.6.0",
"hyper-util",
"hyperlocal",
"lazy_static",
"serde_json",
"time",
"tokio",
"tokio-util",
]
[[package]]
@ -5344,7 +5375,7 @@ dependencies = [
"quinn-udp",
"rustc-hash",
"rustls",
"socket2 0.5.8",
"socket2 0.5.9",
"thiserror 2.0.12",
"tokio",
"tracing",
@ -5380,7 +5411,7 @@ dependencies = [
"cfg_aliases 0.2.1",
"libc",
"once_cell",
"socket2 0.5.8",
"socket2 0.5.9",
"tracing",
"windows-sys 0.59.0",
]
@ -6484,9 +6515,9 @@ dependencies = [
[[package]]
name = "socket2"
version = "0.5.8"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8"
checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef"
dependencies = [
"libc",
"windows-sys 0.52.0",
@ -7484,7 +7515,7 @@ dependencies = [
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
"socket2 0.5.8",
"socket2 0.5.9",
"tokio-macros",
"tracing",
"windows-sys 0.52.0",

View File

@ -2,6 +2,7 @@ use super::CmdResult;
use crate::{
config::*, core::*, feat, module::mihomo::MihomoManager, process::AsyncHandler, wrap_err,
};
use mihomo_api::model::MihomoClient;
use serde_yaml::Mapping;
/// 复制Clash环境变量
@ -70,6 +71,7 @@ pub async fn clash_api_get_proxy_delay(
MihomoManager::global()
.test_proxy_delay(&name, url, timeout)
.await
.map_err(|e| e.to_string())
}
/// 测试URL延迟

View File

@ -1,24 +1,23 @@
use mihomo_api::model::MihomoClient;
use super::CmdResult;
use crate::module::mihomo::MihomoManager;
#[tauri::command]
pub async fn get_proxies() -> CmdResult<serde_json::Value> {
let mannager = MihomoManager::global();
mannager
.refresh_proxies()
.await
.map(|_| mannager.get_proxies())
.or_else(|_| Ok(mannager.get_proxies()))
let manager = MihomoManager::global();
manager.refresh_proxies().await.map_err(|e| e.to_string())?;
let data = manager.get_data_proxies().await;
Ok(data)
}
#[tauri::command]
pub async fn get_providers_proxies() -> CmdResult<serde_json::Value> {
let mannager = MihomoManager::global();
mannager
let manager = MihomoManager::global();
manager
.refresh_providers_proxies()
.await
.map(|_| mannager.get_providers_proxies())
.or_else(|_| Ok(mannager.get_providers_proxies()))
.map_err(|e| e.to_string())?;
let data = manager.get_data_providers_proxies().await;
Ok(data)
}

View File

@ -51,6 +51,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(not(target_os = "windows"))]
map.insert("external-controller-unix".into(), "mihomo.sock".into());
#[cfg(target_os = "windows")]
map.insert("external-controller-pipe".into(), r"\\.\pipe\mihomo".into());
let mut cors_map = Mapping::new();
cors_map.insert("allow-private-network".into(), true.into());
cors_map.insert("allow-origins".into(), vec!["*"].into());

View File

@ -15,6 +15,7 @@ use crate::{
},
};
use anyhow::Result;
use mihomo_api::model::MihomoClient;
use once_cell::sync::OnceCell;
use std::{fmt, path::PathBuf, sync::Arc};
use tauri_plugin_shell::{process::CommandChild, ShellExt};

View File

@ -6,6 +6,7 @@ use crate::{
process::AsyncHandler,
utils::{logging::Type, resolve},
};
use mihomo_api::model::MihomoClient;
use serde_yaml::{Mapping, Value};
use tauri::Manager;

View File

@ -1,3 +1,5 @@
use mihomo_api::model::MihomoClient;
#[cfg(target_os = "macos")]
use crate::AppHandleManager;
use crate::{
@ -74,12 +76,11 @@ pub fn quit() {
"enable": false
}
});
timeout(
Duration::from_secs(1),
MihomoManager::global().patch_configs(disable_tun),
)
.await
.is_ok()
let patch_future =
async { MihomoManager::global().patch_configs(disable_tun).await };
timeout(Duration::from_secs(1), patch_future).await.is_ok()
} else {
true
};

View File

@ -1,4 +1,4 @@
use crate::config::Config;
use crate::{config::Config, utils::dirs::app_socket_path};
use mihomo_api;
use once_cell::sync::{Lazy, OnceCell};
use std::sync::Mutex;
@ -24,20 +24,18 @@ impl MihomoManager {
&INSTANCE
}
pub fn global() -> mihomo_api::MihomoManager {
pub fn global() -> &'static mihomo_api::MihomoManager {
let instance = MihomoManager::__global();
let (current_server, headers) = MihomoManager::get_clash_client_info().unwrap();
let lock = instance.mihomo.lock().unwrap();
if let Some(mihomo) = lock.get() {
if mihomo.get_mihomo_server() == current_server {
return mihomo.clone();
}
let mihomo = &instance.mihomo;
let lock = mihomo.lock().unwrap();
if lock.get().is_none() {
let socket_path = MihomoManager::get_socket_path();
lock.set(mihomo_api::MihomoManager::new(socket_path)).ok();
}
lock.set(mihomo_api::MihomoManager::new(current_server, headers))
.ok();
lock.get().unwrap().clone()
unsafe { std::mem::transmute(lock.get().unwrap()) }
}
}
@ -67,4 +65,12 @@ impl MihomoManager {
let token = http::header::HeaderValue::from_str(&auth).unwrap();
(ws_url, token)
}
fn get_socket_path() -> String {
#[cfg(unix)]
let socket_path = app_socket_path().unwrap();
#[cfg(windows)]
let socket_path = r"\\.\pipe\mihomo";
socket_path
}
}

View File

@ -179,3 +179,10 @@ pub fn get_encryption_key() -> Result<Vec<u8>> {
Ok(key)
}
}
pub fn app_socket_path() -> Result<String> {
let app_dir = app_home_dir()?;
let socket_path = app_dir.join("mihomo.sock");
let socket_path = path_to_str(&socket_path)?;
Ok(socket_path.to_string())
}

View File

@ -15,8 +15,7 @@ use parking_lot::RwLock;
use percent_encoding::percent_decode_str;
use serde_json;
use serde_yaml::Mapping;
use std::net::TcpListener;
use std::sync::Arc;
use std::{net::TcpListener, sync::Arc};
use tauri::{App, Manager};
use tauri::Url;

View File

@ -1,13 +0,0 @@
$pipeName = "\\.\pipe\mihomo"
$pipe = new-object System.IO.Pipes.NamedPipeClientStream(".", "mihomo", [System.IO.Pipes.PipeDirection]::InOut)
$pipe.Connect(1000) # 尝试连接 1 秒
if ($pipe.IsConnected) {
Write-Host "成功连接到管道"
# 示例写入或读取可以加上如下内容
# $writer = new-object System.IO.StreamWriter($pipe)
# $writer.WriteLine("hello pipe")
# $writer.Flush()
$pipe.Close()
} else {
Write-Host "连接失败"
}

View File

@ -42,8 +42,7 @@
// }
pub mod model;
pub use model::E;
pub use model::MihomoData;
pub use model::MihomoManager;
pub use model::{E, MihomoData, MihomoManager};
pub mod platform;
pub mod sock;
pub mod platform;
pub use platform::Client;

View File

@ -40,7 +40,7 @@ pub trait MihomoClient: Sized {
async fn refresh_proxies(&self) -> Result<&Self, E>;
async fn refresh_providers_proxies(&self) -> Result<&Self, E>;
async fn get_connections(&self) -> Result<Value, E>;
async fn delete_connections(&self, id: &str) -> Result<(), E>;
async fn delete_connection(&self, id: &str) -> Result<(), E>;
async fn test_proxy_delay(
&self,
name: &str,

View File

@ -1,10 +1,9 @@
use crate::model::E;
use crate::{model::E, platform::Client};
use async_trait::async_trait;
use hyper::Method;
use serde_json::Value;
use std::sync::Arc;
use tokio::sync::Mutex;
use crate::platform::Client;
use crate::{
MihomoData,
@ -47,7 +46,9 @@ impl MihomoClient for MihomoManager {
body: Option<Value>,
) -> Result<Value, E> {
let client = self.client.lock().await;
client.send_request(self.socket_path.clone(), path, method, body).await
client
.send_request(self.socket_path.clone(), path, method, body)
.await
}
async fn get_version(&self) -> Result<Value, E> {
@ -96,7 +97,7 @@ impl MihomoClient for MihomoManager {
Ok(data)
}
async fn delete_connections(&self, id: &str) -> Result<(), E> {
async fn delete_connection(&self, id: &str) -> Result<(), E> {
let _ = self
.send_request(&format!("/connections/{}", id), Method::DELETE, None)
.await?;