feat: support proxy for ws connection (#9409)

unfortunately tokio-tungstenite doesn't support proxy configuration
outbox, while https://github.com/snapview/tokio-tungstenite/pull/370 is
in review, we can depend on source code for now.
This commit is contained in:
Anton Panasenko 2026-01-20 09:36:30 -08:00 committed by GitHub
parent 7351c12999
commit 7b27aa7707
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 43 additions and 25 deletions

42
codex-rs/Cargo.lock generated
View file

@ -3455,7 +3455,7 @@ dependencies = [
"tokio",
"tokio-rustls",
"tower-service",
"webpki-roots",
"webpki-roots 1.0.2",
]
[[package]]
@ -3505,7 +3505,7 @@ dependencies = [
"libc",
"percent-encoding",
"pin-project-lite",
"socket2 0.6.1",
"socket2 0.5.10",
"system-configuration",
"tokio",
"tower-service",
@ -5342,7 +5342,7 @@ dependencies = [
"quinn-udp",
"rustc-hash",
"rustls",
"socket2 0.6.1",
"socket2 0.5.10",
"thiserror 2.0.17",
"tokio",
"tracing",
@ -5379,7 +5379,7 @@ dependencies = [
"cfg_aliases 0.2.1",
"libc",
"once_cell",
"socket2 0.6.1",
"socket2 0.5.10",
"tracing",
"windows-sys 0.60.2",
]
@ -5664,7 +5664,7 @@ dependencies = [
"wasm-bindgen-futures",
"wasm-streams",
"web-sys",
"webpki-roots",
"webpki-roots 1.0.2",
]
[[package]]
@ -7147,14 +7147,18 @@ dependencies = [
[[package]]
name = "tokio-tungstenite"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38"
version = "0.28.0"
source = "git+https://github.com/JakkuSakura/tokio-tungstenite?rev=2ae536b0de793f3ddf31fc2f22d445bf1ef2023d#2ae536b0de793f3ddf31fc2f22d445bf1ef2023d"
dependencies = [
"futures-util",
"log",
"rustls",
"rustls-native-certs",
"rustls-pki-types",
"tokio",
"tokio-rustls",
"tungstenite",
"webpki-roots 0.26.11",
]
[[package]]
@ -7553,20 +7557,19 @@ dependencies = [
[[package]]
name = "tungstenite"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1"
version = "0.28.0"
source = "git+https://github.com/JakkuSakura/tungstenite-rs?rev=f514de8644821113e5d18a027d6d28a5c8cc0a6e#f514de8644821113e5d18a027d6d28a5c8cc0a6e"
dependencies = [
"byteorder",
"bytes",
"data-encoding",
"http 1.3.1",
"httparse",
"log",
"rand 0.8.5",
"rand 0.9.2",
"rustls",
"rustls-pki-types",
"sha1",
"thiserror 1.0.69",
"url",
"thiserror 2.0.17",
"utf-8",
]
@ -8037,6 +8040,15 @@ dependencies = [
"rustls-pki-types",
]
[[package]]
name = "webpki-roots"
version = "0.26.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9"
dependencies = [
"webpki-roots 1.0.2",
]
[[package]]
name = "webpki-roots"
version = "1.0.2"

View file

@ -182,7 +182,7 @@ ratatui-core = "0.1.0"
ratatui-macros = "0.6.0"
regex = "1.12.2"
regex-lite = "0.1.8"
reqwest = "0.12"
reqwest = { version = "0.12", features = ["rustls-tls-webpki-roots", "rustls-tls-native-roots"] }
rmcp = { version = "0.12.0", default-features = false }
schemars = "0.8.22"
seccompiler = "0.5.0"
@ -212,7 +212,7 @@ tiny_http = "0.12"
tokio = "1"
tokio-stream = "0.1.18"
tokio-test = "0.4"
tokio-tungstenite = "0.21.0"
tokio-tungstenite = { version = "0.28.0", features = ["rustls-tls-webpki-roots", "rustls-tls-native-roots", "proxy"] }
tokio-util = "0.7.18"
toml = "0.9.5"
toml_edit = "0.24.0"
@ -303,6 +303,10 @@ opt-level = 0
# ratatui = { path = "../../ratatui" }
crossterm = { git = "https://github.com/nornagon/crossterm", branch = "nornagon/color-query" }
ratatui = { git = "https://github.com/nornagon/ratatui", branch = "nornagon-v0.29.0-patch" }
tokio-tungstenite = { git = "https://github.com/JakkuSakura/tokio-tungstenite", rev = "2ae536b0de793f3ddf31fc2f22d445bf1ef2023d" }
# Uncomment to debug local changes.
# rmcp = { path = "../../rust-sdk/crates/rmcp" }
[patch."ssh://git@github.com/JakkuSakura/tungstenite-rs.git"]
tungstenite = { git = "https://github.com/JakkuSakura/tungstenite-rs", rev = "f514de8644821113e5d18a027d6d28a5c8cc0a6e" }

View file

@ -12,7 +12,7 @@ path = "src/lib.rs"
anyhow = "1"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] }
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls-webpki-roots", "rustls-tls-native-roots"] }
codex-backend-openapi-models = { path = "../codex-backend-openapi-models" }
codex-protocol = { workspace = true }
codex-core = { workspace = true }

View file

@ -150,7 +150,7 @@ async fn connect_websocket(
turn_state: Option<Arc<OnceLock<String>>>,
) -> Result<(WsStream, bool), ApiError> {
let mut request = url
.clone()
.as_str()
.into_client_request()
.map_err(|err| ApiError::Stream(format!("failed to build websocket request: {err}")))?;
request.headers_mut().extend(headers);
@ -209,7 +209,7 @@ async fn run_websocket_response_stream(
}
};
if let Err(err) = ws_stream.send(Message::Text(request_text)).await {
if let Err(err) = ws_stream.send(Message::Text(request_text.into())).await {
return Err(ApiError::Stream(format!(
"failed to send websocket request: {err}"
)));

View file

@ -169,7 +169,8 @@ pub fn build_reqwest_client() -> reqwest::Client {
let mut builder = reqwest::Client::builder()
// Set UA via dedicated helper to avoid header validation pitfalls
.user_agent(ua)
.default_headers(headers);
.default_headers(headers)
.use_rustls_tls();
if is_sandboxed() {
builder = builder.no_proxy();
}

View file

@ -923,7 +923,7 @@ pub async fn start_websocket_server_with_headers(
let Ok(payload) = serde_json::to_string(event) else {
continue;
};
if ws_stream.send(Message::Text(payload)).await.is_err() {
if ws_stream.send(Message::Text(payload.into())).await.is_err() {
break;
}
}

View file

@ -43,7 +43,7 @@ opentelemetry-otlp = { workspace = true, features = [
opentelemetry-semantic-conventions = { workspace = true }
opentelemetry_sdk = { workspace = true, features = ["logs", "metrics", "rt-tokio", "testing", "trace"] }
http = { workspace = true }
reqwest = { workspace = true, features = ["blocking", "rustls-tls"] }
reqwest = { workspace = true, features = ["blocking", "rustls-tls-webpki-roots", "rustls-tls-native-roots"] }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
strum_macros = { workspace = true }

View file

@ -21,7 +21,7 @@ clap = { workspace = true, features = ["derive"] }
codex-process-hardening = { workspace = true }
ctor = { workspace = true }
libc = { workspace = true }
reqwest = { workspace = true, features = ["blocking", "json", "rustls-tls"] }
reqwest = { workspace = true, features = ["blocking", "json", "rustls-tls-webpki-roots", "rustls-tls-native-roots"] }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
tiny_http = { workspace = true }

View file

@ -23,7 +23,8 @@ oauth2 = "5"
reqwest = { version = "0.12", default-features = false, features = [
"json",
"stream",
"rustls-tls",
"rustls-tls-webpki-roots",
"rustls-tls-native-roots",
] }
rmcp = { workspace = true, default-features = false, features = [
"auth",