From 172303bbfa6f28e195033144056edf08717f568f Mon Sep 17 00:00:00 2001 From: viyatb-oai Date: Tue, 5 May 2026 10:50:59 -0700 Subject: [PATCH] chore: add minimal proxy egress diagnostics (#21220) ## Why Recent Auto Review reports show Git traffic hanging through the local proxy on both SSH and HTTPS paths. Today the support bundle does not make it obvious whether a request is stuck before upstream dialing, during the proxy hop, or after the upstream response begins, which slows down root-cause triage. This adds a small amount of runtime visibility at the existing proxy boundaries without changing routing or policy behavior. ## What changed - log whether HTTP and CONNECT traffic take the direct or upstream-proxy route - log start / success / failure timings for CONNECT, HTTP, and SOCKS5 upstream dials - log CONNECT forwarding lifecycle events - describe HTTP success at the response-header boundary that is actually observed, rather than implying the full body finished ## Verification - `cargo test -p codex-network-proxy` - `cargo clippy -p codex-network-proxy --all-targets -- -D warnings` --- codex-rs/network-proxy/src/http_proxy.rs | 47 +++++++++++++-- codex-rs/network-proxy/src/socks5.rs | 16 ++++- codex-rs/network-proxy/src/upstream.rs | 76 ++++++++++++++++++------ 3 files changed, 114 insertions(+), 25 deletions(-) diff --git a/codex-rs/network-proxy/src/http_proxy.rs b/codex-rs/network-proxy/src/http_proxy.rs index 658ee9f61..fd42fc92e 100644 --- a/codex-rs/network-proxy/src/http_proxy.rs +++ b/codex-rs/network-proxy/src/http_proxy.rs @@ -75,6 +75,7 @@ use std::convert::Infallible; use std::net::SocketAddr; use std::net::TcpListener as StdTcpListener; use std::sync::Arc; +use std::time::Instant; use tracing::error; use tracing::info; use tracing::warn; @@ -370,6 +371,16 @@ async fn http_connect_proxy(upgraded: Upgraded) -> Result<(), Infallible> { } else { None }; + match proxy.as_ref() { + Some(proxy) => info!( + "CONNECT route selected (host={}, port={}, route=upstream_proxy, proxy={})", + target.host, target.port, proxy.address + ), + None => info!( + "CONNECT route selected (host={}, port={}, route=direct)", + target.host, target.port + ), + } if let Err(err) = forward_connect_tunnel(upgraded, proxy, app_state).await { warn!("tunnel error: {err}"); @@ -402,21 +413,47 @@ async fn forward_connect_tunnel( let connector = TlsConnectorLayer::tunnel(None) .with_connector_data(tls_config) .into_layer(proxy_connector); - let EstablishedClientConnection { conn: target, .. } = - connector.connect(req).await.map_err(|err| { - OpaqueError::from_boxed(err) + info!("CONNECT upstream dial started (target={authority})"); + let connect_started_at = Instant::now(); + let EstablishedClientConnection { conn: target, .. } = match connector.connect(req).await { + Ok(connection) => { + info!( + "CONNECT upstream dial established (target={authority}, elapsed_ms={})", + connect_started_at.elapsed().as_millis() + ); + connection + } + Err(err) => { + warn!( + "CONNECT upstream dial failed (target={authority}, elapsed_ms={})", + connect_started_at.elapsed().as_millis() + ); + return Err(OpaqueError::from_boxed(err) .with_context(|| format!("establish CONNECT tunnel to {authority}")) - .into_boxed() - })?; + .into_boxed()); + } + }; let proxy_req = ProxyRequest { source: upgraded, target, }; + info!("CONNECT tunnel forwarding started (target={authority})"); + let forward_started_at = Instant::now(); StreamForwardService::default() .serve(proxy_req) .await + .map(|_| { + info!( + "CONNECT tunnel forwarding completed (target={authority}, elapsed_ms={})", + forward_started_at.elapsed().as_millis() + ); + }) .map_err(|err| { + warn!( + "CONNECT tunnel forwarding failed (target={authority}, elapsed_ms={})", + forward_started_at.elapsed().as_millis() + ); OpaqueError::from_boxed(err.into()) .with_context(|| format!("forward CONNECT tunnel to {authority}")) .into_boxed() diff --git a/codex-rs/network-proxy/src/socks5.rs b/codex-rs/network-proxy/src/socks5.rs index 2d4c05f95..a1c430c7d 100644 --- a/codex-rs/network-proxy/src/socks5.rs +++ b/codex-rs/network-proxy/src/socks5.rs @@ -40,6 +40,7 @@ use std::io; use std::net::SocketAddr; use std::net::TcpListener as StdTcpListener; use std::sync::Arc; +use std::time::Instant; use tracing::error; use tracing::info; use tracing::warn; @@ -290,7 +291,20 @@ async fn handle_socks5_tcp( } } - tcp_connector.serve(req).await + info!("SOCKS upstream dial started (host={host}, port={port})"); + let connect_started_at = Instant::now(); + let result = tcp_connector.serve(req).await; + match &result { + Ok(_) => info!( + "SOCKS upstream dial established (host={host}, port={port}, elapsed_ms={})", + connect_started_at.elapsed().as_millis() + ), + Err(_) => warn!( + "SOCKS upstream dial failed (host={host}, port={port}, elapsed_ms={})", + connect_started_at.elapsed().as_millis() + ), + } + result } async fn inspect_socks5_udp( diff --git a/codex-rs/network-proxy/src/upstream.rs b/codex-rs/network-proxy/src/upstream.rs index c7b67cc18..72b7290f1 100644 --- a/codex-rs/network-proxy/src/upstream.rs +++ b/codex-rs/network-proxy/src/upstream.rs @@ -3,7 +3,7 @@ use crate::state::NetworkProxyState; use rama_core::Layer; use rama_core::Service; use rama_core::error::BoxError; -use rama_core::error::ErrorContext as _; +use rama_core::error::ErrorExt as _; use rama_core::error::OpaqueError; use rama_core::extensions::ExtensionsMut; use rama_core::extensions::ExtensionsRef; @@ -21,6 +21,8 @@ use rama_net::http::RequestContext; use rama_tls_rustls::client::TlsConnectorDataBuilder; use rama_tls_rustls::client::TlsConnectorLayer; use std::sync::Arc; +use std::time::Instant; +use tracing::info; use tracing::warn; #[cfg(target_os = "macos")] @@ -41,13 +43,6 @@ impl ProxyConfig { Self { http, https, all } } - fn proxy_for_request(&self, req: &Request) -> Option { - let is_secure = RequestContext::try_from(req) - .map(|ctx| ctx.protocol.is_secure()) - .unwrap_or(false); - self.proxy_for_protocol(is_secure) - } - fn proxy_for_protocol(&self, is_secure: bool) -> Option { if is_secure { self.https @@ -155,28 +150,71 @@ impl Service> for UpstreamClient { type Error = OpaqueError; async fn serve(&self, mut req: Request) -> Result { - if let Some(proxy) = self.proxy_config.proxy_for_request(&req) { + let request_context = RequestContext::try_from(&req).ok(); + let authority = request_context + .as_ref() + .map(|ctx| ctx.host_with_port().to_string()) + .unwrap_or_else(|| "".to_string()); + let proxy = self.proxy_config.proxy_for_protocol( + request_context + .as_ref() + .map(|ctx| ctx.protocol.is_secure()) + .unwrap_or(false), + ); + match proxy.as_ref() { + Some(proxy) => info!( + "HTTP upstream route selected (target={authority}, route=upstream_proxy, proxy={})", + proxy.address + ), + None => info!("HTTP upstream route selected (target={authority}, route=direct)"), + } + if let Some(proxy) = proxy { req.extensions_mut().insert(proxy); } let uri = req.uri().clone(); + let connect_started_at = Instant::now(); let EstablishedClientConnection { input: mut req, conn: http_connection, - } = self - .connector - .serve(req) - .await - .map_err(OpaqueError::from_boxed)?; + } = match self.connector.serve(req).await { + Ok(connection) => { + info!( + "HTTP upstream connection established (target={authority}, elapsed_ms={})", + connect_started_at.elapsed().as_millis() + ); + connection + } + Err(err) => { + warn!( + "HTTP upstream connection failed (target={authority}, elapsed_ms={})", + connect_started_at.elapsed().as_millis() + ); + return Err(OpaqueError::from_boxed(err)); + } + }; req.extensions_mut() .extend(http_connection.extensions().clone()); - http_connection - .serve(req) - .await - .map_err(OpaqueError::from_boxed) - .with_context(|| format!("http request failure for uri: {uri}")) + let request_started_at = Instant::now(); + match http_connection.serve(req).await { + Ok(resp) => { + info!( + "HTTP upstream response headers received (target={authority}, elapsed_ms={})", + request_started_at.elapsed().as_millis() + ); + Ok(resp) + } + Err(err) => { + warn!( + "HTTP upstream response headers failed (target={authority}, elapsed_ms={})", + request_started_at.elapsed().as_millis() + ); + Err(OpaqueError::from_boxed(err) + .context(format!("http request failure for uri: {uri}"))) + } + } } }