fix(openai): 修复 OAuth 透传流式断开与压缩头问题

- 透传流式在客户端断开后继续 drain 上游并解析 usage,避免计费信息丢失

- 阻断透传 accept-encoding,避免压缩响应影响 SSE/usage 解析

- 阻断 proxy-authorization,避免透传代理鉴权信息

- 补充回归测试:请求头阻断与断流后 usage 采集
This commit is contained in:
yangjianbo
2026-02-11 22:17:38 +08:00
parent f1e884ce2b
commit a88bb8684f
2 changed files with 75 additions and 5 deletions

View File

@@ -1183,7 +1183,10 @@ func isOpenAIPassthroughBlockedRequestHeader(lowerKey string) bool {
case "connection", "transfer-encoding", "keep-alive", "proxy-connection", "upgrade", "te", "trailer":
return true
// 入站鉴权与潜在泄露
case "authorization", "x-api-key", "x-goog-api-key", "cookie":
case "authorization", "x-api-key", "x-goog-api-key", "cookie", "proxy-authorization":
return true
// 由 Go http client 自动协商压缩;透传模式需避免上游返回压缩体影响 SSE/usage 解析
case "accept-encoding":
return true
// 由 HTTP 库管理
case "host", "content-length":
@@ -1224,6 +1227,7 @@ func (s *OpenAIGatewayService) handleStreamingResponsePassthrough(
usage := &OpenAIUsage{}
var firstTokenMs *int
clientDisconnected := false
scanner := bufio.NewScanner(resp.Body)
maxLineSize := defaultMaxLineSize
@@ -1245,13 +1249,20 @@ func (s *OpenAIGatewayService) handleStreamingResponsePassthrough(
s.parseSSEUsage(data, usage)
}
if _, err := fmt.Fprintln(w, line); err != nil {
// 客户端断开时停止写入
break
if !clientDisconnected {
if _, err := fmt.Fprintln(w, line); err != nil {
clientDisconnected = true
log.Printf("[OpenAI passthrough] Client disconnected during streaming, continue draining upstream for usage: account=%d", account.ID)
} else {
flusher.Flush()
}
}
flusher.Flush()
}
if err := scanner.Err(); err != nil {
if clientDisconnected {
log.Printf("[OpenAI passthrough] Upstream read error after client disconnect: account=%d err=%v", account.ID, err)
return &openaiStreamingResultPassthrough{usage: usage, firstTokenMs: firstTokenMs}, nil
}
if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
return &openaiStreamingResultPassthrough{usage: usage, firstTokenMs: firstTokenMs}, nil
}