feat: 添加自定义HTTP客户端和WebSocket拨号器,支持TLS配置

This commit is contained in:
Akizon77
2025-10-15 12:25:32 +08:00
parent 8e31514e9c
commit 0dcdb89bb5
5 changed files with 152 additions and 76 deletions

72
build_all.ps1 Normal file
View File

@@ -0,0 +1,72 @@
# Requires: PowerShell 5+, Go toolchain, git (optional for version)
# Colors
$Red = 'Red'
$Green = 'Green'
$White = 'White'
# OS/ARCH matrix
$osList = @('windows','linux','darwin','freebsd')
$archList = @('amd64','arm64','386','arm')
# Ensure build directory
$buildDir = Join-Path -Path (Get-Location) -ChildPath 'build'
New-Item -ItemType Directory -Force -Path $buildDir | Out-Null
# Detect version from git tags or fallback to dev
$version = (git describe --tags --abbrev=0 2>$null)
if (-not $version) { $version = 'dev' }
$version = $version.Trim()
# Check go exists
if (-not (Get-Command go -ErrorAction SilentlyContinue)) {
Write-Host 'Go toolchain not found in PATH. Please install Go and try again.' -ForegroundColor $Red
exit 1
}
$failedBuilds = @()
foreach ($goos in $osList) {
foreach ($goarch in $archList) {
# Skip unsupported combos: windows/arm, darwin/386, darwin/arm
if ((($goos -eq 'windows') -and ($goarch -eq 'arm')) -or
(($goos -eq 'darwin') -and (($goarch -eq '386') -or ($goarch -eq 'arm')))) {
continue
}
Write-Host "Building for $goos/$goarch..." -ForegroundColor $White
$binaryName = "komari-agent-$goos-$goarch"
if ($goos -eq 'windows') { $binaryName = "$binaryName.exe" }
$outPath = Join-Path $buildDir $binaryName
# Set env per invocation
$env:GOOS = $goos
$env:GOARCH = $goarch
$env:CGO_ENABLED = '0'
& go build -trimpath -ldflags "-s -w -X github.com/komari-monitor/komari-agent/update.CurrentVersion=$version" -o "$outPath"
if ($LASTEXITCODE -ne 0) {
Write-Host "Failed to build for $goos/$goarch" -ForegroundColor $Red
$failedBuilds += "$goos/$goarch"
}
else {
Write-Host "Successfully built $binaryName" -ForegroundColor $Green
}
# Clear env to avoid affecting subsequent shells (optional)
Remove-Item Env:GOOS -ErrorAction SilentlyContinue
Remove-Item Env:GOARCH -ErrorAction SilentlyContinue
Remove-Item Env:CGO_ENABLED -ErrorAction SilentlyContinue
}
}
if ($failedBuilds.Count -gt 0) {
Write-Host "`nThe following builds failed:" -ForegroundColor $Red
foreach ($b in $failedBuilds) { Write-Host "- $b" -ForegroundColor $Red }
}
else {
Write-Host "`nAll builds completed successfully." -ForegroundColor $Green
}
Write-Host "`nBinaries are in the ./build directory." -ForegroundColor $White

View File

@@ -2,6 +2,7 @@ package dnsresolver
import ( import (
"context" "context"
"crypto/tls"
"fmt" "fmt"
"log" "log"
"net" "net"
@@ -10,6 +11,8 @@ import (
"strings" "strings"
"sync" "sync"
"time" "time"
"github.com/komari-monitor/komari-agent/cmd/flags"
) )
var ( var (
@@ -110,15 +113,13 @@ func GetCustomResolver() *net.Resolver {
} }
// GetHTTPClient 返回一个使用自定义DNS解析器的HTTP客户端 // GetHTTPClient 返回一个使用自定义DNS解析器的HTTP客户端
func GetHTTPClient(timeout time.Duration) *http.Client { // buildTransport 构建带有自定义解析/拨号策略的 HTTP 传输层,可注入 TLS 配置
func buildTransport(timeout time.Duration, tlsConfig *tls.Config) *http.Transport {
if timeout <= 0 { if timeout <= 0 {
timeout = 30 * time.Second timeout = 30 * time.Second
} }
customResolver := GetCustomResolver() customResolver := GetCustomResolver()
return &http.Transport{
return &http.Client{
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment, Proxy: http.ProxyFromEnvironment,
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
host, port, err := net.SplitHostPort(addr) host, port, err := net.SplitHostPort(addr)
@@ -145,7 +146,7 @@ func GetHTTPClient(timeout time.Duration) *http.Client {
}) })
for _, ip := range ips { for _, ip := range ips {
dialer := &net.Dialer{ dialer := &net.Dialer{
Timeout: 30 * time.Second, Timeout: timeout,
KeepAlive: 30 * time.Second, KeepAlive: 30 * time.Second,
DualStack: true, DualStack: true,
} }
@@ -160,7 +161,16 @@ func GetHTTPClient(timeout time.Duration) *http.Client {
IdleConnTimeout: 90 * time.Second, IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second, TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second, ExpectContinueTimeout: 1 * time.Second,
}, TLSClientConfig: tlsConfig,
ForceAttemptHTTP2: true,
}
}
func GetHTTPClient(timeout time.Duration) *http.Client {
return &http.Client{
Transport: buildTransport(timeout, &tls.Config{
InsecureSkipVerify: flags.IgnoreUnsafeCert,
}),
Timeout: timeout, Timeout: timeout,
} }
} }

View File

@@ -87,7 +87,6 @@ func tryUploadData(data map[string]interface{}) error {
req.Header.Set("CF-Access-Client-Secret", flags.CFAccessClientSecret) req.Header.Set("CF-Access-Client-Secret", flags.CFAccessClientSecret)
} }
// 使用dnsresolver获取自定义HTTP客户端
client := dnsresolver.GetHTTPClient(30 * time.Second) client := dnsresolver.GetHTTPClient(30 * time.Second)
resp, err := client.Do(req) resp, err := client.Do(req)

View File

@@ -92,23 +92,9 @@ func EstablishWebSocketConnection() {
} }
func connectWebSocket(websocketEndpoint string) (*ws.SafeConn, error) { func connectWebSocket(websocketEndpoint string) (*ws.SafeConn, error) {
// 使用自定义解析和连接策略IPv4 优先,较长超时) dialer := newWSDialer()
dialer := &websocket.Dialer{
HandshakeTimeout: 15 * time.Second,
NetDialContext: dnsresolver.GetDialContext(15 * time.Second),
}
// 可选:忽略 TLS 证书(当用户显式设置) headers := newWSHeaders()
if flags.IgnoreUnsafeCert {
dialer.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
}
// 创建请求头并添加Cloudflare Access头部
headers := http.Header{}
if flags.CFAccessClientID != "" && flags.CFAccessClientSecret != "" {
headers.Set("CF-Access-Client-Id", flags.CFAccessClientID)
headers.Set("CF-Access-Client-Secret", flags.CFAccessClientSecret)
}
conn, resp, err := dialer.Dial(websocketEndpoint, headers) conn, resp, err := dialer.Dial(websocketEndpoint, headers)
if err != nil { if err != nil {
@@ -170,20 +156,9 @@ func establishTerminalConnection(token, id, endpoint string) {
endpoint = "ws" + strings.TrimPrefix(endpoint, "http") endpoint = "ws" + strings.TrimPrefix(endpoint, "http")
// 使用与主 WS 相同的拨号策略 // 使用与主 WS 相同的拨号策略
dialer := &websocket.Dialer{ dialer := newWSDialer()
HandshakeTimeout: 15 * time.Second,
NetDialContext: dnsresolver.GetDialContext(15 * time.Second),
}
if flags.IgnoreUnsafeCert {
dialer.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
}
// 创建请求头并添加Cloudflare Access头部 headers := newWSHeaders()
headers := http.Header{}
if flags.CFAccessClientID != "" && flags.CFAccessClientSecret != "" {
headers.Set("CF-Access-Client-Id", flags.CFAccessClientID)
headers.Set("CF-Access-Client-Secret", flags.CFAccessClientSecret)
}
conn, _, err := dialer.Dial(endpoint, headers) conn, _, err := dialer.Dial(endpoint, headers)
if err != nil { if err != nil {
@@ -197,3 +172,25 @@ func establishTerminalConnection(token, id, endpoint string) {
conn.Close() conn.Close()
} }
} }
// newWSDialer 构造统一的 WebSocket 拨号器自定义解析、IPv4/IPv6 动态排序、可选 TLS 忽略)
func newWSDialer() *websocket.Dialer {
d := &websocket.Dialer{
HandshakeTimeout: 15 * time.Second,
NetDialContext: dnsresolver.GetDialContext(15 * time.Second),
}
if flags.IgnoreUnsafeCert {
d.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
}
return d
}
// newWSHeaders 统一构造 WS 请求头(含 Cloudflare Access 头)
func newWSHeaders() http.Header {
headers := http.Header{}
if flags.CFAccessClientID != "" && flags.CFAccessClientSecret != "" {
headers.Set("CF-Access-Client-Id", flags.CFAccessClientID)
headers.Set("CF-Access-Client-Secret", flags.CFAccessClientSecret)
}
return headers
}

View File

@@ -47,9 +47,7 @@ func CheckAndUpdate() error {
return fmt.Errorf("failed to parse current version: %v", err) return fmt.Errorf("failed to parse current version: %v", err)
} }
// 使用dnsresolver创建自定义HTTP客户端并设置为全局默认客户端 http.DefaultClient = dnsresolver.GetHTTPClient(60 * time.Second)
// 这会影响所有HTTP请求包括selfupdate库中的请求
http.DefaultClient = dnsresolver.GetHTTPClient(60 * time.Second) // Create selfupdate configuration
config := selfupdate.Config{} config := selfupdate.Config{}
updater, err := selfupdate.NewUpdater(config) updater, err := selfupdate.NewUpdater(config)
if err != nil { if err != nil {