From 14bc8a00e50c7db50b624b14884ed8975b82c4db Mon Sep 17 00:00:00 2001 From: YoVinchen Date: Sun, 14 Dec 2025 16:02:04 +0800 Subject: [PATCH] feat(proxy): add error mapper for HTTP status code mapping - Add error_mapper.rs module to map ProxyError to HTTP status codes - Implement map_proxy_error_to_status() for error classification - Implement get_error_message() for user-friendly error messages - Support all error types: upstream, timeout, connection, provider failures - Include comprehensive unit tests for all mappings --- src-tauri/src/proxy/error_mapper.rs | 110 ++++++++++++++++++++++++++++ src-tauri/src/proxy/mod.rs | 1 + 2 files changed, 111 insertions(+) create mode 100644 src-tauri/src/proxy/error_mapper.rs diff --git a/src-tauri/src/proxy/error_mapper.rs b/src-tauri/src/proxy/error_mapper.rs new file mode 100644 index 000000000..b8b44e9a3 --- /dev/null +++ b/src-tauri/src/proxy/error_mapper.rs @@ -0,0 +1,110 @@ +//! 错误类型到 HTTP 状态码的映射 +//! +//! 将 ProxyError 映射到合适的 HTTP 状态码,用于日志记录 + +use super::ProxyError; + +/// 将 ProxyError 映射到 HTTP 状态码 +/// +/// 映射规则: +/// - 上游错误:直接使用上游返回的状态码 +/// - 超时:504 Gateway Timeout +/// - 连接失败:502 Bad Gateway +/// - 无可用 Provider:503 Service Unavailable +/// - 重试耗尽:503 Service Unavailable +/// - 其他错误:500 Internal Server Error +pub fn map_proxy_error_to_status(error: &ProxyError) -> u16 { + match error { + // 上游错误:使用实际状态码 + ProxyError::UpstreamError { status, .. } => *status, + + // 超时错误:504 Gateway Timeout + ProxyError::Timeout(_) => 504, + + // 转发失败/连接失败:502 Bad Gateway + ProxyError::ForwardFailed(_) => 502, + + // 无可用 Provider:503 Service Unavailable + ProxyError::NoAvailableProvider => 503, + + // 重试耗尽:503 Service Unavailable + ProxyError::MaxRetriesExceeded => 503, + + // Provider 不健康:503 Service Unavailable + ProxyError::ProviderUnhealthy(_) => 503, + + // 数据库错误:500 Internal Server Error + ProxyError::DatabaseError(_) => 500, + + // 转换错误:500 Internal Server Error + ProxyError::TransformError(_) => 500, + + // 其他未知错误:500 Internal Server Error + _ => 500, + } +} + +/// 将 ProxyError 转换为用户友好的错误消息 +pub fn get_error_message(error: &ProxyError) -> String { + match error { + ProxyError::UpstreamError { status, body } => { + if let Some(body) = body { + format!("上游错误 ({status}): {body}") + } else { + format!("上游错误 ({status})") + } + } + ProxyError::Timeout(msg) => format!("请求超时: {msg}"), + ProxyError::ForwardFailed(msg) => format!("转发失败: {msg}"), + ProxyError::NoAvailableProvider => "无可用 Provider".to_string(), + ProxyError::MaxRetriesExceeded => "所有 Provider 都失败,重试耗尽".to_string(), + ProxyError::ProviderUnhealthy(msg) => format!("Provider 不健康: {msg}"), + ProxyError::DatabaseError(msg) => format!("数据库错误: {msg}"), + ProxyError::TransformError(msg) => format!("请求/响应转换错误: {msg}"), + _ => error.to_string(), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_map_upstream_error() { + let error = ProxyError::UpstreamError { + status: 401, + body: Some("Unauthorized".to_string()), + }; + assert_eq!(map_proxy_error_to_status(&error), 401); + } + + #[test] + fn test_map_timeout_error() { + let error = ProxyError::Timeout("Request timeout".to_string()); + assert_eq!(map_proxy_error_to_status(&error), 504); + } + + #[test] + fn test_map_connection_error() { + let error = ProxyError::ForwardFailed("Connection refused".to_string()); + assert_eq!(map_proxy_error_to_status(&error), 502); + } + + #[test] + fn test_map_no_provider_error() { + let error = ProxyError::NoAvailableProvider; + assert_eq!(map_proxy_error_to_status(&error), 503); + } + + #[test] + fn test_get_error_message() { + let error = ProxyError::UpstreamError { + status: 500, + body: Some("Internal Server Error".to_string()), + }; + let msg = get_error_message(&error); + assert!(msg.contains("上游错误")); + assert!(msg.contains("500")); + assert!(msg.contains("Internal Server Error")); + } +} diff --git a/src-tauri/src/proxy/mod.rs b/src-tauri/src/proxy/mod.rs index c23d277c1..9a9170868 100644 --- a/src-tauri/src/proxy/mod.rs +++ b/src-tauri/src/proxy/mod.rs @@ -4,6 +4,7 @@ pub mod circuit_breaker; pub mod error; +pub mod error_mapper; mod forwarder; mod handlers; mod health;