package repository // ============================================================== // antigravity — Go 原生 TLS 指纹扩展 // // 此文件包含 Antigravity fork 新增的 TLS 指纹代理功能, // 与 upstream 代码完全隔离,便于 upstream 更新时的合并维护。 // // 上游文件 http_upstream.go 中的钩子调用点: // Do() — 匹配主机时路由到 doWithTLSFingerprint // DoWithTLS() — profile==nil 时回退到 Do(),触发同样的路由 // // 替代原先的 Node.js TLS 代理(node-tls-proxy), // 直接使用 Go utls 库模拟 Claude CLI 的 TLS 指纹。 // ============================================================== import ( "log/slog" "net/http" "github.com/Wei-Shaw/sub2api/internal/pkg/tlsfingerprint" "github.com/Wei-Shaw/sub2api/internal/util/logredact" ) // isTLSFingerprintRoutingEnabled 检查 TLS 指纹路由是否启用 // 使用 TLSFingerprint.Enabled 配置项(而不是旧的 NodeTLSProxy.Enabled) func (s *httpUpstreamService) isTLSFingerprintRoutingEnabled() bool { if s.cfg == nil { return false } return s.cfg.Gateway.TLSFingerprint.Enabled } // shouldRouteWithTLSFingerprint 判断请求是否应该使用 TLS 指纹 // 拦截目标主机在 proxy_hosts 白名单中的 HTTPS 请求 // 白名单为空时默认代理 api.anthropic.com 和 Antigravity API 主机 func (s *httpUpstreamService) shouldRouteWithTLSFingerprint(req *http.Request) bool { if req == nil || req.URL == nil || req.URL.Scheme != "https" { return false } reqHost := req.URL.Hostname() if reqHost == "" { return false } hosts := s.cfg.Gateway.NodeTLSProxy.ProxyHosts if len(hosts) == 0 { // 默认白名单:api.anthropic.com 和 Antigravity API 主机 defaultHosts := map[string]bool{ "api.anthropic.com": true, "cloudcode-pa.googleapis.com": true, "daily-cloudcode-pa.googleapis.com": true, } return defaultHosts[reqHost] } for _, h := range hosts { if reqHost == h { return true } } return false } // defaultTLSProfile 返回模拟 Claude CLI (Node.js 24.x) 的默认 TLS 指纹配置 // 所有 slice 字段留空 → dialer.go 自动使用内置的 Node.js 24.x 默认值 // ALPN 仅声明 http/1.1,与真实 CLI 行为一致(undici allowH2=false) func defaultTLSProfile() *tlsfingerprint.Profile { return &tlsfingerprint.Profile{ Name: "claude_cli_builtin", EnableGREASE: true, } } // doWithTLSFingerprint 使用 Go 原生 utls TLS 指纹发送请求 // 直接通过 DoWithTLS 路径,利用已有的 utls dialer 基础设施: // - 直连:Dialer (TCP → utls handshake) // - HTTP 代理:HTTPProxyDialer (CONNECT 隧道 → utls handshake) // - SOCKS5 代理:SOCKS5ProxyDialer (SOCKS5 隧道 → utls handshake) func (s *httpUpstreamService) doWithTLSFingerprint(req *http.Request, proxyURL string, accountID int64, accountConcurrency int) (*http.Response, error) { proxyInfo := "direct" if proxyURL != "" { proxyInfo = logredact.RedactProxyURL(proxyURL) } slog.Debug("tls_fingerprint_routing", "account_id", accountID, "target", req.URL.Host, "proxy", proxyInfo, ) return s.DoWithTLS(req, proxyURL, accountID, accountConcurrency, defaultTLSProfile()) }