sub2api/backend/internal/repository/http_upstream_antigravity.go
win 35c6c2b097 chore: merge upstream v0.1.126 — Airwallex, OpenAI fixes, Antigravity UA config
吸收上游 26 个新 commit:
- feat: Airwallex 支付 + 多币种支持 (b23055af)
- feat: Antigravity user agent 版本可配置 (a07a0dac)
- fix(mimic): 同步 messages 里 tool_use 名称 (f97b8534)
- fix: cache_control 改写默认关闭 (9377c967)
- fix(openai): 多 tool_use 上下文延续 (87d73236)
- fix(openai): 未定价模型零成本记录 (6d69ae87)
- fix(openai): WS replay tool 输出延续 (16a31557)
- fix(openai): 429 plan type 同步 (c3a14717)
- fix(gemini): Vertex token 走 account proxy (2a17c0b2)
- fix(ccswitch): codex 模型 import deeplink (65493df9)
- fix: 订单详情/支付页 NaN 修复 (ba1c6fa5, 6884b03e)
- 系统设置标签导航优化 (18cc4691)

本地解决:
- config.go CSP: 合并 Firebase Auth (Windsurf) + Airwallex 域名
- KeysView.vue: 删除死代码(已被 buildCcSwitchImportDeeplink 取代)
- ccswitchImport.ts: 补充 windsurf 平台 case
- 修复 NewOpsHandler/RegisterGatewayRoutes/SelectAccountWithScheduler 测试签名

保留:
- Antigravity newapi 兼容 (ForwardUpstream /v1/messages 透传)
- Antigravity 核心(gateway_service, oauth, client, credits_overages 等)
- Windsurf 全套
- Claude 网关 + TLS 指纹路由
- 其他本地 feat: P2C 调度 / viewer / context 压缩 / RPM / fallback / health
2026-05-12 13:48:40 +08:00

92 lines
3.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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())
}