mirror of
https://github.com/openai/codex.git
synced 2026-10-21 17:48:44 +00:00
Remove WebSocket transport from code-mode hosts (#40692)
## What changed - Accept only `http://` and `https://` gRPC endpoints for app server `--code-mode-host` connections. - Limit the standalone code-mode host to stdio and gRPC listeners. - Remove the WebSocket session provider, dual-WebSocket negotiation, transport lanes, and related dependencies and tests. - Keep coverage for shared and prewarmed gRPC hosts and reject WebSocket host URLs in CLI parsing tests. GitOrigin-RevId: 8792604e79ffd54ce776074a4dc3f7d55e63752a
This commit is contained in:
committed by
copyberry
parent
9be8d6e1c3
commit
725b3a44f8
@@ -13,7 +13,6 @@ use codex_code_mode_protocol::host::Capability;
|
||||
use codex_code_mode_protocol::host::CapabilitySet;
|
||||
use codex_code_mode_protocol::host::ClientHello;
|
||||
use codex_code_mode_protocol::host::ClientToHost;
|
||||
use codex_code_mode_protocol::host::DUAL_WEBSOCKET_CAPABILITY;
|
||||
use codex_code_mode_protocol::host::EncodedFrame;
|
||||
use codex_code_mode_protocol::host::FramedReader;
|
||||
use codex_code_mode_protocol::host::FramedWriter;
|
||||
@@ -36,7 +35,6 @@ use tokio::sync::mpsc;
|
||||
use tokio::sync::oneshot;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tokio_util::task::TaskTracker;
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::HostLimits;
|
||||
use super::HostState;
|
||||
@@ -45,12 +43,8 @@ use super::MAX_RECENT_REQUEST_IDS;
|
||||
use super::RequestKind;
|
||||
use super::RequestRegistry;
|
||||
use super::SeenSessionIds;
|
||||
use super::negotiate;
|
||||
use super::peer::HostPeer;
|
||||
use super::run;
|
||||
use super::transport::BulkConnectionRegistry;
|
||||
use super::transport::ConnectionReader;
|
||||
use super::transport::ConnectionWriter;
|
||||
|
||||
fn client_hello(
|
||||
versions: impl IntoIterator<Item = ProtocolVersion>,
|
||||
@@ -260,111 +254,8 @@ impl AsyncWrite for BlockingWriter {
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn failed_host_hello_removes_the_bulk_pairing_reservation() {
|
||||
let (host_reader, client_writer) = tokio::io::duplex(/*max_buf_size*/ 4096);
|
||||
let capability = Capability::new(DUAL_WEBSOCKET_CAPABILITY).expect("dual websocket capability");
|
||||
let hello = ClientToHost::ClientHello(
|
||||
ClientHello::new(
|
||||
SupportedProtocolVersions::try_new([ProtocolVersion::V1]).expect("supported versions"),
|
||||
CapabilitySet::empty(),
|
||||
CapabilitySet::try_new([capability]).expect("optional capabilities"),
|
||||
)
|
||||
.expect("client hello"),
|
||||
);
|
||||
FramedWriter::new(client_writer)
|
||||
.write(&hello)
|
||||
.await
|
||||
.expect("write client hello");
|
||||
|
||||
let bytes = Arc::new(Mutex::new(Vec::new()));
|
||||
let registry = BulkConnectionRegistry::default();
|
||||
let mut reader = ConnectionReader::from_reader(host_reader);
|
||||
let mut writer = ConnectionWriter::from_writer(FailingHandshakeWriter {
|
||||
bytes: Arc::clone(&bytes),
|
||||
});
|
||||
let result = negotiate(&mut reader, &mut writer, Some(®istry)).await;
|
||||
assert!(result.is_err());
|
||||
|
||||
let message = EncodedFrame::decode_framed::<HostToClient>(
|
||||
&bytes.lock().unwrap_or_else(PoisonError::into_inner),
|
||||
)
|
||||
.expect("decode failed host hello");
|
||||
let HostToClient::HostHello(hello) = message else {
|
||||
panic!("expected a host hello");
|
||||
};
|
||||
let token = hello
|
||||
.bulk_connection_token()
|
||||
.expect("dual websocket pairing token");
|
||||
let token = Uuid::parse_str(token).expect("UUID pairing token");
|
||||
assert!(registry.remove(token).is_none());
|
||||
}
|
||||
|
||||
struct FailingHandshakeWriter {
|
||||
bytes: Arc<Mutex<Vec<u8>>>,
|
||||
}
|
||||
|
||||
impl AsyncWrite for FailingHandshakeWriter {
|
||||
fn poll_write(
|
||||
self: Pin<&mut Self>,
|
||||
_cx: &mut Context<'_>,
|
||||
bytes: &[u8],
|
||||
) -> Poll<std::io::Result<usize>> {
|
||||
self.bytes
|
||||
.lock()
|
||||
.unwrap_or_else(PoisonError::into_inner)
|
||||
.extend_from_slice(bytes);
|
||||
Poll::Ready(Ok(bytes.len()))
|
||||
}
|
||||
|
||||
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
|
||||
Poll::Ready(Err(std::io::Error::other("host hello write failed")))
|
||||
}
|
||||
|
||||
fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn optional_dual_websocket_capability_falls_back_to_a_single_connection() {
|
||||
let (host_stream, client_stream) = tokio::io::duplex(/*max_buf_size*/ 1024);
|
||||
let (host_reader, host_writer) = tokio::io::split(host_stream);
|
||||
let (client_reader, client_writer) = tokio::io::split(client_stream);
|
||||
let host = tokio::spawn(run(host_reader, host_writer));
|
||||
let mut reader = FramedReader::new(client_reader);
|
||||
let mut writer = FramedWriter::new(client_writer);
|
||||
let capability = Capability::new(DUAL_WEBSOCKET_CAPABILITY).expect("dual websocket capability");
|
||||
|
||||
writer
|
||||
.write(&ClientToHost::ClientHello(
|
||||
ClientHello::new(
|
||||
SupportedProtocolVersions::try_new([ProtocolVersion::V1])
|
||||
.expect("supported versions"),
|
||||
CapabilitySet::empty(),
|
||||
CapabilitySet::try_new([capability]).expect("optional capabilities"),
|
||||
)
|
||||
.expect("client hello"),
|
||||
))
|
||||
.await
|
||||
.expect("write hello");
|
||||
assert_eq!(
|
||||
reader.read::<HostToClient>().await.expect("host hello"),
|
||||
Some(HostToClient::HostHello(HostHello::new(
|
||||
ProtocolVersion::V1,
|
||||
CapabilitySet::empty(),
|
||||
)))
|
||||
);
|
||||
|
||||
drop(writer);
|
||||
drop(reader);
|
||||
host.await.expect("host task").expect("host connection");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn session_resource_limits_are_negotiated_when_optional_or_required() {
|
||||
let dual_capability =
|
||||
Capability::new(DUAL_WEBSOCKET_CAPABILITY).expect("dual websocket capability");
|
||||
let resource_limits_capability =
|
||||
Capability::new(SESSION_RESOURCE_LIMITS_CAPABILITY).expect("resource limits capability");
|
||||
let resource_limits =
|
||||
@@ -373,8 +264,7 @@ async fn session_resource_limits_are_negotiated_when_optional_or_required() {
|
||||
for (required, optional) in [
|
||||
(
|
||||
CapabilitySet::empty(),
|
||||
CapabilitySet::try_new([dual_capability, resource_limits_capability])
|
||||
.expect("optional capabilities"),
|
||||
CapabilitySet::try_new([resource_limits_capability]).expect("optional capabilities"),
|
||||
),
|
||||
(resource_limits.clone(), CapabilitySet::empty()),
|
||||
] {
|
||||
|
||||
Reference in New Issue
Block a user