Conversation
This was referenced Jul 7, 2026
11a2bda to
1c63ae4
Compare
This was referenced Jul 7, 2026
bolinfest
added a commit
that referenced
this pull request
Jul 7, 2026
## Why #31335 lets HTTP callers obtain proxy-aware clients from `HttpClientFactory`, but a non-HTTP transport such as WebSockets also needs two pieces of policy owned by `codex-http-client`: a concrete route decision for its destination and the same custom-CA-aware rustls trust configuration used by HTTPS. Keeping these prerequisites in the shared abstraction means the dependent Responses WebSocket change (#31441) cannot independently reinterpret `features.respect_system_proxy`, PAC results, or enterprise CA settings. ## What changed - Add a redaction-safe `OutboundProxyRoute` with explicit transport-default, direct, and concrete-proxy outcomes. - Add `HttpClientFactory::resolve_proxy_route()` so transports can resolve a destination through the already-selected outbound proxy policy. - Resolve `ws://` and `wss://` URLs through their HTTP equivalents so system and PAC rules apply consistently. - Add an always-returned rustls config builder that starts from native roots and layers in any configured Codex custom CA bundle. The existing optional builder remains available to callers that can delegate the default configuration to their transport. - Continue redacting proxy URLs from `Debug` output because they may contain credentials. ## Review guide 1. `http-client/src/outbound_proxy.rs` defines the transport-neutral route result and WebSocket URL normalization. 2. `http-client/src/custom_ca.rs` factors the native-root/custom-CA construction so callers that perform TLS themselves can always obtain a config. 3. `http-client/src/outbound_proxy_tests.rs` verifies WebSocket normalization and legacy transport-default behavior. ## Test plan - `just test -p codex-http-client outbound_proxy` - `just test -p codex-http-client custom_ca` --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/openai/codex/pull/31342). * #31431 * #31363 * #31362 * #31361 * #31442 * #31441 * __->__ #31342
bolinfest
added a commit
that referenced
this pull request
Jul 8, 2026
## Why Responses WebSockets are the normal lower-latency transport for WebSocket-capable providers. They must not bypass an OS-selected proxy when `features.respect_system_proxy` is enabled, but disabling WebSockets whenever the feature is enabled would impose a substantial performance penalty. Merged PR #31622 introduced the reusable proxy-aware WebSocket transport. This PR makes the Responses API its first consumer so the existing fast path uses the same effective proxy and trust policy as HTTP. ## What changed - Register `codex-websocket-client` as a workspace dependency and use it from `codex-api`. - Feed the shared crate’s route-independent `WebSocketConnection` into the existing Responses message pump. - Require a configured `HttpClientFactory` for normal Responses WebSocket connections and the CLI doctor probe, so neither path can open a connection without consulting the effective proxy policy. - Pass the session factory from `core` and the effective configuration factory from `doctor`. - Add an end-to-end Responses test that enables `RespectSystemProxy`, asserts the resolved policy, completes a turn over WebSocket, and verifies the connection and request counts. - Keep the existing Responses protocol handling, ping/pong pump, and session-scoped HTTP fallback unchanged. The DNS, proxy, TLS, custom-CA, and Happy Eyeballs implementation and its transport tests live in merged PR #31622. This PR deliberately contains only the Responses integration and does not duplicate that transport code. ## Review guide 1. `codex-rs/codex-api/src/endpoint/responses_websocket.rs` constructs the shared connector and adapts its uniform stream to the existing pump. 2. `codex-rs/core/src/client.rs` supplies the session-scoped factory for production Responses connections. 3. `codex-rs/cli/src/doctor.rs` supplies the effective configuration factory to the handshake probe. 4. `codex-rs/core/tests/suite/client_websockets.rs` covers the enabled-feature path end to end. ## Test plan - `cargo check --tests -p codex-api -p codex-core -p codex-cli` - `just test -p codex-api` - `just test -p codex-core responses_websocket_streams_with_system_proxy_feature` - `cargo shear` - `just bazel-lock-check` --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/openai/codex/pull/31441). * #31637 * #31431 * #31363 * #31362 * #31361 * __->__ #31441
viyatb-oai
reviewed
Jul 8, 2026
viyatb-oai
reviewed
Jul 8, 2026
viyatb-oai
left a comment
Contributor
There was a problem hiding this comment.
One cancellation-safety issue remains in the bounded PAC client build.
Comment on lines
+277
to
+286
| let _permit = ROUTE_AWARE_CLIENT_BUILD_PERMIT | ||
| .acquire() | ||
| .await | ||
| .map_err(std::io::Error::other)?; | ||
| tokio::task::spawn_blocking(move || { | ||
| build_default_reqwest_client_for_route(&http_client_factory, &request_url, route_class) | ||
| .map_err(std::io::Error::from) | ||
| }) | ||
| .await | ||
| .map_err(std::io::Error::other)? |
Contributor
There was a problem hiding this comment.
Can we move the permit into the blocking closure? The outer five-second timeout cancels this future and drops _permit, but Tokio keeps an already-started spawn_blocking task running. A later refresh can then acquire the permit and enqueue another PAC/WinHTTP lookup, so repeated timeouts defeat the intended bound.
Suggested change
| let _permit = ROUTE_AWARE_CLIENT_BUILD_PERMIT | |
| .acquire() | |
| .await | |
| .map_err(std::io::Error::other)?; | |
| tokio::task::spawn_blocking(move || { | |
| build_default_reqwest_client_for_route(&http_client_factory, &request_url, route_class) | |
| .map_err(std::io::Error::from) | |
| }) | |
| .await | |
| .map_err(std::io::Error::other)? | |
| let permit = ROUTE_AWARE_CLIENT_BUILD_PERMIT | |
| .acquire() | |
| .await | |
| .map_err(std::io::Error::other)?; | |
| tokio::task::spawn_blocking(move || { | |
| let _permit = permit; | |
| build_default_reqwest_client_for_route(&http_client_factory, &request_url, route_class) | |
| .map_err(std::io::Error::from) | |
| }) | |
| .await | |
| .map_err(std::io::Error::other)? |
Collaborator
Author
There was a problem hiding this comment.
Fixed in 130fc9e36209. The acquired semaphore permit is now moved into the spawn_blocking closure, so cancelling or timing out the outer future cannot release the permit while the already-started PAC/WinHTTP lookup is still running.
anp-oai
reviewed
Jul 9, 2026
anp-oai
approved these changes
Jul 9, 2026
viyatb-oai
approved these changes
Jul 9, 2026
anp-oai
approved these changes
Jul 9, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Model discovery is a startup-critical request path, but
/modelsstill constructed a defaultreqwestclient directly. As a result,features.respect_system_proxycould cover Responses traffic while model catalog refreshes bypassed the same OS proxy policy.The proxy policy must come from the
Configfor the operation that actually triggers model discovery. Thread start and resume can apply configuration overrides, so capturing anHttpClientFactorywhen the process-wide models manager is constructed is not sufficient.What changed
HttpClientFactoryon model-manager operations that may fetch the remote catalog, and propagate the current request/session config through every call site./models?client_version=...URL once and use it both to resolve the outbound route and to execute the request.Review guide
models-manager/src/manager.rsmakes the factory a request-time dependency and forwards it toModelsEndpointClient.model-provider/src/models_endpoint.rsbuilds the exact models URL, selects the route-aware transport, and applies one timeout to resolution plus request execution.login/src/auth/default_client.rsmoves the synchronous client build off the async runtime;http-client/src/outbound_proxy.rsmakes cache misses single-flight.core, app-server, and CLI edits are mechanical propagation from theConfigthat owns each operation.Validation
cargo check --tests -p codex-http-client -p codex-login -p codex-api -p codex-models-manager -p codex-model-provider -p codex-core -p codex-app-serverjust test -p codex-http-client -p codex-api -p codex-models-manager -p codex-model-providerjust test -p codex-app-server models_refresh_workerFollow-up
Other direct
reqwestconsumers are migrated in the subsequent PRs in this stack.Stack created with Sapling. Best reviewed with ReviewStack.