Sub2API v1.0 - AI API 网关(二开初始版本,基于上游 Wei-Shaw/sub2api)
Release / update-version (push) Has been cancelled
Release / build-frontend (push) Has been cancelled
Release / release (push) Has been cancelled
Release / sync-version-file (push) Has been cancelled
CI / shell (push) Canceled after 0s
CI / test (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CI / golangci-lint (push) Canceled after 0s
Security Scan / backend-security (push) Canceled after 0s
Security Scan / frontend-security (push) Canceled after 0s
Release / update-version (push) Has been cancelled
Release / build-frontend (push) Has been cancelled
Release / release (push) Has been cancelled
Release / sync-version-file (push) Has been cancelled
CI / shell (push) Canceled after 0s
CI / test (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CI / golangci-lint (push) Canceled after 0s
Security Scan / backend-security (push) Canceled after 0s
Security Scan / frontend-security (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/Wei-Shaw/sub2api/internal/pkg/proxyurl"
|
||||
"github.com/Wei-Shaw/sub2api/internal/pkg/servertiming"
|
||||
|
||||
"github.com/imroc/req/v3"
|
||||
)
|
||||
|
||||
// reqClientOptions 定义 req 客户端的构建参数
|
||||
type reqClientOptions struct {
|
||||
ProxyURL string // 代理 URL(支持 http/https/socks5)
|
||||
Timeout time.Duration // 请求超时时间
|
||||
Impersonate bool // 是否模拟 Chrome 浏览器指纹
|
||||
ForceHTTP2 bool // 是否强制使用 HTTP/2
|
||||
}
|
||||
|
||||
// sharedReqClients 存储按配置参数缓存的 req 客户端实例
|
||||
//
|
||||
// 性能优化说明:
|
||||
// 原实现在每次 OAuth 刷新时都创建新的 req.Client:
|
||||
// 1. claude_oauth_service.go: 每次刷新创建新客户端
|
||||
// 2. openai_oauth_service.go: 每次刷新创建新客户端
|
||||
// 3. gemini_oauth_client.go: 每次刷新创建新客户端
|
||||
//
|
||||
// 新实现使用 sync.Map 缓存客户端:
|
||||
// 1. 相同配置(代理+超时+模拟设置)复用同一客户端
|
||||
// 2. 复用底层连接池,减少 TLS 握手开销
|
||||
// 3. LoadOrStore 保证并发安全,避免重复创建
|
||||
var sharedReqClients sync.Map
|
||||
|
||||
// getSharedReqClient 获取共享的 req 客户端实例
|
||||
// 性能优化:相同配置复用同一客户端,避免重复创建
|
||||
func getSharedReqClient(opts reqClientOptions) (*req.Client, error) {
|
||||
key := buildReqClientKey(opts)
|
||||
if cached, ok := sharedReqClients.Load(key); ok {
|
||||
if c, ok := cached.(*req.Client); ok {
|
||||
return c, nil
|
||||
}
|
||||
}
|
||||
|
||||
client := req.C().SetTimeout(opts.Timeout)
|
||||
if opts.ForceHTTP2 {
|
||||
client = client.EnableForceHTTP2()
|
||||
}
|
||||
if opts.Impersonate {
|
||||
client = client.ImpersonateChrome()
|
||||
}
|
||||
trimmed, _, err := proxyurl.Parse(opts.ProxyURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if trimmed != "" {
|
||||
client.SetProxyURL(trimmed)
|
||||
}
|
||||
client = instrumentReqClient(client)
|
||||
|
||||
actual, _ := sharedReqClients.LoadOrStore(key, client)
|
||||
if c, ok := actual.(*req.Client); ok {
|
||||
return c, nil
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func instrumentReqClient(client *req.Client) *req.Client {
|
||||
if client == nil {
|
||||
return nil
|
||||
}
|
||||
client.GetTransport().WrapRoundTripFunc(func(rt http.RoundTripper) req.HttpRoundTripFunc {
|
||||
timed := servertiming.WrapRoundTripper(rt)
|
||||
return timed.RoundTrip
|
||||
})
|
||||
return client
|
||||
}
|
||||
|
||||
func buildReqClientKey(opts reqClientOptions) string {
|
||||
return fmt.Sprintf("%s|%s|%t|%t",
|
||||
strings.TrimSpace(opts.ProxyURL),
|
||||
opts.Timeout.String(),
|
||||
opts.Impersonate,
|
||||
opts.ForceHTTP2,
|
||||
)
|
||||
}
|
||||
|
||||
// CreatePrivacyReqClient creates an HTTP client for OpenAI privacy settings API
|
||||
// This is exported for use by OpenAIPrivacyService
|
||||
// Uses Chrome TLS fingerprint impersonation to bypass Cloudflare checks
|
||||
func CreatePrivacyReqClient(proxyURL string) (*req.Client, error) {
|
||||
return getSharedReqClient(reqClientOptions{
|
||||
ProxyURL: proxyURL,
|
||||
Timeout: 30 * time.Second,
|
||||
Impersonate: true, // Enable Chrome TLS fingerprint impersonation
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user