From 0d318767b443dab00b07f73df65c6c7022e3d83d Mon Sep 17 00:00:00 2001 From: celia-oai Date: Mon, 17 Aug 2026 15:47:26 -0700 Subject: [PATCH] Migrate app-server tests to shared HTTP client --- codex-rs/Cargo.lock | 1 - codex-rs/app-server/Cargo.toml | 1 - .../request_processors/account_processor.rs | 7 ++++--- .../request_processors/feedback_processor.rs | 5 ++--- codex-rs/app-server/tests/suite/v2/account.rs | 7 ++++--- .../suite/v2/connection_handling_websocket.rs | 13 ++++++------ .../app-server/tests/suite/v2/executor_mcp.rs | 20 +++++++++---------- .../tests/suite/v2/mcp_server_status.rs | 20 +++++++++---------- codex-rs/deny.toml | 1 - 9 files changed, 36 insertions(+), 39 deletions(-) diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock index f90ba32e77..9f1b05bf5b 100644 --- a/codex-rs/Cargo.lock +++ b/codex-rs/Cargo.lock @@ -2056,7 +2056,6 @@ dependencies = [ "opentelemetry", "opentelemetry_sdk", "pretty_assertions", - "reqwest 0.12.28", "rmcp", "serde", "serde_json", diff --git a/codex-rs/app-server/Cargo.toml b/codex-rs/app-server/Cargo.toml index 98dfb724c2..f5890bd46e 100644 --- a/codex-rs/app-server/Cargo.toml +++ b/codex-rs/app-server/Cargo.toml @@ -132,7 +132,6 @@ hmac = { workspace = true } opentelemetry = { workspace = true } opentelemetry_sdk = { workspace = true } pretty_assertions = { workspace = true } -reqwest = { workspace = true, features = ["rustls-tls"] } rmcp = { workspace = true, default-features = false, features = [ "elicitation", "server", diff --git a/codex-rs/app-server/src/request_processors/account_processor.rs b/codex-rs/app-server/src/request_processors/account_processor.rs index b2df78a545..080d821aed 100644 --- a/codex-rs/app-server/src/request_processors/account_processor.rs +++ b/codex-rs/app-server/src/request_processors/account_processor.rs @@ -1410,6 +1410,7 @@ fn workspace_messages_feature_disabled(err: &BackendRequestError) -> bool { #[cfg(test)] mod tests { use super::*; + use axum::http::StatusCode; use codex_backend_client::TokenUsageProfileDailyBucket; use codex_backend_client::TokenUsageProfileStats; use pretty_assertions::assert_eq; @@ -1483,9 +1484,9 @@ mod tests { #[test] fn workspace_messages_feature_disabled_only_for_not_found() { let cases = [ - (reqwest::StatusCode::NOT_FOUND, true), - (reqwest::StatusCode::UNAUTHORIZED, false), - (reqwest::StatusCode::FORBIDDEN, false), + (StatusCode::NOT_FOUND, true), + (StatusCode::UNAUTHORIZED, false), + (StatusCode::FORBIDDEN, false), ]; for (status, expected) in cases { diff --git a/codex-rs/app-server/src/request_processors/feedback_processor.rs b/codex-rs/app-server/src/request_processors/feedback_processor.rs index ce9222419e..29bc328d00 100644 --- a/codex-rs/app-server/src/request_processors/feedback_processor.rs +++ b/codex-rs/app-server/src/request_processors/feedback_processor.rs @@ -468,6 +468,7 @@ fn windows_sandbox_log_attachment(_codex_home: &Path) -> Option Result<()> .query_pairs() .find_map(|(key, value)| (key == "state").then(|| value.into_owned())) .ok_or_else(|| anyhow::anyhow!("missing state"))?; - let client = reqwest::Client::builder() - .redirect(reqwest::redirect::Policy::none()) - .build()?; + let client = HttpClientBuilder::new() + .without_redirects() + .build_direct()?; let token_redirect_uri = callback_url.clone(); let mut callback_url = Url::parse(&callback_url)?; diff --git a/codex-rs/app-server/tests/suite/v2/connection_handling_websocket.rs b/codex-rs/app-server/tests/suite/v2/connection_handling_websocket.rs index ece66c8405..83e49005c5 100644 --- a/codex-rs/app-server/tests/suite/v2/connection_handling_websocket.rs +++ b/codex-rs/app-server/tests/suite/v2/connection_handling_websocket.rs @@ -4,6 +4,7 @@ use anyhow::bail; use app_test_support::DISABLE_PLUGIN_STARTUP_TASKS_ARG; use app_test_support::create_mock_responses_server_sequence_unchecked; use app_test_support::to_response; +use axum::http::StatusCode; use base64::Engine; use base64::engine::general_purpose::URL_SAFE_NO_PAD; use codex_app_server_protocol::ClientInfo; @@ -20,12 +21,14 @@ use codex_app_server_protocol::ThreadLoadedListResponse; use codex_app_server_protocol::ThreadStartParams; use codex_app_server_protocol::ThreadStartResponse; use codex_core::config::set_project_trust_level; +use codex_http_client::HttpClient; +use codex_http_client::HttpClientBuilder; +use codex_http_client::HttpResponse; use codex_protocol::config_types::TrustLevel; use futures::SinkExt; use futures::StreamExt; use hmac::Hmac; use hmac::Mac; -use reqwest::StatusCode; use serde_json::json; use sha2::Sha256; use std::net::SocketAddr; @@ -214,7 +217,7 @@ async fn websocket_transport_serves_health_endpoints_on_same_listener() -> Resul create_config_toml(codex_home.path(), &server.uri(), "never")?; let (mut process, bind_addr) = spawn_websocket_server(codex_home.path()).await?; - let client = reqwest::Client::new(); + let client = HttpClientBuilder::new().build_direct()?; let readyz = http_get(&client, bind_addr, "/readyz").await?; assert_eq!(readyz.status(), StatusCode::OK); @@ -641,11 +644,7 @@ async fn run_websocket_server_to_completion_with_args( .context("failed to run websocket app-server") } -async fn http_get( - client: &reqwest::Client, - bind_addr: SocketAddr, - path: &str, -) -> Result { +async fn http_get(client: &HttpClient, bind_addr: SocketAddr, path: &str) -> Result { let connectable_bind_addr = connectable_bind_addr(bind_addr); let deadline = Instant::now() + DEFAULT_READ_TIMEOUT; loop { diff --git a/codex-rs/app-server/tests/suite/v2/executor_mcp.rs b/codex-rs/app-server/tests/suite/v2/executor_mcp.rs index 3817c8c9dc..1ff52f88ee 100644 --- a/codex-rs/app-server/tests/suite/v2/executor_mcp.rs +++ b/codex-rs/app-server/tests/suite/v2/executor_mcp.rs @@ -21,6 +21,7 @@ use codex_app_server_protocol::ThreadStartResponse; use codex_app_server_protocol::TurnStartParams; use codex_app_server_protocol::TurnStartResponse; use codex_app_server_protocol::UserInput; +use codex_http_client::HttpClientBuilder; use codex_utils_path_uri::PathUri; use core_test_support::responses; use core_test_support::stdio_server_bin; @@ -48,6 +49,7 @@ use tempfile::TempDir; use tokio::net::TcpListener; use tokio::sync::mpsc; use tokio::time::timeout; +use url::Url; const DEFAULT_READ_TIMEOUT: Duration = Duration::from_secs(20); const EXECUTOR_HTTP_MCP_URL: &str = "http://executor-only.invalid/mcp"; @@ -295,7 +297,7 @@ startup_timeout_sec = 10 .await?; let response: McpServerOauthLoginResponse = timeout(DEFAULT_READ_TIMEOUT, app_server.read_response(request_id)).await??; - let authorization_url = reqwest::Url::parse(&response.authorization_url)?; + let authorization_url = Url::parse(&response.authorization_url)?; let parameters = authorization_url .query_pairs() .into_owned() @@ -308,14 +310,13 @@ startup_timeout_sec = 10 registration_request_rx.try_recv().is_err(), "configured OAuth client must skip dynamic registration" ); - let mut callback_url = reqwest::Url::parse(¶meters["redirect_uri"])?; + let mut callback_url = Url::parse(¶meters["redirect_uri"])?; callback_url .query_pairs_mut() .append_pair("code", "configured-test-code") .append_pair("state", ¶meters["state"]); - reqwest::Client::builder() - .no_proxy() - .build()? + HttpClientBuilder::new() + .build_direct()? .get(callback_url) .send() .await? @@ -350,7 +351,7 @@ startup_timeout_sec = 10 .authorization_url .starts_with("https://oauth-only.invalid/authorize?") ); - let authorization_url = reqwest::Url::parse(&response.authorization_url)?; + let authorization_url = Url::parse(&response.authorization_url)?; let client_id = authorization_url .query_pairs() .find_map(|(key, value)| (key == "client_id").then(|| value.into_owned())); @@ -371,15 +372,14 @@ startup_timeout_sec = 10 registration_request["redirect_uris"], json!([redirect_uri.clone()]) ); - let mut callback_url = reqwest::Url::parse(&redirect_uri)?; + let mut callback_url = Url::parse(&redirect_uri)?; assert_eq!(callback_url.port(), Some(plugin_callback_port)); callback_url .query_pairs_mut() .append_pair("code", "executor-test-code") .append_pair("state", &state); - reqwest::Client::builder() - .no_proxy() - .build()? + HttpClientBuilder::new() + .build_direct()? .get(callback_url) .send() .await? diff --git a/codex-rs/app-server/tests/suite/v2/mcp_server_status.rs b/codex-rs/app-server/tests/suite/v2/mcp_server_status.rs index f1f2fb5e6f..db9dc57453 100644 --- a/codex-rs/app-server/tests/suite/v2/mcp_server_status.rs +++ b/codex-rs/app-server/tests/suite/v2/mcp_server_status.rs @@ -28,6 +28,7 @@ use codex_app_server_protocol::RequestId; use codex_app_server_protocol::ThreadStartParams; use codex_app_server_protocol::ThreadStartResponse; use codex_core::config::set_project_trust_level; +use codex_http_client::HttpClientBuilder; use codex_protocol::config_types::TrustLevel; use core_test_support::stdio_server_bin; use pretty_assertions::assert_eq; @@ -53,6 +54,7 @@ use tokio::sync::mpsc; use tokio::task::JoinHandle; use tokio::time::sleep; use tokio::time::timeout; +use url::Url; use wiremock::Mock; use wiremock::MockServer; use wiremock::ResponseTemplate; @@ -118,16 +120,15 @@ async fn oauth_login_uses_http_headers_helper() -> Result<()> { .await?; let response: McpServerOauthLoginResponse = timeout(DEFAULT_READ_TIMEOUT, mcp.read_response(request_id)).await??; - let authorization_url = reqwest::Url::parse(&response.authorization_url)?; + let authorization_url = Url::parse(&response.authorization_url)?; let query: BTreeMap<_, _> = authorization_url.query_pairs().into_owned().collect(); - let mut callback_url = reqwest::Url::parse(&query["redirect_uri"])?; + let mut callback_url = Url::parse(&query["redirect_uri"])?; callback_url .query_pairs_mut() .append_pair("code", "test-code") .append_pair("state", &query["state"]); - reqwest::Client::builder() - .no_proxy() - .build()? + HttpClientBuilder::new() + .build_direct()? .get(callback_url) .send() .await? @@ -348,13 +349,13 @@ async fn oauth_login_automatically_selects_callback_specific_cimd_without_metada .await?; let response: McpServerOauthLoginResponse = timeout(DEFAULT_READ_TIMEOUT, app_server.read_response(request_id)).await??; - let authorization_url = reqwest::Url::parse(&response.authorization_url)?; + let authorization_url = Url::parse(&response.authorization_url)?; let parameters = authorization_url .query_pairs() .into_owned() .collect::>(); let redirect_uri = parameters["redirect_uri"].clone(); - let mut callback_url = reqwest::Url::parse(&redirect_uri)?; + let mut callback_url = Url::parse(&redirect_uri)?; let callback_id = callback_url .path() .strip_prefix("/callback/") @@ -371,9 +372,8 @@ async fn oauth_login_automatically_selects_callback_specific_cimd_without_metada .query_pairs_mut() .append_pair("code", "cimd-authorization-code") .append_pair("state", ¶meters["state"]); - reqwest::Client::builder() - .no_proxy() - .build()? + HttpClientBuilder::new() + .build_direct()? .get(callback_url) .send() .await? diff --git a/codex-rs/deny.toml b/codex-rs/deny.toml index d6ccfcc642..35d94549e2 100644 --- a/codex-rs/deny.toml +++ b/codex-rs/deny.toml @@ -244,7 +244,6 @@ deny = [ # upstream HTTP transport independently of Codex. "codex-responses-api-proxy", # Temporary migration exceptions. - "codex-app-server", "codex-otel", # Third-party crates that own their reqwest integration. These are not part of the # first-party migration count above.