fix: 双模型审查 Critical 修复
1. Sora session_key 按 accountID 隔离(消除跨账号指纹关联) 2. 有 per-account 代理的 Sora 账号跳过 sidecar(保持代理 IP) 3. 请求体用 base64 编码传输(防止二进制数据损坏) 4. Node.js 代理 Body 用 GetBody 安全复制(修复重试时 Body 枯竭)
This commit is contained in:
parent
2b0192e704
commit
a16db8e367
@ -286,7 +286,12 @@ func (s *httpUpstreamService) doViaNodeTLSProxy(req *http.Request, proxyURL stri
|
||||
|
||||
// 克隆请求,避免修改原始 req(重试时需要原始 URL)
|
||||
proxyReq := req.Clone(req.Context())
|
||||
proxyReq.Body = req.Body // Clone 不复制 Body
|
||||
// 安全复制 Body:优先用 GetBody 工厂方法
|
||||
if req.GetBody != nil {
|
||||
proxyReq.Body, _ = req.GetBody()
|
||||
} else {
|
||||
proxyReq.Body = req.Body
|
||||
}
|
||||
|
||||
// 保存原始目标主机,通过自定义头传给 Node.js 代理
|
||||
originalHost := req.URL.Host
|
||||
|
||||
@ -3,6 +3,7 @@ package service
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@ -1026,11 +1027,19 @@ func (c *SoraSDKClient) debugLogf(format string, args ...any) {
|
||||
}
|
||||
|
||||
// doSoraHTTP 执行 Sora HTTP 请求,优先走 curl_cffi sidecar(Chrome TLS 指纹绕过 Cloudflare)
|
||||
// 如果 sidecar 未启用或不可用,回退到 httpUpstream.Do() / http.DefaultClient
|
||||
// 如果 sidecar 未启用、账号有独立代理、或 sidecar 不可用,回退到 httpUpstream.Do()
|
||||
func (c *SoraSDKClient) doSoraHTTP(req *http.Request, proxyURL string, accountID int64, accountConcurrency int) (*http.Response, error) {
|
||||
// 如果账号配置了独立代理,跳过 sidecar(sidecar 不支持 per-account 代理)
|
||||
if proxyURL != "" {
|
||||
if c.httpUpstream != nil {
|
||||
return c.httpUpstream.Do(req, proxyURL, accountID, accountConcurrency)
|
||||
}
|
||||
return http.DefaultClient.Do(req)
|
||||
}
|
||||
|
||||
// 检查 sidecar 是否启用
|
||||
if c.cfg != nil && c.cfg.Sora.Client.CurlCFFISidecar.Enabled && c.cfg.Sora.Client.CurlCFFISidecar.BaseURL != "" {
|
||||
resp, err := c.doViaSidecar(req)
|
||||
resp, err := c.doViaSidecar(req, accountID)
|
||||
if err == nil {
|
||||
return resp, nil
|
||||
}
|
||||
@ -1046,17 +1055,17 @@ func (c *SoraSDKClient) doSoraHTTP(req *http.Request, proxyURL string, accountID
|
||||
}
|
||||
|
||||
// doViaSidecar 通过 curl_cffi sidecar 发送请求(Chrome TLS 指纹)
|
||||
func (c *SoraSDKClient) doViaSidecar(originalReq *http.Request) (*http.Response, error) {
|
||||
func (c *SoraSDKClient) doViaSidecar(originalReq *http.Request, accountID int64) (*http.Response, error) {
|
||||
sidecarURL := strings.TrimRight(c.cfg.Sora.Client.CurlCFFISidecar.BaseURL, "/") + "/proxy"
|
||||
|
||||
// 读取原始请求体
|
||||
var bodyStr string
|
||||
// 读取原始请求体,用 base64 编码(安全传输二进制数据)
|
||||
var bodyB64 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)
|
||||
bodyB64 = base64.StdEncoding.EncodeToString(bodyBytes)
|
||||
// 恢复 body 以备回退
|
||||
originalReq.Body = io.NopCloser(bytes.NewReader(bodyBytes))
|
||||
}
|
||||
@ -1073,8 +1082,9 @@ func (c *SoraSDKClient) doViaSidecar(originalReq *http.Request) (*http.Response,
|
||||
"url": originalReq.URL.String(),
|
||||
"method": originalReq.Method,
|
||||
"headers": headers,
|
||||
"body": bodyStr,
|
||||
"session_key": fmt.Sprintf("account_%d", 0), // 简单的 session key
|
||||
"body": bodyB64,
|
||||
"is_base64": true,
|
||||
"session_key": fmt.Sprintf("account_%d", accountID), // 每账号独立会话
|
||||
}
|
||||
|
||||
payloadBytes, err := json.Marshal(payload)
|
||||
|
||||
@ -4,7 +4,7 @@ sub2api 通过 HTTP 调用此服务转发 Sora 请求
|
||||
"""
|
||||
from flask import Flask, request, Response
|
||||
from curl_cffi import requests as cffi_requests
|
||||
import json, os, time, threading
|
||||
import json, os, time, threading, base64
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
@ -54,18 +54,30 @@ def proxy():
|
||||
method = data.get("method", "GET").upper()
|
||||
headers = data.get("headers", {})
|
||||
body = data.get("body")
|
||||
is_base64 = data.get("is_base64", False)
|
||||
session_key = data.get("session_key", "default")
|
||||
|
||||
if not url:
|
||||
return json.dumps({"error": "url required"}), 400
|
||||
|
||||
# 解码 base64 编码的请求体(安全传输二进制数据)
|
||||
req_data = None
|
||||
if body:
|
||||
if is_base64:
|
||||
try:
|
||||
req_data = base64.b64decode(body)
|
||||
except Exception:
|
||||
req_data = body.encode("utf-8") if isinstance(body, str) else body
|
||||
else:
|
||||
req_data = body.encode("utf-8") if isinstance(body, str) else body
|
||||
|
||||
try:
|
||||
sess = get_session(session_key)
|
||||
resp = sess.request(
|
||||
method=method,
|
||||
url=url,
|
||||
headers=headers,
|
||||
data=body.encode("utf-8") if isinstance(body, str) else body,
|
||||
data=req_data,
|
||||
timeout=TIMEOUT,
|
||||
allow_redirects=True,
|
||||
)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user