From a17ac50118d2234aa5240d5a5057af04e9127a03 Mon Sep 17 00:00:00 2001 From: 7976723 <7976723@users.noreply.github.com> Date: Wed, 11 Mar 2026 11:25:16 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=20Chat=20Completions?= =?UTF-8?q?=20=E7=BC=96=E8=AF=91=E9=94=99=E8=AF=AF=E5=92=8C=E8=BF=90?= =?UTF-8?q?=E8=A1=8C=E6=97=B6=20panic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 修复 WriteFilteredHeaders API 不兼容(2处): 将 s.cfg.Security.ResponseHeaders 改为 s.responseHeaderFilter, 因为 main 分支已将函数签名改为接受 *responseheaders.CompiledHeaderFilter 2. 修复 writer 生命周期导致的 nil pointer panic: ChatCompletions handler 替换了 c.Writer 但未恢复,导致 OpsErrorLogger 中间件的 defer 释放 opsCaptureWriter 后, Logger 中间件调用 c.Writer.Status() 触发空指针解引用。 通过保存并恢复 originalWriter 修复。 3. 为 chatCompletionsResponseWriter 添加防御性 Status() 和 Written() 方法,包含 nil 安全检查 4. 恢复 gateway.go 中被误删的 net/http import --- .../internal/handler/openai_chat_completions.go | 16 ++++++++++++++++ backend/internal/server/routes/gateway.go | 2 ++ .../service/openai_chat_completions_forward.go | 8 +++++--- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/backend/internal/handler/openai_chat_completions.go b/backend/internal/handler/openai_chat_completions.go index 30e2c6b9..cd30a63f 100644 --- a/backend/internal/handler/openai_chat_completions.go +++ b/backend/internal/handler/openai_chat_completions.go @@ -62,6 +62,7 @@ func (h *OpenAIGatewayHandler) ChatCompletions(c *gin.Context) { stream, _ := converted["stream"].(bool) model, _ := converted["model"].(string) + originalWriter := c.Writer writer := newChatCompletionsResponseWriter(c.Writer, stream, includeUsage, model) c.Writer = writer c.Request.Body = io.NopCloser(bytes.NewReader(convertedBody)) @@ -69,6 +70,7 @@ func (h *OpenAIGatewayHandler) ChatCompletions(c *gin.Context) { h.Responses(c) writer.Finalize() + c.Writer = originalWriter } type chatCompletionsResponseWriter struct { @@ -167,6 +169,20 @@ func (w *chatCompletionsResponseWriter) SetPassthrough() { w.passthrough = true } +func (w *chatCompletionsResponseWriter) Status() int { + if w.ResponseWriter == nil { + return 0 + } + return w.ResponseWriter.Status() +} + +func (w *chatCompletionsResponseWriter) Written() bool { + if w.ResponseWriter == nil { + return false + } + return w.ResponseWriter.Written() +} + func (w *chatCompletionsResponseWriter) flushStreamBuffer() { for { buf := w.streamBuf.Bytes() diff --git a/backend/internal/server/routes/gateway.go b/backend/internal/server/routes/gateway.go index 382e78dd..ea40f2f1 100644 --- a/backend/internal/server/routes/gateway.go +++ b/backend/internal/server/routes/gateway.go @@ -1,6 +1,8 @@ package routes import ( + "net/http" + "github.com/Wei-Shaw/sub2api/internal/config" "github.com/Wei-Shaw/sub2api/internal/handler" "github.com/Wei-Shaw/sub2api/internal/server/middleware" diff --git a/backend/internal/service/openai_chat_completions_forward.go b/backend/internal/service/openai_chat_completions_forward.go index 703f3af1..0eefdb35 100644 --- a/backend/internal/service/openai_chat_completions_forward.go +++ b/backend/internal/service/openai_chat_completions_forward.go @@ -209,8 +209,8 @@ func (s *OpenAIGatewayService) buildChatCompletionsRequest(ctx context.Context, } func (s *OpenAIGatewayService) handleChatCompletionsStreamingResponse(ctx context.Context, resp *http.Response, c *gin.Context, account *Account, startTime time.Time, originalModel, mappedModel string) (*chatStreamingResult, error) { - if s.cfg != nil { - responseheaders.WriteFilteredHeaders(c.Writer.Header(), resp.Header, s.cfg.Security.ResponseHeaders) + if s.responseHeaderFilter != nil { + responseheaders.WriteFilteredHeaders(c.Writer.Header(), resp.Header, s.responseHeaderFilter) } c.Header("Content-Type", "text/event-stream") @@ -409,7 +409,9 @@ func (s *OpenAIGatewayService) handleChatCompletionsNonStreamingResponse(resp *h } body = s.correctToolCallsInResponseBody(body) - responseheaders.WriteFilteredHeaders(c.Writer.Header(), resp.Header, s.cfg.Security.ResponseHeaders) + if s.responseHeaderFilter != nil { + responseheaders.WriteFilteredHeaders(c.Writer.Header(), resp.Header, s.responseHeaderFilter) + } contentType := "application/json" if s.cfg != nil && !s.cfg.Security.ResponseHeaders.Enabled {