- Do() 路由从 doViaNodeTLSProxy(转发到 localhost:3456 Node.js 进程) 改为 doWithTLSFingerprint(直接使用 Go utls dialer),解决 h2 connect timeout 问题(Node.js proxy 的 H2 路径不支持 per-account 代理隧道) - 新增 internal/pkg/telemetry 包,从 proxy.js 移植全部遥测逻辑: Anthropic event_logging/batch + Datadog log intake + 虚拟主机身份 + 会话状态管理 + process metrics 模拟 - 保留 proxy.js 中的 H1 降级修复作为备用
86 lines
2.9 KiB
Go
86 lines
2.9 KiB
Go
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 指纹路由是否启用
|
||
// 复用 NodeTLSProxy.Enabled 配置项,保持配置兼容
|
||
func (s *httpUpstreamService) isTLSFingerprintRoutingEnabled() bool {
|
||
if s.cfg == nil {
|
||
return false
|
||
}
|
||
return s.cfg.Gateway.NodeTLSProxy.Enabled
|
||
}
|
||
|
||
// shouldRouteWithTLSFingerprint 判断请求是否应该使用 TLS 指纹
|
||
// 仅拦截目标主机在 proxy_hosts 白名单中的 HTTPS 请求,
|
||
// 白名单为空时默认只代理 api.anthropic.com。
|
||
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 {
|
||
return reqHost == "api.anthropic.com"
|
||
}
|
||
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())
|
||
}
|