mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-11-08 02:52:07 +08:00
fix(sysproxy): retry a transient system proxy write on Windows
This commit is contained in:
@@ -25,6 +25,9 @@ use std::{
|
||||
};
|
||||
use tokio::{sync::Mutex, time::timeout};
|
||||
|
||||
const CLEAR_ATTEMPTS: u32 = 3;
|
||||
const CLEAR_RETRY_DELAY: Duration = Duration::from_millis(100);
|
||||
|
||||
/// Actionable system-proxy failure attached to an `anyhow` chain.
|
||||
///
|
||||
/// Classification remains downcastable while the original error stays available for diagnostics.
|
||||
@@ -40,6 +43,8 @@ pub enum SysproxyFailure {
|
||||
GuardStopped { detail: String },
|
||||
/// The Core was not ready, so enabling the system proxy was refused.
|
||||
CoreNotReady,
|
||||
/// A Windows system call refused the write, usually a transient RPC hiccup.
|
||||
SystemCallFailed,
|
||||
}
|
||||
|
||||
impl SysproxyFailure {
|
||||
@@ -51,6 +56,7 @@ impl SysproxyFailure {
|
||||
Self::SidecarWhileServiceReady => "SYSPROXY_SIDECAR_WHILE_SERVICE_READY",
|
||||
Self::GuardStopped { .. } => "SYSPROXY_GUARD_STOPPED",
|
||||
Self::CoreNotReady => "SYSPROXY_CORE_NOT_READY",
|
||||
Self::SystemCallFailed => "SYSPROXY_SYSTEM_CALL_FAILED",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,6 +80,7 @@ impl std::fmt::Display for SysproxyFailure {
|
||||
write!(f, "system proxy guard stopped after repeated failures: {detail}")
|
||||
}
|
||||
Self::CoreNotReady => f.write_str("the core is not ready, so the system proxy was not enabled"),
|
||||
Self::SystemCallFailed => f.write_str("a Windows system call failed while writing the system proxy"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -121,10 +128,28 @@ pub fn rollback_failure(caused_by: anyhow::Error, rollback: anyhow::Error) -> an
|
||||
}
|
||||
|
||||
fn classify_local_failure(error: anyhow::Error) -> anyhow::Error {
|
||||
if !was_refused_locally(&error) {
|
||||
return error;
|
||||
if was_refused_locally(&error) {
|
||||
return error.context(SysproxyFailure::PrivilegeRequired);
|
||||
}
|
||||
error.context(SysproxyFailure::PrivilegeRequired)
|
||||
if was_a_failed_system_call(&error) {
|
||||
return error.context(SysproxyFailure::SystemCallFailed);
|
||||
}
|
||||
error
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn was_a_failed_system_call(error: &anyhow::Error) -> bool {
|
||||
error.chain().any(|cause| {
|
||||
matches!(
|
||||
cause.downcast_ref::<sysproxy::Error>(),
|
||||
Some(sysproxy::Error::SystemCall(_))
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
const fn was_a_failed_system_call(_error: &anyhow::Error) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn was_refused_locally(error: &anyhow::Error) -> bool {
|
||||
@@ -447,7 +472,7 @@ pub async fn apply() -> Result<()> {
|
||||
}
|
||||
|
||||
pub async fn clear() -> Result<()> {
|
||||
let result = clear_inner().await;
|
||||
let result = clear_with_retry().await;
|
||||
// Clear failures must be filed even when a restart aborts before apply.
|
||||
settle_table(
|
||||
clear_table_effect(&result),
|
||||
@@ -459,6 +484,29 @@ pub async fn clear() -> Result<()> {
|
||||
result
|
||||
}
|
||||
|
||||
/// A failed system call is usually a transient RPC hiccup, so give it a few tries.
|
||||
async fn clear_with_retry() -> Result<()> {
|
||||
for _ in 1..CLEAR_ATTEMPTS {
|
||||
match clear_inner().await {
|
||||
Err(error)
|
||||
if matches!(
|
||||
SysproxyFailure::from_chain(&error),
|
||||
Some(SysproxyFailure::SystemCallFailed)
|
||||
) =>
|
||||
{
|
||||
logging!(
|
||||
warn,
|
||||
Type::Core,
|
||||
"clearing the system proxy failed; retrying: {error:#}"
|
||||
);
|
||||
tokio::time::sleep(CLEAR_RETRY_DELAY).await;
|
||||
}
|
||||
other => return other,
|
||||
}
|
||||
}
|
||||
clear_inner().await
|
||||
}
|
||||
|
||||
async fn clear_inner() -> Result<()> {
|
||||
let running_mode = CoreManager::global().get_running_mode();
|
||||
match proxy_backend_route(cfg!(target_os = "macos"), &running_mode) {
|
||||
|
||||
@@ -108,7 +108,7 @@ pub async fn save_proxy_ports(settings: ProxyPortSettings) -> Result<SaveProxyPo
|
||||
);
|
||||
return Ok(outcome);
|
||||
}
|
||||
return Err(activation_error).context("Mihomo rejected the proxy port configuration");
|
||||
return Err(activation_error).context("failed to activate the proxy port configuration");
|
||||
}
|
||||
|
||||
if let Err(persist_error) = persist_proxy_port_sources().await {
|
||||
|
||||
@@ -661,7 +661,8 @@
|
||||
"directFallback": "تعذّر ضبط وكيل النظام؛ تمت إعادة حركة البيانات إلى اتصال مباشر.",
|
||||
"guardStopped": "توقف حارس وكيل النظام بعد عدة إخفاقات متتالية. لم يعد وكيل النظام يُحفَظ تلقائيًا.",
|
||||
"sidecarWhileServiceReady": "تعذّر ضبط وكيل النظام: خدمة Clash Verge جاهزة لكن النواة ما زالت تعمل بوضع Sidecar. أعد تشغيل النواة للتبديل إلى وضع الخدمة.",
|
||||
"coreNotReady": "تعذّر تفعيل وكيل النظام لأن النواة لا تعمل. شغّل النواة (أو ثبّت/أصلح خدمة Clash Verge) ثم أعد المحاولة."
|
||||
"coreNotReady": "تعذّر تفعيل وكيل النظام لأن النواة لا تعمل. شغّل النواة (أو ثبّت/أصلح خدمة Clash Verge) ثم أعد المحاولة.",
|
||||
"systemCallFailed": "تعذّرت كتابة وكيل النظام لأن استدعاء نظام Windows أخفق. عادةً ما يكون هذا عارضًا، فأعد المحاولة."
|
||||
}
|
||||
},
|
||||
"notifications": {
|
||||
|
||||
@@ -661,7 +661,8 @@
|
||||
"directFallback": "Systemproxy konnte nicht gesetzt werden; der Datenverkehr wurde auf eine direkte Verbindung zurückgesetzt.",
|
||||
"guardStopped": "Der Systemproxy-Wächter wurde nach mehreren Fehlversuchen in Folge gestoppt. Der Systemproxy wird nicht mehr automatisch aufrechterhalten.",
|
||||
"sidecarWhileServiceReady": "Der Systemproxy konnte nicht gesetzt werden: Der Clash-Verge-Dienst ist bereit, der Kern läuft aber noch als Sidecar. Starten Sie den Kern neu, um in den Dienstmodus zu wechseln.",
|
||||
"coreNotReady": "Der Systemproxy konnte nicht aktiviert werden, da der Kern nicht läuft. Starten Sie den Kern (oder installieren/reparieren Sie den Clash-Verge-Dienst) und versuchen Sie es erneut."
|
||||
"coreNotReady": "Der Systemproxy konnte nicht aktiviert werden, da der Kern nicht läuft. Starten Sie den Kern (oder installieren/reparieren Sie den Clash-Verge-Dienst) und versuchen Sie es erneut.",
|
||||
"systemCallFailed": "Der Systemproxy konnte nicht geschrieben werden, da ein Windows-Systemaufruf fehlschlug. Das ist meist vorübergehend, versuchen Sie es erneut."
|
||||
}
|
||||
},
|
||||
"notifications": {
|
||||
|
||||
@@ -661,7 +661,8 @@
|
||||
"directFallback": "Failed to set the system proxy; traffic has been switched back to a direct connection.",
|
||||
"guardStopped": "The system proxy guard stopped after failing several times running. The system proxy is no longer being kept in place.",
|
||||
"sidecarWhileServiceReady": "The system proxy could not be set because the core is still running as a Sidecar, although the Clash Verge service is ready. Restart the core to switch to service mode.",
|
||||
"coreNotReady": "The system proxy could not be enabled because the core is not running. Start the core (or install/repair the Clash Verge service) and try again."
|
||||
"coreNotReady": "The system proxy could not be enabled because the core is not running. Start the core (or install/repair the Clash Verge service) and try again.",
|
||||
"systemCallFailed": "The system proxy could not be written because a Windows system call failed. This is usually transient, so try again."
|
||||
}
|
||||
},
|
||||
"notifications": {
|
||||
|
||||
@@ -661,7 +661,8 @@
|
||||
"directFallback": "No se pudo establecer el proxy del sistema; el tráfico volvió a una conexión directa.",
|
||||
"guardStopped": "El guardián del proxy del sistema se detuvo tras varios fallos seguidos. El proxy del sistema ya no se mantiene automáticamente.",
|
||||
"sidecarWhileServiceReady": "No se pudo establecer el proxy del sistema: el servicio de Clash Verge está listo, pero el núcleo sigue ejecutándose como Sidecar. Reinicie el núcleo para cambiar al modo de servicio.",
|
||||
"coreNotReady": "No se pudo activar el proxy del sistema porque el núcleo no está en ejecución. Inicie el núcleo (o instale/repare el servicio de Clash Verge) e inténtelo de nuevo."
|
||||
"coreNotReady": "No se pudo activar el proxy del sistema porque el núcleo no está en ejecución. Inicie el núcleo (o instale/repare el servicio de Clash Verge) e inténtelo de nuevo.",
|
||||
"systemCallFailed": "No se pudo escribir el proxy del sistema porque falló una llamada al sistema de Windows. Suele ser transitorio; inténtelo de nuevo."
|
||||
}
|
||||
},
|
||||
"notifications": {
|
||||
|
||||
@@ -661,7 +661,8 @@
|
||||
"directFallback": "تنظیم پراکسی سیستم ناموفق بود؛ ترافیک به اتصال مستقیم بازگردانده شد.",
|
||||
"guardStopped": "نگهبان پراکسی سیستم پس از چند شکست پیاپی متوقف شد. پراکسی سیستم دیگر بهطور خودکار نگه داشته نمیشود.",
|
||||
"sidecarWhileServiceReady": "تنظیم پراکسی سیستم ممکن نشد: سرویس Clash Verge آماده است اما هسته هنوز بهصورت Sidecar اجرا میشود. برای رفتن به حالت سرویس، هسته را دوباره راهاندازی کنید.",
|
||||
"coreNotReady": "فعالسازی پراکسی سیستم ممکن نشد: هسته در حال اجرا نیست. ابتدا هسته را اجرا کنید (یا سرویس Clash Verge را نصب/تعمیر کنید) و دوباره تلاش کنید."
|
||||
"coreNotReady": "فعالسازی پراکسی سیستم ممکن نشد: هسته در حال اجرا نیست. ابتدا هسته را اجرا کنید (یا سرویس Clash Verge را نصب/تعمیر کنید) و دوباره تلاش کنید.",
|
||||
"systemCallFailed": "نوشتن پراکسی سیستم ناموفق بود: یک فراخوانی سیستمی ویندوز شکست خورد. این معمولاً گذراست؛ دوباره تلاش کنید."
|
||||
}
|
||||
},
|
||||
"notifications": {
|
||||
|
||||
@@ -661,7 +661,8 @@
|
||||
"directFallback": "Gagal mengatur proxy sistem; lalu lintas dikembalikan ke koneksi langsung.",
|
||||
"guardStopped": "Penjaga proxy sistem berhenti setelah gagal beberapa kali berturut-turut. Proxy sistem tidak lagi dipertahankan secara otomatis.",
|
||||
"sidecarWhileServiceReady": "Proxy sistem tidak dapat diatur: layanan Clash Verge sudah siap, tetapi inti masih berjalan sebagai Sidecar. Mulai ulang inti untuk beralih ke mode layanan.",
|
||||
"coreNotReady": "Proxy sistem tidak dapat diaktifkan karena inti tidak berjalan. Jalankan inti (atau pasang/perbaiki layanan Clash Verge) lalu coba lagi."
|
||||
"coreNotReady": "Proxy sistem tidak dapat diaktifkan karena inti tidak berjalan. Jalankan inti (atau pasang/perbaiki layanan Clash Verge) lalu coba lagi.",
|
||||
"systemCallFailed": "Proxy sistem gagal ditulis karena panggilan sistem Windows gagal. Ini biasanya sementara, coba lagi."
|
||||
}
|
||||
},
|
||||
"notifications": {
|
||||
|
||||
@@ -661,7 +661,8 @@
|
||||
"directFallback": "システムプロキシの設定に失敗したため、直接接続に戻しました。",
|
||||
"guardStopped": "システムプロキシガードが連続して失敗したため停止しました。システムプロキシは自動的に維持されなくなりました。",
|
||||
"sidecarWhileServiceReady": "システムプロキシを設定できませんでした: Clash Verge サービスは準備できていますが、コアはまだ Sidecar で動作しています。コアを再起動するとサービスモードに切り替わります。",
|
||||
"coreNotReady": "コアが実行されていないため、システムプロキシを有効にできませんでした。コアを起動する(または Clash Verge サービスをインストール/修復する)してから再試行してください。"
|
||||
"coreNotReady": "コアが実行されていないため、システムプロキシを有効にできませんでした。コアを起動する(または Clash Verge サービスをインストール/修復する)してから再試行してください。",
|
||||
"systemCallFailed": "Windows のシステムコールが失敗したため、システムプロキシを書き込めませんでした。多くは一時的な問題です。再試行してください。"
|
||||
}
|
||||
},
|
||||
"notifications": {
|
||||
|
||||
@@ -661,7 +661,8 @@
|
||||
"directFallback": "시스템 프록시 설정에 실패하여 직접 연결로 되돌렸습니다.",
|
||||
"guardStopped": "시스템 프록시 가드가 연속으로 실패하여 중지되었습니다. 시스템 프록시가 더 이상 자동으로 유지되지 않습니다.",
|
||||
"sidecarWhileServiceReady": "시스템 프록시를 설정하지 못했습니다: Clash Verge 서비스는 준비되었지만 코어가 아직 Sidecar로 실행 중입니다. 코어를 재시작하면 서비스 모드로 전환됩니다.",
|
||||
"coreNotReady": "코어가 실행 중이 아니어서 시스템 프록시를 켤 수 없습니다. 코어를 시작한 뒤(또는 Clash Verge 서비스를 설치/복구한 뒤) 다시 시도하세요."
|
||||
"coreNotReady": "코어가 실행 중이 아니어서 시스템 프록시를 켤 수 없습니다. 코어를 시작한 뒤(또는 Clash Verge 서비스를 설치/복구한 뒤) 다시 시도하세요.",
|
||||
"systemCallFailed": "Windows 시스템 호출이 실패하여 시스템 프록시를 기록하지 못했습니다. 대개 일시적인 문제이니 다시 시도하세요."
|
||||
}
|
||||
},
|
||||
"notifications": {
|
||||
|
||||
@@ -661,7 +661,8 @@
|
||||
"directFallback": "Не удалось настроить системный прокси; трафик возвращён к прямому подключению.",
|
||||
"guardStopped": "Страж системного прокси остановился после нескольких неудач подряд. Системный прокси больше не поддерживается автоматически.",
|
||||
"sidecarWhileServiceReady": "Не удалось настроить системный прокси: служба Clash Verge готова, но ядро всё ещё работает в режиме Sidecar. Перезапустите ядро, чтобы переключиться в режим службы.",
|
||||
"coreNotReady": "Не удалось включить системный прокси: ядро не запущено. Запустите ядро (или установите/восстановите службу Clash Verge) и повторите попытку."
|
||||
"coreNotReady": "Не удалось включить системный прокси: ядро не запущено. Запустите ядро (или установите/восстановите службу Clash Verge) и повторите попытку.",
|
||||
"systemCallFailed": "Не удалось записать системный прокси: системный вызов Windows завершился с ошибкой. Обычно это временный сбой, попробуйте ещё раз."
|
||||
}
|
||||
},
|
||||
"notifications": {
|
||||
|
||||
@@ -661,7 +661,8 @@
|
||||
"directFallback": "Sistem vekil sunucusu ayarlanamadı; trafik doğrudan bağlantıya geri döndürüldü.",
|
||||
"guardStopped": "Sistem vekil sunucusu koruması art arda birkaç başarısızlıktan sonra durdu. Sistem vekil sunucusu artık otomatik olarak korunmuyor.",
|
||||
"sidecarWhileServiceReady": "Sistem vekil sunucusu ayarlanamadı: Clash Verge hizmeti hazır ancak çekirdek hâlâ Sidecar olarak çalışıyor. Hizmet moduna geçmek için çekirdeği yeniden başlatın.",
|
||||
"coreNotReady": "Çekirdek çalışmadığı için sistem vekil sunucusu etkinleştirilemedi. Çekirdeği başlatın (veya Clash Verge hizmetini kurun/onarın) ve yeniden deneyin."
|
||||
"coreNotReady": "Çekirdek çalışmadığı için sistem vekil sunucusu etkinleştirilemedi. Çekirdeği başlatın (veya Clash Verge hizmetini kurun/onarın) ve yeniden deneyin.",
|
||||
"systemCallFailed": "Bir Windows sistem çağrısı başarısız olduğu için sistem vekil sunucusu yazılamadı. Bu genellikle geçicidir, yeniden deneyin."
|
||||
}
|
||||
},
|
||||
"notifications": {
|
||||
|
||||
@@ -661,7 +661,8 @@
|
||||
"directFallback": "Система проксиен көйләү уңышсыз булды; трафик туры тоташуга кайтарылды.",
|
||||
"guardStopped": "Система проксие сакчысы берничә тапкыр рәттән уңышсыз булганнан соң туктады. Система проксие бүтән автоматик рәвештә саклап торылмый.",
|
||||
"sidecarWhileServiceReady": "Система проксиен көйләп булмады: Clash Verge хезмәте әзер, әмма ядро һаман Sidecar буларак эшли. Хезмәт режимына күчәр өчен ядрону яңадан эшләтегез.",
|
||||
"coreNotReady": "Система проксиен кабызып булмады: ядро эшләми. Ядрону җибәрегез (яки Clash Verge хезмәтен урнаштырыгыз/төзәтегез) һәм тагын бер тапкыр карагыз."
|
||||
"coreNotReady": "Система проксиен кабызып булмады: ядро эшләми. Ядрону җибәрегез (яки Clash Verge хезмәтен урнаштырыгыз/төзәтегез) һәм тагын бер тапкыр карагыз.",
|
||||
"systemCallFailed": "Система проксиен язып булмады: Windows система чакыруы уңышсыз тәмамланды. Гадәттә бу вакытлыча, тагын бер тапкыр карагыз."
|
||||
}
|
||||
},
|
||||
"notifications": {
|
||||
|
||||
@@ -661,7 +661,8 @@
|
||||
"directFallback": "系统代理设置失败,已回退为直连。",
|
||||
"guardStopped": "系统代理守卫连续多次失败后已停止,系统代理不再被自动维持。",
|
||||
"sidecarWhileServiceReady": "系统代理设置失败:Clash Verge 服务已就绪,但内核仍以 Sidecar 运行。重启内核即可切换到服务模式。",
|
||||
"coreNotReady": "无法开启系统代理:内核尚未运行。请先启动内核(或安装/修复 Clash Verge 服务)后重试。"
|
||||
"coreNotReady": "无法开启系统代理:内核尚未运行。请先启动内核(或安装/修复 Clash Verge 服务)后重试。",
|
||||
"systemCallFailed": "系统代理写入失败:Windows 系统调用未成功,通常是瞬时故障。请稍后重试。"
|
||||
}
|
||||
},
|
||||
"notifications": {
|
||||
|
||||
@@ -661,7 +661,8 @@
|
||||
"directFallback": "系統代理設定失敗,已回退為直連。",
|
||||
"guardStopped": "系統代理守衛連續多次失敗後已停止,系統代理不再被自動維持。",
|
||||
"sidecarWhileServiceReady": "系統代理設定失敗:Clash Verge 服務已就緒,但內核仍以 Sidecar 執行。重新啟動內核即可切換到服務模式。",
|
||||
"coreNotReady": "無法開啟系統代理:內核尚未執行。請先啟動內核(或安裝/修復 Clash Verge 服務)後重試。"
|
||||
"coreNotReady": "無法開啟系統代理:內核尚未執行。請先啟動內核(或安裝/修復 Clash Verge 服務)後重試。",
|
||||
"systemCallFailed": "系統代理寫入失敗:Windows 系統呼叫未成功,通常是瞬時故障。請稍後重試。"
|
||||
}
|
||||
},
|
||||
"notifications": {
|
||||
|
||||
@@ -89,6 +89,8 @@ const CODED_ERROR_TRANSLATION_KEYS: Readonly<Record<string, TranslationKey>> = {
|
||||
'settings.feedback.errors.sysproxy.sidecarWhileServiceReady',
|
||||
SYSPROXY_PRIVILEGE_REQUIRED:
|
||||
'settings.feedback.errors.sysproxy.privilegeRequired',
|
||||
SYSPROXY_SYSTEM_CALL_FAILED:
|
||||
'settings.feedback.errors.sysproxy.systemCallFailed',
|
||||
}
|
||||
|
||||
let nextId = 0
|
||||
|
||||
@@ -780,6 +780,7 @@ export const translationKeys = [
|
||||
'settings.feedback.errors.sysproxy.guardStopped',
|
||||
'settings.feedback.errors.sysproxy.sidecarWhileServiceReady',
|
||||
'settings.feedback.errors.sysproxy.coreNotReady',
|
||||
'settings.feedback.errors.sysproxy.systemCallFailed',
|
||||
'settings.feedback.notifications.clash.restartSuccess',
|
||||
'settings.feedback.notifications.clash.versionUpdated',
|
||||
'settings.feedback.notifications.clash.alreadyLatestVersion',
|
||||
|
||||
@@ -817,6 +817,7 @@ export interface TranslationResources {
|
||||
guardStopped: string
|
||||
privilegeRequired: string
|
||||
sidecarWhileServiceReady: string
|
||||
systemCallFailed: string
|
||||
}
|
||||
}
|
||||
notifications: {
|
||||
|
||||
Reference in New Issue
Block a user