feat(xray/outbounds): TCP probe mode + Test All + timing breakdown
- service.TestOutbound now dispatches on `mode`:
- "tcp": parallel net.DialTimeout to every server/peer endpoint
(vmess/vless/trojan/ss/socks/http/wireguard). No xray spin-up,
no semaphore — safe to run concurrently across outbounds.
- "http" (default): existing temp-xray + SOCKS path, now with an
httptrace.ClientTrace breakdown (DNS / Connect / TLS / TTFB)
alongside the total delay and status code.
- testSemaphore renamed to httpTestSemaphore — only HTTP probes
serialise, TCP runs free.
- TestOutboundResult carries the per-mode extras: timing fields for
HTTP, per-endpoint dial list for TCP, plus a `mode` echo.
- Controller reads `mode` from the form and passes it through.
- useXraySetting: testOutbound accepts mode (default "tcp"); new
testAllOutbounds(mode) runs a worker pool (concurrency 8 for TCP,
1 for HTTP) and skips blackhole / loopback / blocked outbounds —
also skips freedom / dns under TCP since they have no endpoint.
- OutboundsTab: TCP/HTTP radio toggle and a Test All button land in
the toolbar; the per-row ⚡ now uses the selected mode. Results
surface in a popover with the full timing breakdown plus the
endpoint list for TCP probes. Latency header replaces the duplicate
"check" column title.
Practical effect: testing ten outbounds in TCP mode drops from ~50–100s
(serial HTTP) to ~1–2s (parallel dial × 8). HTTP mode stays as the
authoritative probe and now shows where the latency actually lives.
This commit is contained in:
@@ -199,9 +199,12 @@ func (a *XraySettingController) resetOutboundsTraffic(c *gin.Context) {
|
||||
|
||||
// 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.
|
||||
// Optional form "mode": "tcp" for a fast dial-only probe (parallel-safe),
|
||||
// anything else (default) for a full HTTP probe through a temp xray instance.
|
||||
func (a *XraySettingController) testOutbound(c *gin.Context) {
|
||||
outboundJSON := c.PostForm("outbound")
|
||||
allOutboundsJSON := c.PostForm("allOutbounds")
|
||||
mode := c.PostForm("mode")
|
||||
|
||||
if outboundJSON == "" {
|
||||
jsonMsg(c, I18nWeb(c, "somethingWentWrong"), common.NewError("outbound parameter is required"))
|
||||
@@ -211,7 +214,7 @@ func (a *XraySettingController) testOutbound(c *gin.Context) {
|
||||
// Load the test URL from server settings to prevent SSRF via user-controlled URLs
|
||||
testURL, _ := a.SettingService.GetXrayOutboundTestUrl()
|
||||
|
||||
result, err := a.OutboundService.TestOutbound(outboundJSON, testURL, allOutboundsJSON)
|
||||
result, err := a.OutboundService.TestOutbound(outboundJSON, testURL, allOutboundsJSON, mode)
|
||||
if err != nil {
|
||||
jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err)
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user