Honor system proxy settings in the daemon updater (#34495)

## Why

The PID-managed daemon updater downloaded the standalone installer directly,
bypassing Codex's configured outbound proxy policy.

## What changed

- Build the updater's HTTP client from the effective Codex configuration so
  `features.respect_system_proxy` also applies to installer downloads.
- Use the route-aware client pool for the installer request.
- Warn and fall back to the default `reqwest` proxy behavior when configuration
  cannot be loaded, allowing updates to continue.

## Testing

Added tests for proxy-policy selection, configuration-load fallback, exact
installer URL usage, byte preservation, and non-success HTTP responses.

GitOrigin-RevId: 559668eb40314d43afa6512da66e59e32cc1b93c
This commit is contained in:
Michael Bolin
2026-07-21 08:50:21 +00:00
committed by copyberry
parent 0e15c31d91
commit 99eb575649
8 changed files with 191 additions and 19 deletions
+52 -1
View File
@@ -1182,7 +1182,16 @@ async fn cli_main(
print_app_server_daemon_output(AppServerLifecycleCommand::Version).await?;
}
AppServerDaemonSubcommand::PidUpdateLoop => {
codex_app_server_daemon::run_pid_update_loop().await?;
let cli_overrides = root_config_overrides
.parse_overrides()
.map_err(anyhow::Error::msg)?;
let config = ConfigBuilder::default()
.cli_overrides(cli_overrides)
.build()
.await
.map_err(anyhow::Error::from);
let http_client_factory = updater_http_client_factory(config);
codex_app_server_daemon::run_pid_update_loop(http_client_factory).await?;
}
},
Some(AppServerSubcommand::Proxy(proxy_cli)) => {
@@ -2205,6 +2214,20 @@ async fn print_app_server_daemon_output(command: AppServerLifecycleCommand) -> a
Ok(())
}
fn updater_http_client_factory(
config: anyhow::Result<codex_core::config::Config>,
) -> codex_http_client::HttpClientFactory {
match config {
Ok(config) => config.http_client_factory(),
Err(error) => {
eprintln!("warning: failed to load updater network configuration: {error}");
codex_http_client::HttpClientFactory::new(
codex_http_client::OutboundProxyPolicy::ReqwestDefault,
)
}
}
}
async fn print_app_server_remote_control_output(
mode: AppServerRemoteControlMode,
) -> anyhow::Result<()> {
@@ -2501,6 +2524,34 @@ mod tests {
use codex_tui::TokenUsage;
use pretty_assertions::assert_eq;
#[tokio::test]
async fn updater_http_client_factory_honors_respect_system_proxy() {
let codex_home = tempfile::tempdir().expect("temporary Codex home");
let config = ConfigBuilder::default()
.codex_home(codex_home.path().to_path_buf())
.cli_overrides(vec![(
"features.respect_system_proxy".to_string(),
toml::Value::Boolean(true),
)])
.build()
.await
.expect("config should load");
assert_eq!(
updater_http_client_factory(Ok(config)).outbound_proxy_policy(),
codex_http_client::OutboundProxyPolicy::RespectSystemProxy
);
}
#[test]
fn updater_http_client_factory_falls_back_when_config_load_fails() {
assert_eq!(
updater_http_client_factory(Err(anyhow::anyhow!("invalid config")))
.outbound_proxy_policy(),
codex_http_client::OutboundProxyPolicy::ReqwestDefault
);
}
#[test]
fn exec_server_remote_auth_accepts_api_key_auth() {
let auth = CodexAuth::from_api_key("sk-test");