From 4c995310e648c0c908923ccf214732fb25da5644 Mon Sep 17 00:00:00 2001 From: Michael Bolin Date: Mon, 6 Jul 2026 17:42:43 -0700 Subject: [PATCH] http-client: extract shared HTTP transport --- codex-rs/Cargo.lock | 54 +++++++++++-------- codex-rs/Cargo.toml | 2 + codex-rs/codex-client/BUILD.bazel | 1 - codex-rs/codex-client/Cargo.toml | 35 +----------- codex-rs/codex-client/README.md | 6 +-- codex-rs/codex-client/src/lib.rs | 42 ++------------- codex-rs/codex-client/src/retry.rs | 4 +- codex-rs/codex-client/src/sse.rs | 4 +- codex-rs/codex-client/src/telemetry.rs | 2 +- codex-rs/http-client/BUILD.bazel | 7 +++ codex-rs/http-client/Cargo.toml | 49 +++++++++++++++++ codex-rs/http-client/README.md | 9 ++++ .../src/bin/custom_ca_probe.rs | 8 +-- .../src/chatgpt_cloudflare_cookies.rs | 2 +- .../src/chatgpt_hosts.rs | 2 +- .../src/custom_ca.rs | 2 +- .../src/default_client.rs | 16 +++--- .../src/error.rs | 2 + codex-rs/http-client/src/lib.rs | 40 ++++++++++++++ .../src/outbound_proxy.rs | 2 +- .../src/outbound_proxy/macos.rs | 2 + .../src/outbound_proxy/windows.rs | 2 + .../src/outbound_proxy/windows_tests.rs | 2 + .../src/outbound_proxy_tests.rs | 2 + .../src/request.rs | 6 +-- .../src/transport.rs | 10 ++-- .../tests/ca_env.rs | 2 +- .../tests/fixtures/test-ca-trusted.pem | 0 .../tests/fixtures/test-ca.pem | 0 .../tests/fixtures/test-intermediate.pem | 0 30 files changed, 187 insertions(+), 128 deletions(-) create mode 100644 codex-rs/http-client/BUILD.bazel create mode 100644 codex-rs/http-client/Cargo.toml create mode 100644 codex-rs/http-client/README.md rename codex-rs/{codex-client => http-client}/src/bin/custom_ca_probe.rs (91%) rename codex-rs/{codex-client => http-client}/src/chatgpt_cloudflare_cookies.rs (98%) rename codex-rs/{codex-client => http-client}/src/chatgpt_hosts.rs (96%) rename codex-rs/{codex-client => http-client}/src/custom_ca.rs (99%) rename codex-rs/{codex-client => http-client}/src/default_client.rs (93%) rename codex-rs/{codex-client => http-client}/src/error.rs (91%) create mode 100644 codex-rs/http-client/src/lib.rs rename codex-rs/{codex-client => http-client}/src/outbound_proxy.rs (99%) rename codex-rs/{codex-client => http-client}/src/outbound_proxy/macos.rs (99%) rename codex-rs/{codex-client => http-client}/src/outbound_proxy/windows.rs (99%) rename codex-rs/{codex-client => http-client}/src/outbound_proxy/windows_tests.rs (93%) rename codex-rs/{codex-client => http-client}/src/outbound_proxy_tests.rs (99%) rename codex-rs/{codex-client => http-client}/src/request.rs (98%) rename codex-rs/{codex-client => http-client}/src/transport.rs (94%) rename codex-rs/{codex-client => http-client}/tests/ca_env.rs (99%) rename codex-rs/{codex-client => http-client}/tests/fixtures/test-ca-trusted.pem (100%) rename codex-rs/{codex-client => http-client}/tests/fixtures/test-ca.pem (100%) rename codex-rs/{codex-client => http-client}/tests/fixtures/test-intermediate.pem (100%) diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock index c0ff4a585a2d..c6eeb7f97895 100644 --- a/codex-rs/Cargo.lock +++ b/codex-rs/Cargo.lock @@ -2392,33 +2392,12 @@ dependencies = [ name = "codex-client" version = "0.0.0" dependencies = [ - "bytes", - "codex-utils-cargo-bin", - "codex-utils-rustls-provider", + "codex-http-client", "eventsource-stream", "futures", "http 1.4.0", - "opentelemetry", - "opentelemetry_sdk", - "pretty_assertions", "rand 0.9.3", - "rcgen", - "reqwest 0.12.28", - "rustls", - "rustls-native-certs", - "rustls-pki-types", - "serde", - "serde_json", - "sha2 0.10.9", - "system-configuration", - "tempfile", - "thiserror 2.0.18", "tokio", - "tracing", - "tracing-opentelemetry", - "tracing-subscriber", - "windows-sys 0.52.0", - "zstd 0.13.3", ] [[package]] @@ -3214,6 +3193,37 @@ dependencies = [ "uuid", ] +[[package]] +name = "codex-http-client" +version = "0.0.0" +dependencies = [ + "bytes", + "codex-utils-cargo-bin", + "codex-utils-rustls-provider", + "futures", + "http 1.4.0", + "opentelemetry", + "opentelemetry_sdk", + "pretty_assertions", + "rcgen", + "reqwest 0.12.28", + "rustls", + "rustls-native-certs", + "rustls-pki-types", + "serde", + "serde_json", + "sha2 0.10.9", + "system-configuration", + "tempfile", + "thiserror 2.0.18", + "tokio", + "tracing", + "tracing-opentelemetry", + "tracing-subscriber", + "windows-sys 0.52.0", + "zstd 0.13.3", +] + [[package]] name = "codex-image-generation-extension" version = "0.0.0" diff --git a/codex-rs/Cargo.toml b/codex-rs/Cargo.toml index 2f73c216a938..ad08ce8e024b 100644 --- a/codex-rs/Cargo.toml +++ b/codex-rs/Cargo.toml @@ -41,6 +41,7 @@ members = [ "core-plugins", "core-skills", "hooks", + "http-client", "secrets", "exec", "file-system", @@ -164,6 +165,7 @@ codex-cloud-tasks-mock-client = { path = "cloud-tasks-mock-client" } codex-code-mode = { path = "code-mode" } codex-code-mode-protocol = { path = "code-mode-protocol" } codex-home = { path = "codex-home" } +codex-http-client = { path = "http-client" } codex-config = { path = "config" } codex-connectors = { path = "connectors" } codex-connectors-extension = { path = "ext/connectors" } diff --git a/codex-rs/codex-client/BUILD.bazel b/codex-rs/codex-client/BUILD.bazel index 3d26819111b0..dd7e5046342a 100644 --- a/codex-rs/codex-client/BUILD.bazel +++ b/codex-rs/codex-client/BUILD.bazel @@ -2,6 +2,5 @@ load("//:defs.bzl", "codex_rust_crate") codex_rust_crate( name = "codex-client", - compile_data = glob(["tests/fixtures/**"]), crate_name = "codex_client", ) diff --git a/codex-rs/codex-client/Cargo.toml b/codex-rs/codex-client/Cargo.toml index fcd97b6bda48..8e354ebeea41 100644 --- a/codex-rs/codex-client/Cargo.toml +++ b/codex-rs/codex-client/Cargo.toml @@ -5,47 +5,16 @@ name = "codex-client" version.workspace = true [dependencies] -bytes = { workspace = true } +codex-http-client = { workspace = true } eventsource-stream = { workspace = true } futures = { workspace = true } http = { workspace = true } -opentelemetry = { workspace = true } rand = { workspace = true } -reqwest = { workspace = true, features = ["json", "rustls-tls-native-roots", "stream"] } -rustls = { workspace = true } -rustls-native-certs = { workspace = true } -rustls-pki-types = { workspace = true } -serde = { workspace = true, features = ["derive"] } -serde_json = { workspace = true } -thiserror = { workspace = true } tokio = { workspace = true, features = ["macros", "rt", "time", "sync"] } -tracing = { workspace = true } -tracing-opentelemetry = { workspace = true } -codex-utils-rustls-provider = { workspace = true } -zstd = { workspace = true } - -[target.'cfg(any(target_os = "windows", target_os = "macos"))'.dependencies] -sha2 = { workspace = true } - -[target.'cfg(target_os = "macos")'.dependencies] -system-configuration = { workspace = true } - -[target.'cfg(target_os = "windows")'.dependencies] -windows-sys = { version = "0.52", features = [ - "Win32_Foundation", - "Win32_Networking_WinHttp", -] } [lints] workspace = true -[dev-dependencies] -codex-utils-cargo-bin = { workspace = true } -opentelemetry_sdk = { workspace = true } -pretty_assertions = { workspace = true } -rcgen = { workspace = true } -tempfile = { workspace = true } -tracing-subscriber = { workspace = true } - [lib] doctest = false +test = false diff --git a/codex-rs/codex-client/README.md b/codex-rs/codex-client/README.md index 045ee7b34377..1e4073117ad5 100644 --- a/codex-rs/codex-client/README.md +++ b/codex-rs/codex-client/README.md @@ -1,8 +1,8 @@ # codex-client -Generic transport layer that wraps HTTP requests, retries, and streaming primitives without any Codex/OpenAI awareness. +Higher-level request policy layered on `codex-http-client` without any Codex/OpenAI API awareness. -- Defines `HttpTransport` and a default `ReqwestTransport` plus thin `Request`/`Response` types. - Provides retry utilities (`RetryPolicy`, `RetryOn`, `run_with_retry`, `backoff`) that callers plug into for unary and streaming calls. - Supplies the `sse_stream` helper to turn byte streams into raw SSE `data:` frames with idle timeouts and surfaced stream errors. -- Consumed by higher-level crates like `codex-api`; it stays neutral on endpoints, headers, or API-specific error shapes. +- Defines the request telemetry callback used by higher-level clients. +- Re-exports the low-level HTTP types temporarily so consumers can migrate to `codex-http-client` incrementally. diff --git a/codex-rs/codex-client/src/lib.rs b/codex-rs/codex-client/src/lib.rs index 78daa6c28817..4b83ba0dd099 100644 --- a/codex-rs/codex-client/src/lib.rs +++ b/codex-rs/codex-client/src/lib.rs @@ -1,49 +1,13 @@ -mod chatgpt_cloudflare_cookies; -mod chatgpt_hosts; -mod custom_ca; -mod default_client; -mod error; -mod outbound_proxy; -mod request; mod retry; mod sse; mod telemetry; -mod transport; -pub use crate::chatgpt_cloudflare_cookies::with_chatgpt_cloudflare_cookie_store; -pub use crate::chatgpt_hosts::is_allowed_chatgpt_host; -pub use crate::custom_ca::BuildCustomCaTransportError; -/// Test-only subprocess hook for custom CA coverage. -/// -/// This stays public only so the `custom_ca_probe` binary target can reuse the shared helper. It -/// is hidden from normal docs because ordinary callers should use -/// [`build_reqwest_client_with_custom_ca`] instead. -#[doc(hidden)] -pub use crate::custom_ca::build_reqwest_client_for_subprocess_tests; -pub use crate::custom_ca::build_reqwest_client_with_custom_ca; -pub use crate::custom_ca::maybe_build_rustls_client_config_with_custom_ca; -pub use crate::default_client::CodexHttpClient; -pub use crate::default_client::CodexRequestBuilder; -pub use crate::error::StreamError; -pub use crate::error::TransportError; -pub use crate::outbound_proxy::BuildRouteAwareHttpClientError; -pub use crate::outbound_proxy::ClientRouteClass; -pub use crate::outbound_proxy::OutboundProxyConfig; -pub use crate::outbound_proxy::RouteFailureClass; -pub use crate::outbound_proxy::build_reqwest_client_for_route; -pub use crate::request::EncodedJsonBody; -pub use crate::request::PreparedRequestBody; -pub use crate::request::Request; -pub use crate::request::RequestBody; -pub use crate::request::RequestCompression; -pub use crate::request::Response; pub use crate::retry::RetryOn; pub use crate::retry::RetryPolicy; pub use crate::retry::backoff; pub use crate::retry::run_with_retry; pub use crate::sse::sse_stream; pub use crate::telemetry::RequestTelemetry; -pub use crate::transport::ByteStream; -pub use crate::transport::HttpTransport; -pub use crate::transport::ReqwestTransport; -pub use crate::transport::StreamResponse; +pub use codex_http_client::HttpClient as CodexHttpClient; +pub use codex_http_client::RequestBuilder as CodexRequestBuilder; +pub use codex_http_client::*; diff --git a/codex-rs/codex-client/src/retry.rs b/codex-rs/codex-client/src/retry.rs index c7bdd34b1ef0..9eb408878b8a 100644 --- a/codex-rs/codex-client/src/retry.rs +++ b/codex-rs/codex-client/src/retry.rs @@ -1,5 +1,5 @@ -use crate::error::TransportError; -use crate::request::Request; +use codex_http_client::Request; +use codex_http_client::TransportError; use rand::Rng; use std::future::Future; use std::time::Duration; diff --git a/codex-rs/codex-client/src/sse.rs b/codex-rs/codex-client/src/sse.rs index f3aba3a2c59d..ed1591f6de4b 100644 --- a/codex-rs/codex-client/src/sse.rs +++ b/codex-rs/codex-client/src/sse.rs @@ -1,5 +1,5 @@ -use crate::error::StreamError; -use crate::transport::ByteStream; +use codex_http_client::ByteStream; +use codex_http_client::StreamError; use eventsource_stream::Eventsource; use futures::StreamExt; use tokio::sync::mpsc; diff --git a/codex-rs/codex-client/src/telemetry.rs b/codex-rs/codex-client/src/telemetry.rs index 457d47f4fcad..b856414d3332 100644 --- a/codex-rs/codex-client/src/telemetry.rs +++ b/codex-rs/codex-client/src/telemetry.rs @@ -1,4 +1,4 @@ -use crate::error::TransportError; +use codex_http_client::TransportError; use http::StatusCode; use std::time::Duration; diff --git a/codex-rs/http-client/BUILD.bazel b/codex-rs/http-client/BUILD.bazel new file mode 100644 index 000000000000..6092c05bf21b --- /dev/null +++ b/codex-rs/http-client/BUILD.bazel @@ -0,0 +1,7 @@ +load("//:defs.bzl", "codex_rust_crate") + +codex_rust_crate( + name = "http-client", + compile_data = glob(["tests/fixtures/**"]), + crate_name = "codex_http_client", +) diff --git a/codex-rs/http-client/Cargo.toml b/codex-rs/http-client/Cargo.toml new file mode 100644 index 000000000000..46afa51ba5d8 --- /dev/null +++ b/codex-rs/http-client/Cargo.toml @@ -0,0 +1,49 @@ +[package] +edition.workspace = true +license.workspace = true +name = "codex-http-client" +version.workspace = true + +[dependencies] +bytes = { workspace = true } +codex-utils-rustls-provider = { workspace = true } +futures = { workspace = true } +http = { workspace = true } +opentelemetry = { workspace = true } +reqwest = { workspace = true, features = ["json", "rustls-tls-native-roots", "stream"] } +rustls = { workspace = true } +rustls-native-certs = { workspace = true } +rustls-pki-types = { workspace = true } +serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true } +thiserror = { workspace = true } +tokio = { workspace = true, features = ["macros", "rt", "time", "sync"] } +tracing = { workspace = true } +tracing-opentelemetry = { workspace = true } +zstd = { workspace = true } + +[target.'cfg(any(target_os = "windows", target_os = "macos"))'.dependencies] +sha2 = { workspace = true } + +[target.'cfg(target_os = "macos")'.dependencies] +system-configuration = { workspace = true } + +[target.'cfg(target_os = "windows")'.dependencies] +windows-sys = { version = "0.52", features = [ + "Win32_Foundation", + "Win32_Networking_WinHttp", +] } + +[lints] +workspace = true + +[dev-dependencies] +codex-utils-cargo-bin = { workspace = true } +opentelemetry_sdk = { workspace = true } +pretty_assertions = { workspace = true } +rcgen = { workspace = true } +tempfile = { workspace = true } +tracing-subscriber = { workspace = true } + +[lib] +doctest = false diff --git a/codex-rs/http-client/README.md b/codex-rs/http-client/README.md new file mode 100644 index 000000000000..89e35ac59d2d --- /dev/null +++ b/codex-rs/http-client/README.md @@ -0,0 +1,9 @@ +# codex-http-client + +Low-level HTTP transport shared by Codex crates. + +- Defines the request, response, streaming, and transport types used for outbound HTTP calls. +- Owns the `reqwest` implementation, custom CA handling, and ChatGPT Cloudflare cookie policy. +- Resolves system, PAC/WPAD, environment, and direct proxy routes for supported clients. + +Higher-level retry, SSE, and request telemetry policy remains in `codex-client`. diff --git a/codex-rs/codex-client/src/bin/custom_ca_probe.rs b/codex-rs/http-client/src/bin/custom_ca_probe.rs similarity index 91% rename from codex-rs/codex-client/src/bin/custom_ca_probe.rs rename to codex-rs/http-client/src/bin/custom_ca_probe.rs index 81f5ba9bc2b1..c8db21510d6a 100644 --- a/codex-rs/codex-client/src/bin/custom_ca_probe.rs +++ b/codex-rs/http-client/src/bin/custom_ca_probe.rs @@ -10,9 +10,9 @@ //! - error messages guide users when CA files are invalid. //! - optional HTTPS probes can complete a request through the constructed client. //! -//! The detailed explanation of what "hermetic" means here lives in `codex_client::custom_ca`. +//! The detailed explanation of what "hermetic" means here lives in `codex_http_client::custom_ca`. //! This binary exists so the tests can exercise -//! [`codex_client::build_reqwest_client_for_subprocess_tests`] in a separate process without +//! [`codex_http_client::build_reqwest_client_for_subprocess_tests`] in a separate process without //! duplicating client-construction logic. use std::env; @@ -69,11 +69,11 @@ fn build_probe_client( if let Some(proxy_url) = proxy_url { let proxy = reqwest::Proxy::https(proxy_url) .map_err(|error| format!("failed to configure probe proxy {proxy_url}: {error}"))?; - return codex_client::build_reqwest_client_with_custom_ca(builder.proxy(proxy)) + return codex_http_client::build_reqwest_client_with_custom_ca(builder.proxy(proxy)) .map_err(|error| error.to_string()); } - codex_client::build_reqwest_client_for_subprocess_tests(builder) + codex_http_client::build_reqwest_client_for_subprocess_tests(builder) .map_err(|error| error.to_string()) } diff --git a/codex-rs/codex-client/src/chatgpt_cloudflare_cookies.rs b/codex-rs/http-client/src/chatgpt_cloudflare_cookies.rs similarity index 98% rename from codex-rs/codex-client/src/chatgpt_cloudflare_cookies.rs rename to codex-rs/http-client/src/chatgpt_cloudflare_cookies.rs index 7e5d22057c50..f63763dad4f3 100644 --- a/codex-rs/codex-client/src/chatgpt_cloudflare_cookies.rs +++ b/codex-rs/http-client/src/chatgpt_cloudflare_cookies.rs @@ -7,7 +7,7 @@ use reqwest::header::HeaderValue; use crate::chatgpt_hosts::is_allowed_chatgpt_host; -// WARNING: this store is process-global and may be shared across auth contexts. +// WARNING: this HTTP cookie store is process-global and may be shared across auth contexts. // It must only ever contain Cloudflare infrastructure cookies. Never extend this // store to persist ChatGPT account, session, auth, or other user-specific cookie // data. diff --git a/codex-rs/codex-client/src/chatgpt_hosts.rs b/codex-rs/http-client/src/chatgpt_hosts.rs similarity index 96% rename from codex-rs/codex-client/src/chatgpt_hosts.rs rename to codex-rs/http-client/src/chatgpt_hosts.rs index dd0b99589cad..0426c8bb36b2 100644 --- a/codex-rs/codex-client/src/chatgpt_hosts.rs +++ b/codex-rs/http-client/src/chatgpt_hosts.rs @@ -1,5 +1,5 @@ /// Returns whether `host` is one of the ChatGPT hosts Codex is allowed to treat -/// as first-party ChatGPT traffic. +/// as first-party ChatGPT HTTP traffic. pub fn is_allowed_chatgpt_host(host: &str) -> bool { const EXACT_HOSTS: &[&str] = &["chatgpt.com", "chat.openai.com", "chatgpt-staging.com"]; const SUBDOMAIN_SUFFIXES: &[&str] = &[".chatgpt.com", ".chatgpt-staging.com"]; diff --git a/codex-rs/codex-client/src/custom_ca.rs b/codex-rs/http-client/src/custom_ca.rs similarity index 99% rename from codex-rs/codex-client/src/custom_ca.rs rename to codex-rs/http-client/src/custom_ca.rs index 1a211beedf8f..4cfae8f23d73 100644 --- a/codex-rs/codex-client/src/custom_ca.rs +++ b/codex-rs/http-client/src/custom_ca.rs @@ -1,4 +1,4 @@ -//! Custom CA handling for Codex outbound HTTP and websocket clients. +//! Custom CA handling shared by Codex outbound HTTP and websocket clients. //! //! Codex constructs outbound reqwest clients and secure websocket connections in a few crates, but //! they all need the same trust-store policy when enterprise proxies or gateways intercept TLS. diff --git a/codex-rs/codex-client/src/default_client.rs b/codex-rs/http-client/src/default_client.rs similarity index 93% rename from codex-rs/codex-client/src/default_client.rs rename to codex-rs/http-client/src/default_client.rs index 56b3ce4b1634..b4465e7f8cf2 100644 --- a/codex-rs/codex-client/src/default_client.rs +++ b/codex-rs/http-client/src/default_client.rs @@ -14,47 +14,47 @@ use tracing::Span; use tracing_opentelemetry::OpenTelemetrySpanExt; #[derive(Clone, Debug)] -pub struct CodexHttpClient { +pub struct HttpClient { inner: reqwest::Client, } -impl CodexHttpClient { +impl HttpClient { pub fn new(inner: reqwest::Client) -> Self { Self { inner } } - pub fn get(&self, url: U) -> CodexRequestBuilder + pub fn get(&self, url: U) -> RequestBuilder where U: IntoUrl, { self.request(Method::GET, url) } - pub fn post(&self, url: U) -> CodexRequestBuilder + pub fn post(&self, url: U) -> RequestBuilder where U: IntoUrl, { self.request(Method::POST, url) } - pub fn request(&self, method: Method, url: U) -> CodexRequestBuilder + pub fn request(&self, method: Method, url: U) -> RequestBuilder where U: IntoUrl, { let url_str = url.as_str().to_string(); - CodexRequestBuilder::new(self.inner.request(method.clone(), url), method, url_str) + RequestBuilder::new(self.inner.request(method.clone(), url), method, url_str) } } #[must_use = "requests are not sent unless `send` is awaited"] #[derive(Debug)] -pub struct CodexRequestBuilder { +pub struct RequestBuilder { builder: reqwest::RequestBuilder, method: Method, url: String, } -impl CodexRequestBuilder { +impl RequestBuilder { fn new(builder: reqwest::RequestBuilder, method: Method, url: String) -> Self { Self { builder, diff --git a/codex-rs/codex-client/src/error.rs b/codex-rs/http-client/src/error.rs similarity index 91% rename from codex-rs/codex-client/src/error.rs rename to codex-rs/http-client/src/error.rs index fa2bfb4f7976..6b4922902692 100644 --- a/codex-rs/codex-client/src/error.rs +++ b/codex-rs/http-client/src/error.rs @@ -1,3 +1,5 @@ +//! Errors returned by the shared Codex HTTP transport. + use http::HeaderMap; use http::StatusCode; use thiserror::Error; diff --git a/codex-rs/http-client/src/lib.rs b/codex-rs/http-client/src/lib.rs new file mode 100644 index 000000000000..5f90435452a2 --- /dev/null +++ b/codex-rs/http-client/src/lib.rs @@ -0,0 +1,40 @@ +mod chatgpt_cloudflare_cookies; +mod chatgpt_hosts; +mod custom_ca; +mod default_client; +mod error; +mod outbound_proxy; +mod request; +mod transport; + +pub use crate::chatgpt_cloudflare_cookies::with_chatgpt_cloudflare_cookie_store; +pub use crate::chatgpt_hosts::is_allowed_chatgpt_host; +pub use crate::custom_ca::BuildCustomCaTransportError; +/// Test-only subprocess hook for custom CA coverage. +/// +/// This stays public only so the `custom_ca_probe` binary target can reuse the shared helper. It +/// is hidden from normal docs because ordinary callers should use +/// [`build_reqwest_client_with_custom_ca`] instead. +#[doc(hidden)] +pub use crate::custom_ca::build_reqwest_client_for_subprocess_tests; +pub use crate::custom_ca::build_reqwest_client_with_custom_ca; +pub use crate::custom_ca::maybe_build_rustls_client_config_with_custom_ca; +pub use crate::default_client::HttpClient; +pub use crate::default_client::RequestBuilder; +pub use crate::error::StreamError; +pub use crate::error::TransportError; +pub use crate::outbound_proxy::BuildRouteAwareHttpClientError; +pub use crate::outbound_proxy::ClientRouteClass; +pub use crate::outbound_proxy::OutboundProxyConfig; +pub use crate::outbound_proxy::RouteFailureClass; +pub use crate::outbound_proxy::build_reqwest_client_for_route; +pub use crate::request::EncodedJsonBody; +pub use crate::request::PreparedRequestBody; +pub use crate::request::Request; +pub use crate::request::RequestBody; +pub use crate::request::RequestCompression; +pub use crate::request::Response; +pub use crate::transport::ByteStream; +pub use crate::transport::HttpTransport; +pub use crate::transport::ReqwestTransport; +pub use crate::transport::StreamResponse; diff --git a/codex-rs/codex-client/src/outbound_proxy.rs b/codex-rs/http-client/src/outbound_proxy.rs similarity index 99% rename from codex-rs/codex-client/src/outbound_proxy.rs rename to codex-rs/http-client/src/outbound_proxy.rs index ea6587052e91..e643698a8c3c 100644 --- a/codex-rs/codex-client/src/outbound_proxy.rs +++ b/codex-rs/http-client/src/outbound_proxy.rs @@ -1,4 +1,4 @@ -//! Conservative outbound proxy selection for resolver-aware clients. +//! Conservative outbound proxy selection for resolver-aware HTTP clients. //! //! When enabled, platform system discovery is tried first, explicit environment //! proxies are the fallback, and the final fallback is a direct connection. diff --git a/codex-rs/codex-client/src/outbound_proxy/macos.rs b/codex-rs/http-client/src/outbound_proxy/macos.rs similarity index 99% rename from codex-rs/codex-client/src/outbound_proxy/macos.rs rename to codex-rs/http-client/src/outbound_proxy/macos.rs index 3913ecc40b36..fe07a6b498de 100644 --- a/codex-rs/codex-client/src/outbound_proxy/macos.rs +++ b/codex-rs/http-client/src/outbound_proxy/macos.rs @@ -1,3 +1,5 @@ +//! macOS system proxy resolution through SystemConfiguration and CFNetwork. + use std::ffi::c_void; use std::ptr; use std::time::Duration; diff --git a/codex-rs/codex-client/src/outbound_proxy/windows.rs b/codex-rs/http-client/src/outbound_proxy/windows.rs similarity index 99% rename from codex-rs/codex-client/src/outbound_proxy/windows.rs rename to codex-rs/http-client/src/outbound_proxy/windows.rs index 9e6419670541..4ff927723aff 100644 --- a/codex-rs/codex-client/src/outbound_proxy/windows.rs +++ b/codex-rs/http-client/src/outbound_proxy/windows.rs @@ -1,3 +1,5 @@ +//! Windows system proxy resolution through WinHTTP. + use std::ffi::c_void; use std::ptr; diff --git a/codex-rs/codex-client/src/outbound_proxy/windows_tests.rs b/codex-rs/http-client/src/outbound_proxy/windows_tests.rs similarity index 93% rename from codex-rs/codex-client/src/outbound_proxy/windows_tests.rs rename to codex-rs/http-client/src/outbound_proxy/windows_tests.rs index 3772c057dbb6..589c06661cb3 100644 --- a/codex-rs/codex-client/src/outbound_proxy/windows_tests.rs +++ b/codex-rs/http-client/src/outbound_proxy/windows_tests.rs @@ -1,3 +1,5 @@ +//! Windows proxy parsing tests. + use super::*; #[test] diff --git a/codex-rs/codex-client/src/outbound_proxy_tests.rs b/codex-rs/http-client/src/outbound_proxy_tests.rs similarity index 99% rename from codex-rs/codex-client/src/outbound_proxy_tests.rs rename to codex-rs/http-client/src/outbound_proxy_tests.rs index 46868e8f7372..60a1d563967e 100644 --- a/codex-rs/codex-client/src/outbound_proxy_tests.rs +++ b/codex-rs/http-client/src/outbound_proxy_tests.rs @@ -1,3 +1,5 @@ +//! Shared outbound proxy policy tests. + use super::*; use pretty_assertions::assert_eq; use std::io::Read; diff --git a/codex-rs/codex-client/src/request.rs b/codex-rs/http-client/src/request.rs similarity index 98% rename from codex-rs/codex-client/src/request.rs rename to codex-rs/http-client/src/request.rs index 6d50f7cb5715..7608860c3daa 100644 --- a/codex-rs/codex-client/src/request.rs +++ b/codex-rs/http-client/src/request.rs @@ -1,7 +1,7 @@ use bytes::Bytes; +use http::HeaderMap; +use http::HeaderValue; use http::Method; -use reqwest::header::HeaderMap; -use reqwest::header::HeaderValue; use serde::Serialize; use serde_json::Value; use std::time::Duration; @@ -121,7 +121,7 @@ impl Request { Some(RequestBody::Json(_) | RequestBody::EncodedJson(_)) ); let trace_bytes = if self.compression != RequestCompression::None - && tracing::enabled!(target: "codex_client::transport", tracing::Level::TRACE) + && tracing::enabled!(target: "codex_http_client::transport", tracing::Level::TRACE) { match self.body.as_ref() { Some(RequestBody::Json(body)) => Some(Bytes::from( diff --git a/codex-rs/codex-client/src/transport.rs b/codex-rs/http-client/src/transport.rs similarity index 94% rename from codex-rs/codex-client/src/transport.rs rename to codex-rs/http-client/src/transport.rs index 708b73bf2b6f..bda6c28ee9f6 100644 --- a/codex-rs/codex-client/src/transport.rs +++ b/codex-rs/http-client/src/transport.rs @@ -1,5 +1,5 @@ -use crate::default_client::CodexHttpClient; -use crate::default_client::CodexRequestBuilder; +use crate::default_client::HttpClient; +use crate::default_client::RequestBuilder; use crate::error::TransportError; use crate::request::Request; use crate::request::RequestBody; @@ -35,17 +35,17 @@ pub trait HttpTransport: Send + Sync { #[derive(Clone, Debug)] pub struct ReqwestTransport { - client: CodexHttpClient, + client: HttpClient, } impl ReqwestTransport { pub fn new(client: reqwest::Client) -> Self { Self { - client: CodexHttpClient::new(client), + client: HttpClient::new(client), } } - fn build(&self, req: Request) -> Result { + fn build(&self, req: Request) -> Result { let prepared = req.prepare_body_for_send().map_err(TransportError::Build)?; let Request { diff --git a/codex-rs/codex-client/tests/ca_env.rs b/codex-rs/http-client/tests/ca_env.rs similarity index 99% rename from codex-rs/codex-client/tests/ca_env.rs rename to codex-rs/http-client/tests/ca_env.rs index 7ddd138a337a..f35225fee778 100644 --- a/codex-rs/codex-client/tests/ca_env.rs +++ b/codex-rs/http-client/tests/ca_env.rs @@ -3,7 +3,7 @@ //! //! These tests intentionally run through `custom_ca_probe` and //! `build_reqwest_client_for_subprocess_tests` instead of calling the helper in-process. The -//! detailed explanation of what "hermetic" means here lives in `codex_client::custom_ca`; these +//! detailed explanation of what "hermetic" means here lives in `codex_http_client::custom_ca`; these //! tests add the process-level half of that contract by scrubbing inherited CA environment //! variables before each subprocess launch. Most assertions here cover CA file selection, PEM //! parsing, and user-facing errors. The HTTPS probes go further and perform real POSTs against diff --git a/codex-rs/codex-client/tests/fixtures/test-ca-trusted.pem b/codex-rs/http-client/tests/fixtures/test-ca-trusted.pem similarity index 100% rename from codex-rs/codex-client/tests/fixtures/test-ca-trusted.pem rename to codex-rs/http-client/tests/fixtures/test-ca-trusted.pem diff --git a/codex-rs/codex-client/tests/fixtures/test-ca.pem b/codex-rs/http-client/tests/fixtures/test-ca.pem similarity index 100% rename from codex-rs/codex-client/tests/fixtures/test-ca.pem rename to codex-rs/http-client/tests/fixtures/test-ca.pem diff --git a/codex-rs/codex-client/tests/fixtures/test-intermediate.pem b/codex-rs/http-client/tests/fixtures/test-intermediate.pem similarity index 100% rename from codex-rs/codex-client/tests/fixtures/test-intermediate.pem rename to codex-rs/http-client/tests/fixtures/test-intermediate.pem