SagerNet / SagerNet/sing-box

Feature request: raw TCP/UDP RTT probe mode for urltest/priority groups (latency to non-HTTP targets — game servers, VoIP)

Open
#4,436 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Go
Stars
38.1k
Forks
4.6k
Avg merge
19d 15h
Merged PRs (30d)
1

Description

Problem

urltest currently has exactly one probe mode, and it is inherently HTTP(S)-only: a HEAD request against a URL, timed to response headers (common/urltest/urltest.go). The proposed throughput mode in #4397 changes what's measured (bandwidth vs. latency) but keeps the same HTTP(S) transport requirement — it solves a different problem and still can't probe a target that doesn't speak HTTP.

There's no way to auto-select an outbound by latency to a target that isn't an HTTP(S) endpoint — e.g. a game server (Battlefield-style titles use a bespoke UDP/TCP protocol with no TLS/HTTP handshake) or a VoIP endpoint. Pointing url at such a target doesn't work: the probe never gets a valid HTTP response, the group considers every outbound "dead", and the result is worse than not testing at all.

The current workaround is picking an HTTPS endpoint that's topologically close to the real target (same datacenter/ASN/region) and hoping its latency correlates. It's a guess, not a measurement, and it breaks whenever the real route to the game server diverges from the route to the HTTPS surrogate (different peering, different exchange points — common in practice).

Related issues
  • #4397 proposes a strategy field on the urltest group (latency / throughput / throughput_with_latency_floor). This request would fit naturally as a further strategy value rather than a separate mechanism.
  • #3604 is adjacent, same root constraint ("HTTP(S) only"): the Clash API /delay?url=... silently rewrites a non-HTTPS url param back to the hardcoded gstatic target, so even ad-hoc probing of a non-HTTPS target through the API isn't possible today.
  • #3023 / #4110 (load balancing / weighted modes) are also about strategy choices inside a group, but neither addresses measuring latency to a non-HTTP target.
Proposed solution

Add a raw_tcp (and optionally raw_udp) strategy value:

  • raw_tcp: instead of an HTTP HEAD, just DialContext(ctx, "tcp", host:port) through the outbound being probed, timing to a successful TCP handshake (SYN → SYN-ACK). No HTTP client, no URL/scheme — just host:port.
  • raw_udp (optional, harder to define "success" for without server cooperation): send a UDP packet and time to any response with a short timeout, or — simpler for a first pass — just time session establishment where the protocol allows it.

The metric (RTT) would land in the same URLTestHistory.Delay field used today — no change to the history/Clash API schema, just a different way of populating the existing field.

Example config
{
  "type": "urltest",
  "tag": "game-latency-out",
  "outbounds": ["node-a", "node-b", "node-c"],
  "strategy": "raw_tcp",
  "test_target": "server.battlefield-provider.example:port",
  "interval": "30s",
  "tolerance": 20
}

test_target would be a new host:port field, relevant only when strategy != latency (for latency, the existing url field continues to be used).

Workaround

Writing a custom adapter to intercept traffic and swap in a fake test target — not something most users can do.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading common/urltest/urltest.go and trace how the current HTTP HEAD probe populates URLTestHistory.Delay. Define the scope of the proposed raw_tcp strategy and test_target configuration, including how successful TCP timing should be represented alongside existing latency results; raw_udp remains explicitly optional and unresolved in the issue.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
networking
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.