diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock index 5d17acab9..38d44c44b 100644 --- a/codex-rs/Cargo.lock +++ b/codex-rs/Cargo.lock @@ -2236,6 +2236,7 @@ dependencies = [ "tokio-tungstenite", "walkdir", "wiremock", + "zstd", ] [[package]] diff --git a/codex-rs/core/src/features.rs b/codex-rs/core/src/features.rs index a571b3195..26798c29d 100644 --- a/codex-rs/core/src/features.rs +++ b/codex-rs/core/src/features.rs @@ -506,8 +506,8 @@ pub const FEATURES: &[FeatureSpec] = &[ FeatureSpec { id: Feature::EnableRequestCompression, key: "enable_request_compression", - stage: Stage::UnderDevelopment, - default_enabled: false, + stage: Stage::Stable, + default_enabled: true, }, FeatureSpec { id: Feature::Collab, diff --git a/codex-rs/core/tests/common/Cargo.toml b/codex-rs/core/tests/common/Cargo.toml index 8e9f53943..1c76e5a16 100644 --- a/codex-rs/core/tests/common/Cargo.toml +++ b/codex-rs/core/tests/common/Cargo.toml @@ -25,6 +25,7 @@ tokio-tungstenite = { workspace = true } walkdir = { workspace = true } wiremock = { workspace = true } shlex = { workspace = true } +zstd = { workspace = true } [dev-dependencies] pretty_assertions = { workspace = true } diff --git a/codex-rs/core/tests/common/responses.rs b/codex-rs/core/tests/common/responses.rs index e36d9592f..86a332874 100644 --- a/codex-rs/core/tests/common/responses.rs +++ b/codex-rs/core/tests/common/responses.rs @@ -76,9 +76,32 @@ impl ResponseMock { #[derive(Debug, Clone)] pub struct ResponsesRequest(wiremock::Request); +fn is_zstd_encoding(value: &str) -> bool { + value + .split(',') + .any(|entry| entry.trim().eq_ignore_ascii_case("zstd")) +} + +fn decode_body_bytes(body: &[u8], content_encoding: Option<&str>) -> Vec { + if content_encoding.is_some_and(is_zstd_encoding) { + zstd::stream::decode_all(std::io::Cursor::new(body)).unwrap_or_else(|err| { + panic!("failed to decode zstd request body: {err}"); + }) + } else { + body.to_vec() + } +} + impl ResponsesRequest { pub fn body_json(&self) -> Value { - self.0.body_json().unwrap() + let body = decode_body_bytes( + &self.0.body, + self.0 + .headers + .get("content-encoding") + .and_then(|value| value.to_str().ok()), + ); + serde_json::from_slice(&body).unwrap() } pub fn body_bytes(&self) -> Vec { @@ -105,7 +128,7 @@ impl ResponsesRequest { } pub fn input(&self) -> Vec { - self.0.body_json::().unwrap()["input"] + self.body_json()["input"] .as_array() .expect("input array not found in request") .clone() @@ -1083,7 +1106,14 @@ fn validate_request_body_invariants(request: &wiremock::Request) { if request.method != "POST" || !request.url.path().ends_with("/responses") { return; } - let Ok(body): Result = request.body_json() else { + let body_bytes = decode_body_bytes( + &request.body, + request + .headers + .get("content-encoding") + .and_then(|value| value.to_str().ok()), + ); + let Ok(body): Result = serde_json::from_slice(&body_bytes) else { return; }; let Some(items) = body.get("input").and_then(Value::as_array) else {