From 039784af73c7183682a30f5bd3e852afea94a667 Mon Sep 17 00:00:00 2001 From: Jason Date: Thu, 14 May 2026 08:21:53 +0800 Subject: [PATCH] refactor(proxy): share auth_header_value helper across provider adapters claude.rs and gemini.rs each defined an identical `hv` closure that wrapped `HeaderValue::from_str` into a ProxyError::AuthError result, and codex.rs spelled the same conversion out inline. /simplify reviewers flagged this as drift-prone copy-paste. Move the conversion into a single `pub fn auth_header_value` in providers/adapter.rs and have the three adapters import it locally. Same error wording everywhere, one place to update if HeaderValue semantics ever change. --- src-tauri/src/proxy/providers/adapter.rs | 12 ++++++++++++ src-tauri/src/proxy/providers/claude.rs | 5 +---- src-tauri/src/proxy/providers/codex.rs | 5 ++--- src-tauri/src/proxy/providers/gemini.rs | 5 +---- 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src-tauri/src/proxy/providers/adapter.rs b/src-tauri/src/proxy/providers/adapter.rs index 41cde627f..c5998fbd7 100644 --- a/src-tauri/src/proxy/providers/adapter.rs +++ b/src-tauri/src/proxy/providers/adapter.rs @@ -55,3 +55,15 @@ pub trait ProviderAdapter: Send + Sync { Ok(body) } } + +/// Build an HTTP `HeaderValue` from a credential / token string. +/// +/// Returns `ProxyError::AuthError` when the string contains characters that +/// cannot live in an HTTP header value (control bytes, CR/LF, non-ASCII). +/// Adapters call this for every header value derived from user-pasted +/// material so a malformed key surfaces as a 401 instead of panicking +/// the worker via `HeaderValue::from_str(...).unwrap()`. +pub fn auth_header_value(s: &str) -> Result { + http::HeaderValue::from_str(s) + .map_err(|e| ProxyError::AuthError(format!("invalid auth header value: {e}"))) +} diff --git a/src-tauri/src/proxy/providers/claude.rs b/src-tauri/src/proxy/providers/claude.rs index c55a1064d..45b3d64ae 100644 --- a/src-tauri/src/proxy/providers/claude.rs +++ b/src-tauri/src/proxy/providers/claude.rs @@ -593,13 +593,10 @@ impl ProviderAdapter for ClaudeAdapter { &self, auth: &AuthInfo, ) -> Result, ProxyError> { + use super::adapter::auth_header_value as hv; use http::{HeaderName, HeaderValue}; // 注意:anthropic-version 由 forwarder.rs 统一处理(透传客户端值或设置默认值) let bearer = format!("Bearer {}", auth.api_key); - let hv = |s: &str| -> Result { - HeaderValue::from_str(s) - .map_err(|e| ProxyError::AuthError(format!("invalid auth header value: {e}"))) - }; Ok(match auth.strategy { AuthStrategy::Anthropic => { vec![(HeaderName::from_static("x-api-key"), hv(&auth.api_key)?)] diff --git a/src-tauri/src/proxy/providers/codex.rs b/src-tauri/src/proxy/providers/codex.rs index 405e20f3e..bc5f36b4c 100644 --- a/src-tauri/src/proxy/providers/codex.rs +++ b/src-tauri/src/proxy/providers/codex.rs @@ -177,12 +177,11 @@ impl ProviderAdapter for CodexAdapter { &self, auth: &AuthInfo, ) -> Result, ProxyError> { + use super::adapter::auth_header_value; let bearer = format!("Bearer {}", auth.api_key); - let value = http::HeaderValue::from_str(&bearer) - .map_err(|e| ProxyError::AuthError(format!("invalid auth header value: {e}")))?; Ok(vec![( http::HeaderName::from_static("authorization"), - value, + auth_header_value(&bearer)?, )]) } } diff --git a/src-tauri/src/proxy/providers/gemini.rs b/src-tauri/src/proxy/providers/gemini.rs index 675c3b648..2d20aede5 100644 --- a/src-tauri/src/proxy/providers/gemini.rs +++ b/src-tauri/src/proxy/providers/gemini.rs @@ -232,11 +232,8 @@ impl ProviderAdapter for GeminiAdapter { &self, auth: &AuthInfo, ) -> Result, ProxyError> { + use super::adapter::auth_header_value as hv; use http::{HeaderName, HeaderValue}; - let hv = |s: &str| -> Result { - HeaderValue::from_str(s) - .map_err(|e| ProxyError::AuthError(format!("invalid auth header value: {e}"))) - }; Ok(match auth.strategy { AuthStrategy::GoogleOAuth => { let token = auth.access_token.as_ref().unwrap_or(&auth.api_key);