mirror of
https://github.com/openai/codex.git
synced 2026-11-13 06:45:07 +00:00
uds: add async Unix socket crate (#18254)
## Summary - add a codex-uds crate with async UnixListener and UnixStream wrappers - expose helpers for private socket directory setup and stale socket path checks - migrate codex-stdio-to-uds onto codex-uds and Tokio-based stdio/socket relaying - update the CLI stdio-to-uds command path for the async runner ## Tests - cargo test -p codex-uds -p codex-stdio-to-uds - cargo test -p codex-cli - just fmt - just fix -p codex-uds - just fix -p codex-stdio-to-uds - just fix -p codex-cli - just bazel-lock-check - git diff --check
This commit is contained in:
1 parent
1029742cf7
commit
97d4b42583
12 files changed
+641
-133
No files matched your search
@@ -0,0 +1,121 @@
|
||||
use std::io::ErrorKind;
|
||||
|
||||
use pretty_assertions::assert_eq;
|
||||
use tokio::io::AsyncReadExt;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn prepare_private_socket_directory_creates_directory() {
|
||||
let temp_dir = tempfile::TempDir::new().expect("temp dir");
|
||||
let socket_dir = temp_dir.path().join("app-server-control");
|
||||
|
||||
prepare_private_socket_directory(&socket_dir)
|
||||
.await
|
||||
.expect("socket dir should be created");
|
||||
|
||||
assert!(socket_dir.is_dir());
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[tokio::test]
|
||||
async fn prepare_private_socket_directory_sets_existing_permissions_to_owner_only() {
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
let temp_dir = tempfile::TempDir::new().expect("temp dir");
|
||||
for mode in [0o755, 0o600] {
|
||||
let socket_dir = temp_dir.path().join(format!("app-server-control-{mode:o}"));
|
||||
std::fs::create_dir(&socket_dir).expect("socket dir should be created");
|
||||
std::fs::set_permissions(&socket_dir, std::fs::Permissions::from_mode(mode))
|
||||
.expect("socket dir permissions should be changed");
|
||||
|
||||
prepare_private_socket_directory(&socket_dir)
|
||||
.await
|
||||
.expect("socket dir permissions should be set exactly");
|
||||
|
||||
let mode = std::fs::metadata(&socket_dir)
|
||||
.expect("socket dir metadata")
|
||||
.permissions()
|
||||
.mode();
|
||||
assert_eq!(mode & 0o777, 0o700);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[tokio::test]
|
||||
async fn regular_file_path_is_not_stale_socket_path() {
|
||||
let temp_dir = tempfile::TempDir::new().expect("temp dir");
|
||||
let regular_file = temp_dir.path().join("not-a-socket");
|
||||
std::fs::write(®ular_file, b"not a socket").expect("regular file should be created");
|
||||
|
||||
assert!(
|
||||
!is_stale_socket_path(®ular_file)
|
||||
.await
|
||||
.expect("stale socket check should succeed")
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn bound_listener_path_is_stale_socket_path() {
|
||||
let temp_dir = tempfile::TempDir::new().expect("temp dir");
|
||||
let socket_path = temp_dir.path().join("socket");
|
||||
let _listener = match UnixListener::bind(&socket_path).await {
|
||||
Ok(listener) => listener,
|
||||
Err(err) if err.kind() == ErrorKind::PermissionDenied => {
|
||||
eprintln!("skipping test: failed to bind unix socket: {err}");
|
||||
return;
|
||||
}
|
||||
Err(err) => panic!("failed to bind test socket: {err}"),
|
||||
};
|
||||
|
||||
assert!(
|
||||
is_stale_socket_path(&socket_path)
|
||||
.await
|
||||
.expect("stale socket check should succeed")
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn stream_round_trips_data_between_listener_and_client() {
|
||||
let temp_dir = tempfile::TempDir::new().expect("temp dir");
|
||||
let socket_path = temp_dir.path().join("socket");
|
||||
let mut listener = match UnixListener::bind(&socket_path).await {
|
||||
Ok(listener) => listener,
|
||||
Err(err) if err.kind() == ErrorKind::PermissionDenied => {
|
||||
eprintln!("skipping test: failed to bind unix socket: {err}");
|
||||
return;
|
||||
}
|
||||
Err(err) => panic!("failed to bind test socket: {err}"),
|
||||
};
|
||||
|
||||
let server_task = tokio::spawn(async move {
|
||||
let mut server_stream = listener.accept().await.expect("connection should accept");
|
||||
let mut request = [0; 7];
|
||||
server_stream
|
||||
.read_exact(&mut request)
|
||||
.await
|
||||
.expect("server should read request");
|
||||
assert_eq!(&request, b"request");
|
||||
server_stream
|
||||
.write_all(b"response")
|
||||
.await
|
||||
.expect("server should write response");
|
||||
});
|
||||
|
||||
let mut client_stream = UnixStream::connect(&socket_path)
|
||||
.await
|
||||
.expect("client should connect");
|
||||
client_stream
|
||||
.write_all(b"request")
|
||||
.await
|
||||
.expect("client should write request");
|
||||
let mut response = [0; 8];
|
||||
client_stream
|
||||
.read_exact(&mut response)
|
||||
.await
|
||||
.expect("client should read response");
|
||||
assert_eq!(&response, b"response");
|
||||
|
||||
server_task.await.expect("server task should join");
|
||||
}
|
||||
Reference in new issue
Block a user