mirror of
https://github.com/openai/codex.git
synced 2026-10-23 18:08:45 +00:00
Route exec-server HTTP through configured proxy policy (#35023)
## Why Delegated HTTP requests need to honor the same outbound proxy policy as the Codex process that starts the exec server. ## What changed - Pass the configured `HttpClientFactory` through local and remote exec-server startup and use route-aware client pools for delegated HTTP and local MCP requests. - Preserve per-request timeouts and follow-or-stop redirect behavior while keeping request URLs and sensitive response headers out of diagnostics. ## Testing - Cover configured system-proxy routing across the exec-server transport. - Cover both redirect policies and verify that success and failure logs do not expose request or response secrets. GitOrigin-RevId: 4af6aec1d265c4db62dfcb6e1fb076fb31736137
This commit is contained in:
@@ -5,6 +5,7 @@ use std::sync::atomic::Ordering;
|
||||
|
||||
use codex_exec_server_protocol::JSONRPCErrorError;
|
||||
use codex_exec_server_protocol::RequestId;
|
||||
use codex_http_client::HttpClientFactory;
|
||||
use serde_json::to_value;
|
||||
use std::collections::HashSet;
|
||||
use tokio::sync::Mutex;
|
||||
@@ -13,6 +14,7 @@ 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::protocol::CapabilityRootsDiscoverParams;
|
||||
use crate::protocol::CapabilityRootsDiscoverResponse;
|
||||
@@ -73,6 +75,7 @@ pub(crate) struct ExecServerHandler {
|
||||
background_tasks: TaskTracker,
|
||||
file_system: FileSystemHandler,
|
||||
runtime_paths: ExecServerRuntimePaths,
|
||||
http_client: ReqwestHttpClient,
|
||||
initialize_requested: AtomicBool,
|
||||
initialized: AtomicBool,
|
||||
}
|
||||
@@ -82,6 +85,7 @@ impl ExecServerHandler {
|
||||
session_registry: Arc<SessionRegistry>,
|
||||
notifications: RpcNotificationSender,
|
||||
runtime_paths: ExecServerRuntimePaths,
|
||||
http_client_factory: HttpClientFactory,
|
||||
) -> Self {
|
||||
Self {
|
||||
session_registry,
|
||||
@@ -92,6 +96,7 @@ impl ExecServerHandler {
|
||||
background_tasks: TaskTracker::new(),
|
||||
file_system: FileSystemHandler::new(runtime_paths.clone()),
|
||||
runtime_paths,
|
||||
http_client: ReqwestHttpClient::new(http_client_factory),
|
||||
initialize_requested: AtomicBool::new(false),
|
||||
initialized: AtomicBool::new(false),
|
||||
}
|
||||
@@ -222,7 +227,9 @@ impl ExecServerHandler {
|
||||
if stream_response {
|
||||
self.reserve_http_body_stream(&http_request_id).await?;
|
||||
}
|
||||
let response = ReqwestHttpRequestRunner::new(params.timeout_ms, params.redirect_policy)?
|
||||
let response = self
|
||||
.http_client
|
||||
.runner(params.redirect_policy)
|
||||
.run(params)
|
||||
.await;
|
||||
if response.is_err() && stream_response {
|
||||
|
||||
@@ -2,6 +2,8 @@ use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use codex_http_client::HttpClientFactory;
|
||||
use codex_http_client::OutboundProxyPolicy;
|
||||
use codex_utils_path_uri::PathUri;
|
||||
use pretty_assertions::assert_eq;
|
||||
use tokio::sync::mpsc;
|
||||
@@ -88,6 +90,7 @@ async fn initialized_handler() -> Arc<ExecServerHandler> {
|
||||
registry,
|
||||
RpcNotificationSender::new(outgoing_tx),
|
||||
test_runtime_paths(),
|
||||
HttpClientFactory::new(OutboundProxyPolicy::ReqwestDefault),
|
||||
));
|
||||
let initialize_response = handler
|
||||
.initialize(InitializeParams {
|
||||
@@ -166,6 +169,7 @@ async fn long_poll_read_fails_after_session_resume() {
|
||||
Arc::clone(®istry),
|
||||
RpcNotificationSender::new(first_tx),
|
||||
test_runtime_paths(),
|
||||
HttpClientFactory::new(OutboundProxyPolicy::ReqwestDefault),
|
||||
));
|
||||
let initialize_response = first_handler
|
||||
.initialize(InitializeParams {
|
||||
@@ -206,6 +210,7 @@ async fn long_poll_read_fails_after_session_resume() {
|
||||
registry,
|
||||
RpcNotificationSender::new(second_tx),
|
||||
test_runtime_paths(),
|
||||
HttpClientFactory::new(OutboundProxyPolicy::ReqwestDefault),
|
||||
));
|
||||
second_handler
|
||||
.initialize(InitializeParams {
|
||||
@@ -239,6 +244,7 @@ async fn active_session_resume_is_rejected() {
|
||||
Arc::clone(®istry),
|
||||
RpcNotificationSender::new(first_tx),
|
||||
test_runtime_paths(),
|
||||
HttpClientFactory::new(OutboundProxyPolicy::ReqwestDefault),
|
||||
));
|
||||
let initialize_response = first_handler
|
||||
.initialize(InitializeParams {
|
||||
@@ -253,6 +259,7 @@ async fn active_session_resume_is_rejected() {
|
||||
registry,
|
||||
RpcNotificationSender::new(second_tx),
|
||||
test_runtime_paths(),
|
||||
HttpClientFactory::new(OutboundProxyPolicy::ReqwestDefault),
|
||||
));
|
||||
let err = second_handler
|
||||
.initialize(InitializeParams {
|
||||
@@ -281,6 +288,7 @@ async fn output_and_exit_are_retained_after_notification_receiver_closes() {
|
||||
SessionRegistry::new(crate::ExecServerTelemetry::default()),
|
||||
RpcNotificationSender::new(outgoing_tx),
|
||||
test_runtime_paths(),
|
||||
HttpClientFactory::new(OutboundProxyPolicy::ReqwestDefault),
|
||||
));
|
||||
handler
|
||||
.initialize(InitializeParams {
|
||||
|
||||
@@ -21,28 +21,38 @@ use crate::server::registry::build_router;
|
||||
use crate::server::session_registry::SessionRegistry;
|
||||
use crate::telemetry::ConnectionTransport;
|
||||
use crate::telemetry::ExecServerTelemetry;
|
||||
use codex_http_client::HttpClientFactory;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct ConnectionProcessor {
|
||||
session_registry: Arc<SessionRegistry>,
|
||||
runtime_paths: ExecServerRuntimePaths,
|
||||
telemetry: ExecServerTelemetry,
|
||||
http_client_factory: HttpClientFactory,
|
||||
}
|
||||
|
||||
impl ConnectionProcessor {
|
||||
#[cfg(test)]
|
||||
pub(crate) fn new(runtime_paths: ExecServerRuntimePaths) -> Self {
|
||||
Self::new_with_telemetry(runtime_paths, ExecServerTelemetry::default())
|
||||
Self::new_with_telemetry(
|
||||
runtime_paths,
|
||||
ExecServerTelemetry::default(),
|
||||
codex_http_client::HttpClientFactory::new(
|
||||
codex_http_client::OutboundProxyPolicy::ReqwestDefault,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn new_with_telemetry(
|
||||
runtime_paths: ExecServerRuntimePaths,
|
||||
telemetry: ExecServerTelemetry,
|
||||
http_client_factory: HttpClientFactory,
|
||||
) -> Self {
|
||||
Self {
|
||||
session_registry: SessionRegistry::new(telemetry.clone()),
|
||||
runtime_paths,
|
||||
telemetry,
|
||||
http_client_factory,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,6 +66,7 @@ impl ConnectionProcessor {
|
||||
Arc::clone(&self.session_registry),
|
||||
self.runtime_paths.clone(),
|
||||
self.telemetry.clone(),
|
||||
self.http_client_factory.clone(),
|
||||
transport,
|
||||
)
|
||||
.await;
|
||||
@@ -71,6 +82,7 @@ async fn run_connection(
|
||||
session_registry: Arc<SessionRegistry>,
|
||||
runtime_paths: ExecServerRuntimePaths,
|
||||
telemetry: ExecServerTelemetry,
|
||||
http_client_factory: HttpClientFactory,
|
||||
transport: ConnectionTransport,
|
||||
) {
|
||||
let _connection_metrics = telemetry.connection_started(transport);
|
||||
@@ -90,6 +102,7 @@ async fn run_connection(
|
||||
session_registry,
|
||||
notifications,
|
||||
runtime_paths,
|
||||
http_client_factory,
|
||||
));
|
||||
|
||||
let outbound_task = tokio::spawn(async move {
|
||||
@@ -530,6 +543,9 @@ mod tests {
|
||||
registry,
|
||||
test_runtime_paths(),
|
||||
crate::ExecServerTelemetry::default(),
|
||||
codex_http_client::HttpClientFactory::new(
|
||||
codex_http_client::OutboundProxyPolicy::ReqwestDefault,
|
||||
),
|
||||
crate::telemetry::ConnectionTransport::Stdio,
|
||||
));
|
||||
(client_writer, BufReader::new(client_reader).lines(), task)
|
||||
|
||||
@@ -12,6 +12,7 @@ use axum::response::IntoResponse;
|
||||
use axum::response::Response;
|
||||
use axum::routing::any;
|
||||
use axum::routing::get;
|
||||
use codex_http_client::HttpClientFactory;
|
||||
use std::io::Write as _;
|
||||
use std::net::SocketAddr;
|
||||
use tokio::io;
|
||||
@@ -83,20 +84,32 @@ pub(crate) async fn run_transport(
|
||||
listen_url: &str,
|
||||
runtime_paths: ExecServerRuntimePaths,
|
||||
telemetry: ExecServerTelemetry,
|
||||
http_client_factory: HttpClientFactory,
|
||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
match parse_listen_url(listen_url)? {
|
||||
ExecServerListenTransport::WebSocket(bind_address) => {
|
||||
run_websocket_listener(bind_address, runtime_paths, telemetry).await
|
||||
run_websocket_listener(bind_address, runtime_paths, telemetry, http_client_factory)
|
||||
.await
|
||||
}
|
||||
ExecServerListenTransport::Stdio => {
|
||||
run_stdio_connection(runtime_paths, telemetry, http_client_factory).await
|
||||
}
|
||||
ExecServerListenTransport::Stdio => run_stdio_connection(runtime_paths, telemetry).await,
|
||||
}
|
||||
}
|
||||
|
||||
async fn run_stdio_connection(
|
||||
runtime_paths: ExecServerRuntimePaths,
|
||||
telemetry: ExecServerTelemetry,
|
||||
http_client_factory: HttpClientFactory,
|
||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
run_stdio_connection_with_io(io::stdin(), io::stdout(), runtime_paths, telemetry).await
|
||||
run_stdio_connection_with_io(
|
||||
io::stdin(),
|
||||
io::stdout(),
|
||||
runtime_paths,
|
||||
telemetry,
|
||||
http_client_factory,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn run_stdio_connection_with_io<R, W>(
|
||||
@@ -104,12 +117,14 @@ async fn run_stdio_connection_with_io<R, W>(
|
||||
writer: W,
|
||||
runtime_paths: ExecServerRuntimePaths,
|
||||
telemetry: ExecServerTelemetry,
|
||||
http_client_factory: HttpClientFactory,
|
||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync>>
|
||||
where
|
||||
R: AsyncRead + Unpin + Send + 'static,
|
||||
W: AsyncWrite + Unpin + Send + 'static,
|
||||
{
|
||||
let processor = ConnectionProcessor::new_with_telemetry(runtime_paths, telemetry);
|
||||
let processor =
|
||||
ConnectionProcessor::new_with_telemetry(runtime_paths, telemetry, http_client_factory);
|
||||
tracing::info!("codex-exec-server listening on stdio");
|
||||
processor
|
||||
.run_connection(
|
||||
@@ -126,10 +141,12 @@ async fn run_websocket_listener(
|
||||
bind_address: SocketAddr,
|
||||
runtime_paths: ExecServerRuntimePaths,
|
||||
telemetry: ExecServerTelemetry,
|
||||
http_client_factory: HttpClientFactory,
|
||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
let listener = TcpListener::bind(bind_address).await?;
|
||||
let local_addr = listener.local_addr()?;
|
||||
let processor = ConnectionProcessor::new_with_telemetry(runtime_paths, telemetry);
|
||||
let processor =
|
||||
ConnectionProcessor::new_with_telemetry(runtime_paths, telemetry, http_client_factory);
|
||||
info!("codex-exec-server listening on ws://{local_addr}");
|
||||
println!("ws://{local_addr}");
|
||||
std::io::stdout().flush()?;
|
||||
|
||||
@@ -62,6 +62,9 @@ async fn stdio_listen_transport_serves_initialize() {
|
||||
server_writer,
|
||||
test_runtime_paths(),
|
||||
crate::ExecServerTelemetry::default(),
|
||||
codex_http_client::HttpClientFactory::new(
|
||||
codex_http_client::OutboundProxyPolicy::ReqwestDefault,
|
||||
),
|
||||
));
|
||||
let mut client_lines = BufReader::new(client_reader).lines();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user