mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-09-24 16:05:44 +08:00
fix: force Content-Type to application/json on non-streaming responses
When the gateway forces streaming to the upstream, the upstream returns Content-Type: text/event-stream. In the non-streaming branches, the body is correctly aggregated into JSON, but WriteFilteredHeaders has already passed the upstream's text/event-stream header through. Since Gin's c.JSON/c.Data only set Content-Type via writeContentType when it is not already present, the SSE header is never overwritten -- so the body is JSON while the header says SSE. Downstream middlewares that decide "is this streaming?" by Content-Type (e.g. new-api) then mis-handle the response as SSE, breaking non-streaming JSON clients. Fix: explicitly Set Content-Type to application/json right after WriteFilteredHeaders in the three non-streaming JSON write paths.
This commit is contained in:
@@ -318,6 +318,12 @@ func (s *GatewayService) handleCCBufferedFromAnthropic(
|
||||
if s.responseHeaderFilter != nil {
|
||||
responseheaders.WriteFilteredHeaders(c.Writer.Header(), resp.Header, s.responseHeaderFilter)
|
||||
}
|
||||
// 非流式响应必须是 application/json。上游被强制流式后会返回
|
||||
// Content-Type: text/event-stream,经 WriteFilteredHeaders 透传后会污染
|
||||
// 响应头;而 c.Data/c.JSON 走 Gin 的 writeContentType(仅当头不存在时才设置),
|
||||
// 无法覆盖已存在的 SSE 头。这里显式 Set 强制改回 JSON,避免下游中间层
|
||||
// (如 new-api)按 Content-Type 误判为流式。
|
||||
c.Writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
// Marshal then bytes-replace so tool name mapping is reversed at byte level
|
||||
// (parity with Parrot non-stream flow that marshals → restore → emit).
|
||||
if respBytes, err := json.Marshal(ccResp); err == nil {
|
||||
|
||||
@@ -337,6 +337,12 @@ func (s *GatewayService) handleResponsesBufferedStreamingResponse(
|
||||
if s.responseHeaderFilter != nil {
|
||||
responseheaders.WriteFilteredHeaders(c.Writer.Header(), resp.Header, s.responseHeaderFilter)
|
||||
}
|
||||
// 非流式响应必须是 application/json。上游被强制流式后会返回
|
||||
// Content-Type: text/event-stream,经 WriteFilteredHeaders 透传后会污染
|
||||
// 响应头;而 c.Data/c.JSON 走 Gin 的 writeContentType(仅当头不存在时才设置),
|
||||
// 无法覆盖已存在的 SSE 头。这里显式 Set 强制改回 JSON,避免下游中间层
|
||||
// (如 new-api)按 Content-Type 误判为流式。
|
||||
c.Writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
if respBytes, err := json.Marshal(responsesResp); err == nil {
|
||||
respBytes = reverseToolNamesIfPresent(c, respBytes)
|
||||
c.Data(http.StatusOK, "application/json; charset=utf-8", respBytes)
|
||||
|
||||
@@ -407,6 +407,11 @@ func (s *OpenAIGatewayService) handleChatBufferedStreamingResponse(
|
||||
if s.responseHeaderFilter != nil {
|
||||
responseheaders.WriteFilteredHeaders(c.Writer.Header(), resp.Header, s.responseHeaderFilter)
|
||||
}
|
||||
// 非流式响应必须为标准 JSON。上游被强制流式,其响应头 Content-Type 为
|
||||
// text/event-stream,会经 WriteFilteredHeaders 透传进来;而 c.JSON 走 Gin 的
|
||||
// writeContentType 仅在头不存在时才设置,无法覆盖。这里显式 Set 强制改回 JSON,
|
||||
// 否则下游"看头判流式"的中间层(如 new-api)会把本应聚合的 JSON 当成 SSE 处理。
|
||||
c.Writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
c.JSON(http.StatusOK, chatResp)
|
||||
|
||||
return &OpenAIForwardResult{
|
||||
|
||||
Reference in New Issue
Block a user