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:
Celia Chen
2026-07-23 22:39:28 +00:00
committed by copyberry
parent fe0d472c4c
commit 1ee8f49175
25 changed files with 569 additions and 85 deletions
@@ -5,12 +5,13 @@
//! - in a remote environment, that means the remote runtime after the
//! orchestrator has forwarded `http/request` over JSON-RPC
use std::error::Error as StdError;
use std::time::Duration;
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 codex_http_client::ClientRouteClass;
use codex_http_client::HttpClientFactory;
use codex_http_client::RouteAwareClientPool;
use codex_http_client::RouteAwareRequestError;
use futures::FutureExt;
use futures::StreamExt;
use futures::future::BoxFuture;
@@ -35,10 +36,12 @@ use crate::rpc::RpcNotificationSender;
use crate::rpc::internal_error;
use crate::rpc::invalid_params;
/// `HttpClient` implementation that performs the actual HTTP request with
/// `reqwest`.
#[derive(Clone, Default)]
pub struct ReqwestHttpClient;
/// HTTP capability implementation backed by the shared route-aware transport.
#[derive(Clone)]
pub struct ReqwestHttpClient {
follow_redirects: RouteAwareClientPool,
stop_redirects: RouteAwareClientPool,
}
/// Streaming response state held between the initial HTTP response and
/// downstream body-delta forwarding.
@@ -50,26 +53,32 @@ pub(crate) struct PendingReqwestHttpBodyStream {
/// Validates `http/request` parameters and runs the actual `reqwest` call used
/// by the exec-server route and the local [`HttpClient`] backend.
pub(crate) struct ReqwestHttpRequestRunner {
client: reqwest::Client,
client: RouteAwareClientPool,
}
impl ReqwestHttpClient {
fn build_client(
timeout_ms: Option<u64>,
redirect_policy: HttpRedirectPolicy,
) -> Result<reqwest::Client, ExecServerError> {
let builder = match timeout_ms {
None => reqwest::Client::builder(),
Some(timeout_ms) => {
reqwest::Client::builder().timeout(Duration::from_millis(timeout_ms))
}
pub fn new(http_client_factory: HttpClientFactory) -> Self {
Self {
follow_redirects: RouteAwareClientPool::with_chatgpt_cloudflare_cookies_without_request_logging(
http_client_factory.clone(),
// Delegated HTTP targets arbitrary endpoints; route class only labels diagnostics.
ClientRouteClass::Other,
),
stop_redirects:
RouteAwareClientPool::with_chatgpt_cloudflare_cookies_without_redirects_or_request_logging(
http_client_factory,
// Proxy routing comes from the factory, not this diagnostic-only route class.
ClientRouteClass::Other,
),
}
}
pub(crate) fn runner(&self, redirect_policy: HttpRedirectPolicy) -> ReqwestHttpRequestRunner {
let client = match redirect_policy {
HttpRedirectPolicy::Follow => self.follow_redirects.clone(),
HttpRedirectPolicy::Stop => self.stop_redirects.clone(),
};
let builder = match redirect_policy {
HttpRedirectPolicy::Follow => builder,
HttpRedirectPolicy::Stop => builder.redirect(reqwest::redirect::Policy::none()),
};
build_reqwest_client_with_custom_ca(with_chatgpt_cloudflare_cookie_store(builder))
.map_err(|error| ExecServerError::HttpRequest(error.to_string()))
ReqwestHttpRequestRunner { client }
}
}
@@ -79,8 +88,7 @@ impl HttpClient for ReqwestHttpClient {
params: HttpRequestParams,
) -> BoxFuture<'_, Result<HttpRequestResponse, ExecServerError>> {
async move {
let runner = ReqwestHttpRequestRunner::new(params.timeout_ms, params.redirect_policy)
.map_err(|error| ExecServerError::HttpRequest(error.message))?;
let runner = self.runner(params.redirect_policy);
let (response, _) = runner
.run(HttpRequestParams {
stream_response: false,
@@ -98,8 +106,7 @@ impl HttpClient for ReqwestHttpClient {
params: HttpRequestParams,
) -> BoxFuture<'_, Result<(HttpRequestResponse, HttpResponseBodyStream), ExecServerError>> {
async move {
let runner = ReqwestHttpRequestRunner::new(params.timeout_ms, params.redirect_policy)
.map_err(|error| ExecServerError::HttpRequest(error.message))?;
let runner = self.runner(params.redirect_policy);
let (response, pending_stream) = runner
.run(HttpRequestParams {
stream_response: true,
@@ -122,15 +129,6 @@ impl HttpClient for ReqwestHttpClient {
}
impl ReqwestHttpRequestRunner {
pub(crate) fn new(
timeout_ms: Option<u64>,
redirect_policy: HttpRedirectPolicy,
) -> Result<Self, JSONRPCErrorError> {
let client = ReqwestHttpClient::build_client(timeout_ms, redirect_policy)
.map_err(|error| internal_error(error.to_string()))?;
Ok(Self { client })
}
pub(crate) async fn run(
&self,
params: HttpRequestParams,
@@ -164,6 +162,9 @@ impl ReqwestHttpRequestRunner {
if let Some(body) = params.body {
request = request.body(body.into_inner());
}
if let Some(timeout_ms) = params.timeout_ms {
request = request.timeout(Duration::from_millis(timeout_ms));
}
let response = match request.send().instrument(request_span.clone()).await {
Ok(response) => response,
@@ -301,25 +302,18 @@ impl ReqwestHttpRequestRunner {
}
}
fn log_send_error(method: &Method, error: reqwest::Error) {
let error = error.without_url();
let source_chain = error_source_chain(&error);
fn log_send_error(method: &Method, error: RouteAwareRequestError) {
let error_is_timeout = error.is_timeout();
let error_is_connect = error.is_connect();
let error = match error {
RouteAwareRequestError::Request(error) => error.without_url().to_string(),
error => error.to_string(),
};
tracing::warn!(
http_method = method.as_str(),
error_is_timeout = error.is_timeout(),
error_is_connect = error.is_connect(),
error_is_timeout,
error_is_connect,
error = %error,
error_sources = ?source_chain,
"http/request send failed"
);
}
fn error_source_chain(error: &reqwest::Error) -> Option<String> {
let mut sources = Vec::new();
let mut source = error.source();
while let Some(error) = source {
sources.push(error.to_string());
source = error.source();
}
(!sources.is_empty()).then(|| sources.join(": "))
}
+22 -6
View File
@@ -139,6 +139,7 @@ impl EnvironmentManager {
)])),
local_environment: Some(Arc::new(Environment::default_for_tests())),
local_runtime_paths: None,
// Test-only construction has no application config from which to resolve proxy policy.
http_client_factory: HttpClientFactory::new(OutboundProxyPolicy::ReqwestDefault),
}
}
@@ -163,6 +164,7 @@ impl EnvironmentManager {
match Self::from_snapshot(
provider.snapshot_inner(),
local_runtime_paths,
// Test-only construction has no application config from which to resolve proxy policy.
HttpClientFactory::new(OutboundProxyPolicy::ReqwestDefault),
) {
Ok(manager) => manager,
@@ -249,6 +251,7 @@ impl EnvironmentManager {
match Self::from_snapshot(
snapshot,
Some(local_runtime_paths),
// Test-only construction has no application config from which to resolve proxy policy.
HttpClientFactory::new(OutboundProxyPolicy::ReqwestDefault),
) {
Ok(manager) => manager,
@@ -274,7 +277,10 @@ impl EnvironmentManager {
"local environment requires configured runtime paths".to_string(),
)
})?;
let local_environment = Arc::new(Environment::local(local_runtime_paths));
let local_environment = Arc::new(Environment::local(
local_runtime_paths,
http_client_factory.clone(),
));
environment_map.insert(
LOCAL_ENVIRONMENT_ID.to_string(),
Arc::clone(&local_environment),
@@ -619,7 +625,10 @@ impl Environment {
startup_task: Arc::new(Mutex::new(None)),
exec_backend: Arc::new(LocalProcess::default()),
filesystem: Arc::new(LocalFileSystem::unsandboxed()),
http_client: Arc::new(ReqwestHttpClient),
// Test-only construction has no application config from which to resolve proxy policy.
http_client: Arc::new(ReqwestHttpClient::new(HttpClientFactory::new(
OutboundProxyPolicy::ReqwestDefault,
))),
local_runtime_paths: None,
}
}
@@ -661,13 +670,20 @@ impl Environment {
Ok(match exec_server_url {
Some(exec_server_url) => Self::remote_inner(exec_server_url, local_runtime_paths),
None => match local_runtime_paths {
Some(local_runtime_paths) => Self::local(local_runtime_paths),
Some(local_runtime_paths) => Self::local(
local_runtime_paths,
// This legacy constructor has no resolved application proxy configuration.
HttpClientFactory::new(OutboundProxyPolicy::ReqwestDefault),
),
None => Self::default_for_tests(),
},
})
}
pub(crate) fn local(local_runtime_paths: ExecServerRuntimePaths) -> Self {
pub(crate) fn local(
local_runtime_paths: ExecServerRuntimePaths,
http_client_factory: HttpClientFactory,
) -> Self {
Self {
remote_client: None,
ready_info: None,
@@ -678,7 +694,7 @@ impl Environment {
filesystem: Arc::new(LocalFileSystem::with_runtime_paths(
local_runtime_paths.clone(),
)),
http_client: Arc::new(ReqwestHttpClient),
http_client: Arc::new(ReqwestHttpClient::new(http_client_factory)),
local_runtime_paths: Some(local_runtime_paths),
}
}
@@ -1548,7 +1564,7 @@ mod tests {
#[tokio::test]
async fn local_environment_passes_runtime_paths_to_exec_backend() {
let environment = Environment::local(test_runtime_paths());
let environment = Environment::local(test_runtime_paths(), legacy_http_client_factory());
#[cfg(unix)]
let uri = "file://server/share/checkout";
#[cfg(windows)]
+27 -2
View File
@@ -4,6 +4,7 @@ use std::time::Instant;
use codex_api::AuthProvider;
use codex_api::SharedAuthProvider;
use codex_http_client::HttpClientFactory;
use futures::FutureExt;
use http::HeaderMap;
use http::HeaderName;
@@ -421,6 +422,7 @@ pub struct RemoteEnvironmentConfig {
pub name: String,
auth_provider: SharedAuthProvider,
telemetry: ExecServerTelemetry,
http_client_factory: HttpClientFactory,
}
impl std::fmt::Debug for RemoteEnvironmentConfig {
@@ -439,6 +441,7 @@ impl RemoteEnvironmentConfig {
base_url: String,
environment_id: String,
auth_provider: SharedAuthProvider,
http_client_factory: HttpClientFactory,
) -> Result<Self, ExecServerError> {
let environment_id = normalize_environment_id(environment_id)?;
Ok(Self {
@@ -447,6 +450,7 @@ impl RemoteEnvironmentConfig {
name: "codex-exec-server".to_string(),
auth_provider,
telemetry: ExecServerTelemetry::default(),
http_client_factory,
})
}
@@ -472,8 +476,11 @@ pub async fn run_remote_environment(
config.auth_provider.clone(),
config.telemetry.clone(),
)?;
let processor =
ConnectionProcessor::new_with_telemetry(runtime_paths, config.telemetry.clone());
let processor = ConnectionProcessor::new_with_telemetry(
runtime_paths,
config.telemetry.clone(),
config.http_client_factory.clone(),
);
let identity = NoiseChannelIdentity::generate().map_err(|error| {
ExecServerError::Protocol(format!("failed to generate Noise relay identity: {error}"))
})?;
@@ -672,6 +679,7 @@ mod tests {
use std::sync::Arc;
use codex_api::AuthProvider;
use codex_http_client::OutboundProxyPolicy;
use http::HeaderMap;
use http::HeaderValue;
use opentelemetry::trace::TracerProvider as _;
@@ -880,12 +888,29 @@ mod tests {
));
}
#[test]
fn remote_environment_config_preserves_http_client_factory_policy() {
let config = RemoteEnvironmentConfig::new(
"https://registry.example".to_string(),
"env-1".to_string(),
static_registry_auth_provider(),
HttpClientFactory::new(OutboundProxyPolicy::RespectSystemProxy),
)
.expect("config");
assert_eq!(
config.http_client_factory.outbound_proxy_policy(),
OutboundProxyPolicy::RespectSystemProxy
);
}
#[test]
fn debug_output_redacts_auth_provider() {
let config = RemoteEnvironmentConfig::new(
"https://registry.example".to_string(),
"env-1".to_string(),
static_registry_auth_provider(),
HttpClientFactory::new(OutboundProxyPolicy::ReqwestDefault),
)
.expect("config");
@@ -4,6 +4,8 @@ use std::time::Duration;
use anyhow::Result;
use codex_api::AuthProvider;
use codex_api::SharedAuthProvider;
use codex_http_client::HttpClientFactory;
use codex_http_client::OutboundProxyPolicy;
use http::HeaderMap;
use http::HeaderValue;
use tokio::io::AsyncReadExt;
@@ -59,6 +61,7 @@ async fn reconnect_reuses_registration_until_url_is_rejected() -> Result<()> {
registry.uri(),
"environment-requested".to_string(),
static_registry_auth_provider(),
HttpClientFactory::new(OutboundProxyPolicy::ReqwestDefault),
)?;
let environment_task = tokio::spawn(run_remote_environment(
config,
+14 -2
View File
@@ -13,12 +13,20 @@ pub use transport::ExecServerListenUrlParseError;
use crate::ExecServerRuntimePaths;
use crate::ExecServerTelemetry;
use codex_http_client::HttpClientFactory;
pub async fn run_main(
listen_url: &str,
runtime_paths: ExecServerRuntimePaths,
http_client_factory: HttpClientFactory,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
run_main_with_telemetry(listen_url, runtime_paths, ExecServerTelemetry::default()).await
run_main_with_telemetry(
listen_url,
runtime_paths,
ExecServerTelemetry::default(),
http_client_factory,
)
.await
}
#[tracing::instrument(
@@ -30,12 +38,15 @@ pub async fn run_main_with_telemetry(
listen_url: &str,
runtime_paths: ExecServerRuntimePaths,
telemetry: ExecServerTelemetry,
http_client_factory: HttpClientFactory,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
transport::run_transport(listen_url, runtime_paths, telemetry).await
transport::run_transport(listen_url, runtime_paths, telemetry, http_client_factory).await
}
#[cfg(test)]
mod tests {
use codex_http_client::HttpClientFactory;
use codex_http_client::OutboundProxyPolicy;
use opentelemetry::trace::TracerProvider as _;
use opentelemetry_sdk::trace::InMemorySpanExporter;
use opentelemetry_sdk::trace::SdkTracerProvider;
@@ -65,6 +76,7 @@ mod tests {
)
.expect("runtime paths"),
ExecServerTelemetry::default(),
HttpClientFactory::new(OutboundProxyPolicy::ReqwestDefault),
)
.await
.expect_err("invalid listen URL should fail");
+8 -1
View File
@@ -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(&registry),
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(&registry),
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 {
+17 -1
View File
@@ -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)
+22 -5
View File
@@ -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();