mirror of
https://github.com/openai/codex.git
synced 2026-10-20 17:38:45 +00:00
Decouple exec-server HTTP from reqwest types (#35059)
## What changed - Rename `ReqwestHttpClient` to `RouteAwareHttpClient` to reflect that delegated HTTP uses Codex's shared route-aware transport. - Use `codex_http_client` response and error types plus transport-neutral `http` and `url` types, removing the exec server's direct `reqwest` dependency. ## Testing - Cover fragment stripping and Unicode hostname normalization for delegated HTTP requests. GitOrigin-RevId: 8b0fc60a76004feb57198bfb4afb1371c9ceb1bd
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
//!
|
||||
//! This module is the facade for the environment-owned [`crate::HttpClient`]
|
||||
//! capability:
|
||||
//! - [`ReqwestHttpClient`] executes requests directly with `reqwest`
|
||||
//! - [`RouteAwareHttpClient`] executes requests through the shared transport
|
||||
//! - [`ExecServerClient`] forwards requests over the JSON-RPC transport
|
||||
//! - [`HttpResponseBodyStream`] presents buffered local bodies and streamed
|
||||
//! remote `http/request/bodyDelta` notifications through one byte-stream API
|
||||
@@ -13,14 +13,14 @@
|
||||
//! - remote runtime: serves the `http/request` RPC and runs the concrete local
|
||||
//! HTTP request there when the orchestrator uses [`ExecServerClient`]
|
||||
|
||||
#[path = "reqwest_http_client.rs"]
|
||||
mod reqwest_http_client;
|
||||
#[path = "http_response_body_stream.rs"]
|
||||
pub(crate) mod response_body_stream;
|
||||
#[path = "route_aware_http_client.rs"]
|
||||
mod route_aware_http_client;
|
||||
#[path = "rpc_http_client.rs"]
|
||||
mod rpc_http_client;
|
||||
|
||||
pub(crate) use reqwest_http_client::PendingReqwestHttpBodyStream;
|
||||
pub use reqwest_http_client::ReqwestHttpClient;
|
||||
pub(crate) use reqwest_http_client::ReqwestHttpRequestRunner;
|
||||
pub use response_body_stream::HttpResponseBodyStream;
|
||||
pub(crate) use route_aware_http_client::PendingRouteAwareHttpBodyStream;
|
||||
pub use route_aware_http_client::RouteAwareHttpClient;
|
||||
pub(crate) use route_aware_http_client::RouteAwareHttpRequestRunner;
|
||||
|
||||
@@ -10,8 +10,9 @@ use std::sync::Arc;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use bytes::Bytes;
|
||||
use codex_http_client::HttpError;
|
||||
use codex_http_client::HttpResponse;
|
||||
use futures::StreamExt;
|
||||
use reqwest::Response;
|
||||
use serde_json::Value;
|
||||
use serde_json::from_value;
|
||||
use tokio::runtime::Handle;
|
||||
@@ -55,7 +56,7 @@ pub(super) struct HttpBodyStreamRegistration {
|
||||
|
||||
enum HttpResponseBodyStreamInner {
|
||||
Local {
|
||||
body: Pin<Box<dyn futures::Stream<Item = Result<Bytes, reqwest::Error>> + Send>>,
|
||||
body: Pin<Box<dyn futures::Stream<Item = Result<Bytes, HttpError>> + Send>>,
|
||||
},
|
||||
Remote {
|
||||
inner: Arc<Inner>,
|
||||
@@ -77,7 +78,7 @@ pub struct HttpResponseBodyStream {
|
||||
}
|
||||
|
||||
impl HttpResponseBodyStream {
|
||||
pub(super) fn local(response: Response) -> Self {
|
||||
pub(super) fn local(response: HttpResponse) -> Self {
|
||||
Self {
|
||||
inner: HttpResponseBodyStreamInner::Local {
|
||||
body: Box::pin(response.bytes_stream()),
|
||||
|
||||
+23
-20
@@ -1,4 +1,4 @@
|
||||
//! `reqwest`-backed `HttpClient` implementation.
|
||||
//! Route-aware local HTTP capability implementation.
|
||||
//!
|
||||
//! This code runs wherever the real network request should originate:
|
||||
//! - in a local environment, that means the orchestrator process
|
||||
@@ -15,12 +15,12 @@ use codex_http_client::RouteAwareRequestError;
|
||||
use futures::FutureExt;
|
||||
use futures::StreamExt;
|
||||
use futures::future::BoxFuture;
|
||||
use reqwest::Method;
|
||||
use reqwest::Url;
|
||||
use reqwest::header::HeaderMap;
|
||||
use reqwest::header::HeaderName;
|
||||
use reqwest::header::HeaderValue;
|
||||
use http::HeaderMap;
|
||||
use http::HeaderName;
|
||||
use http::HeaderValue;
|
||||
use http::Method;
|
||||
use tracing::Instrument;
|
||||
use url::Url;
|
||||
|
||||
use super::HttpResponseBodyStream;
|
||||
use super::response_body_stream::send_body_delta;
|
||||
@@ -38,25 +38,25 @@ use crate::rpc::invalid_params;
|
||||
|
||||
/// HTTP capability implementation backed by the shared route-aware transport.
|
||||
#[derive(Clone)]
|
||||
pub struct ReqwestHttpClient {
|
||||
pub struct RouteAwareHttpClient {
|
||||
follow_redirects: RouteAwareClientPool,
|
||||
stop_redirects: RouteAwareClientPool,
|
||||
}
|
||||
|
||||
/// Streaming response state held between the initial HTTP response and
|
||||
/// downstream body-delta forwarding.
|
||||
pub(crate) struct PendingReqwestHttpBodyStream {
|
||||
pub(crate) struct PendingRouteAwareHttpBodyStream {
|
||||
pub(crate) request_id: String,
|
||||
pub(crate) response: reqwest::Response,
|
||||
pub(crate) response: codex_http_client::HttpResponse,
|
||||
}
|
||||
|
||||
/// Validates `http/request` parameters and runs the actual `reqwest` call used
|
||||
/// Validates `http/request` parameters and runs the actual HTTP call used
|
||||
/// by the exec-server route and the local [`HttpClient`] backend.
|
||||
pub(crate) struct ReqwestHttpRequestRunner {
|
||||
pub(crate) struct RouteAwareHttpRequestRunner {
|
||||
client: RouteAwareClientPool,
|
||||
}
|
||||
|
||||
impl ReqwestHttpClient {
|
||||
impl RouteAwareHttpClient {
|
||||
pub fn new(http_client_factory: HttpClientFactory) -> Self {
|
||||
Self {
|
||||
follow_redirects: RouteAwareClientPool::with_chatgpt_cloudflare_cookies_without_request_logging(
|
||||
@@ -73,16 +73,19 @@ impl ReqwestHttpClient {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn runner(&self, redirect_policy: HttpRedirectPolicy) -> ReqwestHttpRequestRunner {
|
||||
pub(crate) fn runner(
|
||||
&self,
|
||||
redirect_policy: HttpRedirectPolicy,
|
||||
) -> RouteAwareHttpRequestRunner {
|
||||
let client = match redirect_policy {
|
||||
HttpRedirectPolicy::Follow => self.follow_redirects.clone(),
|
||||
HttpRedirectPolicy::Stop => self.stop_redirects.clone(),
|
||||
};
|
||||
ReqwestHttpRequestRunner { client }
|
||||
RouteAwareHttpRequestRunner { client }
|
||||
}
|
||||
}
|
||||
|
||||
impl HttpClient for ReqwestHttpClient {
|
||||
impl HttpClient for RouteAwareHttpClient {
|
||||
fn http_request(
|
||||
&self,
|
||||
params: HttpRequestParams,
|
||||
@@ -128,11 +131,11 @@ impl HttpClient for ReqwestHttpClient {
|
||||
}
|
||||
}
|
||||
|
||||
impl ReqwestHttpRequestRunner {
|
||||
impl RouteAwareHttpRequestRunner {
|
||||
pub(crate) async fn run(
|
||||
&self,
|
||||
params: HttpRequestParams,
|
||||
) -> Result<(HttpRequestResponse, Option<PendingReqwestHttpBodyStream>), JSONRPCErrorError>
|
||||
) -> Result<(HttpRequestResponse, Option<PendingRouteAwareHttpBodyStream>), JSONRPCErrorError>
|
||||
{
|
||||
let method = Method::from_bytes(params.method.as_bytes())
|
||||
.map_err(|error| invalid_params(format!("http/request method is invalid: {error}")))?;
|
||||
@@ -188,7 +191,7 @@ impl ReqwestHttpRequestRunner {
|
||||
headers,
|
||||
body: Vec::new().into(),
|
||||
},
|
||||
Some(PendingReqwestHttpBodyStream {
|
||||
Some(PendingRouteAwareHttpBodyStream {
|
||||
request_id: params.request_id,
|
||||
response,
|
||||
}),
|
||||
@@ -212,10 +215,10 @@ impl ReqwestHttpRequestRunner {
|
||||
}
|
||||
|
||||
pub(crate) async fn stream_body(
|
||||
pending_stream: PendingReqwestHttpBodyStream,
|
||||
pending_stream: PendingRouteAwareHttpBodyStream,
|
||||
notifications: RpcNotificationSender,
|
||||
) {
|
||||
let PendingReqwestHttpBodyStream {
|
||||
let PendingRouteAwareHttpBodyStream {
|
||||
request_id,
|
||||
response,
|
||||
} = pending_stream;
|
||||
@@ -6,7 +6,7 @@ use super::*;
|
||||
use crate::protocol::ExecOutputStream;
|
||||
use crate::protocol::ProcessOutputChunk;
|
||||
|
||||
fn registry_error(status: reqwest::StatusCode, code: Option<&str>) -> ExecServerError {
|
||||
fn registry_error(status: http::StatusCode, code: Option<&str>) -> ExecServerError {
|
||||
ExecServerError::EnvironmentRegistryHttp {
|
||||
status,
|
||||
code: code.map(str::to_string),
|
||||
@@ -37,7 +37,7 @@ fn registry_recovery_retry_delay_exponentially_backs_off_and_caps() {
|
||||
|
||||
#[test]
|
||||
fn recovery_retries_transient_registry_errors() {
|
||||
let error = registry_error(reqwest::StatusCode::TOO_MANY_REQUESTS, /*code*/ None);
|
||||
let error = registry_error(http::StatusCode::TOO_MANY_REQUESTS, /*code*/ None);
|
||||
|
||||
assert!(is_retryable_registry_error(&error));
|
||||
assert!(is_retryable_recovery_error(&error));
|
||||
@@ -45,7 +45,7 @@ fn recovery_retries_transient_registry_errors() {
|
||||
|
||||
#[test]
|
||||
fn recovery_retries_environment_offline_conflicts() {
|
||||
let error = registry_error(reqwest::StatusCode::CONFLICT, Some("environment_offline"));
|
||||
let error = registry_error(http::StatusCode::CONFLICT, Some("environment_offline"));
|
||||
|
||||
assert!(is_retryable_registry_error(&error));
|
||||
assert!(is_retryable_recovery_error(&error));
|
||||
@@ -53,7 +53,7 @@ fn recovery_retries_environment_offline_conflicts() {
|
||||
|
||||
#[test]
|
||||
fn recovery_does_not_retry_other_registry_conflicts() {
|
||||
let error = registry_error(reqwest::StatusCode::CONFLICT, Some("registration_conflict"));
|
||||
let error = registry_error(http::StatusCode::CONFLICT, Some("registration_conflict"));
|
||||
|
||||
assert!(!is_retryable_registry_error(&error));
|
||||
assert!(!is_retryable_recovery_error(&error));
|
||||
|
||||
@@ -20,7 +20,7 @@ use crate::HttpClient;
|
||||
use crate::NoiseChannelIdentity;
|
||||
use crate::NoiseRendezvousConnectProvider;
|
||||
use crate::client::LazyRemoteExecServerClient;
|
||||
use crate::client::http_client::ReqwestHttpClient;
|
||||
use crate::client::http_client::RouteAwareHttpClient;
|
||||
use crate::client_api::DEFAULT_REMOTE_EXEC_SERVER_CONNECT_TIMEOUT;
|
||||
use crate::client_api::ExecServerTransportParams;
|
||||
use crate::environment_bootstrap::PreparedEnvironmentManager;
|
||||
@@ -632,7 +632,7 @@ impl Environment {
|
||||
exec_backend: Arc::new(LocalProcess::default()),
|
||||
filesystem: Arc::new(LocalFileSystem::unsandboxed()),
|
||||
// Test-only construction has no application config from which to resolve proxy policy.
|
||||
http_client: Arc::new(ReqwestHttpClient::new(HttpClientFactory::new(
|
||||
http_client: Arc::new(RouteAwareHttpClient::new(HttpClientFactory::new(
|
||||
OutboundProxyPolicy::ReqwestDefault,
|
||||
))),
|
||||
local_runtime_paths: None,
|
||||
@@ -713,7 +713,7 @@ impl Environment {
|
||||
filesystem: Arc::new(LocalFileSystem::with_runtime_paths(
|
||||
local_runtime_paths.clone(),
|
||||
)),
|
||||
http_client: Arc::new(ReqwestHttpClient::new(http_client_factory)),
|
||||
http_client: Arc::new(RouteAwareHttpClient::new(http_client_factory)),
|
||||
local_runtime_paths: Some(local_runtime_paths),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ pub use capability_discovery_cache::ExecutorCapabilityDiscoveryCache;
|
||||
pub use client::ExecServerClient;
|
||||
pub use client::ExecServerError;
|
||||
pub use client::http_client::HttpResponseBodyStream;
|
||||
pub use client::http_client::ReqwestHttpClient;
|
||||
pub use client::http_client::RouteAwareHttpClient;
|
||||
pub use client_api::ExecServerClientConnectOptions;
|
||||
pub use client_api::HttpClient;
|
||||
pub use client_api::NoiseRendezvousConnectArgs;
|
||||
|
||||
@@ -12,7 +12,7 @@ use futures::FutureExt;
|
||||
use http::HeaderMap;
|
||||
use http::HeaderName;
|
||||
use http::HeaderValue;
|
||||
use reqwest::StatusCode;
|
||||
use http::StatusCode;
|
||||
use serde::Deserialize;
|
||||
use tokio::time::sleep;
|
||||
use tokio_tungstenite::tungstenite::client::IntoClientRequest;
|
||||
|
||||
@@ -13,9 +13,9 @@ use tokio_util::sync::CancellationToken;
|
||||
use tokio_util::task::TaskTracker;
|
||||
|
||||
use crate::ExecServerRuntimePaths;
|
||||
use crate::client::http_client::PendingReqwestHttpBodyStream;
|
||||
use crate::client::http_client::ReqwestHttpClient;
|
||||
use crate::client::http_client::ReqwestHttpRequestRunner;
|
||||
use crate::client::http_client::PendingRouteAwareHttpBodyStream;
|
||||
use crate::client::http_client::RouteAwareHttpClient;
|
||||
use crate::client::http_client::RouteAwareHttpRequestRunner;
|
||||
use crate::protocol::CapabilityRootsDiscoverParams;
|
||||
use crate::protocol::CapabilityRootsDiscoverResponse;
|
||||
use crate::protocol::EnvironmentInfo;
|
||||
@@ -75,7 +75,7 @@ pub(crate) struct ExecServerHandler {
|
||||
background_tasks: TaskTracker,
|
||||
file_system: FileSystemHandler,
|
||||
runtime_paths: ExecServerRuntimePaths,
|
||||
http_client: ReqwestHttpClient,
|
||||
http_client: RouteAwareHttpClient,
|
||||
initialize_requested: AtomicBool,
|
||||
initialized: AtomicBool,
|
||||
}
|
||||
@@ -96,7 +96,7 @@ impl ExecServerHandler {
|
||||
background_tasks: TaskTracker::new(),
|
||||
file_system: FileSystemHandler::new(runtime_paths.clone()),
|
||||
runtime_paths,
|
||||
http_client: ReqwestHttpClient::new(http_client_factory),
|
||||
http_client: RouteAwareHttpClient::new(http_client_factory),
|
||||
initialize_requested: AtomicBool::new(false),
|
||||
initialized: AtomicBool::new(false),
|
||||
}
|
||||
@@ -405,7 +405,7 @@ impl ExecServerHandler {
|
||||
|
||||
async fn start_http_body_stream(
|
||||
self: &Arc<Self>,
|
||||
pending_stream: PendingReqwestHttpBodyStream,
|
||||
pending_stream: PendingRouteAwareHttpBodyStream,
|
||||
) {
|
||||
let request_id = pending_stream.request_id.clone();
|
||||
if self.background_task_shutdown.is_cancelled() {
|
||||
@@ -419,7 +419,7 @@ impl ExecServerHandler {
|
||||
self.background_tasks.spawn(async move {
|
||||
tokio::select! {
|
||||
_ = shutdown.cancelled() => {}
|
||||
_ = ReqwestHttpRequestRunner::stream_body(pending_stream, notifications) => {}
|
||||
_ = RouteAwareHttpRequestRunner::stream_body(pending_stream, notifications) => {}
|
||||
}
|
||||
handler.release_http_body_stream(&finished_request_id).await;
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use reqwest::header::HeaderMap;
|
||||
use reqwest::header::HeaderValue;
|
||||
use http::HeaderMap;
|
||||
use http::HeaderValue;
|
||||
|
||||
pub(crate) fn current_trace_context_headers() -> HeaderMap {
|
||||
let mut headers = HeaderMap::new();
|
||||
|
||||
Reference in New Issue
Block a user