From bb446718450e53fd18065d484abfac0352c19a9e Mon Sep 17 00:00:00 2001 From: pzy <2360718056@qq.com> Date: Thu, 2 Apr 2026 19:12:55 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=8F=8D=E4=BB=A3?= =?UTF-8?q?=E6=A3=80=E6=B5=8B=E5=AF=B9=E6=8A=97=E7=9A=84=203=20=E4=B8=AA?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - computeFingerprint 使用 rune 索引替代字节索引,修复多字节字符指纹不匹配 - utls Chrome TLS 指纹仅对 Anthropic 官方域名生效,自定义 base_url 走标准 transport - IPv6 地址使用 net.JoinHostPort 正确拼接端口 --- internal/runtime/executor/claude_executor.go | 13 +++++----- .../runtime/executor/helps/utls_client.go | 24 ++++++++++++------- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/internal/runtime/executor/claude_executor.go b/internal/runtime/executor/claude_executor.go index 3d9e5e0d..b1d4c3ac 100644 --- a/internal/runtime/executor/claude_executor.go +++ b/internal/runtime/executor/claude_executor.go @@ -1200,15 +1200,16 @@ const fingerprintSalt = "59cf53e54c78" // Algorithm: SHA256(salt + messageText[4] + messageText[7] + messageText[20] + version)[:3] func computeFingerprint(messageText, version string) string { indices := [3]int{4, 7, 20} - var chars [3]byte - for i, idx := range indices { - if idx < len(messageText) { - chars[i] = messageText[idx] + runes := []rune(messageText) + var sb strings.Builder + for _, idx := range indices { + if idx < len(runes) { + sb.WriteRune(runes[idx]) } else { - chars[i] = '0' + sb.WriteRune('0') } } - input := fingerprintSalt + string(chars[:]) + version + input := fingerprintSalt + sb.String() + version h := sha256.Sum256([]byte(input)) return hex.EncodeToString(h[:])[:3] } diff --git a/internal/runtime/executor/helps/utls_client.go b/internal/runtime/executor/helps/utls_client.go index d41a90f3..39512a58 100644 --- a/internal/runtime/executor/helps/utls_client.go +++ b/internal/runtime/executor/helps/utls_client.go @@ -103,13 +103,12 @@ func (t *utlsRoundTripper) createConnection(host, addr string) (*http2.ClientCon } func (t *utlsRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { - host := req.URL.Host - addr := host - if !strings.Contains(addr, ":") { - addr += ":443" - } - hostname := req.URL.Hostname() + port := req.URL.Port() + if port == "" { + port = "443" + } + addr := net.JoinHostPort(hostname, port) h2Conn, err := t.getOrCreateConnection(hostname, addr) if err != nil { @@ -129,8 +128,13 @@ func (t *utlsRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) return resp, nil } -// fallbackRoundTripper tries utls first; if the target is plain HTTP or a -// non-HTTPS scheme it falls back to a standard transport. +// anthropicHosts contains the hosts that should use utls Chrome TLS fingerprint. +var anthropicHosts = map[string]struct{}{ + "api.anthropic.com": {}, +} + +// fallbackRoundTripper uses utls for Anthropic HTTPS hosts and falls back to +// standard transport for all other requests (non-HTTPS or non-Anthropic hosts). type fallbackRoundTripper struct { utls *utlsRoundTripper fallback http.RoundTripper @@ -138,7 +142,9 @@ type fallbackRoundTripper struct { func (f *fallbackRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { if req.URL.Scheme == "https" { - return f.utls.RoundTrip(req) + if _, ok := anthropicHosts[strings.ToLower(req.URL.Hostname())]; ok { + return f.utls.RoundTrip(req) + } } return f.fallback.RoundTrip(req) }