mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-24 21:30:17 +08:00
chore(proxy): remove dead code, redundant tests and debug scaffolding
- Inline should_force_identity_encoding() (was just `needs_transform`) and delete its 5 test cases - Remove ExtensionDebugMarker diagnostic type - Remove unused has_system_proxy_env() and its test - Remove strip_entity_headers test - Simplify hyper path: remove redundant is_socks_proxy ternary - Update hyper_client module doc to reflect CONNECT tunnel support
This commit is contained in:
@@ -794,12 +794,7 @@ impl RequestForwarder {
|
||||
// 过滤私有参数(以 `_` 开头的字段),防止内部信息泄露到上游
|
||||
// 默认使用空白名单,过滤所有 _ 前缀字段
|
||||
let filtered_body = filter_private_params_with_whitelist(request_body, &[]);
|
||||
let force_identity_encoding = should_force_identity_encoding(
|
||||
effective_endpoint,
|
||||
&filtered_body,
|
||||
headers,
|
||||
needs_transform,
|
||||
);
|
||||
let force_identity_encoding = needs_transform;
|
||||
|
||||
// 获取认证头(提前准备,用于内联替换)
|
||||
let auth_headers = if let Some(mut auth) = adapter.extract_auth(provider) {
|
||||
@@ -1124,21 +1119,14 @@ impl RequestForwarder {
|
||||
} else {
|
||||
// HTTP 代理或直连:走 hyper raw write(保持 header 大小写)
|
||||
// 如果有 HTTP 代理,hyper_client 会用 CONNECT 隧道穿过代理
|
||||
let http_proxy = if is_socks_proxy {
|
||||
None
|
||||
} else {
|
||||
upstream_proxy_url.as_deref()
|
||||
};
|
||||
let mut ext = extensions.clone();
|
||||
ext.insert(super::hyper_client::ExtensionDebugMarker);
|
||||
super::hyper_client::send_request(
|
||||
uri,
|
||||
http::Method::POST,
|
||||
ordered_headers,
|
||||
ext,
|
||||
extensions.clone(),
|
||||
body_bytes,
|
||||
timeout,
|
||||
http_proxy,
|
||||
upstream_proxy_url.as_deref(),
|
||||
)
|
||||
.await?
|
||||
};
|
||||
@@ -1305,22 +1293,6 @@ fn extract_json_error_message(body: &Value) -> Option<String> {
|
||||
.find_map(|value| value.as_str().map(ToString::to_string))
|
||||
}
|
||||
|
||||
/// Determine whether to force `accept-encoding: identity` on the upstream request.
|
||||
///
|
||||
/// Only forced for **transform** paths where the proxy must decompress and re-encode
|
||||
/// the response body. For transparent passthrough (including streaming / SSE), the
|
||||
/// client's original `accept-encoding` is preserved so the wire behaviour matches a
|
||||
/// direct connection. SSE usage parsing may fail on compressed streams, but the bytes
|
||||
/// are still forwarded correctly to the client.
|
||||
fn should_force_identity_encoding(
|
||||
_endpoint: &str,
|
||||
_body: &Value,
|
||||
_headers: &axum::http::HeaderMap,
|
||||
needs_transform: bool,
|
||||
) -> bool {
|
||||
needs_transform
|
||||
}
|
||||
|
||||
fn summarize_text_for_log(text: &str, max_chars: usize) -> String {
|
||||
let normalized = text.split_whitespace().collect::<Vec<_>>().join(" ");
|
||||
let trimmed = normalized.trim();
|
||||
@@ -1337,7 +1309,6 @@ fn summarize_text_for_log(text: &str, max_chars: usize) -> String {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use axum::http::{header::ACCEPT, HeaderMap, HeaderValue};
|
||||
use serde_json::json;
|
||||
|
||||
#[test]
|
||||
@@ -1405,67 +1376,4 @@ mod tests {
|
||||
assert_eq!(summary, "line1 line2...");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn force_identity_for_transform_requests() {
|
||||
let headers = HeaderMap::new();
|
||||
|
||||
assert!(should_force_identity_encoding(
|
||||
"/v1/messages",
|
||||
&json!({ "model": "gpt-5" }),
|
||||
&headers,
|
||||
true,
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transparent_encoding_for_streaming_passthrough() {
|
||||
let headers = HeaderMap::new();
|
||||
|
||||
// Streaming passthrough should NOT force identity — transparent proxy
|
||||
assert!(!should_force_identity_encoding(
|
||||
"/v1/responses",
|
||||
&json!({ "stream": true }),
|
||||
&headers,
|
||||
false,
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transparent_encoding_for_gemini_stream_endpoints() {
|
||||
let headers = HeaderMap::new();
|
||||
|
||||
// SSE endpoints without transform: transparent
|
||||
assert!(!should_force_identity_encoding(
|
||||
"/v1beta/models/gemini-2.5-pro:streamGenerateContent?alt=sse",
|
||||
&json!({ "model": "gemini-2.5-pro" }),
|
||||
&headers,
|
||||
false,
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transparent_encoding_for_sse_accept_header() {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(ACCEPT, HeaderValue::from_static("text/event-stream"));
|
||||
|
||||
// SSE accept header without transform: transparent
|
||||
assert!(!should_force_identity_encoding(
|
||||
"/v1/responses",
|
||||
&json!({ "model": "gpt-5" }),
|
||||
&headers,
|
||||
false,
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn non_streaming_requests_keep_original_encoding_behavior() {
|
||||
let headers = HeaderMap::new();
|
||||
|
||||
assert!(!should_force_identity_encoding(
|
||||
"/v1/responses",
|
||||
&json!({ "model": "gpt-5" }),
|
||||
&headers,
|
||||
false,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,28 +280,6 @@ fn system_proxy_points_to_loopback() -> bool {
|
||||
.any(|value| proxy_points_to_loopback(&value))
|
||||
}
|
||||
|
||||
/// 检查当前环境是否存在可用的系统代理配置。
|
||||
#[allow(dead_code)]
|
||||
pub fn has_system_proxy_env() -> bool {
|
||||
const KEYS: [&str; 6] = [
|
||||
"HTTP_PROXY",
|
||||
"http_proxy",
|
||||
"HTTPS_PROXY",
|
||||
"https_proxy",
|
||||
"ALL_PROXY",
|
||||
"all_proxy",
|
||||
];
|
||||
|
||||
if system_proxy_points_to_loopback() {
|
||||
return false;
|
||||
}
|
||||
|
||||
KEYS.iter()
|
||||
.filter_map(|key| env::var(key).ok())
|
||||
.map(|value| value.trim().to_string())
|
||||
.any(|value| !value.is_empty())
|
||||
}
|
||||
|
||||
fn proxy_points_to_loopback(value: &str) -> bool {
|
||||
fn host_is_loopback(host: &str) -> bool {
|
||||
if host.eq_ignore_ascii_case("localhost") {
|
||||
@@ -567,18 +545,4 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_has_system_proxy_env_ignores_own_loopback_proxy() {
|
||||
let _guard = env_lock().lock().unwrap();
|
||||
set_proxy_port(15721);
|
||||
std::env::remove_var("HTTP_PROXY");
|
||||
|
||||
std::env::set_var("HTTP_PROXY", "http://127.0.0.1:15721");
|
||||
assert!(!has_system_proxy_env());
|
||||
|
||||
std::env::set_var("HTTP_PROXY", "http://127.0.0.1:7890");
|
||||
assert!(has_system_proxy_env());
|
||||
|
||||
std::env::remove_var("HTTP_PROXY");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
//! Hyper-based HTTP client for proxy forwarding
|
||||
//!
|
||||
//! Uses hyper directly (instead of reqwest) to support:
|
||||
//! - `preserve_header_case(true)` — keeps original header name casing
|
||||
//! - Header order preservation via `HeaderCaseMap` extension transfer
|
||||
//! - `title_case_headers(true)` — fallback if HeaderCaseMap is absent
|
||||
//!
|
||||
//! Falls back to reqwest when an upstream proxy (HTTP/SOCKS5) is configured,
|
||||
//! since hyper-util's legacy client doesn't natively support proxy tunneling.
|
||||
//! Uses raw TCP/TLS writes to preserve exact original header name casing.
|
||||
//! Supports HTTP CONNECT tunneling through upstream proxies.
|
||||
//! Falls back to hyper-util Client (title-case headers) when raw write is not feasible.
|
||||
|
||||
use super::ProxyError;
|
||||
use bytes::Bytes;
|
||||
@@ -16,12 +12,6 @@ use hyper_rustls::HttpsConnectorBuilder;
|
||||
use hyper_util::{client::legacy::Client, rt::TokioExecutor};
|
||||
use std::sync::OnceLock;
|
||||
|
||||
/// Debug marker inserted into extensions to verify they survive the chain.
|
||||
/// If this marker is found in hyper_client but HeaderCaseMap is not used,
|
||||
/// the issue is in hyper's encoder, not in extension passing.
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct ExtensionDebugMarker;
|
||||
|
||||
/// Our own header case map: maps lowercase header name → original wire-casing bytes.
|
||||
///
|
||||
/// This is a backup mechanism independent of hyper's internal `HeaderCaseMap` (which is
|
||||
|
||||
@@ -691,22 +691,6 @@ mod tests {
|
||||
assert_eq!(super::strip_sse_field("id:1", "data"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn strip_entity_headers_removes_encoding_and_length_headers() {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert("content-encoding", "gzip".parse().unwrap());
|
||||
headers.insert("content-length", "123".parse().unwrap());
|
||||
headers.insert("transfer-encoding", "chunked".parse().unwrap());
|
||||
headers.insert("content-type", "application/json".parse().unwrap());
|
||||
|
||||
strip_entity_headers_for_rebuilt_body(&mut headers);
|
||||
|
||||
assert!(!headers.contains_key("content-encoding"));
|
||||
assert!(!headers.contains_key("content-length"));
|
||||
assert!(!headers.contains_key("transfer-encoding"));
|
||||
assert_eq!(headers.get("content-type").unwrap(), "application/json");
|
||||
}
|
||||
|
||||
fn build_state(db: Arc<Database>) -> ProxyState {
|
||||
ProxyState {
|
||||
db: db.clone(),
|
||||
|
||||
Reference in New Issue
Block a user