From 3257de014e1618654fa75acda540b83109e7e1fd Mon Sep 17 00:00:00 2001 From: ant-black <1016930479@qq.com> Date: Wed, 10 Jun 2026 23:53:44 +0800 Subject: [PATCH] fix: measure proxy latency through browser bridge --- backend/app_backup_runtime.go | 2 +- backend/app_proxy_health.go | 11 ++-- backend/app_proxy_query.go | 2 +- backend/app_startup.go | 2 +- backend/internal/proxy/runtime_config_test.go | 62 +++++++++++++++++++ .../internal/proxy/singbox_runtime_helpers.go | 2 +- backend/internal/proxy/utils_connectivity.go | 20 +++++- backend/internal/proxy/xray_runtime_config.go | 2 +- 8 files changed, 93 insertions(+), 10 deletions(-) create mode 100644 backend/internal/proxy/runtime_config_test.go diff --git a/backend/app_backup_runtime.go b/backend/app_backup_runtime.go index be14ae45..ef58e659 100644 --- a/backend/app_backup_runtime.go +++ b/backend/app_backup_runtime.go @@ -73,7 +73,7 @@ func (a *App) backupReloadAfterMutation() error { a.speedScheduler = browser.NewProxySpeedScheduler( a.browserMgr.ProxyDAO, func(proxyID string) (bool, int64, string) { - r := proxy.SpeedTest(proxyID, a.config.Browser.Proxies, a.xrayMgr, a.singboxMgr, nil) + r := proxy.TestRealConnectivityWithConfig(proxyID, a.config.Browser.Proxies, a.xrayMgr, a.singboxMgr, nil) return r.Ok, r.LatencyMs, r.Error }, 5*time.Minute, diff --git a/backend/app_proxy_health.go b/backend/app_proxy_health.go index a52ebf62..ada77392 100644 --- a/backend/app_proxy_health.go +++ b/backend/app_proxy_health.go @@ -15,7 +15,7 @@ import ( // BrowserProxyTestSpeed 手动触发单个代理测速并持久化结果 func (a *App) BrowserProxyTestSpeed(proxyId string) ProxyTestResult { proxies := a.getLatestProxies() - result := proxy.SpeedTest(proxyId, proxies, a.xrayMgr, a.singboxMgr, a.proxySpeedTestConfig()) + result := proxy.TestRealConnectivityWithConfig(proxyId, proxies, a.xrayMgr, a.singboxMgr, a.proxySpeedTestConfig()) if a.browserMgr.ProxyDAO != nil { testedAt := time.Now().Format(time.RFC3339) _ = a.browserMgr.ProxyDAO.UpdateSpeedResult(proxyId, result.Ok, result.LatencyMs, testedAt) @@ -23,13 +23,16 @@ func (a *App) BrowserProxyTestSpeed(proxyId string) ProxyTestResult { return ProxyTestResult{ProxyId: result.ProxyId, Ok: result.Ok, LatencyMs: result.LatencyMs, Error: result.Error} } -// BrowserProxyBatchTestSpeed 批量并发测速,concurrency 控制并发数(默认 20) +// BrowserProxyBatchTestSpeed 批量并发测速,concurrency 控制并发数(默认 5,最高 5)。 func (a *App) BrowserProxyBatchTestSpeed(proxyIds []string, concurrency int) []ProxyTestResult { if len(proxyIds) == 0 { return []ProxyTestResult{} } if concurrency <= 0 { - concurrency = 20 + concurrency = 5 + } + if concurrency > 5 { + concurrency = 5 } if concurrency > len(proxyIds) { concurrency = len(proxyIds) @@ -49,7 +52,7 @@ func (a *App) BrowserProxyBatchTestSpeed(proxyIds []string, concurrency int) []P go func() { defer wg.Done() for job := range jobs { - result := proxy.SpeedTest(job.ProxyId, proxies, a.xrayMgr, a.singboxMgr, a.proxySpeedTestConfig()) + result := proxy.TestRealConnectivityWithConfig(job.ProxyId, proxies, a.xrayMgr, a.singboxMgr, a.proxySpeedTestConfig()) if a.browserMgr.ProxyDAO != nil { testedAt := time.Now().Format(time.RFC3339) _ = a.browserMgr.ProxyDAO.UpdateSpeedResult(job.ProxyId, result.Ok, result.LatencyMs, testedAt) diff --git a/backend/app_proxy_query.go b/backend/app_proxy_query.go index 3b12198c..eb716314 100644 --- a/backend/app_proxy_query.go +++ b/backend/app_proxy_query.go @@ -43,7 +43,7 @@ func (a *App) TestProxyConnectivity(proxyId string, proxyConfig string) ProxyTes // 参考 Clash URLTest 策略:多 URL fallback + 复用桥接 + TCP ping 降级 func (a *App) TestProxyRealConnectivity(proxyId string) ProxyTestResult { proxies := a.getLatestProxies() - result := proxy.SpeedTest(proxyId, proxies, a.xrayMgr, a.singboxMgr, nil) + result := proxy.TestRealConnectivityWithConfig(proxyId, proxies, a.xrayMgr, a.singboxMgr, nil) return ProxyTestResult{ProxyId: result.ProxyId, Ok: result.Ok, LatencyMs: result.LatencyMs, Error: result.Error} } diff --git a/backend/app_startup.go b/backend/app_startup.go index d1be3b97..55df0037 100644 --- a/backend/app_startup.go +++ b/backend/app_startup.go @@ -202,7 +202,7 @@ func (a *App) startupInitSpeedScheduler() { a.speedScheduler = browser.NewProxySpeedScheduler( a.browserMgr.ProxyDAO, func(proxyId string) (bool, int64, string) { - r := proxy.SpeedTest(proxyId, a.config.Browser.Proxies, a.xrayMgr, a.singboxMgr, nil) + r := proxy.TestRealConnectivityWithConfig(proxyId, a.config.Browser.Proxies, a.xrayMgr, a.singboxMgr, nil) return r.Ok, r.LatencyMs, r.Error }, 5*time.Minute, diff --git a/backend/internal/proxy/runtime_config_test.go b/backend/internal/proxy/runtime_config_test.go new file mode 100644 index 00000000..4efcdafb --- /dev/null +++ b/backend/internal/proxy/runtime_config_test.go @@ -0,0 +1,62 @@ +package proxy + +import ( + "encoding/json" + "os" + "testing" + + "ant-chrome/backend/internal/config" +) + +func TestXrayRuntimeConfigUsesWarningLogLevel(t *testing.T) { + cfg := config.DefaultConfig() + cfg.Browser.UserDataRoot = t.TempDir() + manager := &XrayManager{Config: cfg, AppRoot: t.TempDir()} + + cfgPath, err := manager.buildRuntimeConfigWithRoute( + "log-level-test", + []interface{}{map[string]interface{}{"protocol": "freedom", "tag": "proxy-out"}}, + []interface{}{}, + 19092, + "", + ) + if err != nil { + t.Fatalf("buildRuntimeConfigWithRoute returned error: %v", err) + } + + runtimeConfig := readRuntimeConfigMap(t, cfgPath) + logConfig := runtimeConfig["log"].(map[string]interface{}) + if got := logConfig["loglevel"]; got != "warning" { + t.Fatalf("xray loglevel = %v, want warning", got) + } +} + +func TestSingBoxRuntimeConfigUsesWarnLogLevel(t *testing.T) { + cfg := config.DefaultConfig() + cfg.Browser.UserDataRoot = t.TempDir() + manager := &SingBoxManager{Config: cfg, AppRoot: t.TempDir()} + + cfgPath, err := manager.buildConfig("log-level-test", map[string]interface{}{"type": "direct", "tag": "proxy-out"}, 19093) + if err != nil { + t.Fatalf("buildConfig returned error: %v", err) + } + + runtimeConfig := readRuntimeConfigMap(t, cfgPath) + logConfig := runtimeConfig["log"].(map[string]interface{}) + if got := logConfig["level"]; got != "warn" { + t.Fatalf("sing-box log level = %v, want warn", got) + } +} + +func readRuntimeConfigMap(t *testing.T, path string) map[string]interface{} { + t.Helper() + data, err := os.ReadFile(path) + if err != nil { + t.Fatalf("read runtime config failed: %v", err) + } + var runtimeConfig map[string]interface{} + if err := json.Unmarshal(data, &runtimeConfig); err != nil { + t.Fatalf("unmarshal runtime config failed: %v", err) + } + return runtimeConfig +} diff --git a/backend/internal/proxy/singbox_runtime_helpers.go b/backend/internal/proxy/singbox_runtime_helpers.go index 55c79e5d..75a37fdf 100644 --- a/backend/internal/proxy/singbox_runtime_helpers.go +++ b/backend/internal/proxy/singbox_runtime_helpers.go @@ -87,7 +87,7 @@ func (m *SingBoxManager) buildConfig(key string, outbound map[string]interface{} cfg := map[string]interface{}{ "log": map[string]interface{}{ - "level": "info", + "level": "warn", "output": filepath.Join(baseDir, "singbox.log"), "timestamp": true, }, diff --git a/backend/internal/proxy/utils_connectivity.go b/backend/internal/proxy/utils_connectivity.go index 8bb99e5b..eb8b00f7 100644 --- a/backend/internal/proxy/utils_connectivity.go +++ b/backend/internal/proxy/utils_connectivity.go @@ -60,6 +60,16 @@ func TestRealConnectivityWithSingBox( proxies []config.BrowserProxy, xrayMgr *XrayManager, singboxMgr *SingBoxManager, +) TestResult { + return TestRealConnectivityWithConfig(proxyId, proxies, xrayMgr, singboxMgr, nil) +} + +func TestRealConnectivityWithConfig( + proxyId string, + proxies []config.BrowserProxy, + xrayMgr *XrayManager, + singboxMgr *SingBoxManager, + cfg *SpeedTestConfig, ) TestResult { src := resolveProxyConfig("", proxies, proxyId) if src == "" { @@ -67,10 +77,18 @@ func TestRealConnectivityWithSingBox( } targetURL := strings.TrimSpace(DefaultSpeedTestURL) + timeout := 15 * time.Second + if cfg != nil { + if len(cfg.URLs) > 0 && strings.TrimSpace(cfg.URLs[0]) != "" { + targetURL = strings.TrimSpace(cfg.URLs[0]) + } + if cfg.Timeout > 0 { + timeout = cfg.Timeout + } + } if targetURL == "" { return TestResult{ProxyId: proxyId, Ok: false, Error: "真实连通性测试目标 URL 为空"} } - const timeout = 15 * time.Second client, err := buildProxyHTTPClient(src, proxyId, proxies, xrayMgr, singboxMgr, timeout) if err != nil { diff --git a/backend/internal/proxy/xray_runtime_config.go b/backend/internal/proxy/xray_runtime_config.go index 499571a9..08fe3686 100644 --- a/backend/internal/proxy/xray_runtime_config.go +++ b/backend/internal/proxy/xray_runtime_config.go @@ -32,7 +32,7 @@ func (m *XrayManager) buildRuntimeConfigWithRoute(key string, outbounds []interf cfgPath := filepath.Join(baseDir, "xray-config.json") cfg := map[string]interface{}{ "log": map[string]interface{}{ - "loglevel": "info", + "loglevel": "warning", "error": filepath.Join(baseDir, "xray-error.log"), }, "inbounds": []interface{}{