mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-08-03 02:51:17 +08:00
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:
@@ -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!(
|
||||||
|
|||||||
@@ -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]
|
||||||
|
|||||||
Reference in New Issue
Block a user