http-client: extract shared HTTP transport

This commit is contained in:
Michael Bolin
2026-07-06 17:32:10 -07:00
parent 7226904ed1
commit cb5f515622
49 changed files with 233 additions and 173 deletions
+38 -27
View File
@@ -1936,6 +1936,7 @@ dependencies = [
"bytes",
"chrono",
"codex-client",
"codex-http-client",
"codex-protocol",
"codex-utils-rustls-provider",
"eventsource-stream",
@@ -2262,7 +2263,7 @@ dependencies = [
"anyhow",
"codex-api",
"codex-backend-openapi-models",
"codex-client",
"codex-http-client",
"codex-login",
"codex-model-provider",
"codex-protocol",
@@ -2392,33 +2393,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]]
@@ -2452,11 +2432,11 @@ dependencies = [
"anyhow",
"chrono",
"clap",
"codex-client",
"codex-cloud-tasks-client",
"codex-cloud-tasks-mock-client",
"codex-core",
"codex-git-utils",
"codex-http-client",
"codex-login",
"codex-model-provider",
"codex-tui",
@@ -2916,9 +2896,9 @@ dependencies = [
"bytes",
"clatter",
"codex-api",
"codex-client",
"codex-exec-server-protocol",
"codex-file-system",
"codex-http-client",
"codex-network-proxy",
"codex-otel",
"codex-protocol",
@@ -3214,6 +3194,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"
@@ -3305,8 +3316,8 @@ dependencies = [
"base64 0.22.1",
"chrono",
"codex-agent-identity",
"codex-client",
"codex-config",
"codex-http-client",
"codex-keyring-store",
"codex-model-provider-info",
"codex-otel",
@@ -3520,8 +3531,8 @@ dependencies = [
"codex-agent-identity",
"codex-api",
"codex-aws-auth",
"codex-client",
"codex-feedback",
"codex-http-client",
"codex-login",
"codex-model-provider-info",
"codex-models-manager",
+2
View File
@@ -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" }
+1 -1
View File
@@ -19,7 +19,7 @@ serde_json = "1"
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] }
codex-backend-openapi-models = { path = "../codex-backend-openapi-models" }
codex-api = { workspace = true }
codex-client = { workspace = true }
codex-http-client = { workspace = true }
codex-login = { workspace = true }
codex-model-provider = { workspace = true }
codex-protocol = { workspace = true }
+2 -2
View File
@@ -9,8 +9,8 @@ use crate::types::TokenUsageProfile;
use crate::types::TurnAttemptsSiblingTurnsResponse;
use anyhow::Result;
use codex_api::SharedAuthProvider;
use codex_client::build_reqwest_client_with_custom_ca;
use codex_client::with_chatgpt_cloudflare_cookie_store;
use codex_http_client::build_reqwest_client_with_custom_ca;
use codex_http_client::with_chatgpt_cloudflare_cookie_store;
use codex_login::CodexAuth;
use codex_login::default_client::get_codex_user_agent;
use codex_protocol::account::PlanType as AccountPlanType;
+1 -1
View File
@@ -16,7 +16,7 @@ workspace = true
anyhow = { workspace = true }
chrono = { workspace = true, features = ["serde"] }
clap = { workspace = true, features = ["derive"] }
codex-client = { workspace = true }
codex-http-client = { workspace = true }
codex-cloud-tasks-client = { workspace = true }
# TODO: codex-cloud-tasks-mock-client should be in dev-dependencies.
codex-cloud-tasks-mock-client = { workspace = true }
+1 -1
View File
@@ -1,4 +1,4 @@
use codex_client::build_reqwest_client_with_custom_ca;
use codex_http_client::build_reqwest_client_with_custom_ca;
use reqwest::header::CONTENT_TYPE;
use reqwest::header::HeaderMap;
use std::collections::HashMap;
+1
View File
@@ -10,6 +10,7 @@ base64 = { workspace = true }
bytes = { workspace = true }
chrono = { workspace = true }
codex-client = { workspace = true }
codex-http-client = { workspace = true }
codex-protocol = { workspace = true }
codex-utils-rustls-provider = { workspace = true }
futures = { workspace = true }
@@ -16,7 +16,7 @@ use crate::endpoint::realtime_websocket::protocol::parse_realtime_event;
use crate::error::ApiError;
use crate::provider::Provider;
use codex_client::backoff;
use codex_client::maybe_build_rustls_client_config_with_custom_ca;
use codex_http_client::maybe_build_rustls_client_config_with_custom_ca;
use codex_protocol::protocol::ConversationTextRole;
use codex_protocol::protocol::RealtimeTranscriptDelta;
use codex_utils_rustls_provider::ensure_rustls_crypto_provider;
@@ -11,7 +11,7 @@ use crate::sse::ResponsesStreamEvent;
use crate::sse::process_responses_event;
use crate::telemetry::WebsocketTelemetry;
use codex_client::TransportError;
use codex_client::maybe_build_rustls_client_config_with_custom_ca;
use codex_http_client::maybe_build_rustls_client_config_with_custom_ca;
use codex_utils_rustls_provider::ensure_rustls_crypto_provider;
use futures::SinkExt;
use futures::StreamExt;
+1 -1
View File
@@ -2,7 +2,7 @@ use std::time::Duration;
use crate::AuthProvider;
use bytes::Bytes;
use codex_client::build_reqwest_client_with_custom_ca;
use codex_http_client::build_reqwest_client_with_custom_ca;
use futures::Stream;
use reqwest::StatusCode;
use reqwest::header::CONTENT_LENGTH;
-1
View File
@@ -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",
)
+1 -33
View File
@@ -5,47 +5,15 @@ 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
+3 -3
View File
@@ -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.
+3 -39
View File
@@ -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::*;
+2 -2
View File
@@ -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;
+2 -2
View File
@@ -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;
+1 -1
View File
@@ -1,4 +1,4 @@
use crate::error::TransportError;
use codex_http_client::TransportError;
use http::StatusCode;
use std::time::Duration;
+1 -1
View File
@@ -17,7 +17,7 @@ base64 = { workspace = true }
bytes = { workspace = true }
clatter = { workspace = true }
codex-api = { workspace = true }
codex-client = { workspace = true }
codex-http-client = { workspace = true }
codex-exec-server-protocol = { workspace = true }
codex-file-system = { workspace = true }
codex-network-proxy = { workspace = true }
@@ -8,9 +8,9 @@
use std::error::Error as StdError;
use std::time::Duration;
use codex_client::build_reqwest_client_with_custom_ca;
use codex_client::with_chatgpt_cloudflare_cookie_store;
use codex_exec_server_protocol::JSONRPCErrorError;
use codex_http_client::build_reqwest_client_with_custom_ca;
use codex_http_client::with_chatgpt_cloudflare_cookie_store;
use futures::FutureExt;
use futures::StreamExt;
use futures::future::BoxFuture;
+7
View File
@@ -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",
)
+49
View File
@@ -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
+9
View File
@@ -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`.
@@ -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())
}
@@ -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.
@@ -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"];
@@ -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.
@@ -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<U>(&self, url: U) -> CodexRequestBuilder
pub fn get<U>(&self, url: U) -> RequestBuilder
where
U: IntoUrl,
{
self.request(Method::GET, url)
}
pub fn post<U>(&self, url: U) -> CodexRequestBuilder
pub fn post<U>(&self, url: U) -> RequestBuilder
where
U: IntoUrl,
{
self.request(Method::POST, url)
}
pub fn request<U>(&self, method: Method, url: U) -> CodexRequestBuilder
pub fn request<U>(&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,
@@ -1,3 +1,5 @@
//! Errors returned by the shared Codex HTTP transport.
use http::HeaderMap;
use http::StatusCode;
use thiserror::Error;
+40
View File
@@ -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;
@@ -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.
@@ -1,3 +1,5 @@
//! macOS system proxy resolution through SystemConfiguration and CFNetwork.
use std::ffi::c_void;
use std::ptr;
use std::time::Duration;
@@ -1,3 +1,5 @@
//! Windows system proxy resolution through WinHTTP.
use std::ffi::c_void;
use std::ptr;
@@ -1,3 +1,5 @@
//! Windows proxy parsing tests.
use super::*;
#[test]
@@ -1,3 +1,5 @@
//! Shared outbound proxy policy tests.
use super::*;
use pretty_assertions::assert_eq;
use std::io::Read;
@@ -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(
@@ -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<CodexRequestBuilder, TransportError> {
fn build(&self, req: Request) -> Result<RequestBuilder, TransportError> {
let prepared = req.prepare_body_for_send().map_err(TransportError::Build)?;
let Request {
@@ -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
+1 -1
View File
@@ -11,7 +11,7 @@ workspace = true
base64 = { workspace = true }
chrono = { workspace = true, features = ["serde"] }
codex-agent-identity = { workspace = true }
codex-client = { workspace = true }
codex-http-client = { workspace = true }
codex-config = { workspace = true }
codex-keyring-store = { workspace = true }
codex-model-provider-info = { workspace = true }
+13 -13
View File
@@ -1,17 +1,17 @@
//! Default Codex HTTP client: shared `User-Agent`, `originator`, optional residency header, and
//! reqwest/`CodexHttpClient` construction.
//! reqwest/`HttpClient` construction.
//!
//! Use [`crate::default_client`] or [`codex_login::default_client`] from other crates in this
//! workspace.
use codex_client::BuildCustomCaTransportError;
use codex_client::BuildRouteAwareHttpClientError;
use codex_client::ClientRouteClass;
use codex_client::CodexHttpClient;
pub use codex_client::CodexRequestBuilder;
use codex_client::build_reqwest_client_for_route;
use codex_client::build_reqwest_client_with_custom_ca;
use codex_client::with_chatgpt_cloudflare_cookie_store;
use codex_http_client::BuildCustomCaTransportError;
use codex_http_client::BuildRouteAwareHttpClientError;
use codex_http_client::ClientRouteClass;
use codex_http_client::HttpClient;
pub use codex_http_client::RequestBuilder as CodexRequestBuilder;
use codex_http_client::build_reqwest_client_for_route;
use codex_http_client::build_reqwest_client_with_custom_ca;
use codex_http_client::with_chatgpt_cloudflare_cookie_store;
use codex_terminal_detection::user_agent;
use reqwest::header::HeaderMap;
use reqwest::header::HeaderValue;
@@ -197,9 +197,9 @@ fn sanitize_user_agent(candidate: String, fallback: &str) -> String {
///
/// This supported default path preserves reqwest's existing proxy behavior and does not opt into
/// Codex's route-aware system/PAC resolution.
pub fn create_client() -> CodexHttpClient {
pub fn create_client() -> HttpClient {
let inner = build_reqwest_client();
CodexHttpClient::new(inner)
HttpClient::new(inner)
}
/// Builds the default reqwest client used for ordinary Codex HTTP traffic.
@@ -282,8 +282,8 @@ pub(crate) fn build_default_auth_reqwest_client(
pub(crate) fn create_default_auth_client(
endpoint: &str,
auth_route_config: Option<&AuthRouteConfig>,
) -> Result<CodexHttpClient, BuildRouteAwareHttpClientError> {
build_default_auth_reqwest_client(endpoint, auth_route_config).map(CodexHttpClient::new)
) -> Result<HttpClient, BuildRouteAwareHttpClientError> {
build_default_auth_reqwest_client(endpoint, auth_route_config).map(HttpClient::new)
}
pub fn default_headers() -> HeaderMap {
+4 -4
View File
@@ -55,8 +55,8 @@ use crate::outbound_proxy::AuthRouteConfig;
use crate::token_data::TokenData;
use crate::token_data::parse_chatgpt_jwt_claims;
use crate::token_data::parse_jwt_expiration;
use codex_client::CodexHttpClient;
use codex_config::types::AuthCredentialsStoreMode;
use codex_http_client::HttpClient;
use codex_protocol::account::PlanType as AccountPlanType;
use codex_protocol::auth::PlanType as InternalPlanType;
use codex_protocol::auth::RefreshTokenFailedError;
@@ -171,7 +171,7 @@ pub struct ChatgptAuthTokens {
#[derive(Debug, Clone)]
struct ChatgptAuthState {
auth_dot_json: Arc<Mutex<Option<AuthDotJson>>>,
client: CodexHttpClient,
client: HttpClient,
}
const TOKEN_REFRESH_INTERVAL: i64 = 8;
@@ -805,7 +805,7 @@ impl ChatgptAuth {
&self.storage
}
fn client(&self) -> &CodexHttpClient {
fn client(&self) -> &HttpClient {
&self.state.client
}
@@ -1331,7 +1331,7 @@ fn persist_tokens(
// The caller is responsible for persisting any returned tokens.
async fn request_chatgpt_token_refresh(
refresh_token: String,
client: &CodexHttpClient,
client: &HttpClient,
) -> Result<RefreshResponse, RefreshTokenError> {
let refresh_request = RefreshRequest {
client_id: oauth_client_id(),
@@ -1,4 +1,4 @@
use codex_client::CodexHttpClient;
use codex_http_client::HttpClient;
use codex_protocol::account::PlanType as AccountPlanType;
use codex_protocol::auth::PlanType as InternalPlanType;
use serde::Deserialize;
@@ -77,7 +77,7 @@ impl PersonalAccessTokenAuth {
}
async fn hydrate_personal_access_token(
client: &CodexHttpClient,
client: &HttpClient,
endpoint: &str,
access_token: &str,
) -> std::io::Result<PersonalAccessTokenAuth> {
+3 -3
View File
@@ -7,7 +7,7 @@
use serde::Serialize;
use std::time::Duration;
use codex_client::CodexHttpClient;
use codex_http_client::HttpClient;
use codex_protocol::auth::AuthMode;
use super::manager::REFRESH_TOKEN_URL_OVERRIDE_ENV_VAR;
@@ -95,7 +95,7 @@ fn resolved_auth_mode(auth_dot_json: &AuthDotJson) -> AuthMode {
}
async fn revoke_oauth_token(
client: &CodexHttpClient,
client: &HttpClient,
endpoint: &str,
token: &str,
kind: RevokeTokenKind,
@@ -181,7 +181,7 @@ mod tests {
.mount(&server)
.await;
let client = CodexHttpClient::new(reqwest::Client::new());
let client = HttpClient::new(reqwest::Client::new());
let endpoint = format!("{}/oauth/revoke", server.uri());
let error = revoke_oauth_token(
&client,
+1 -1
View File
@@ -7,8 +7,8 @@ mod outbound_proxy;
mod pkce;
mod server;
pub use codex_client::BuildCustomCaTransportError as BuildLoginHttpClientError;
pub use codex_config::types::AuthCredentialsStoreMode;
pub use codex_http_client::BuildCustomCaTransportError as BuildLoginHttpClientError;
pub use device_code_auth::DeviceCode;
pub use device_code_auth::complete_device_code_login;
pub use device_code_auth::request_device_code;
+1 -1
View File
@@ -1,4 +1,4 @@
use codex_client::OutboundProxyConfig;
use codex_http_client::OutboundProxyConfig;
/// Auth-layer adapter around client-owned proxy policy.
///
+1 -1
View File
@@ -16,7 +16,7 @@ workspace = true
codex-api = { workspace = true }
codex-agent-identity = { workspace = true }
codex-aws-auth = { workspace = true }
codex-client = { workspace = true }
codex-http-client = { workspace = true }
codex-feedback = { workspace = true }
codex-login = { workspace = true }
codex-model-provider-info = { workspace = true }
@@ -6,9 +6,9 @@ use codex_api::SharedAuthProvider;
use codex_aws_auth::AwsAuthContext;
use codex_aws_auth::AwsAuthError;
use codex_aws_auth::AwsRequestToSign;
use codex_client::Request;
use codex_client::RequestBody;
use codex_client::RequestCompression;
use codex_http_client::Request;
use codex_http_client::RequestBody;
use codex_http_client::RequestCompression;
use codex_login::auth::BedrockApiKeyAuth;
use codex_model_provider_info::ModelProviderAwsAuthInfo;
use codex_protocol::error::CodexErr;