Release 3.8.2.11: idle rev-cache, port-safe Connect, single-instance tray UX.
Faster unchanged polls; orphan-core cleanup on listen ports; clearer busy-port errors; cores only from binDir; incremental server list; CSP without Google Fonts; single-instance activates existing window; close-to-tray without kill-others or balloon. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -29,9 +29,12 @@ type Result struct {
|
||||
}
|
||||
|
||||
// Target describes one profile to ping.
|
||||
// Prefer Host (host or host:port) so large proxy/AWG bodies are not copied;
|
||||
// Proxy is used only when Host is empty.
|
||||
type Target struct {
|
||||
Name string
|
||||
Protocol config.Protocol
|
||||
Host string
|
||||
Proxy string
|
||||
}
|
||||
|
||||
@@ -70,6 +73,10 @@ func PingAll(ctx context.Context, targets []Target) []Result {
|
||||
defer wg.Done()
|
||||
for i := range jobs {
|
||||
t := targets[i]
|
||||
if hp := strings.TrimSpace(t.Host); hp != "" {
|
||||
out[i] = PingHost(ctx, t.Name, t.Protocol, hp)
|
||||
continue
|
||||
}
|
||||
if strings.TrimSpace(t.Proxy) == "" {
|
||||
out[i] = Result{Name: t.Name, Error: "нет ссылки сервера"}
|
||||
continue
|
||||
@@ -114,6 +121,65 @@ func BestOK(results []Result) (Result, bool) {
|
||||
return best, found
|
||||
}
|
||||
|
||||
// PingHost probes a pre-parsed host or host:port (no proxy URI / secrets).
|
||||
// Naive/Xray use TCP; Hysteria 2 / AWG use UDP.
|
||||
func PingHost(ctx context.Context, name string, proto config.Protocol, hostPort string) Result {
|
||||
res := Result{Name: name}
|
||||
hostPort = strings.TrimSpace(hostPort)
|
||||
if hostPort == "" {
|
||||
res.Error = "нет хоста"
|
||||
return res
|
||||
}
|
||||
host, port := splitHostPort(hostPort, "443")
|
||||
res.Host = host
|
||||
res.Port = port
|
||||
if host == "" {
|
||||
res.Error = "нет хоста"
|
||||
return res
|
||||
}
|
||||
udp := proto == config.ProtocolHysteria2 || proto == config.ProtocolAWG
|
||||
var (
|
||||
rtt time.Duration
|
||||
soft bool
|
||||
err error
|
||||
)
|
||||
if udp {
|
||||
rtt, soft, err = probeUDP(ctx, host, port)
|
||||
} else {
|
||||
rtt, err = probeTCP(ctx, host, port)
|
||||
}
|
||||
if err != nil {
|
||||
res.Error = friendlyDialError(err, udp)
|
||||
return res
|
||||
}
|
||||
ms := rtt.Milliseconds()
|
||||
if ms < 1 {
|
||||
ms = 1
|
||||
}
|
||||
res.Ms = ms
|
||||
res.OK = true
|
||||
res.Soft = soft
|
||||
return res
|
||||
}
|
||||
|
||||
func splitHostPort(hostPort, defaultPort string) (host, port string) {
|
||||
hostPort = strings.TrimSpace(hostPort)
|
||||
if hostPort == "" {
|
||||
return "", defaultPort
|
||||
}
|
||||
if h, p, err := net.SplitHostPort(hostPort); err == nil {
|
||||
return h, p
|
||||
}
|
||||
// bare IPv6 without port
|
||||
if strings.Count(hostPort, ":") >= 2 && !strings.HasPrefix(hostPort, "[") {
|
||||
return hostPort, defaultPort
|
||||
}
|
||||
if i := strings.LastIndex(hostPort, ":"); i >= 0 && !strings.Contains(hostPort[i+1:], "]") {
|
||||
return hostPort[:i], hostPort[i+1:]
|
||||
}
|
||||
return hostPort, defaultPort
|
||||
}
|
||||
|
||||
// PingProfile pings using protocol hints when available.
|
||||
// Naive uses TCP; Hysteria 2 / AWG use UDP — TCP would always look "refused".
|
||||
// When proto is set, Detect is skipped (trust stored protocol).
|
||||
|
||||
@@ -32,6 +32,30 @@ func TestPingProfileEmpty(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSplitHostPort(t *testing.T) {
|
||||
h, p := splitHostPort("example.com:8443", "443")
|
||||
if h != "example.com" || p != "8443" {
|
||||
t.Fatalf("%s %s", h, p)
|
||||
}
|
||||
h, p = splitHostPort("example.com", "443")
|
||||
if h != "example.com" || p != "443" {
|
||||
t.Fatalf("%s %s", h, p)
|
||||
}
|
||||
h, p = splitHostPort("[2001:db8::1]:443", "80")
|
||||
if h != "2001:db8::1" || p != "443" {
|
||||
t.Fatalf("%s %s", h, p)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPingHostEmpty(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||
defer cancel()
|
||||
r := PingHost(ctx, "x", config.ProtocolNaive, "")
|
||||
if r.OK || r.Error == "" {
|
||||
t.Fatalf("%+v", r)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProbeUDPSoftOKWhenSilent(t *testing.T) {
|
||||
pc, err := net.ListenPacket("udp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user