mirror of
https://github.com/black-ant/Ant-Browser.git
synced 2026-07-14 18:48:55 +08:00
153 lines
5.5 KiB
Go
153 lines
5.5 KiB
Go
package proxy
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
"time"
|
|
|
|
"ant-chrome/backend/internal/config"
|
|
"ant-chrome/backend/internal/logger"
|
|
|
|
xproxy "golang.org/x/net/proxy"
|
|
)
|
|
|
|
// buildProxyHTTPClient 根据代理配置构建 HTTP 客户端,统一用于测速/健康检测场景。
|
|
func BuildProxyHTTPClient(
|
|
src string,
|
|
proxyId string,
|
|
proxies []config.BrowserProxy,
|
|
xrayMgr *XrayManager,
|
|
singboxMgr *SingBoxManager,
|
|
clashMgr *ClashManager,
|
|
connectorType string,
|
|
timeout time.Duration,
|
|
) (*http.Client, error) {
|
|
return buildProxyHTTPClient(src, proxyId, proxies, xrayMgr, singboxMgr, clashMgr, connectorType, timeout)
|
|
}
|
|
|
|
func buildProxyHTTPClient(
|
|
src string,
|
|
proxyId string,
|
|
proxies []config.BrowserProxy,
|
|
xrayMgr *XrayManager,
|
|
singboxMgr *SingBoxManager,
|
|
clashMgr *ClashManager,
|
|
connectorType string,
|
|
timeout time.Duration,
|
|
) (*http.Client, error) {
|
|
src = strings.TrimSpace(resolveProxyConfig(src, proxies, proxyId))
|
|
log := logger.New("ProxyHTTPClient")
|
|
resolution, err := ResolveProxyKernelForConnector(src, proxies, proxyId, connectorType)
|
|
if err != nil {
|
|
log.Warn("代理内核解析失败",
|
|
logger.F("proxy_id", proxyId),
|
|
logger.F("error", err.Error()),
|
|
)
|
|
return nil, err
|
|
}
|
|
log.Info("代理 HTTP 客户端内核选择",
|
|
logger.F("proxy_id", proxyId),
|
|
logger.F("protocol", resolution.Protocol),
|
|
logger.F("kernel", resolution.Kernel),
|
|
logger.F("preferred_kernel", resolution.PreferredKernel),
|
|
logger.F("supported_kernels", strings.Join(resolution.SupportedKernels, ",")),
|
|
logger.F("reason", resolution.Reason),
|
|
)
|
|
l := strings.ToLower(strings.TrimSpace(src))
|
|
if resolution.Kernel == ProxyKernelNative || l == "" || l == "direct://" {
|
|
if strings.HasPrefix(l, "socks5://") {
|
|
u, err := url.Parse(src)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("SOCKS5 地址解析失败: %w", err)
|
|
}
|
|
var auth *xproxy.Auth
|
|
if u.User != nil {
|
|
pass, _ := u.User.Password()
|
|
auth = &xproxy.Auth{User: u.User.Username(), Password: pass}
|
|
}
|
|
dialer, err := xproxy.SOCKS5("tcp", u.Host, auth, xproxy.Direct)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("SOCKS5 dialer 创建失败: %w", err)
|
|
}
|
|
contextDialer, ok := dialer.(xproxy.ContextDialer)
|
|
if !ok {
|
|
return nil, fmt.Errorf("SOCKS5 dialer 不支持 ContextDialer")
|
|
}
|
|
return &http.Client{Transport: &http.Transport{DialContext: contextDialer.DialContext}, Timeout: timeout}, nil
|
|
}
|
|
if strings.HasPrefix(l, "http://") || strings.HasPrefix(l, "https://") {
|
|
proxyURL, err := url.Parse(src)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("代理地址解析失败: %w", err)
|
|
}
|
|
return &http.Client{Transport: &http.Transport{Proxy: http.ProxyURL(proxyURL)}, Timeout: timeout}, nil
|
|
}
|
|
return &http.Client{Timeout: timeout}, nil
|
|
}
|
|
|
|
switch resolution.Kernel {
|
|
case ProxyKernelMihomo:
|
|
if clashMgr == nil {
|
|
log.Warn("Mihomo 管理器未初始化", logger.F("proxy_id", proxyId))
|
|
return nil, fmt.Errorf("Mihomo 管理器未初始化")
|
|
}
|
|
proxyAddr, err := clashMgr.EnsureNodeBridge(src, proxies, proxyId)
|
|
if err != nil {
|
|
log.Warn("Mihomo 桥接启动失败", logger.F("proxy_id", proxyId), logger.F("error", err.Error()))
|
|
return nil, fmt.Errorf("Mihomo 桥接启动失败: %w", err)
|
|
}
|
|
log.Info("Mihomo 桥接已就绪", logger.F("proxy_id", proxyId), logger.F("proxy_addr", proxyAddr))
|
|
return buildHTTPProxyClient(proxyAddr, timeout)
|
|
case ProxyKernelSingBox:
|
|
if singboxMgr == nil {
|
|
log.Warn("sing-box 管理器未初始化", logger.F("proxy_id", proxyId))
|
|
return nil, fmt.Errorf("sing-box 管理器未初始化")
|
|
}
|
|
socks5Addr, err := singboxMgr.EnsureBridge(src, proxies, proxyId)
|
|
if err != nil {
|
|
log.Warn("sing-box 桥接启动失败", logger.F("proxy_id", proxyId), logger.F("error", err.Error()))
|
|
return nil, fmt.Errorf("sing-box 桥接启动失败: %w", err)
|
|
}
|
|
log.Info("sing-box 桥接已就绪", logger.F("proxy_id", proxyId), logger.F("socks5_addr", socks5Addr))
|
|
return buildSocks5HTTPClient(strings.TrimPrefix(socks5Addr, "socks5://"), timeout)
|
|
case ProxyKernelXray:
|
|
if xrayMgr == nil {
|
|
log.Warn("xray 管理器未初始化", logger.F("proxy_id", proxyId))
|
|
return nil, fmt.Errorf("xray 管理器未初始化")
|
|
}
|
|
socks5Addr, err := xrayMgr.EnsureBridge(src, proxies, proxyId)
|
|
if err != nil {
|
|
log.Warn("xray 桥接启动失败", logger.F("proxy_id", proxyId), logger.F("error", err.Error()))
|
|
return nil, fmt.Errorf("xray 桥接启动失败: %w", err)
|
|
}
|
|
log.Info("xray 桥接已就绪", logger.F("proxy_id", proxyId), logger.F("socks5_addr", socks5Addr))
|
|
return buildSocks5HTTPClient(strings.TrimPrefix(socks5Addr, "socks5://"), timeout)
|
|
default:
|
|
return nil, fmt.Errorf("无法为协议 %s 选择代理内核", resolution.Protocol)
|
|
}
|
|
}
|
|
|
|
func buildHTTPProxyClient(proxyAddr string, timeout time.Duration) (*http.Client, error) {
|
|
proxyURL, err := url.Parse(proxyAddr)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("HTTP 代理地址解析失败: %w", err)
|
|
}
|
|
transport := &http.Transport{Proxy: http.ProxyURL(proxyURL)}
|
|
return &http.Client{Transport: transport, Timeout: timeout}, nil
|
|
}
|
|
|
|
func buildSocks5HTTPClient(socks5Host string, timeout time.Duration) (*http.Client, error) {
|
|
dialer, err := xproxy.SOCKS5("tcp", socks5Host, nil, xproxy.Direct)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("SOCKS5 dialer 创建失败: %w", err)
|
|
}
|
|
contextDialer, ok := dialer.(xproxy.ContextDialer)
|
|
if !ok {
|
|
return nil, fmt.Errorf("SOCKS5 dialer 不支持 ContextDialer")
|
|
}
|
|
transport := &http.Transport{DialContext: contextDialer.DialContext}
|
|
return &http.Client{Transport: transport, Timeout: timeout}, nil
|
|
}
|