From 4779939424eb047d30161631fd89a9876104084c Mon Sep 17 00:00:00 2001 From: surbiks <43953720+surbiks@users.noreply.github.com> Date: Tue, 10 Feb 2026 00:13:17 +0330 Subject: [PATCH] Add url speed test for outbound (#3767) * add outbound testing functionality with configurable test URL * use no kernel tun for conflict errors --- web/controller/xray_setting.go | 46 +++- web/html/settings/xray/basics.html | 163 +++++++++---- web/html/settings/xray/outbounds.html | 77 ++++-- web/html/xray.html | 156 ++++++++++--- web/job/xray_traffic_job.go | 2 +- web/service/outbound.go | 322 ++++++++++++++++++++++++++ web/service/setting.go | 10 + web/translation/translate.ar_EG.toml | 2 + web/translation/translate.en_US.toml | 8 + web/translation/translate.es_ES.toml | 2 + web/translation/translate.fa_IR.toml | 2 + web/translation/translate.id_ID.toml | 2 + web/translation/translate.ja_JP.toml | 2 + web/translation/translate.pt_BR.toml | 2 + web/translation/translate.ru_RU.toml | 2 + web/translation/translate.tr_TR.toml | 2 + web/translation/translate.uk_UA.toml | 2 + web/translation/translate.vi_VN.toml | 2 + web/translation/translate.zh_CN.toml | 2 + web/translation/translate.zh_TW.toml | 2 + web/websocket/notifier.go | 2 +- xray/process.go | 38 ++- 22 files changed, 746 insertions(+), 102 deletions(-) diff --git a/web/controller/xray_setting.go b/web/controller/xray_setting.go index b78925f0..a48726de 100644 --- a/web/controller/xray_setting.go +++ b/web/controller/xray_setting.go @@ -1,6 +1,9 @@ package controller import ( + "encoding/json" + + "github.com/mhsanaei/3x-ui/v2/util/common" "github.com/mhsanaei/3x-ui/v2/web/service" "github.com/gin-gonic/gin" @@ -34,9 +37,10 @@ func (a *XraySettingController) initRouter(g *gin.RouterGroup) { g.POST("/warp/:action", a.warp) g.POST("/update", a.updateSetting) g.POST("/resetOutboundsTraffic", a.resetOutboundsTraffic) + g.POST("/testOutbound", a.testOutbound) } -// getXraySetting retrieves the Xray configuration template and inbound tags. +// getXraySetting retrieves the Xray configuration template, inbound tags, and outbound test URL. func (a *XraySettingController) getXraySetting(c *gin.Context) { xraySetting, err := a.SettingService.GetXrayConfigTemplate() if err != nil { @@ -48,15 +52,28 @@ func (a *XraySettingController) getXraySetting(c *gin.Context) { jsonMsg(c, I18nWeb(c, "pages.settings.toasts.getSettings"), err) return } - xrayResponse := "{ \"xraySetting\": " + xraySetting + ", \"inboundTags\": " + inboundTags + " }" + outboundTestUrl, _ := a.SettingService.GetXrayOutboundTestUrl() + if outboundTestUrl == "" { + outboundTestUrl = "https://www.google.com/generate_204" + } + urlJSON, _ := json.Marshal(outboundTestUrl) + xrayResponse := "{ \"xraySetting\": " + xraySetting + ", \"inboundTags\": " + inboundTags + ", \"outboundTestUrl\": " + string(urlJSON) + " }" jsonObj(c, xrayResponse, nil) } // updateSetting updates the Xray configuration settings. func (a *XraySettingController) updateSetting(c *gin.Context) { xraySetting := c.PostForm("xraySetting") - err := a.XraySettingService.SaveXraySetting(xraySetting) - jsonMsg(c, I18nWeb(c, "pages.settings.toasts.modifySettings"), err) + if err := a.XraySettingService.SaveXraySetting(xraySetting); err != nil { + jsonMsg(c, I18nWeb(c, "pages.settings.toasts.modifySettings"), err) + return + } + outboundTestUrl := c.PostForm("outboundTestUrl") + if outboundTestUrl == "" { + outboundTestUrl = "https://www.google.com/generate_204" + } + _ = a.SettingService.SetXrayOutboundTestUrl(outboundTestUrl) + jsonMsg(c, I18nWeb(c, "pages.settings.toasts.modifySettings"), nil) } // getDefaultXrayConfig retrieves the default Xray configuration. @@ -118,3 +135,24 @@ func (a *XraySettingController) resetOutboundsTraffic(c *gin.Context) { } jsonObj(c, "", nil) } + +// testOutbound tests an outbound configuration and returns the delay/response time. +// Optional form "allOutbounds": JSON array of all outbounds; used to resolve sockopt.dialerProxy dependencies. +func (a *XraySettingController) testOutbound(c *gin.Context) { + outboundJSON := c.PostForm("outbound") + testURL := c.PostForm("testURL") + allOutboundsJSON := c.PostForm("allOutbounds") + + if outboundJSON == "" { + jsonMsg(c, I18nWeb(c, "somethingWentWrong"), common.NewError("outbound parameter is required")) + return + } + + result, err := a.OutboundService.TestOutbound(outboundJSON, testURL, allOutboundsJSON) + if err != nil { + jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err) + return + } + + jsonObj(c, result, nil) +} diff --git a/web/html/settings/xray/basics.html b/web/html/settings/xray/basics.html index 71aa0d7c..9a31038a 100644 --- a/web/html/settings/xray/basics.html +++ b/web/html/settings/xray/basics.html @@ -4,18 +4,22 @@ - +