feat: Sora 请求优先走 curl_cffi sidecar(Chrome 指纹绕过 Cloudflare)
Some checks failed
CI / test (push) Failing after 12s
CI / golangci-lint (push) Failing after 6m30s
Security Scan / backend-security (push) Failing after 3s
Security Scan / frontend-security (push) Failing after 3s

This commit is contained in:
win 2026-03-22 03:38:24 +08:00
parent 0bfd6edde6
commit fba8cc82ae

View File

@ -478,7 +478,7 @@ func (c *SoraSDKClient) GetWatermarkFreeURLCustom(ctx context.Context, account *
}
var resp *http.Response
if c.httpUpstream != nil {
resp, err = c.httpUpstream.Do(req, proxyURL, accountID, accountConcurrency)
resp, err = c.doSoraHTTP(req, proxyURL, accountID, accountConcurrency)
} else {
resp, err = http.DefaultClient.Do(req)
}
@ -901,7 +901,7 @@ func (c *SoraSDKClient) exchangeSessionToken(ctx context.Context, account *Accou
var resp *http.Response
if c.httpUpstream != nil {
resp, err = c.httpUpstream.Do(req, proxyURL, accountID, accountConcurrency)
resp, err = c.doSoraHTTP(req, proxyURL, accountID, accountConcurrency)
} else {
resp, err = http.DefaultClient.Do(req)
}
@ -1024,3 +1024,70 @@ func (c *SoraSDKClient) debugLogf(format string, args ...any) {
log.Printf("[SoraSDK] "+format, args...)
}
}
// doSoraHTTP 执行 Sora HTTP 请求,优先走 curl_cffi sidecarChrome TLS 指纹绕过 Cloudflare
// 如果 sidecar 未启用或不可用,回退到 httpUpstream.Do() / http.DefaultClient
func (c *SoraSDKClient) doSoraHTTP(req *http.Request, proxyURL string, accountID int64, accountConcurrency int) (*http.Response, error) {
// 检查 sidecar 是否启用
if c.cfg != nil && c.cfg.Sora.Client.CurlCFFISidecar.Enabled && c.cfg.Sora.Client.CurlCFFISidecar.BaseURL != "" {
resp, err := c.doViaSidecar(req)
if err == nil {
return resp, nil
}
// sidecar 失败,回退到直连
logger.LegacyPrintf("service.sora", "Warning: sidecar failed, falling back to direct: %v", err)
}
// 回退路径
if c.httpUpstream != nil {
return c.httpUpstream.Do(req, proxyURL, accountID, accountConcurrency)
}
return http.DefaultClient.Do(req)
}
// doViaSidecar 通过 curl_cffi sidecar 发送请求Chrome TLS 指纹)
func (c *SoraSDKClient) doViaSidecar(originalReq *http.Request) (*http.Response, error) {
sidecarURL := strings.TrimRight(c.cfg.Sora.Client.CurlCFFISidecar.BaseURL, "/") + "/proxy"
// 读取原始请求体
var bodyStr string
if originalReq.Body != nil {
bodyBytes, err := io.ReadAll(originalReq.Body)
if err != nil {
return nil, fmt.Errorf("read request body: %w", err)
}
bodyStr = string(bodyBytes)
// 恢复 body 以备回退
originalReq.Body = io.NopCloser(bytes.NewReader(bodyBytes))
}
// 构建 sidecar 请求
headers := make(map[string]string)
for k, vs := range originalReq.Header {
if len(vs) > 0 {
headers[k] = vs[0]
}
}
payload := map[string]any{
"url": originalReq.URL.String(),
"method": originalReq.Method,
"headers": headers,
"body": bodyStr,
"session_key": fmt.Sprintf("account_%d", 0), // 简单的 session key
}
payloadBytes, err := json.Marshal(payload)
if err != nil {
return nil, fmt.Errorf("marshal sidecar payload: %w", err)
}
ctx := originalReq.Context()
sidecarReq, err := http.NewRequestWithContext(ctx, http.MethodPost, sidecarURL, bytes.NewReader(payloadBytes))
if err != nil {
return nil, fmt.Errorf("create sidecar request: %w", err)
}
sidecarReq.Header.Set("Content-Type", "application/json")
return http.DefaultClient.Do(sidecarReq)
}