feat: port el-ui vessels — rename crates→vessels, add El source + manifests
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
//! 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()
|
||||
}
|
||||
Reference in New Issue
Block a user