fix: panic error
This commit is contained in:
@@ -583,31 +583,98 @@ func (ps *ProxyServer) handleStreamResponse(c *gin.Context, body io.ReadCloser)
|
|||||||
// 实现零缓存、实时转发
|
// 实现零缓存、实时转发
|
||||||
copyDone := make(chan bool)
|
copyDone := make(chan bool)
|
||||||
|
|
||||||
|
// 检查客户端连接状态
|
||||||
|
ctx := c.Request.Context()
|
||||||
|
|
||||||
// 在一个独立的goroutine中定期flush,确保数据被立即发送
|
// 在一个独立的goroutine中定期flush,确保数据被立即发送
|
||||||
go func() {
|
go func() {
|
||||||
|
defer func() {
|
||||||
|
// 防止panic
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
logrus.Errorf("Flush goroutine panic: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
ticker := time.NewTicker(50 * time.Millisecond)
|
ticker := time.NewTicker(50 * time.Millisecond)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-copyDone:
|
case <-copyDone:
|
||||||
// io.Copy完成后,执行最后一次flush并退出
|
// io.Copy完成后,执行最后一次flush并退出
|
||||||
flusher.Flush()
|
ps.safeFlush(flusher)
|
||||||
|
return
|
||||||
|
case <-ctx.Done():
|
||||||
|
// 客户端断开连接,停止flush
|
||||||
return
|
return
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
flusher.Flush()
|
ps.safeFlush(flusher)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// 使用io.Copy进行高效的数据复制
|
// 使用io.Copy进行高效的数据复制
|
||||||
_, err := io.Copy(c.Writer, body)
|
_, err := io.Copy(c.Writer, body)
|
||||||
close(copyDone) // 通知flush的goroutine可以停止了
|
|
||||||
|
// 安全地关闭channel
|
||||||
|
select {
|
||||||
|
case <-copyDone:
|
||||||
|
// channel已经关闭
|
||||||
|
default:
|
||||||
|
close(copyDone) // 通知flush的goroutine可以停止了
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil && err != io.EOF {
|
if err != nil && err != io.EOF {
|
||||||
logrus.Errorf("复制流式响应失败: %v", err)
|
// 检查是否是连接断开导致的错误
|
||||||
|
if ps.isConnectionError(err) {
|
||||||
|
logrus.Debugf("客户端连接断开: %v", err)
|
||||||
|
} else {
|
||||||
|
logrus.Errorf("复制流式响应失败: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// safeFlush 安全地执行flush操作
|
||||||
|
func (ps *ProxyServer) safeFlush(flusher http.Flusher) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
// 忽略flush时的panic,通常是因为连接已断开
|
||||||
|
logrus.Debugf("Flush panic (connection likely closed): %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if flusher != nil {
|
||||||
|
flusher.Flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// isConnectionError 检查是否是连接相关的错误
|
||||||
|
func (ps *ProxyServer) isConnectionError(err error) bool {
|
||||||
|
if err == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
errStr := err.Error()
|
||||||
|
// 常见的连接断开错误
|
||||||
|
connectionErrors := []string{
|
||||||
|
"broken pipe",
|
||||||
|
"connection reset by peer",
|
||||||
|
"connection aborted",
|
||||||
|
"client disconnected",
|
||||||
|
"write: broken pipe",
|
||||||
|
"use of closed network connection",
|
||||||
|
"context canceled",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, connErr := range connectionErrors {
|
||||||
|
if strings.Contains(errStr, connErr) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// Close 关闭代理服务器
|
// Close 关闭代理服务器
|
||||||
func (ps *ProxyServer) Close() {
|
func (ps *ProxyServer) Close() {
|
||||||
if ps.keyManager != nil {
|
if ps.keyManager != nil {
|
||||||
|
Reference in New Issue
Block a user