mirror of
https://github.com/black-ant/Ant-Browser.git
synced 2026-07-14 18:48:55 +08:00
99 lines
2.6 KiB
Go
99 lines
2.6 KiB
Go
package proxy
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net"
|
|
"net/http"
|
|
"net/netip"
|
|
"strings"
|
|
"time"
|
|
|
|
C "github.com/metacubex/mihomo/constant"
|
|
)
|
|
|
|
// unifiedDelayTest 通过代理访问测速 URL,记录从代理拨号到首个 HTTP 响应完成的端到端耗时。
|
|
// master 版本只统计第二次复用连接 HEAD 的 RTT,会漏掉代理拨号/握手耗时,导致显示延迟偏低。
|
|
func unifiedDelayTest(proxyId string, px C.Proxy, testURL string, timeout time.Duration) TestResult {
|
|
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
|
defer cancel()
|
|
|
|
addr, err := urlToMeta(testURL)
|
|
if err != nil {
|
|
return TestResult{ProxyId: proxyId, Ok: false, Error: fmt.Sprintf("URL 解析失败: %v", err)}
|
|
}
|
|
|
|
start := time.Now()
|
|
conn, err := px.DialContext(ctx, &addr)
|
|
if err != nil {
|
|
return TestResult{ProxyId: proxyId, Ok: false, Error: fmt.Sprintf("代理连接失败: %v", err)}
|
|
}
|
|
defer conn.Close()
|
|
|
|
transport := &http.Transport{
|
|
DialContext: func(context.Context, string, string) (net.Conn, error) {
|
|
return conn, nil
|
|
},
|
|
DisableKeepAlives: false,
|
|
}
|
|
client := &http.Client{
|
|
Transport: transport,
|
|
Timeout: timeout,
|
|
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
|
return http.ErrUseLastResponse
|
|
},
|
|
}
|
|
defer client.CloseIdleConnections()
|
|
|
|
req, _ := http.NewRequestWithContext(ctx, http.MethodHead, testURL, nil)
|
|
resp, err := client.Do(req)
|
|
latency := time.Since(start).Milliseconds()
|
|
if err != nil {
|
|
return TestResult{ProxyId: proxyId, Ok: false, LatencyMs: latency, Error: err.Error()}
|
|
}
|
|
resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNoContent {
|
|
return TestResult{
|
|
ProxyId: proxyId,
|
|
Ok: false,
|
|
LatencyMs: latency,
|
|
Error: fmt.Sprintf("HTTP %d", resp.StatusCode),
|
|
}
|
|
}
|
|
|
|
return TestResult{ProxyId: proxyId, Ok: true, LatencyMs: latency}
|
|
}
|
|
|
|
// urlToMeta 将 URL 转换为 mihomo Metadata
|
|
func urlToMeta(rawURL string) (C.Metadata, error) {
|
|
var host string
|
|
var portNum uint16
|
|
if strings.HasPrefix(rawURL, "https://") {
|
|
host = rawURL[len("https://"):]
|
|
portNum = 443
|
|
} else if strings.HasPrefix(rawURL, "http://") {
|
|
host = rawURL[len("http://"):]
|
|
portNum = 80
|
|
} else {
|
|
return C.Metadata{}, fmt.Errorf("不支持的 URL scheme")
|
|
}
|
|
|
|
if idx := strings.Index(host, "/"); idx >= 0 {
|
|
host = host[:idx]
|
|
}
|
|
if h, p, err := net.SplitHostPort(host); err == nil {
|
|
host = h
|
|
fmt.Sscanf(p, "%d", &portNum)
|
|
}
|
|
|
|
meta := C.Metadata{
|
|
Host: host,
|
|
DstPort: portNum,
|
|
}
|
|
if addr, err := netip.ParseAddr(host); err == nil {
|
|
meta.DstIP = addr
|
|
}
|
|
return meta, nil
|
|
}
|