92 lines
2.6 KiB
Go
92 lines
2.6 KiB
Go
package embedded
|
|
|
|
import (
|
|
"net/http"
|
|
"strings"
|
|
)
|
|
|
|
// codexClientVersion 必须与固定 CPA 依赖的默认及模型 UA 版本一致,由出站请求测试校验。
|
|
const codexClientVersion = "0.154.0"
|
|
|
|
// codexHeadersRoundTripper 保留 CPA 的 UA,并固定版本及 HTTP 会话头。
|
|
type codexHeadersRoundTripper struct {
|
|
base http.RoundTripper
|
|
source http.Header
|
|
configured []string
|
|
}
|
|
|
|
func (transport codexHeadersRoundTripper) RoundTrip(request *http.Request) (*http.Response, error) {
|
|
request = request.Clone(request.Context())
|
|
for _, name := range transport.configured {
|
|
name = http.CanonicalHeaderKey(name)
|
|
switch name {
|
|
case "Originator":
|
|
value, present := codexHeaderValue(transport.source, name)
|
|
if present {
|
|
request.Header.Set(name, value)
|
|
} else {
|
|
request.Header.Del(name)
|
|
}
|
|
}
|
|
}
|
|
request.Header.Set("Version", codexClientVersion)
|
|
normalizeCodexSessionHeader(request.Header)
|
|
// CPA 的直接图片路径不读取 opts.Headers,补回调用者显式提供的会话。
|
|
if request.Header.Get("Session-Id") == "" {
|
|
if session := transport.source.Get("Session-Id"); session != "" {
|
|
request.Header.Set("Session-Id", session)
|
|
}
|
|
}
|
|
return transport.base.RoundTrip(request)
|
|
}
|
|
|
|
func normalizedCodexHeaders(headers http.Header) http.Header {
|
|
cloned := headers.Clone()
|
|
if cloned == nil {
|
|
cloned = make(http.Header)
|
|
}
|
|
// 仅 Codex 忽略客户端及分组规则提供的版本身份,UA 由 CPA 生成。
|
|
for name := range cloned {
|
|
if strings.EqualFold(name, "User-Agent") || strings.EqualFold(name, "Version") {
|
|
delete(cloned, name)
|
|
}
|
|
}
|
|
cloned.Set("Version", codexClientVersion)
|
|
normalizeCodexSessionHeader(cloned)
|
|
return cloned
|
|
}
|
|
|
|
// normalizeCodexSessionHeader 兼容下划线拼法;同时存在时以连字符字段为准。
|
|
func normalizeCodexSessionHeader(headers http.Header) {
|
|
session, _ := codexHeaderValue(headers, "Session-Id")
|
|
session = strings.TrimSpace(session)
|
|
if session == "" {
|
|
session, _ = codexHeaderValue(headers, "Session_id")
|
|
session = strings.TrimSpace(session)
|
|
}
|
|
for name := range headers {
|
|
if strings.EqualFold(name, "Session-Id") || strings.EqualFold(name, "Session_id") {
|
|
delete(headers, name)
|
|
}
|
|
}
|
|
if session != "" {
|
|
headers.Set("Session-Id", session)
|
|
}
|
|
}
|
|
|
|
func codexHeaderValue(headers http.Header, name string) (string, bool) {
|
|
values, present := headers[http.CanonicalHeaderKey(name)]
|
|
if !present {
|
|
for key, candidate := range headers {
|
|
if strings.EqualFold(key, name) {
|
|
values, present = candidate, true
|
|
break
|
|
}
|
|
}
|
|
}
|
|
if len(values) > 0 {
|
|
return values[0], present
|
|
}
|
|
return "", present
|
|
}
|