core: use HTTP Responses with system proxy

This commit is contained in:
Michael Bolin
2026-07-06 20:33:22 -07:00
parent c6c9f051a2
commit 94cecddf8a
2 changed files with 63 additions and 2 deletions

View File

@@ -64,6 +64,7 @@ use codex_api::create_text_param_for_request;
use codex_api::response_create_client_metadata;
use codex_http_client::ClientRouteClass;
use codex_http_client::HttpClientFactory;
use codex_http_client::OutboundProxyPolicy;
use codex_login::AuthManager;
use codex_login::CodexAuth;
use codex_login::RefreshTokenError;
@@ -912,9 +913,14 @@ impl ModelClient {
/// Returns whether the Responses-over-WebSocket transport is active for this session.
///
/// WebSocket use is controlled by provider capability and session-scoped fallback state.
/// WebSocket use is controlled by outbound proxy policy, provider capability, and
/// session-scoped fallback state. System-proxy sessions conservatively use HTTP until the
/// WebSocket transport supports proxy-aware dialing.
pub fn responses_websocket_enabled(&self) -> bool {
if !self.state.provider.info().supports_websockets
if matches!(
self.http_client_factory.outbound_proxy_policy(),
OutboundProxyPolicy::RespectSystemProxy
) || !self.state.provider.info().supports_websockets
|| self.state.disable_websockets.load(Ordering::Relaxed)
{
return false;

View File

@@ -1,4 +1,5 @@
use anyhow::Result;
use codex_config::CONFIG_TOML_FILE;
use codex_model_provider_info::WireApi;
use codex_protocol::models::PermissionProfile;
use codex_protocol::protocol::AskForApproval;
@@ -26,6 +27,60 @@ use wiremock::http::Method;
use wiremock::matchers::method;
use wiremock::matchers::path_regex;
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn system_proxy_policy_uses_http_without_websocket_handshake() -> Result<()> {
skip_if_no_network!(Ok(()));
let server = responses::start_mock_server().await;
let response_mock = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]),
)
.await;
let mut builder = test_codex()
.with_pre_build_hook(|codex_home| {
std::fs::write(
codex_home.join(CONFIG_TOML_FILE),
"[features]\nrespect_system_proxy = true\n",
)
.expect("system-proxy feature config should be written");
})
.with_config({
let base_url = format!("{}/v1", server.uri());
move |config| {
config.model_provider.base_url = Some(base_url);
config.model_provider.wire_api = WireApi::Responses;
config.model_provider.supports_websockets = true;
config.model_provider.request_max_retries = Some(0);
}
});
let test = builder.build_with_auto_env(&server).await?;
assert!(test.config.respect_system_proxy);
test.submit_turn("hello").await?;
let requests = server.received_requests().await.unwrap_or_default();
let websocket_attempts = requests
.iter()
.filter(|request| {
request.method == Method::GET && request.url.path().ends_with("/responses")
})
.count();
let http_attempts = requests
.iter()
.filter(|request| {
request.method == Method::POST && request.url.path().ends_with("/responses")
})
.count();
assert_eq!(websocket_attempts, 0);
assert_eq!(http_attempts, 1);
assert_eq!(response_mock.requests().len(), 1);
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn websocket_fallback_switches_to_http_on_upgrade_required_connect() -> Result<()> {
skip_if_no_network!(Ok(()));