mirror of
https://github.com/openai/codex.git
synced 2026-10-18 17:18:52 +00:00
Remove app-server's direct reqwest dependency (#39293)
## Why `codex-http-client` is the intended owner of `reqwest`, while direct first-party dependencies are tracked as migration debt. ## What changed - Build app-server test HTTP clients through `HttpClientBuilder`. - Use `http` and `url` types directly where only protocol types are needed. - Remove `reqwest` from `codex-app-server` and its temporary dependency-policy exception. GitOrigin-RevId: ff9f14a54bc20ec1ea39050bbe08c381ee376119
This commit is contained in:
Generated
+1
-1
@@ -2053,10 +2053,10 @@ dependencies = [
|
||||
"flate2",
|
||||
"futures",
|
||||
"hmac 0.12.1",
|
||||
"http 1.4.0",
|
||||
"opentelemetry",
|
||||
"opentelemetry_sdk",
|
||||
"pretty_assertions",
|
||||
"reqwest 0.12.28",
|
||||
"rmcp",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
||||
@@ -129,10 +129,10 @@ codex-utils-cargo-bin = { workspace = true }
|
||||
core_test_support = { workspace = true }
|
||||
flate2 = { workspace = true }
|
||||
hmac = { workspace = true }
|
||||
http = { 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",
|
||||
|
||||
@@ -1406,6 +1406,7 @@ mod tests {
|
||||
use super::*;
|
||||
use codex_backend_client::TokenUsageProfileDailyBucket;
|
||||
use codex_backend_client::TokenUsageProfileStats;
|
||||
use http::StatusCode;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[test]
|
||||
@@ -1477,9 +1478,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 {
|
||||
|
||||
@@ -478,6 +478,7 @@ mod tests {
|
||||
use codex_rollout::RolloutLine;
|
||||
use core_test_support::responses::start_mock_server;
|
||||
use core_test_support::test_codex::test_codex;
|
||||
use http::HeaderMap;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[tokio::test]
|
||||
@@ -790,9 +791,7 @@ mod tests {
|
||||
#[test]
|
||||
fn tool_cache_feedback_attachments_include_directory_cache_without_account_id() {
|
||||
let codex_home = tempfile::tempdir().expect("create tempdir");
|
||||
let auth = CodexAuth::Headers(codex_login::AuthHeaders::new(
|
||||
reqwest::header::HeaderMap::new(),
|
||||
));
|
||||
let auth = CodexAuth::Headers(codex_login::AuthHeaders::new(HeaderMap::new()));
|
||||
let directory_cache_context = ConnectorDirectoryCacheContext::new(
|
||||
codex_home.path().to_path_buf(),
|
||||
ConnectorDirectoryCacheKey::new(
|
||||
|
||||
@@ -40,6 +40,7 @@ use codex_app_server_protocol::ServerRequest;
|
||||
use codex_app_server_protocol::TurnCompletedNotification;
|
||||
use codex_app_server_protocol::TurnStatus;
|
||||
use codex_config::types::AuthCredentialsStoreMode;
|
||||
use codex_http_client::HttpClientBuilder;
|
||||
use codex_login::AuthDotJson;
|
||||
use codex_login::AuthKeyringBackendKind;
|
||||
use codex_login::CLIENT_ID_OVERRIDE_ENV_VAR;
|
||||
@@ -2162,9 +2163,9 @@ async fn login_account_chatgpt_redirects_to_hosted_success_page() -> 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)?;
|
||||
|
||||
@@ -20,12 +20,15 @@ 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 http::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<reqwest::Response> {
|
||||
async fn http_get(client: &HttpClient, bind_addr: SocketAddr, path: &str) -> Result<HttpResponse> {
|
||||
let connectable_bind_addr = connectable_bind_addr(bind_addr);
|
||||
let deadline = Instant::now() + DEFAULT_READ_TIMEOUT;
|
||||
loop {
|
||||
|
||||
@@ -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?
|
||||
|
||||
@@ -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::<BTreeMap<String, String>>();
|
||||
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?
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user