sub2api/backend/internal/repository/http_upstream_antigravity.go
win 8c6e578a84
Some checks failed
CI / test (push) Failing after 6s
CI / golangci-lint (push) Failing after 6s
Security Scan / backend-security (push) Failing after 6s
Security Scan / frontend-security (push) Failing after 6s
feat: IP管理代理与 node-tls-proxy 指纹伪装共存
- Do()/DoWithTLS() 移除 proxyURL=="" 条件,绑了代理也走 node-tls-proxy
- doViaNodeTLSProxy 通过 X-Upstream-Proxy header 传递账号代理给 node-tls-proxy
- node-tls-proxy 支持 per-request 动态上游代理,优先 X-Upstream-Proxy,回退全局 UPSTREAM_PROXY
- 效果:IP管理 = 落地机网络,账号绑代理后指纹伪装仍然生效
2026-03-26 14:00:17 +08:00

101 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 — Node.js TLS 代理扩展
//
// 此文件包含 Antigravity fork 新增的 Node.js TLS 代理功能,
// 与 upstream 代码完全隔离,便于 upstream 更新时的合并维护。
//
// 上游文件 http_upstream.go 中的钩子调用点:
// Do() L128-137 — 直接路由到 doViaNodeTLSProxy
// DoWithTLS() L188-193 — 优先走 Node.js 代理
// ==============================================================
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)
}
// 重写请求 URLhttps://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)
}