This commit is contained in:
Ahmed Ibrahim
2026-02-17 15:15:05 -08:00
parent 2c1d6af908
commit f26c001568
2 changed files with 116 additions and 22 deletions

View File

@@ -320,30 +320,38 @@ impl RealtimeWebsocketEvents {
return Ok(None);
}
let msg = match self.rx_message.lock().await.recv().await {
Some(Ok(msg)) => msg,
Some(Err(err)) => {
self.is_closed.store(true, Ordering::SeqCst);
return Err(ApiError::Stream(format!(
"failed to read websocket message: {err}"
)));
}
None => {
self.is_closed.store(true, Ordering::SeqCst);
return Ok(None);
}
};
loop {
let msg = match self.rx_message.lock().await.recv().await {
Some(Ok(msg)) => msg,
Some(Err(err)) => {
self.is_closed.store(true, Ordering::SeqCst);
return Err(ApiError::Stream(format!(
"failed to read websocket message: {err}"
)));
}
None => {
self.is_closed.store(true, Ordering::SeqCst);
return Ok(None);
}
};
match msg {
Message::Text(text) => Ok(parse_realtime_event(&text)),
Message::Close(_) => {
self.is_closed.store(true, Ordering::SeqCst);
Ok(None)
match msg {
Message::Text(text) => {
if let Some(event) = parse_realtime_event(&text) {
return Ok(Some(event));
}
}
Message::Close(_) => {
self.is_closed.store(true, Ordering::SeqCst);
return Ok(None);
}
Message::Binary(_) => {
return Ok(Some(RealtimeEvent::Error(
"unexpected binary realtime websocket event".to_string(),
)));
}
Message::Frame(_) | Message::Ping(_) | Message::Pong(_) => {}
}
Message::Binary(_) => Ok(Some(RealtimeEvent::Error(
"unexpected binary realtime websocket event".to_string(),
))),
Message::Frame(_) | Message::Ping(_) | Message::Pong(_) => Ok(None),
}
}
}

View File

@@ -305,3 +305,89 @@ async fn realtime_ws_e2e_disconnected_emitted_once() {
server.await.expect("server task");
}
#[tokio::test]
async fn realtime_ws_e2e_ignores_unknown_text_events() {
let listener = TcpListener::bind("127.0.0.1:0").await.expect("bind");
let addr = listener.local_addr().expect("local addr");
let server = tokio::spawn(async move {
let (stream, _) = listener.accept().await.expect("accept");
let mut ws = accept_async(stream).await.expect("accept ws");
let first = ws
.next()
.await
.expect("first msg")
.expect("first msg ok")
.into_text()
.expect("text");
let first_json: Value = serde_json::from_str(&first).expect("json");
assert_eq!(first_json["type"], "session.create");
ws.send(Message::Text(
json!({
"type": "response.created",
"response": {"id": "resp_unknown"}
})
.to_string()
.into(),
))
.await
.expect("send unknown event");
ws.send(Message::Text(
json!({
"type": "session.created",
"session": {"id": "sess_after_unknown"}
})
.to_string()
.into(),
))
.await
.expect("send session.created");
});
let provider = Provider {
name: "test".to_string(),
base_url: "http://localhost".to_string(),
query_params: Some(HashMap::new()),
headers: HeaderMap::new(),
retry: RetryConfig {
max_attempts: 1,
base_delay: Duration::from_millis(1),
retry_429: false,
retry_5xx: false,
retry_transport: false,
},
stream_idle_timeout: Duration::from_secs(5),
};
let client = RealtimeWebsocketClient::new(provider);
let connection = client
.connect(
RealtimeSessionConfig {
api_url: format!("ws://{addr}"),
prompt: "backend prompt".to_string(),
session_id: Some("conv_123".to_string()),
},
HeaderMap::new(),
HeaderMap::new(),
)
.await
.expect("connect");
let event = connection
.next_event()
.await
.expect("next event")
.expect("event");
assert_eq!(
event,
RealtimeEvent::SessionCreated {
session_id: "sess_after_unknown".to_string()
}
);
connection.close().await.expect("close");
server.await.expect("server task");
}