mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-22 16:43:35 +00:00
fix: 改进socket hang up和网络错误处理机制
- 修复socket hang up错误导致返回空字符串的问题 - 改进非流式请求的错误处理,根据错误类型返回适当的HTTP状态码 - 优化流式请求的错误处理,返回SSE格式的错误事件 - 增强错误日志记录,包含详细的网络错误信息 - 确保在任何情况下都返回有效的JSON响应格式 修复内容: - ECONNRESET错误返回502状态码和明确的错误信息 - ENOTFOUND错误返回502状态码和DNS解析失败信息 - ECONNREFUSED错误返回502状态码和连接被拒绝信息 - ETIMEDOUT错误返回504状态码和超时信息 - 流式请求错误时返回符合SSE规范的错误事件 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -145,13 +145,41 @@ router.post('/v1/messages', authenticateApiKey, async (req, res) => {
|
||||
logger.api(`✅ Request completed in ${duration}ms for key: ${req.apiKey.name}`);
|
||||
|
||||
} catch (error) {
|
||||
logger.error('❌ Claude relay error:', error);
|
||||
|
||||
if (!res.headersSent) {
|
||||
res.status(500).json({
|
||||
error: 'Relay service error',
|
||||
message: error.message
|
||||
logger.error('❌ Claude relay error:', error.message, {
|
||||
code: error.code,
|
||||
stack: error.stack
|
||||
});
|
||||
|
||||
// 确保在任何情况下都能返回有效的JSON响应
|
||||
if (!res.headersSent) {
|
||||
// 根据错误类型设置适当的状态码
|
||||
let statusCode = 500;
|
||||
let errorType = 'Relay service error';
|
||||
|
||||
if (error.message.includes('Connection reset') || error.message.includes('socket hang up')) {
|
||||
statusCode = 502;
|
||||
errorType = 'Upstream connection error';
|
||||
} else if (error.message.includes('Connection refused')) {
|
||||
statusCode = 502;
|
||||
errorType = 'Upstream service unavailable';
|
||||
} else if (error.message.includes('timeout')) {
|
||||
statusCode = 504;
|
||||
errorType = 'Upstream timeout';
|
||||
} else if (error.message.includes('resolve') || error.message.includes('ENOTFOUND')) {
|
||||
statusCode = 502;
|
||||
errorType = 'Upstream hostname resolution failed';
|
||||
}
|
||||
|
||||
res.status(statusCode).json({
|
||||
error: errorType,
|
||||
message: error.message || 'An unexpected error occurred',
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
} else {
|
||||
// 如果响应头已经发送,尝试结束响应
|
||||
if (!res.destroyed && !res.finished) {
|
||||
res.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -281,8 +281,27 @@ class ClaudeRelayService {
|
||||
}
|
||||
|
||||
req.on('error', (error) => {
|
||||
logger.error('❌ Claude API request error:', error);
|
||||
reject(error);
|
||||
logger.error('❌ Claude API request error:', error.message, {
|
||||
code: error.code,
|
||||
errno: error.errno,
|
||||
syscall: error.syscall,
|
||||
address: error.address,
|
||||
port: error.port
|
||||
});
|
||||
|
||||
// 根据错误类型提供更具体的错误信息
|
||||
let errorMessage = 'Upstream request failed';
|
||||
if (error.code === 'ECONNRESET') {
|
||||
errorMessage = 'Connection reset by Claude API server';
|
||||
} else if (error.code === 'ENOTFOUND') {
|
||||
errorMessage = 'Unable to resolve Claude API hostname';
|
||||
} else if (error.code === 'ECONNREFUSED') {
|
||||
errorMessage = 'Connection refused by Claude API server';
|
||||
} else if (error.code === 'ETIMEDOUT') {
|
||||
errorMessage = 'Connection timed out to Claude API server';
|
||||
}
|
||||
|
||||
reject(new Error(errorMessage));
|
||||
});
|
||||
|
||||
req.on('timeout', () => {
|
||||
@@ -453,12 +472,46 @@ class ClaudeRelayService {
|
||||
});
|
||||
|
||||
req.on('error', (error) => {
|
||||
logger.error('❌ Claude stream request error:', error);
|
||||
if (!responseStream.headersSent) {
|
||||
responseStream.writeHead(500, { 'Content-Type': 'application/json' });
|
||||
logger.error('❌ Claude stream request error:', error.message, {
|
||||
code: error.code,
|
||||
errno: error.errno,
|
||||
syscall: error.syscall
|
||||
});
|
||||
|
||||
// 根据错误类型提供更具体的错误信息
|
||||
let errorMessage = 'Upstream request failed';
|
||||
let statusCode = 500;
|
||||
if (error.code === 'ECONNRESET') {
|
||||
errorMessage = 'Connection reset by Claude API server';
|
||||
statusCode = 502;
|
||||
} else if (error.code === 'ENOTFOUND') {
|
||||
errorMessage = 'Unable to resolve Claude API hostname';
|
||||
statusCode = 502;
|
||||
} else if (error.code === 'ECONNREFUSED') {
|
||||
errorMessage = 'Connection refused by Claude API server';
|
||||
statusCode = 502;
|
||||
} else if (error.code === 'ETIMEDOUT') {
|
||||
errorMessage = 'Connection timed out to Claude API server';
|
||||
statusCode = 504;
|
||||
}
|
||||
|
||||
if (!responseStream.headersSent) {
|
||||
responseStream.writeHead(statusCode, {
|
||||
'Content-Type': 'text/event-stream',
|
||||
'Cache-Control': 'no-cache',
|
||||
'Connection': 'keep-alive'
|
||||
});
|
||||
}
|
||||
|
||||
if (!responseStream.destroyed) {
|
||||
responseStream.end(JSON.stringify({ error: 'Upstream request failed' }));
|
||||
// 发送 SSE 错误事件
|
||||
responseStream.write(`event: error\n`);
|
||||
responseStream.write(`data: ${JSON.stringify({
|
||||
error: errorMessage,
|
||||
code: error.code,
|
||||
timestamp: new Date().toISOString()
|
||||
})}\n\n`);
|
||||
responseStream.end();
|
||||
}
|
||||
reject(error);
|
||||
});
|
||||
@@ -467,10 +520,21 @@ class ClaudeRelayService {
|
||||
req.destroy();
|
||||
logger.error('❌ Claude stream request timeout');
|
||||
if (!responseStream.headersSent) {
|
||||
responseStream.writeHead(504, { 'Content-Type': 'application/json' });
|
||||
responseStream.writeHead(504, {
|
||||
'Content-Type': 'text/event-stream',
|
||||
'Cache-Control': 'no-cache',
|
||||
'Connection': 'keep-alive'
|
||||
});
|
||||
}
|
||||
if (!responseStream.destroyed) {
|
||||
responseStream.end(JSON.stringify({ error: 'Request timeout' }));
|
||||
// 发送 SSE 错误事件
|
||||
responseStream.write(`event: error\n`);
|
||||
responseStream.write(`data: ${JSON.stringify({
|
||||
error: 'Request timeout',
|
||||
code: 'TIMEOUT',
|
||||
timestamp: new Date().toISOString()
|
||||
})}\n\n`);
|
||||
responseStream.end();
|
||||
}
|
||||
reject(new Error('Request timeout'));
|
||||
});
|
||||
@@ -538,12 +602,46 @@ class ClaudeRelayService {
|
||||
});
|
||||
|
||||
req.on('error', (error) => {
|
||||
logger.error('❌ Claude stream request error:', error);
|
||||
if (!responseStream.headersSent) {
|
||||
responseStream.writeHead(500, { 'Content-Type': 'application/json' });
|
||||
logger.error('❌ Claude stream request error:', error.message, {
|
||||
code: error.code,
|
||||
errno: error.errno,
|
||||
syscall: error.syscall
|
||||
});
|
||||
|
||||
// 根据错误类型提供更具体的错误信息
|
||||
let errorMessage = 'Upstream request failed';
|
||||
let statusCode = 500;
|
||||
if (error.code === 'ECONNRESET') {
|
||||
errorMessage = 'Connection reset by Claude API server';
|
||||
statusCode = 502;
|
||||
} else if (error.code === 'ENOTFOUND') {
|
||||
errorMessage = 'Unable to resolve Claude API hostname';
|
||||
statusCode = 502;
|
||||
} else if (error.code === 'ECONNREFUSED') {
|
||||
errorMessage = 'Connection refused by Claude API server';
|
||||
statusCode = 502;
|
||||
} else if (error.code === 'ETIMEDOUT') {
|
||||
errorMessage = 'Connection timed out to Claude API server';
|
||||
statusCode = 504;
|
||||
}
|
||||
|
||||
if (!responseStream.headersSent) {
|
||||
responseStream.writeHead(statusCode, {
|
||||
'Content-Type': 'text/event-stream',
|
||||
'Cache-Control': 'no-cache',
|
||||
'Connection': 'keep-alive'
|
||||
});
|
||||
}
|
||||
|
||||
if (!responseStream.destroyed) {
|
||||
responseStream.end(JSON.stringify({ error: 'Upstream request failed' }));
|
||||
// 发送 SSE 错误事件
|
||||
responseStream.write(`event: error\n`);
|
||||
responseStream.write(`data: ${JSON.stringify({
|
||||
error: errorMessage,
|
||||
code: error.code,
|
||||
timestamp: new Date().toISOString()
|
||||
})}\n\n`);
|
||||
responseStream.end();
|
||||
}
|
||||
reject(error);
|
||||
});
|
||||
@@ -552,10 +650,21 @@ class ClaudeRelayService {
|
||||
req.destroy();
|
||||
logger.error('❌ Claude stream request timeout');
|
||||
if (!responseStream.headersSent) {
|
||||
responseStream.writeHead(504, { 'Content-Type': 'application/json' });
|
||||
responseStream.writeHead(504, {
|
||||
'Content-Type': 'text/event-stream',
|
||||
'Cache-Control': 'no-cache',
|
||||
'Connection': 'keep-alive'
|
||||
});
|
||||
}
|
||||
if (!responseStream.destroyed) {
|
||||
responseStream.end(JSON.stringify({ error: 'Request timeout' }));
|
||||
// 发送 SSE 错误事件
|
||||
responseStream.write(`event: error\n`);
|
||||
responseStream.write(`data: ${JSON.stringify({
|
||||
error: 'Request timeout',
|
||||
code: 'TIMEOUT',
|
||||
timestamp: new Date().toISOString()
|
||||
})}\n\n`);
|
||||
responseStream.end();
|
||||
}
|
||||
reject(new Error('Request timeout'));
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user