mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-03-30 00:33:35 +00:00
fix: 解压 streaming 响应的 gzip 压缩,修复 SSE 数据损坏
Anthropic API (经 Cloudflare) 返回 Content-Encoding: gzip 的压缩响应。
非 streaming 处理器已有 zlib.gunzipSync 解压,但 streaming 处理器
直接对 gzip 二进制数据调用 chunk.toString(),导致非法 UTF-8 字节
被替换为 U+FFFD,SSE 数据损坏。
修复:在 streaming 的 res.on('data') 之前检测 content-encoding,
如为 gzip/deflate 则通过 zlib 管道解压后再处理。
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2386,7 +2386,28 @@ class ClaudeRelayService {
|
||||
const requestedModel = body?.model || 'unknown'
|
||||
const { isRealClaudeCodeRequest } = requestOptions
|
||||
|
||||
res.on('data', (chunk) => {
|
||||
// 🔧 处理上游 gzip/deflate 压缩:Anthropic (经 Cloudflare) 可能返回压缩响应
|
||||
const upstreamEncoding = res.headers['content-encoding']
|
||||
let dataSource = res
|
||||
if (upstreamEncoding === 'gzip') {
|
||||
dataSource = res.pipe(zlib.createGunzip())
|
||||
dataSource.on('error', (err) => {
|
||||
logger.error('❌ Gzip decompression error in stream:', err.message)
|
||||
if (isStreamWritable(responseStream)) {
|
||||
responseStream.end()
|
||||
}
|
||||
})
|
||||
} else if (upstreamEncoding === 'deflate') {
|
||||
dataSource = res.pipe(zlib.createInflate())
|
||||
dataSource.on('error', (err) => {
|
||||
logger.error('❌ Deflate decompression error in stream:', err.message)
|
||||
if (isStreamWritable(responseStream)) {
|
||||
responseStream.end()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
dataSource.on('data', (chunk) => {
|
||||
try {
|
||||
const chunkStr = chunk.toString()
|
||||
|
||||
@@ -2531,7 +2552,7 @@ class ClaudeRelayService {
|
||||
}
|
||||
})
|
||||
|
||||
res.on('end', async () => {
|
||||
dataSource.on('end', async () => {
|
||||
try {
|
||||
// 处理缓冲区中剩余的数据
|
||||
if (buffer.trim() && isStreamWritable(responseStream)) {
|
||||
|
||||
Reference in New Issue
Block a user