Files
CC-Switch/src-tauri/src/proxy/mod.rs
T
Chenx Dust 1a0e8c7a44 fix: decompress body before forward and support zstd (#3817)
* fix(proxy): decompress Codex request body before forward, support zstd

Codex Desktop sends zstd-compressed request bodies when authenticated
against the Codex backend, which broke local proxy routing because the
handlers parsed the raw bytes with serde_json directly.

Reworked on top of current main so it preserves the response_processor
behavior that landed after this PR was first opened:

- Extract content-encoding helpers into a shared proxy::content_encoding
  module. decompress_body keeps returning Option<Vec<u8>> so unknown
  encodings stay pass-through with their content-encoding header intact,
  and keeps the deflate zlib-then-raw fallback (RFC 9110).
- Add zstd/zst support (zstd 0.13) and disable reqwest's auto zstd
  decompression via .no_zstd() for parity with gzip/br/deflate.
- Decompress the request body before JSON parsing in the three Codex
  handlers (chat_completions / responses / responses_compact) and strip
  the stale content-encoding / content-length / transfer-encoding headers
  so the forwarder regenerates them.
- Support stacked codings (e.g. "gzip, zstd") by decoding in reverse
  order and merge repeated Content-Encoding headers via get_all.

Fixes #3764
Fixes #3696

Co-authored-by: chenx-dust <16610294+chenx-dust@users.noreply.github.com>

* fix(proxy): decompress upstream error bodies before reading them

The forwarder error branch consumes non-2xx responses via String::from_utf8
directly, bypassing read_decoded_body. reqwest has no auto-decompression
feature enabled, so a compressed error body (gzip/br/deflate/zstd) arrives
as raw bytes, fails from_utf8, and gets dropped, hiding upstream rate-limit
and auth details from the client.

Decode the error body with the shared proxy::content_encoding helper,
mirroring the success path. Falls back to the raw bytes when the encoding is
unsupported or decoding fails.

Co-authored-by: chenx-dust <16610294+chenx-dust@users.noreply.github.com>

---------

Co-authored-by: Jason <farion1231@gmail.com>
Co-authored-by: chenx-dust <16610294+chenx-dust@users.noreply.github.com>
2026-06-26 08:50:59 +08:00

61 lines
1.7 KiB
Rust

//! 代理服务器模块
//!
//! 提供本地HTTP代理服务,支持多Provider故障转移和请求透传
pub mod body_filter;
pub mod cache_injector;
pub mod circuit_breaker;
pub(crate) mod content_encoding;
pub mod copilot_optimizer;
pub mod error;
pub mod error_mapper;
pub(crate) mod failover_switch;
mod forwarder;
pub mod gemini_url;
pub mod handler_config;
pub mod handler_context;
mod handlers;
mod health;
pub mod http_client;
pub mod hyper_client;
pub(crate) mod json_canonical;
pub mod log_codes;
pub mod media_sanitizer;
pub mod model_mapper;
pub mod provider_router;
pub mod providers;
pub mod response_handler;
pub mod response_processor;
pub(crate) mod server;
pub mod session;
pub(crate) mod sse;
pub(crate) mod switch_lock;
pub mod thinking_budget_rectifier;
pub mod thinking_optimizer;
pub mod thinking_rectifier;
pub(crate) mod types;
pub mod usage;
// 公开导出给外部使用(commands, services等模块需要)
#[allow(unused_imports)]
pub use circuit_breaker::{
CircuitBreaker, CircuitBreakerConfig, CircuitBreakerStats, CircuitState,
};
#[allow(unused_imports)]
pub use error::ProxyError;
#[allow(unused_imports)]
pub use provider_router::ProviderRouter;
#[allow(unused_imports)]
pub use response_handler::{NonStreamHandler, ResponseType, StreamHandler};
#[allow(unused_imports)]
pub use session::{
extract_session_id, ClientFormat, ProxySession, SessionIdResult, SessionIdSource,
};
#[allow(unused_imports)]
pub use types::{ProxyConfig, ProxyServerInfo, ProxyStatus};
// 内部模块间共享(供子模块使用)
// 注意:这个导出用于模块内部,编译器可能警告未使用但实际被子模块使用
#[allow(unused_imports)]
pub(crate) use types::*;