Improve codex doctor network diagnostics (#38918)

## What changed

- Probe the configured Responses inference endpoint with Codex's route-aware HTTP client, including configured proxy and custom CA behavior.
- Classify TLS, proxy authentication, proxy configuration, resolution, and timeout failures into actionable diagnostics.
- Report privacy-safe macOS system proxy state and suggest enabling `respect_system_proxy` when a configured proxy is otherwise unused.
- Preserve system-root fallback when a custom CA is invalid, and account for provider-specific API key configuration when selecting the probe route.

## Testing

- Cover inference endpoint selection, invalid custom CA fallback, proxy authentication failures, TLS classification, and macOS proxy reporting.

GitOrigin-RevId: c30a3ca5a332119e2b2f8c65341ef9b2251a28a2
This commit is contained in:
chess authored and copyberry committed 2026-08-17 00:09:44 +00:00
1 parent 772e88c8ae
commit c8ddb210d2
12 files changed
+677 -93

No files matched your search

@@ -19,6 +19,96 @@ use tracing_subscriber::layer::SubscriberExt;
use super::*;
use crate::OutboundProxyPolicy;
#[tokio::test]
async fn request_failures_classify_real_untrusted_certificate_handshakes() {
codex_utils_rustls_provider::ensure_rustls_crypto_provider();
let certificate = rcgen::generate_simple_self_signed(vec!["localhost".to_string()])
.expect("self-signed certificate should generate");
let private_key = rustls::pki_types::PrivateKeyDer::Pkcs8(
rustls::pki_types::PrivatePkcs8KeyDer::from(certificate.signing_key.serialize_der()),
);
let configuration = Arc::new(
rustls::ServerConfig::builder()
.with_no_client_auth()
.with_single_cert(vec![certificate.cert.der().clone()], private_key)
.expect("TLS server should be configured"),
);
let listener = TcpListener::bind("127.0.0.1:0").expect("TLS server should bind");
let address = listener
.local_addr()
.expect("TLS server should have an address");
let server = std::thread::spawn(move || {
let (mut stream, _) = listener.accept().expect("TLS server should accept");
stream
.set_read_timeout(Some(Duration::from_secs(3)))
.expect("TLS handshake timeout");
let mut connection = rustls::ServerConnection::new(configuration)
.expect("TLS server connection should be created");
let _ = connection.complete_io(&mut stream);
});
let pool = RouteAwareClientPool::new_without_request_logging(
HttpClientFactory::new(OutboundProxyPolicy::ReqwestDefault),
ClientRouteClass::Api,
);
let request = pool
.get(format!("https://localhost:{}/", address.port()))
.timeout(Duration::from_secs(3))
.request
.expect("TLS request should build");
let error = pool
.send_with_resolver(request, |_| async { Ok(OutboundProxyRoute::Direct) })
.await
.expect_err("self-signed server certificate must not be trusted");
drop(std::net::TcpStream::connect(address));
server.join().expect("TLS server should finish");
assert_eq!(
error.failure_class(),
Some(RouteFailureClass::TlsError),
"unexpected certificate error: {error:?}"
);
}
#[tokio::test]
async fn request_failures_classify_https_proxy_authentication_challenges() {
let (address, proxy) = spawn_response_server(vec![
"HTTP/1.1 407 Proxy Authentication Required\r\n\
Proxy-Authenticate: Basic realm=\"codex\"\r\n\
Content-Length: 0\r\n\
Connection: close\r\n\r\n"
.to_string(),
]);
let pool = RouteAwareClientPool::new_without_request_logging(
HttpClientFactory::new(OutboundProxyPolicy::RespectSystemProxy),
ClientRouteClass::Api,
);
let mut request = reqwest::Request::new(
Method::GET,
reqwest::Url::parse("https://example.com/").expect("request URL should parse"),
);
*request.timeout_mut() = Some(Duration::from_secs(3));
let error = pool
.send_with_resolver(request, move |_| async move {
Ok(OutboundProxyRoute::Proxy {
url: format!("http://{address}"),
no_proxy: None,
})
})
.await
.expect_err("HTTPS proxy challenge should reject the CONNECT request");
let requests = proxy.join().expect("proxy fixture should finish");
assert_eq!(requests.len(), 1);
assert!(requests[0].starts_with("CONNECT example.com:443 HTTP/1.1\r\n"));
assert_eq!(
error.failure_class(),
Some(RouteFailureClass::ProxyAuthenticationRequired),
"unexpected HTTPS proxy error: {error:?}"
);
}
#[test]
fn request_builder_debug_redacts_url_secrets() {
let pool = RouteAwareClientPool::new(