mirror of
https://github.com/openai/codex.git
synced 2026-11-13 14:46:14 +00:00
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:
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(
|
||||
|
||||
Reference in new issue
Block a user