diff --git a/codex-rs/codex-client/src/outbound_proxy.rs b/codex-rs/codex-client/src/outbound_proxy.rs index ef51db4eb0..c444cfc57f 100644 --- a/codex-rs/codex-client/src/outbound_proxy.rs +++ b/codex-rs/codex-client/src/outbound_proxy.rs @@ -32,12 +32,12 @@ mod windows; /// How a resolver-aware client should choose an outbound proxy. #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] pub enum OutboundProxyMode { - /// Preserve explicit env proxy behavior, then use supported system discovery. + /// Preserve explicit env proxy behavior, then use explicit system proxy/PAC settings. #[default] Auto, /// Preserve only the existing reqwest/env proxy path. Env, - /// Require supported system discovery after explicit config/env handling. + /// Require supported system discovery, including WPAD auto-detect on supported platforms. System, /// Disable proxy use for this client. Direct, @@ -62,12 +62,6 @@ impl fmt::Debug for OutboundProxyConfig { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -enum ProxyCacheMode { - Use, - Bypass, -} - /// Error while building a resolver-aware reqwest client. #[derive(Debug, Error)] pub enum BuildProxiedHttpClientError { @@ -101,8 +95,7 @@ pub fn build_reqwest_client_for_route( target: RouteTarget, config: Option<&OutboundProxyConfig>, ) -> Result { - let builder = - configure_proxy_for_route(builder, request_url, target, config, ProxyCacheMode::Use)?; + let builder = configure_proxy_for_route(builder, request_url, target, config)?; build_reqwest_client_with_custom_ca(builder).map_err(Into::into) } @@ -111,7 +104,6 @@ fn configure_proxy_for_route( request_url: &str, target: RouteTarget, config: Option<&OutboundProxyConfig>, - cache_mode: ProxyCacheMode, ) -> Result { let config = config.cloned().unwrap_or_default(); let custom_ca_configured = custom_ca_env_configured(); @@ -196,7 +188,8 @@ fn configure_proxy_for_route( }; }; - match resolve_system_proxy(request_url, &origin, cache_mode) { + let include_auto_detect = config.mode == OutboundProxyMode::System; + match resolve_system_proxy(request_url, &origin, include_auto_detect) { SystemProxyDecision::Direct { source } => { RouteDiagnostic::direct(target, source, custom_ca_configured).emit_opt_in(); Ok(builder) @@ -262,8 +255,11 @@ impl RequestOrigin { Some(Self { scheme, host, port }) } - fn cache_key(&self) -> String { - format!("{}://{}:{}", self.scheme, self.host, self.port) + fn cache_key(&self, include_auto_detect: bool) -> String { + format!( + "{}://{}:{}:auto_detect={include_auto_detect}", + self.scheme, self.host, self.port + ) } } @@ -293,28 +289,31 @@ enum SystemProxyDecision { fn resolve_system_proxy( request_url: &str, origin: &RequestOrigin, - cache_mode: ProxyCacheMode, + include_auto_detect: bool, ) -> SystemProxyDecision { - if cache_mode == ProxyCacheMode::Use - && let Some(decision) = cached_system_proxy_decision(origin) - { + if let Some(decision) = cached_system_proxy_decision(origin, include_auto_detect) { return decision; } - let decision = resolve_platform_system_proxy(request_url, origin); - cache_system_proxy_decision(origin, decision.clone()); + let decision = resolve_platform_system_proxy(request_url, origin, include_auto_detect); + cache_system_proxy_decision(origin, include_auto_detect, decision.clone()); decision } #[cfg(target_os = "windows")] -fn resolve_platform_system_proxy(request_url: &str, origin: &RequestOrigin) -> SystemProxyDecision { - windows::resolve(request_url, origin) +fn resolve_platform_system_proxy( + request_url: &str, + origin: &RequestOrigin, + include_auto_detect: bool, +) -> SystemProxyDecision { + windows::resolve(request_url, origin, include_auto_detect) } #[cfg(not(target_os = "windows"))] fn resolve_platform_system_proxy( _request_url: &str, _origin: &RequestOrigin, + _include_auto_detect: bool, ) -> SystemProxyDecision { SystemProxyDecision::Unavailable { source: RouteSource::Unavailable, @@ -331,10 +330,13 @@ struct CachedSystemProxyDecision { static SYSTEM_PROXY_CACHE: OnceLock>> = OnceLock::new(); -fn cached_system_proxy_decision(origin: &RequestOrigin) -> Option { +fn cached_system_proxy_decision( + origin: &RequestOrigin, + include_auto_detect: bool, +) -> Option { let cache = SYSTEM_PROXY_CACHE.get_or_init(|| Mutex::new(HashMap::new())); let mut cache = cache.lock().ok()?; - let key = origin.cache_key(); + let key = origin.cache_key(include_auto_detect); let cached = cache.get(&key)?; if cached.expires_at > Instant::now() { return Some(cached.decision.clone()); @@ -343,11 +345,15 @@ fn cached_system_proxy_decision(origin: &RequestOrigin) -> Option SystemProxyDecision { +pub(super) fn resolve( + request_url: &str, + origin: &RequestOrigin, + include_auto_detect: bool, +) -> SystemProxyDecision { let ie_config = match current_user_ie_proxy_config() { Ok(config) => config, Err(failure) => { @@ -66,7 +70,7 @@ pub(super) fn resolve(request_url: &str, origin: &RequestOrigin) -> SystemProxyD } } - if ie_config.auto_detect { + if include_auto_detect && ie_config.auto_detect { let decision = resolve_with_auto_detect(request_url, origin); if !matches!(decision, SystemProxyDecision::Unavailable { .. }) { return decision; @@ -86,7 +90,7 @@ pub(super) fn resolve(request_url: &str, origin: &RequestOrigin) -> SystemProxyD return proxy_list_decision(proxy, origin, RouteSource::WindowsStatic); } - if ie_config.auto_config_url.is_some() || ie_config.auto_detect { + if ie_config.auto_config_url.is_some() || (include_auto_detect && ie_config.auto_detect) { SystemProxyDecision::Unavailable { source: RouteSource::WindowsWinHttpPac, failure: RouteFailureClass::PacUnavailable, diff --git a/codex-rs/login/src/auth/manager.rs b/codex-rs/login/src/auth/manager.rs index 1882263749..5c1cccc501 100644 --- a/codex-rs/login/src/auth/manager.rs +++ b/codex-rs/login/src/auth/manager.rs @@ -25,7 +25,6 @@ use codex_protocol::config_types::ForcedLoginMethod; use codex_protocol::config_types::ModelProviderAuthInfo; use super::external_bearer::BearerTokenRefresher; -use super::revoke::revoke_auth_tokens; use super::revoke::revoke_auth_tokens_with_proxy_config; pub use crate::auth::agent_identity::AgentIdentityAuth; pub use crate::auth::storage::AgentIdentityAuthRecord; diff --git a/codex-rs/login/src/auth/mod.rs b/codex-rs/login/src/auth/mod.rs index f31e7421b2..13ac192d76 100644 --- a/codex-rs/login/src/auth/mod.rs +++ b/codex-rs/login/src/auth/mod.rs @@ -11,6 +11,5 @@ mod revoke; pub use error::RefreshTokenFailedError; pub use error::RefreshTokenFailedReason; pub use manager::*; -pub(crate) use revoke::revoke_auth_tokens; pub(crate) use revoke::revoke_auth_tokens_with_proxy_config; pub(crate) use revoke::should_revoke_auth_tokens; diff --git a/codex-rs/login/src/auth/revoke.rs b/codex-rs/login/src/auth/revoke.rs index 4ff41c6467..306be5b86e 100644 --- a/codex-rs/login/src/auth/revoke.rs +++ b/codex-rs/login/src/auth/revoke.rs @@ -53,12 +53,6 @@ struct RevokeTokenRequest<'a> { client_id: Option<&'static str>, } -pub(crate) async fn revoke_auth_tokens( - auth_dot_json: Option<&AuthDotJson>, -) -> Result<(), std::io::Error> { - revoke_auth_tokens_with_proxy_config(auth_dot_json, /*outbound_proxy_config*/ None).await -} - pub(crate) async fn revoke_auth_tokens_with_proxy_config( auth_dot_json: Option<&AuthDotJson>, outbound_proxy_config: Option<&OutboundProxyConfig>,