fix: restore node-tls-proxy routing lost during rebase
- Re-add NodeTLSProxyConfig struct to GatewayConfig (removed by upstream) - Re-create http_upstream_antigravity.go with proxy routing functions - Add proxy intercept hook in Do() for api.anthropic.com requests
This commit is contained in:
parent
d3d885cf75
commit
1eed02c325
@ -381,6 +381,8 @@ type GatewayConfig struct {
|
||||
OpenAIWS GatewayOpenAIWSConfig `mapstructure:"openai_ws"`
|
||||
// AntigravityLSWorker: LS worker 容器控制平面配置
|
||||
AntigravityLSWorker GatewayAntigravityLSWorkerConfig `mapstructure:"antigravity_ls_worker"`
|
||||
// NodeTLSProxy: Node.js TLS 代理配置
|
||||
NodeTLSProxy NodeTLSProxyConfig `mapstructure:"node_tls_proxy"`
|
||||
|
||||
// HTTP 上游连接池配置(性能优化:支持高并发场景调优)
|
||||
// MaxIdleConns: 所有主机的最大空闲连接总数
|
||||
@ -669,6 +671,23 @@ type SoraModelFiltersConfig struct {
|
||||
HidePromptEnhance bool `mapstructure:"hide_prompt_enhance"`
|
||||
}
|
||||
|
||||
// NodeTLSProxyConfig Node.js TLS 代理配置
|
||||
// 通过本地 Node.js 进程转发 HTTPS 请求,利用原生 TLS 栈产生真实 JA3 指纹
|
||||
type NodeTLSProxyConfig struct {
|
||||
// Enabled: 全局开关
|
||||
Enabled bool `mapstructure:"enabled"`
|
||||
// ListenPort: Node.js 代理监听端口
|
||||
ListenPort int `mapstructure:"listen_port"`
|
||||
// ListenHost: Node.js 代理监听地址(Docker 内用服务名,裸机用 127.0.0.1)
|
||||
ListenHost string `mapstructure:"listen_host"`
|
||||
// HealthPath: 健康检查路径
|
||||
HealthPath string `mapstructure:"health_path"`
|
||||
// UpstreamHost: 默认上游主机
|
||||
UpstreamHost string `mapstructure:"upstream_host"`
|
||||
// ProxyHosts: 允许走代理的主机白名单,为空时仅代理 api.anthropic.com
|
||||
ProxyHosts []string `mapstructure:"proxy_hosts"`
|
||||
}
|
||||
|
||||
// TLSFingerprintConfig TLS指纹伪装配置
|
||||
// 用于模拟 Claude CLI (Node.js) 的 TLS 握手特征,避免被识别为非官方客户端
|
||||
type TLSFingerprintConfig struct {
|
||||
|
||||
@ -148,6 +148,16 @@ func NewHTTPUpstream(cfg *config.Config) service.HTTPUpstream {
|
||||
// - 调用方必须关闭 resp.Body,否则会导致 inFlight 计数泄漏
|
||||
// - inFlight > 0 的客户端不会被淘汰,确保活跃请求不被中断
|
||||
func (s *httpUpstreamService) Do(req *http.Request, proxyURL string, accountID int64, accountConcurrency int) (*http.Response, error) {
|
||||
// Node.js TLS 代理:仅 Anthropic API
|
||||
// Antigravity (googleapis) 使用 Go 原生 TLS(更接近真实 BoringCrypto 指纹)
|
||||
// proxyURL 通过 X-Upstream-Proxy header 传递给 node-tls-proxy 动态选择出口
|
||||
if s.isNodeTLSProxyEnabled() && req != nil && req.URL != nil && req.URL.Scheme == "https" {
|
||||
host := req.URL.Hostname()
|
||||
if host == "api.anthropic.com" {
|
||||
return s.doViaNodeTLSProxy(req, proxyURL, accountID, accountConcurrency)
|
||||
}
|
||||
}
|
||||
|
||||
if err := s.validateRequestHost(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
100
backend/internal/repository/http_upstream_antigravity.go
Normal file
100
backend/internal/repository/http_upstream_antigravity.go
Normal file
@ -0,0 +1,100 @@
|
||||
package repository
|
||||
|
||||
// ==============================================================
|
||||
// antigravity — Node.js TLS 代理扩展
|
||||
//
|
||||
// 此文件包含 Antigravity fork 新增的 Node.js TLS 代理功能,
|
||||
// 与 upstream 代码完全隔离,便于 upstream 更新时的合并维护。
|
||||
//
|
||||
// 上游文件 http_upstream.go 中的钩子调用点:
|
||||
// Do() — 直接路由到 doViaNodeTLSProxy
|
||||
// DoWithTLS() — profile==nil 时回退到 Do(),触发同样的路由
|
||||
// ==============================================================
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// isNodeTLSProxyEnabled 检查 Node.js TLS 代理是否启用
|
||||
func (s *httpUpstreamService) isNodeTLSProxyEnabled() bool {
|
||||
if s.cfg == nil {
|
||||
return false
|
||||
}
|
||||
return s.cfg.Gateway.NodeTLSProxy.Enabled
|
||||
}
|
||||
|
||||
// shouldRouteViaNodeProxy 判断请求是否应该走 Node.js TLS 代理
|
||||
// 仅拦截目标主机在 proxy_hosts 白名单中的 HTTPS 请求,
|
||||
// 白名单为空时默认只代理 api.anthropic.com。
|
||||
func (s *httpUpstreamService) shouldRouteViaNodeProxy(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 {
|
||||
// 默认只代理 Anthropic
|
||||
return reqHost == "api.anthropic.com"
|
||||
}
|
||||
for _, h := range hosts {
|
||||
if reqHost == h {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// doViaNodeTLSProxy 通过 Node.js TLS 代理发送请求
|
||||
// 将 HTTPS 请求改为 HTTP 明文发送到本地 Node.js 代理,
|
||||
// 由 Node.js 进程使用原生 TLS 栈完成到上游的 HTTPS 连接。
|
||||
// 原始目标主机通过 X-Forwarded-Host 传递给 Node.js 代理,
|
||||
// 代理据此动态连接到正确的上游主机。
|
||||
func (s *httpUpstreamService) doViaNodeTLSProxy(req *http.Request, proxyURL string, accountID int64, accountConcurrency int) (*http.Response, error) {
|
||||
proxyCfg := s.cfg.Gateway.NodeTLSProxy
|
||||
listenHost := proxyCfg.ListenHost
|
||||
if listenHost == "" {
|
||||
listenHost = "127.0.0.1"
|
||||
}
|
||||
listenPort := proxyCfg.ListenPort
|
||||
if listenPort == 0 {
|
||||
listenPort = 3456
|
||||
}
|
||||
|
||||
// 克隆请求,避免修改原始 req(重试时需要原始 URL)
|
||||
proxyReq := req.Clone(req.Context())
|
||||
// 安全复制 Body:优先用 GetBody 工厂方法
|
||||
if req.GetBody != nil {
|
||||
proxyReq.Body, _ = req.GetBody()
|
||||
} else {
|
||||
proxyReq.Body = req.Body
|
||||
}
|
||||
|
||||
// 保存原始目标主机,通过自定义头传给 Node.js 代理
|
||||
originalHost := req.URL.Host
|
||||
proxyReq.Header.Set("X-Forwarded-Host", originalHost)
|
||||
|
||||
// 如果账号绑定了代理(落地机 GOST),通过 header 传递给 node-tls-proxy
|
||||
// node-tls-proxy 会用此代理作为上游出口,实现动态路由
|
||||
if proxyURL != "" {
|
||||
proxyReq.Header.Set("X-Upstream-Proxy", proxyURL)
|
||||
}
|
||||
|
||||
// 重写请求 URL:https://api.anthropic.com/v1/... → http://127.0.0.1:3456/v1/...
|
||||
proxyReq.URL.Scheme = "http"
|
||||
proxyReq.URL.Host = fmt.Sprintf("%s:%d", listenHost, listenPort)
|
||||
|
||||
slog.Debug("node_tls_proxy_rewrite",
|
||||
"account_id", accountID,
|
||||
"original_host", originalHost,
|
||||
"rewritten_to", proxyReq.URL.Host,
|
||||
)
|
||||
|
||||
// 通过标准 HTTP 客户端发送(不需要 TLS,代理是本地 HTTP)
|
||||
return s.Do(proxyReq, "", accountID, accountConcurrency)
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user