Files
el/ui/vessels/el-services/src/binding/websocket.rs
T

86 lines
2.8 KiB
Rust

//! WebSocket binding — persistent connection, message-based RPC.
//!
//! Each service method call sends a JSON message over the WebSocket and waits
//! for a response message with a matching correlation ID.
//!
//! Message format (over the wire):
//! ```json
//! { "id": "uuid", "service": "UserService", "method": "get_user", "params": { "id": "123" } }
//! ```
//!
//! Response:
//! ```json
//! { "id": "uuid", "status": 200, "body": { ... } }
//! ```
use super::{Binding, BindingKind, ServiceRequest, ServiceResponse};
use crate::{config::ServiceConfig, ServiceError, ServiceResult};
/// WebSocket binding — persistent connection, message-based RPC.
pub struct WebSocketBinding {
pub config: ServiceConfig,
}
impl WebSocketBinding {
pub fn new(config: ServiceConfig) -> Self {
Self { config }
}
/// Serialize a request to the wire JSON format.
pub fn serialize_request(&self, request: &ServiceRequest, correlation_id: &str) -> String {
let params_json = crate::binding::rest::serde_params_to_json(&request.params);
format!(
"{{\"id\":\"{}\",\"service\":\"{}\",\"method\":\"{}\",\"params\":{}}}",
correlation_id, request.service, request.method, params_json
)
}
/// Build the WebSocket URL from the config base_url.
/// Converts `https://` → `wss://` and `http://` → `ws://`.
pub fn ws_url(&self) -> Option<String> {
self.config.base_url.as_ref().map(|url| {
url.replace("https://", "wss://")
.replace("http://", "ws://")
})
}
}
impl Binding for WebSocketBinding {
fn kind(&self) -> BindingKind {
BindingKind::WebSocket
}
fn call(&self, request: ServiceRequest) -> ServiceResult<ServiceResponse> {
if self.config.base_url.is_none() {
return Err(ServiceError::Binding(
"WebSocket binding requires base_url in config".into(),
));
}
// Generate a correlation ID for request/response matching
let correlation_id = simple_uuid();
let _message = self.serialize_request(&request, &correlation_id);
let _ws_url = self.ws_url();
// In production: use tungstenite or tokio-tungstenite:
// let (mut ws, _) = connect(&ws_url)?;
// ws.send(Message::Text(message))?;
// loop { let msg = ws.read_message()?; if msg_id == correlation_id { return parse(msg); } }
Ok(ServiceResponse::ok(format!(
"{{\"id\":\"{}\",\"service\":\"{}\",\"method\":\"{}\"}}",
correlation_id, request.service, request.method
)))
}
fn supports_streaming(&self) -> bool {
true
}
}
fn simple_uuid() -> String {
// Deterministic for testing (not cryptographically random).
// In production: use uuid crate.
"ws-00000000-0000-0000-0000-000000000001".to_string()
}