- 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管理 = 落地机网络,账号绑代理后指纹伪装仍然生效
101 lines
3.2 KiB
Go
101 lines
3.2 KiB
Go
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)
|
||
}
|
||
|
||
// 重写请求 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)
|
||
}
|