[exec-server] keep websocket URL surface unchanged

This commit is contained in:
Ruslan Nigmatullin
2026-05-11 21:16:39 +00:00
parent 6f40914087
commit 734af5228e
13 changed files with 42 additions and 86 deletions
+1 -1
View File
@@ -2094,7 +2094,7 @@ mod tests {
let config = Arc::new(build_test_config().await);
let environment_manager = Arc::new(
EnvironmentManager::create_for_tests(
Some("ws://127.0.0.1:8765/ws".to_string()),
Some("ws://127.0.0.1:8765".to_string()),
ExecServerRuntimePaths::new(
std::env::current_exe().expect("current exe"),
/*codex_linux_sandbox_exe*/ None,
@@ -1808,7 +1808,7 @@ mod tests {
request_id: request_id(),
params: v2::EnvironmentAddParams {
environment_id: "remote-a".to_string(),
exec_server_url: "ws://127.0.0.1:8765/ws".to_string(),
exec_server_url: "ws://127.0.0.1:8765".to_string(),
},
};
assert_eq!(
@@ -2603,7 +2603,7 @@ mod tests {
request_id: RequestId::Integer(9),
params: v2::EnvironmentAddParams {
environment_id: "remote-a".to_string(),
exec_server_url: "ws://127.0.0.1:8765/ws".to_string(),
exec_server_url: "ws://127.0.0.1:8765".to_string(),
},
};
assert_eq!(
@@ -2612,7 +2612,7 @@ mod tests {
"id": 9,
"params": {
"environmentId": "remote-a",
"execServerUrl": "ws://127.0.0.1:8765/ws"
"execServerUrl": "ws://127.0.0.1:8765"
}
}),
serde_json::to_value(&request)?,
@@ -2898,7 +2898,7 @@ mod tests {
request_id: RequestId::Integer(1),
params: v2::EnvironmentAddParams {
environment_id: "remote-a".to_string(),
exec_server_url: "ws://127.0.0.1:8765/ws".to_string(),
exec_server_url: "ws://127.0.0.1:8765".to_string(),
},
};
let reason = crate::experimental_api::ExperimentalApi::experimental_reason(&request);
@@ -2005,7 +2005,7 @@ async fn turn_start_resolves_sticky_thread_local_environment_and_turn_overrides(
r#"
[[environments]]
id = "remote"
url = "ws://127.0.0.1:1/ws"
url = "ws://127.0.0.1:1"
"#,
)?;
+2 -2
View File
@@ -105,7 +105,7 @@ mod tests {
async fn default_thread_environment_selections_use_manager_default_id() {
let cwd = AbsolutePathBuf::current_dir().expect("cwd");
let manager = EnvironmentManager::create_for_tests(
Some("ws://127.0.0.1:8765/ws".to_string()),
Some("ws://127.0.0.1:8765".to_string()),
test_runtime_paths(),
)
.await;
@@ -127,7 +127,7 @@ mod tests {
r#"
[[environments]]
id = "remote"
url = "ws://127.0.0.1:8765/ws"
url = "ws://127.0.0.1:8765"
"#,
)
.expect("write environments.toml");
+1 -1
View File
@@ -21,7 +21,7 @@ the wire.
The CLI entrypoint supports:
- `ws://IP:PORT` (default bind address; the listener advertises `ws://IP:PORT/ws`)
- `ws://IP:PORT` (default)
- `--remote URL --executor-id ID [--name NAME]`
Remote mode registers the local exec-server with the executor registry,
+10 -19
View File
@@ -506,7 +506,7 @@ mod tests {
#[tokio::test]
async fn environment_manager_reports_remote_url() {
let manager = EnvironmentManager::create_for_tests(
Some("ws://127.0.0.1:8765/ws".to_string()),
Some("ws://127.0.0.1:8765".to_string()),
test_runtime_paths(),
)
.await;
@@ -517,10 +517,7 @@ mod tests {
Some(REMOTE_ENVIRONMENT_ID)
);
assert!(environment.is_remote());
assert_eq!(
environment.exec_server_url(),
Some("ws://127.0.0.1:8765/ws")
);
assert_eq!(environment.exec_server_url(), Some("ws://127.0.0.1:8765"));
assert!(Arc::ptr_eq(
&environment,
&manager
@@ -551,7 +548,7 @@ mod tests {
snapshot: EnvironmentProviderSnapshot {
environments: vec![(
REMOTE_ENVIRONMENT_ID.to_string(),
Environment::create_for_tests(Some("ws://127.0.0.1:8765/ws".to_string()))
Environment::create_for_tests(Some("ws://127.0.0.1:8765".to_string()))
.expect("remote environment"),
)],
default: EnvironmentDefault::EnvironmentId(REMOTE_ENVIRONMENT_ID.to_string()),
@@ -623,7 +620,7 @@ mod tests {
snapshot: EnvironmentProviderSnapshot {
environments: vec![(
"devbox".to_string(),
Environment::create_for_tests(Some("ws://127.0.0.1:8765/ws".to_string()))
Environment::create_for_tests(Some("ws://127.0.0.1:8765".to_string()))
.expect("remote environment"),
)],
default: EnvironmentDefault::EnvironmentId("devbox".to_string()),
@@ -648,7 +645,7 @@ mod tests {
snapshot: EnvironmentProviderSnapshot {
environments: vec![(
"devbox".to_string(),
Environment::create_for_tests(Some("ws://127.0.0.1:8765/ws".to_string()))
Environment::create_for_tests(Some("ws://127.0.0.1:8765".to_string()))
.expect("remote environment"),
)],
default: EnvironmentDefault::Disabled,
@@ -675,7 +672,7 @@ mod tests {
snapshot: EnvironmentProviderSnapshot {
environments: vec![(
"devbox".to_string(),
Environment::create_for_tests(Some("ws://127.0.0.1:8765/ws".to_string()))
Environment::create_for_tests(Some("ws://127.0.0.1:8765".to_string()))
.expect("remote environment"),
)],
default: EnvironmentDefault::EnvironmentId("missing".to_string()),
@@ -769,29 +766,23 @@ mod tests {
let manager = EnvironmentManager::disabled_for_tests(test_runtime_paths());
manager
.upsert_environment(
"executor-a".to_string(),
"ws://127.0.0.1:8765/ws".to_string(),
)
.upsert_environment("executor-a".to_string(), "ws://127.0.0.1:8765".to_string())
.expect("remote environment");
let first = manager
.get_environment("executor-a")
.expect("first remote environment");
assert!(first.is_remote());
assert_eq!(first.exec_server_url(), Some("ws://127.0.0.1:8765/ws"));
assert_eq!(first.exec_server_url(), Some("ws://127.0.0.1:8765"));
assert_eq!(manager.default_environment_id(), None);
manager
.upsert_environment(
"executor-a".to_string(),
"ws://127.0.0.1:9876/ws".to_string(),
)
.upsert_environment("executor-a".to_string(), "ws://127.0.0.1:9876".to_string())
.expect("updated remote environment");
let second = manager
.get_environment("executor-a")
.expect("second remote environment");
assert!(second.is_remote());
assert_eq!(second.exec_server_url(), Some("ws://127.0.0.1:9876/ws"));
assert_eq!(second.exec_server_url(), Some("ws://127.0.0.1:9876"));
assert!(!Arc::ptr_eq(&first, &second));
}
@@ -162,7 +162,7 @@ mod tests {
#[tokio::test]
async fn default_provider_adds_remote_environment_for_websocket_url() {
let provider = DefaultEnvironmentProvider::new(Some("ws://127.0.0.1:8765/ws".to_string()));
let provider = DefaultEnvironmentProvider::new(Some("ws://127.0.0.1:8765".to_string()));
let snapshot = provider.snapshot().await.expect("environments");
let EnvironmentProviderSnapshot {
environments,
@@ -177,7 +177,7 @@ mod tests {
assert!(remote_environment.is_remote());
assert_eq!(
remote_environment.exec_server_url(),
Some("ws://127.0.0.1:8765/ws")
Some("ws://127.0.0.1:8765")
);
assert_eq!(
default,
@@ -187,14 +187,13 @@ mod tests {
#[tokio::test]
async fn default_provider_normalizes_exec_server_url() {
let provider =
DefaultEnvironmentProvider::new(Some(" ws://127.0.0.1:8765/ws ".to_string()));
let provider = DefaultEnvironmentProvider::new(Some(" ws://127.0.0.1:8765 ".to_string()));
let snapshot = provider.snapshot().await.expect("environments");
let environments: HashMap<_, _> = snapshot.environments.into_iter().collect();
assert_eq!(
environments[REMOTE_ENVIRONMENT_ID].exec_server_url(),
Some("ws://127.0.0.1:8765/ws")
Some("ws://127.0.0.1:8765")
);
}
}
+13 -13
View File
@@ -333,7 +333,7 @@ mod tests {
environments: vec![
EnvironmentToml {
id: "devbox".to_string(),
url: Some(" ws://127.0.0.1:8765/ws ".to_string()),
url: Some(" ws://127.0.0.1:8765 ".to_string()),
..Default::default()
},
EnvironmentToml {
@@ -370,7 +370,7 @@ mod tests {
assert!(!environments.contains_key(LOCAL_ENVIRONMENT_ID));
assert_eq!(
environments["devbox"].exec_server_url(),
Some("ws://127.0.0.1:8765/ws")
Some("ws://127.0.0.1:8765")
);
assert!(environments["ssh-dev"].is_remote());
assert_eq!(environments["ssh-dev"].exec_server_url(), None);
@@ -411,7 +411,7 @@ mod tests {
(
EnvironmentToml {
id: "local".to_string(),
url: Some("ws://127.0.0.1:8765/ws".to_string()),
url: Some("ws://127.0.0.1:8765".to_string()),
..Default::default()
},
"environment id `local` is reserved",
@@ -419,7 +419,7 @@ mod tests {
(
EnvironmentToml {
id: " devbox ".to_string(),
url: Some("ws://127.0.0.1:8765/ws".to_string()),
url: Some("ws://127.0.0.1:8765".to_string()),
..Default::default()
},
"environment id ` devbox ` must not contain surrounding whitespace",
@@ -427,7 +427,7 @@ mod tests {
(
EnvironmentToml {
id: "dev box".to_string(),
url: Some("ws://127.0.0.1:8765/ws".to_string()),
url: Some("ws://127.0.0.1:8765".to_string()),
..Default::default()
},
"environment id `dev box` must contain only ASCII letters, numbers, '-' or '_'",
@@ -443,7 +443,7 @@ mod tests {
(
EnvironmentToml {
id: "devbox".to_string(),
url: Some("ws://127.0.0.1:8765/ws".to_string()),
url: Some("ws://127.0.0.1:8765".to_string()),
program: Some("codex".to_string()),
..Default::default()
},
@@ -528,7 +528,7 @@ mod tests {
environments: vec![
EnvironmentToml {
id: "devbox".to_string(),
url: Some("ws://127.0.0.1:8765/ws".to_string()),
url: Some("ws://127.0.0.1:8765".to_string()),
connect_timeout_sec: Some(Duration::from_secs(12)),
initialize_timeout_sec: Some(Duration::from_secs(34)),
..Default::default()
@@ -546,7 +546,7 @@ mod tests {
assert_eq!(
provider.environments[0].1,
ExecServerTransportParams::WebSocketUrl {
websocket_url: "ws://127.0.0.1:8765/ws".to_string(),
websocket_url: "ws://127.0.0.1:8765".to_string(),
connect_timeout: Duration::from_secs(12),
initialize_timeout: Duration::from_secs(34),
}
@@ -591,7 +591,7 @@ mod tests {
environments: vec![
EnvironmentToml {
id: "devbox".to_string(),
url: Some("ws://127.0.0.1:8765/ws".to_string()),
url: Some("ws://127.0.0.1:8765".to_string()),
..Default::default()
},
EnvironmentToml {
@@ -616,7 +616,7 @@ mod tests {
default: None,
environments: vec![EnvironmentToml {
id: id.clone(),
url: Some("ws://127.0.0.1:8765/ws".to_string()),
url: Some("ws://127.0.0.1:8765".to_string()),
..Default::default()
}],
})
@@ -655,7 +655,7 @@ default = "ssh-dev"
[[environments]]
id = "devbox"
url = "ws://127.0.0.1:4512/ws"
url = "ws://127.0.0.1:4512"
connect_timeout_sec = 12.0
initialize_timeout_sec = 34.0
@@ -678,7 +678,7 @@ CODEX_LOG = "debug"
environments.environments[0],
EnvironmentToml {
id: "devbox".to_string(),
url: Some("ws://127.0.0.1:4512/ws".to_string()),
url: Some("ws://127.0.0.1:4512".to_string()),
connect_timeout_sec: Some(Duration::from_secs(12)),
initialize_timeout_sec: Some(Duration::from_secs(34)),
..Default::default()
@@ -712,7 +712,7 @@ CODEX_LOG = "debug"
r#"
[[environments]]
id = "devbox"
url = "ws://127.0.0.1:4512/ws"
url = "ws://127.0.0.1:4512"
unknown = true
"#,
"unknown field `unknown`",
+3 -12
View File
@@ -2,10 +2,8 @@ use axum::Router;
use axum::extract::ConnectInfo;
use axum::extract::State;
use axum::extract::ws::WebSocketUpgrade;
use axum::http::StatusCode;
use axum::response::IntoResponse;
use axum::routing::any;
use axum::routing::get;
use std::io::Write as _;
use std::net::SocketAddr;
use tokio::io;
@@ -116,15 +114,12 @@ async fn run_websocket_listener(
let listener = TcpListener::bind(bind_address).await?;
let local_addr = listener.local_addr()?;
let processor = ConnectionProcessor::new(runtime_paths);
info!("codex-exec-server listening on ws://{local_addr}/ws");
println!("ws://{local_addr}/ws");
info!("codex-exec-server listening on ws://{local_addr}");
println!("ws://{local_addr}");
std::io::stdout().flush()?;
let router = Router::new()
.route("/", get(health_check_handler))
.route("/readyz", get(health_check_handler))
.route("/healthz", get(health_check_handler))
.route("/ws", any(websocket_upgrade_handler))
.route("/", any(websocket_upgrade_handler))
.with_state(ExecServerWebSocketState { processor });
axum::serve(
listener,
@@ -139,10 +134,6 @@ struct ExecServerWebSocketState {
processor: ConnectionProcessor,
}
async fn health_check_handler() -> StatusCode {
StatusCode::OK
}
async fn websocket_upgrade_handler(
websocket: WebSocketUpgrade,
ConnectInfo(peer_addr): ConnectInfo<SocketAddr>,
-24
View File
@@ -1,24 +0,0 @@
#![cfg(unix)]
mod common;
use common::exec_server::exec_server;
use pretty_assertions::assert_eq;
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn exec_server_serves_health_checks_alongside_websocket_endpoint() -> anyhow::Result<()> {
let mut server = exec_server().await?;
let http_base_url = server
.websocket_url()
.strip_prefix("ws://")
.and_then(|url| url.strip_suffix("/ws"))
.expect("websocket URL should use ws://.../ws");
for path in ["/", "/readyz", "/healthz"] {
let response = reqwest::get(format!("http://{http_base_url}{path}")).await?;
assert_eq!(response.status(), reqwest::StatusCode::OK);
}
server.shutdown().await?;
Ok(())
}
+1 -1
View File
@@ -2145,7 +2145,7 @@ mod tests {
};
let target = AppServerTarget::Embedded;
let environment_manager = EnvironmentManager::create_for_tests(
Some("ws://127.0.0.1:8765/ws".to_string()),
Some("ws://127.0.0.1:8765".to_string()),
ExecServerRuntimePaths::new(
std::env::current_exe().expect("current exe"),
/*codex_linux_sandbox_exe*/ None,
+1 -2
View File
@@ -161,7 +161,6 @@ if [[ -z "${remote_exec_server_pid}" || -z "${listen_url}" || -z "${remote_repo_
fi
remote_exec_server_port="${listen_url##*:}"
remote_exec_server_port="${remote_exec_server_port%%/*}"
if [[ -z "${remote_exec_server_port}" || "${remote_exec_server_port}" == "${listen_url}" ]]; then
echo "failed to parse remote exec server port from ${listen_url}" >&2
exit 1
@@ -171,7 +170,7 @@ echo "Remote exec server: ${listen_url}"
echo "Remote exec server log: ${remote_exec_server_log_path}"
echo "Press Ctrl-C to stop the SSH tunnel and remote exec server."
echo "Start codex via: "
printf ' CODEX_EXEC_SERVER_URL=ws://127.0.0.1:%s/ws codex -C %q\n' \
printf ' CODEX_EXEC_SERVER_URL=ws://127.0.0.1:%s codex -C %q\n' \
"${local_exec_server_port}" \
"${remote_repo_root}"
+1 -1
View File
@@ -85,7 +85,7 @@ setup_remote_env() {
return 1
fi
export CODEX_TEST_REMOTE_EXEC_SERVER_PID="${remote_exec_server_pid}"
export CODEX_TEST_REMOTE_EXEC_SERVER_URL="ws://${container_ip}:${remote_exec_server_port}/ws"
export CODEX_TEST_REMOTE_EXEC_SERVER_URL="ws://${container_ip}:${remote_exec_server_port}"
fi
export CODEX_TEST_REMOTE_ENV="${container_name}"