Download codex-rs/exec-server/src/remote/direct_tests.rs from SaylorTwift/codex: direct link, hf CLI and curl.
- Browser
- Download file 17.2 kB
-
https://huggingface.co/SaylorTwift/codex/resolve/main/codex-rs/exec-server/src/remote/direct_tests.rs
- Command line
-
hf download hf://SaylorTwift/codex/codex-rs/exec-server/src/remote/direct_tests.rs
-
curl -L -o direct_tests.rs https://huggingface.co/SaylorTwift/codex/resolve/main/codex-rs/exec-server/src/remote/direct_tests.rs
17.2 kB
| use std::sync::Arc; | |
| use std::sync::atomic::AtomicUsize; | |
| use std::sync::atomic::Ordering; | |
| use std::time::Duration; | |
| use std::time::Instant; | |
| use anyhow::Result; | |
| use codex_api::AuthProvider; | |
| use codex_http_client::HttpClientFactory; | |
| use codex_http_client::OutboundProxyPolicy; | |
| use http::HeaderMap; | |
| use http::HeaderValue; | |
| use pretty_assertions::assert_eq; | |
| use tokio::io::AsyncReadExt; | |
| use tokio::io::AsyncWriteExt; | |
| use tokio::net::TcpListener; | |
| use tokio::time::timeout; | |
| use tokio_tungstenite::accept_hdr_async; | |
| use tokio_tungstenite::tungstenite::handshake::server::Request as HandshakeRequest; | |
| use tokio_tungstenite::tungstenite::handshake::server::Response as HandshakeResponse; | |
| use wiremock::Mock; | |
| use wiremock::MockServer; | |
| use wiremock::ResponseTemplate; | |
| use wiremock::matchers::header; | |
| use wiremock::matchers::method; | |
| use wiremock::matchers::path; | |
| use super::*; | |
| use crate::ExecServerRuntimePaths; | |
| use crate::RemoteEnvironmentTransport; | |
| struct StaticAuthProvider; | |
| impl AuthProvider for StaticAuthProvider { | |
| fn add_auth_headers(&self, headers: &mut HeaderMap) { | |
| headers.insert( | |
| http::header::AUTHORIZATION, | |
| HeaderValue::from_static("AWS4-HMAC-SHA256 test-signature"), | |
| ); | |
| } | |
| fn apply_auth( | |
| &self, | |
| mut request: codex_http_client::Request, | |
| ) -> codex_api::AuthProviderFuture<'_> { | |
| Box::pin(async move { | |
| if request.method == http::Method::GET { | |
| assert_eq!(request.headers.len(), 1); | |
| assert!(request.headers.contains_key(http::header::HOST)); | |
| assert!(request.url.starts_with("http")); | |
| } | |
| self.add_auth_headers(&mut request.headers); | |
| Ok(request) | |
| }) | |
| } | |
| } | |
| struct QueryAuthProvider; | |
| impl AuthProvider for QueryAuthProvider { | |
| fn add_auth_headers(&self, _headers: &mut HeaderMap) {} | |
| fn apply_auth( | |
| &self, | |
| mut request: codex_http_client::Request, | |
| ) -> codex_api::AuthProviderFuture<'_> { | |
| Box::pin(async move { | |
| let mut url = url::Url::parse(&request.url) | |
| .map_err(|error| codex_api::AuthError::Build(error.to_string()))?; | |
| url.query_pairs_mut().append_pair("auth", "signed-query"); | |
| request.url = url.into(); | |
| Ok(request) | |
| }) | |
| } | |
| } | |
| async fn direct_websocket_signs_only_host_and_preserves_handshake_headers() -> Result<()> { | |
| let mut request = "wss://executor.example.com/connect".into_client_request()?; | |
| request | |
| .headers_mut() | |
| .insert("traceparent", HeaderValue::from_static("trace-context")); | |
| authenticate_websocket_request(&mut request, &StaticAuthProvider).await?; | |
| assert!(request.headers().contains_key("sec-websocket-key")); | |
| assert_eq!(request.headers()["traceparent"], "trace-context"); | |
| assert_eq!( | |
| request.headers()[http::header::AUTHORIZATION], | |
| "AWS4-HMAC-SHA256 test-signature" | |
| ); | |
| Ok(()) | |
| } | |
| async fn direct_websocket_uses_authenticated_url_query_parameters() -> Result<()> { | |
| let listener = TcpListener::bind("127.0.0.1:0").await?; | |
| let url = format!("ws://{}/connect?existing=value", listener.local_addr()?); | |
| let acceptor = tokio::spawn(async move { | |
| let (socket, _) = listener.accept().await?; | |
| let callback = |request: &HandshakeRequest, response: HandshakeResponse| { | |
| assert_eq!(request.uri().path(), "/connect"); | |
| assert_eq!( | |
| request.uri().query(), | |
| Some("existing=value&auth=signed-query") | |
| ); | |
| Ok(response) | |
| }; | |
| accept_hdr_async(socket, callback).await.map(|_| ()) | |
| }); | |
| let connection = connect_direct( | |
| &url, | |
| &QueryAuthProvider, | |
| &HttpClientFactory::new(OutboundProxyPolicy::ReqwestDefault), | |
| ) | |
| .await?; | |
| drop(connection); | |
| acceptor.await??; | |
| Ok(()) | |
| } | |
| fn direct_endpoints_require_tls_except_on_loopback() { | |
| for (url, scheme, allowed) in [ | |
| ("https://registry.example.com", "https", true), | |
| ("wss://executor.example.com/connect", "wss", true), | |
| ("http://127.0.0.1:8080", "https", true), | |
| ("ws://localhost:8080/connect", "wss", true), | |
| ("http://registry.example.com", "https", false), | |
| ("ws://executor.example.com/connect", "wss", false), | |
| ] { | |
| assert_eq!( | |
| require_tls_or_loopback(url, scheme).is_ok(), | |
| allowed, | |
| "{url}" | |
| ); | |
| } | |
| } | |
| fn direct_authentication_retries_only_transient_credential_failures() { | |
| for (error, retryable) in [ | |
| (AuthError::Transient("temporary".to_string()), true), | |
| (AuthError::Build("invalid".to_string()), false), | |
| ] { | |
| assert_eq!( | |
| is_retryable_recovery_error(&direct_auth_error(error)), | |
| retryable | |
| ); | |
| } | |
| } | |
| async fn direct_registration_validates_connection_data() -> Result<()> { | |
| for (field, invalid_value) in [ | |
| ("environment_id", "different-environment"), | |
| ("transport", "noise_hybrid_ik_v1"), | |
| ("registration_id", ""), | |
| ("url", ""), | |
| ("url", "ws://executor.example.com/connect"), | |
| ] { | |
| let registry = MockServer::start().await; | |
| let mut response = serde_json::json!({ | |
| "environment_id": "environment-requested", | |
| "transport": DIRECT_TRANSPORT, | |
| "registration_id": "registration-1", | |
| "url": "wss://executor.example.com/connect", | |
| }); | |
| response[field] = serde_json::json!(invalid_value); | |
| Mock::given(method("POST")) | |
| .and(path( | |
| "/cloud/environment/environment-requested/direct/register", | |
| )) | |
| .respond_with(ResponseTemplate::new(200).set_body_json(response)) | |
| .expect(1) | |
| .mount(®istry) | |
| .await; | |
| let client = EnvironmentRegistryClient::new(registry.uri(), Arc::new(StaticAuthProvider))?; | |
| let error = client | |
| .register_direct_environment("environment-requested") | |
| .await | |
| .expect_err("invalid registration must be rejected"); | |
| if invalid_value.starts_with("ws://") { | |
| assert!(matches!( | |
| error, | |
| ExecServerError::EnvironmentRegistryConfig(_) | |
| )); | |
| } else { | |
| assert!(matches!(error, ExecServerError::Protocol(_))); | |
| } | |
| registry.verify().await; | |
| } | |
| Ok(()) | |
| } | |
| async fn direct_registration_uses_proxy_policy_without_logging_secrets() -> Result<()> { | |
| use tracing_subscriber::Layer; | |
| use tracing_subscriber::layer::SubscriberExt; | |
| let mut log_file = tempfile::tempfile()?; | |
| let writer = log_file.try_clone()?; | |
| let subscriber = tracing_subscriber::registry().with( | |
| tracing_subscriber::fmt::layer() | |
| .with_ansi(false) | |
| .with_writer(move || writer.try_clone().expect("clone log file")) | |
| .with_filter( | |
| tracing_subscriber::filter::Targets::new() | |
| .with_target("codex_http_client", tracing::Level::TRACE) | |
| .with_target("codex_exec_server", tracing::Level::TRACE), | |
| ), | |
| ); | |
| let _guard = tracing::subscriber::set_default(subscriber); | |
| tracing::debug!(target: "codex_exec_server", "direct registry log capture sentinel"); | |
| let proxy = MockServer::start().await; | |
| let registry_url = "http://direct-registry-proxy.invalid/registry-path-secret"; | |
| let request_url = | |
| format!("{registry_url}/cloud/environment/environment-requested/direct/register"); | |
| codex_http_client::cache_system_proxy_route_for_test(&request_url, proxy.uri()); | |
| Mock::given(method("POST")) | |
| .and(path( | |
| "/registry-path-secret/cloud/environment/environment-requested/direct/register", | |
| )) | |
| .and(header("authorization", "AWS4-HMAC-SHA256 test-signature")) | |
| .respond_with( | |
| ResponseTemplate::new(200) | |
| .insert_header("set-cookie", "session=registry-cookie-secret") | |
| .insert_header( | |
| "location", | |
| "https://registry.example/?token=registry-location-secret", | |
| ) | |
| .set_body_json(serde_json::json!({ | |
| "environment_id": "environment-requested", | |
| "transport": DIRECT_TRANSPORT, | |
| "registration_id": "registration-1", | |
| "url": "wss://executor.example/connect?token=websocket-query-secret", | |
| })), | |
| ) | |
| .expect(1) | |
| .mount(&proxy) | |
| .await; | |
| let client = EnvironmentRegistryClient::new_with_telemetry( | |
| registry_url.to_string(), | |
| Arc::new(StaticAuthProvider), | |
| crate::ExecServerTelemetry::default(), | |
| HttpClientFactory::new(OutboundProxyPolicy::RespectSystemProxy), | |
| )?; | |
| let response = client | |
| .register_direct_environment("environment-requested") | |
| .await?; | |
| assert_eq!( | |
| response.url, | |
| "wss://executor.example/connect?token=websocket-query-secret" | |
| ); | |
| let requests = proxy | |
| .received_requests() | |
| .await | |
| .expect("record proxy requests"); | |
| assert_eq!(requests.len(), 1); | |
| assert_eq!(requests[0].url.as_str(), request_url); | |
| assert_eq!(requests[0].body, br#"{"transport":"direct_jsonrpc_v1"}"#); | |
| proxy.verify().await; | |
| std::io::Seek::rewind(&mut log_file)?; | |
| let mut logs = String::new(); | |
| std::io::Read::read_to_string(&mut log_file, &mut logs)?; | |
| assert!(logs.contains("direct registry log capture sentinel")); | |
| for secret in [ | |
| "test-signature", | |
| "registry-path-secret", | |
| "registry-cookie-secret", | |
| "registry-location-secret", | |
| "websocket-query-secret", | |
| ] { | |
| assert!(!logs.contains(secret), "registry logs exposed {secret}"); | |
| } | |
| Ok(()) | |
| } | |
| async fn direct_registration_failure_stops_initial_and_conflict_attempts() -> Result<()> { | |
| for successful_registrations in [0, 1] { | |
| let listener = TcpListener::bind("127.0.0.1:0").await?; | |
| let websocket_url = format!("ws://{}/connect", listener.local_addr()?); | |
| let registry = MockServer::start().await; | |
| let registration_count = AtomicUsize::new(0); | |
| Mock::given(method("POST")) | |
| .and(path( | |
| "/cloud/environment/environment-requested/direct/register", | |
| )) | |
| .respond_with(move |_: &wiremock::Request| { | |
| let attempt = registration_count.fetch_add(1, Ordering::Relaxed); | |
| if attempt < successful_registrations { | |
| ResponseTemplate::new(200).set_body_json(serde_json::json!({ | |
| "environment_id": "environment-requested", | |
| "transport": DIRECT_TRANSPORT, | |
| "registration_id": "registration-1", | |
| "url": websocket_url, | |
| })) | |
| } else { | |
| ResponseTemplate::new(503) | |
| } | |
| }) | |
| .expect((successful_registrations + 1) as u64) | |
| .mount(®istry) | |
| .await; | |
| let config = RemoteEnvironmentConfig::new_with_transport( | |
| registry.uri(), | |
| "environment-requested".to_string(), | |
| RemoteEnvironmentTransport::Direct, | |
| Arc::new(StaticAuthProvider), | |
| HttpClientFactory::new(OutboundProxyPolicy::ReqwestDefault), | |
| )?; | |
| let runtime_paths = ExecServerRuntimePaths::new( | |
| std::env::current_exe()?, | |
| /*codex_linux_sandbox_exe*/ None, | |
| )?; | |
| let task = tokio::spawn(crate::run_remote_environment(config, runtime_paths)); | |
| if successful_registrations == 1 { | |
| let (mut socket, _) = timeout(Duration::from_secs(5), listener.accept()).await??; | |
| let mut request = [0; 4096]; | |
| let _ = socket.read(&mut request).await?; | |
| socket | |
| .write_all(b"HTTP/1.1 409 Conflict\r\nContent-Length: 0\r\n\r\n") | |
| .await?; | |
| socket.shutdown().await?; | |
| } | |
| let error = timeout(Duration::from_secs(5), task) | |
| .await?? | |
| .expect_err("registration failure should stop the runner"); | |
| assert!(matches!( | |
| error, | |
| ExecServerError::EnvironmentRegistryHttp { | |
| status: StatusCode::SERVICE_UNAVAILABLE, | |
| .. | |
| } | |
| )); | |
| registry.verify().await; | |
| } | |
| Ok(()) | |
| } | |
| async fn direct_websocket_reuses_registration_and_stops_on_permanent_errors() -> Result<()> { | |
| for (status, should_retry) in [ | |
| (Some(StatusCode::BAD_REQUEST), false), | |
| (Some(StatusCode::UNAUTHORIZED), false), | |
| (Some(StatusCode::FORBIDDEN), false), | |
| (Some(StatusCode::NOT_FOUND), false), | |
| (Some(StatusCode::METHOD_NOT_ALLOWED), false), | |
| (Some(StatusCode::GONE), false), | |
| (Some(StatusCode::REQUEST_TIMEOUT), true), | |
| (Some(StatusCode::CONFLICT), true), | |
| (Some(StatusCode::TOO_MANY_REQUESTS), true), | |
| (Some(StatusCode::INTERNAL_SERVER_ERROR), true), | |
| (Some(StatusCode::SERVICE_UNAVAILABLE), true), | |
| (None, true), // Close the TCP socket without sending an HTTP response. | |
| ] { | |
| let listener = TcpListener::bind("127.0.0.1:0").await?; | |
| let websocket_url = format!("ws://{}", listener.local_addr()?); | |
| let registry = MockServer::start().await; | |
| let expected_calls = if status == Some(StatusCode::CONFLICT) { | |
| 2 | |
| } else { | |
| 1 | |
| }; | |
| let registration_count = AtomicUsize::new(0); | |
| Mock::given(method("POST")) | |
| .and(path( | |
| "/cloud/environment/environment-requested/direct/register", | |
| )) | |
| .and(header("authorization", "AWS4-HMAC-SHA256 test-signature")) | |
| .respond_with(move |_: &wiremock::Request| { | |
| let registration = registration_count.fetch_add(1, Ordering::Relaxed) + 1; | |
| ResponseTemplate::new(200).set_body_json(serde_json::json!({ | |
| "environment_id": "environment-requested", | |
| "transport": DIRECT_TRANSPORT, | |
| "registration_id": format!("registration-{registration}"), | |
| "url": format!("{websocket_url}/registration-{registration}"), | |
| })) | |
| }) | |
| .expect(expected_calls) | |
| .mount(®istry) | |
| .await; | |
| let config = RemoteEnvironmentConfig::new_with_transport( | |
| registry.uri(), | |
| "environment-requested".to_string(), | |
| RemoteEnvironmentTransport::Direct, | |
| Arc::new(StaticAuthProvider), | |
| HttpClientFactory::new(OutboundProxyPolicy::ReqwestDefault), | |
| )?; | |
| let runtime_paths = ExecServerRuntimePaths::new( | |
| std::env::current_exe()?, | |
| /*codex_linux_sandbox_exe*/ None, | |
| )?; | |
| let task = tokio::spawn(crate::run_remote_environment(config, runtime_paths)); | |
| let (mut socket, _) = timeout(Duration::from_secs(5), listener.accept()).await??; | |
| let mut request = [0; 4096]; | |
| let _ = socket.read(&mut request).await?; | |
| if let Some(status) = status { | |
| let response = format!( | |
| "HTTP/1.1 {} {}\r\nContent-Length: 0\r\n\r\n", | |
| status.as_u16(), | |
| status.canonical_reason().unwrap_or_default() | |
| ); | |
| socket.write_all(response.as_bytes()).await?; | |
| } | |
| socket.shutdown().await?; | |
| drop(socket); | |
| if should_retry { | |
| let expected_path = format!("/registration-{expected_calls}"); | |
| let check_registration = |request: &HandshakeRequest, response: HandshakeResponse| { | |
| assert_eq!(request.uri().path(), expected_path); | |
| Ok(response) | |
| }; | |
| let (socket, _) = timeout(Duration::from_secs(5), listener.accept()).await??; | |
| let websocket = accept_hdr_async(socket, &check_registration).await?; | |
| let retry_delay = | |
| registry_recovery_retry_delay("environment-requested", /*attempt*/ 0); | |
| let retry_started = Instant::now(); | |
| drop(websocket); | |
| let (socket, _) = timeout(Duration::from_secs(5), listener.accept()).await??; | |
| assert!(retry_started.elapsed() >= retry_delay); | |
| let _websocket = accept_hdr_async(socket, &check_registration).await?; | |
| registry.verify().await; | |
| task.abort(); | |
| let _ = task.await; | |
| } else { | |
| let error = timeout(Duration::from_secs(5), task) | |
| .await?? | |
| .expect_err("permanent WebSocket client error should be terminal"); | |
| assert!(matches!( | |
| error, | |
| ExecServerError::WebSocketConnect { | |
| source: tokio_tungstenite::tungstenite::Error::Http(response), | |
| .. | |
| } if Some(response.status()) == status | |
| )); | |
| } | |
| } | |
| Ok(()) | |
| } | |