fix(proxy): restrict Anthropic headers to Claude adapter only

- Move anthropic-beta and anthropic-version header handling inside
  Claude-specific condition to avoid sending unnecessary headers
  to Codex and Gemini APIs
- Update test cases to reflect ?beta=true query parameter behavior
- Add edge case tests for non-messages endpoints and existing queries
This commit is contained in:
YoVinchen
2026-01-08 10:19:02 +08:00
committed by Jason
parent 70e7feb73b
commit 2fadd8bcdf
2 changed files with 53 additions and 31 deletions
+33 -29
View File
@@ -542,29 +542,31 @@ impl RequestForwarder {
} }
} }
// 处理 anthropic-beta Header // 处理 anthropic-beta Header(仅 Claude
// 关键:确保包含 claude-code-20250219 标记,这是上游服务验证请求来源的依据 // 关键:确保包含 claude-code-20250219 标记,这是上游服务验证请求来源的依据
// 如果客户端发送的 beta 标记中没有包含 claude-code-20250219,需要补充 // 如果客户端发送的 beta 标记中没有包含 claude-code-20250219,需要补充
const CLAUDE_CODE_BETA: &str = "claude-code-20250219"; if adapter.name() == "Claude" {
let beta_value = if let Some(beta) = headers.get("anthropic-beta") { const CLAUDE_CODE_BETA: &str = "claude-code-20250219";
if let Ok(beta_str) = beta.to_str() { let beta_value = if let Some(beta) = headers.get("anthropic-beta") {
// 检查是否已包含 claude-code-20250219 if let Ok(beta_str) = beta.to_str() {
if beta_str.contains(CLAUDE_CODE_BETA) { // 检查是否已包含 claude-code-20250219
beta_str.to_string() if beta_str.contains(CLAUDE_CODE_BETA) {
beta_str.to_string()
} else {
// 补充 claude-code-20250219
format!("{CLAUDE_CODE_BETA},{beta_str}")
}
} else { } else {
// 补充 claude-code-20250219 CLAUDE_CODE_BETA.to_string()
format!("{CLAUDE_CODE_BETA},{beta_str}")
} }
} else { } else {
// 如果客户端没有发送,使用默认值
CLAUDE_CODE_BETA.to_string() CLAUDE_CODE_BETA.to_string()
} };
} else { request = request.header("anthropic-beta", &beta_value);
// 如果客户端没有发送,使用默认值 passed_headers.push(("anthropic-beta".to_string(), beta_value.clone()));
CLAUDE_CODE_BETA.to_string() log::info!("[{}] 设置 anthropic-beta: {}", adapter.name(), beta_value);
}; }
request = request.header("anthropic-beta", &beta_value);
passed_headers.push(("anthropic-beta".to_string(), beta_value.clone()));
log::info!("[{}] 设置 anthropic-beta: {}", adapter.name(), beta_value);
// 客户端 IP 透传(默认开启) // 客户端 IP 透传(默认开启)
if let Some(xff) = headers.get("x-forwarded-for") { if let Some(xff) = headers.get("x-forwarded-for") {
@@ -613,19 +615,21 @@ impl RequestForwarder {
); );
} }
// anthropic-version 统一处理:优先使用客户端的版本号,否则使用默认值 // anthropic-version 统一处理(仅 Claude:优先使用客户端的版本号,否则使用默认值
// 注意:只设置一次,避免重复 // 注意:只设置一次,避免重复
let version_str = headers if adapter.name() == "Claude" {
.get("anthropic-version") let version_str = headers
.and_then(|v| v.to_str().ok()) .get("anthropic-version")
.unwrap_or("2023-06-01"); .and_then(|v| v.to_str().ok())
request = request.header("anthropic-version", version_str); .unwrap_or("2023-06-01");
passed_headers.push(("anthropic-version".to_string(), version_str.to_string())); request = request.header("anthropic-version", version_str);
log::info!( passed_headers.push(("anthropic-version".to_string(), version_str.to_string()));
"[{}] 设置 anthropic-version: {}", log::info!(
adapter.name(), "[{}] 设置 anthropic-version: {}",
version_str adapter.name(),
); version_str
);
}
// ========== 最终发送的 Headers 日志 ========== // ========== 最终发送的 Headers 日志 ==========
log::info!( log::info!(
+20 -2
View File
@@ -425,15 +425,33 @@ mod tests {
#[test] #[test]
fn test_build_url_anthropic() { fn test_build_url_anthropic() {
let adapter = ClaudeAdapter::new(); let adapter = ClaudeAdapter::new();
// /v1/messages 端点会自动添加 ?beta=true 参数
let url = adapter.build_url("https://api.anthropic.com", "/v1/messages"); let url = adapter.build_url("https://api.anthropic.com", "/v1/messages");
assert_eq!(url, "https://api.anthropic.com/v1/messages"); assert_eq!(url, "https://api.anthropic.com/v1/messages?beta=true");
} }
#[test] #[test]
fn test_build_url_openrouter() { fn test_build_url_openrouter() {
let adapter = ClaudeAdapter::new(); let adapter = ClaudeAdapter::new();
// /v1/messages 端点会自动添加 ?beta=true 参数
let url = adapter.build_url("https://openrouter.ai/api", "/v1/messages"); let url = adapter.build_url("https://openrouter.ai/api", "/v1/messages");
assert_eq!(url, "https://openrouter.ai/api/v1/messages"); assert_eq!(url, "https://openrouter.ai/api/v1/messages?beta=true");
}
#[test]
fn test_build_url_no_beta_for_other_endpoints() {
let adapter = ClaudeAdapter::new();
// 非 /v1/messages 端点不添加 ?beta=true
let url = adapter.build_url("https://api.anthropic.com", "/v1/complete");
assert_eq!(url, "https://api.anthropic.com/v1/complete");
}
#[test]
fn test_build_url_preserve_existing_query() {
let adapter = ClaudeAdapter::new();
// 已有查询参数时不重复添加
let url = adapter.build_url("https://api.anthropic.com", "/v1/messages?foo=bar");
assert_eq!(url, "https://api.anthropic.com/v1/messages?foo=bar");
} }
#[test] #[test]