refactor: Transport trait and worker transport, and streamable http client with those new features. (#167)

* refactor: a transport trait and streamable http client

* test: test with js streamable http server

* refactor: separate sse stream connection

* fix(test): streamable http client test with js

* fix(test): wait longer time for js server startup
This commit is contained in:
4t145 2025-05-17 23:53:38 +08:00 • committed by GitHub
commit 9a771fb157
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 2761 additions and 1315 deletions

View file

@ -1,4 +1,3 @@
use futures::{SinkExt, Stream, StreamExt};
use thiserror::Error;
use super::*;
@ -38,29 +37,29 @@ pub enum ClientError {
}
/// Helper function to get the next message from the stream
async fn expect_next_message<S>(
stream: &mut S,
async fn expect_next_message<T>(
transport: &mut T,
context: &str,
) -> Result<ServerJsonRpcMessage, ClientError>
where
S: Stream<Item = ServerJsonRpcMessage> + Unpin,
T: Transport<RoleClient>,
{
stream
.next()
transport
.receive()
.await
.ok_or_else(|| ClientError::ConnectionClosed(context.to_string()))
.map_err(|e| ClientError::Io(std::io::Error::new(std::io::ErrorKind::Other, e)))
}
/// Helper function to expect a response from the stream
async fn expect_response<S>(
stream: &mut S,
async fn expect_response<T>(
transport: &mut T,
context: &str,
) -> Result<(ServerResult, RequestId), ClientError>
where
S: Stream<Item = ServerJsonRpcMessage> + Unpin,
T: Transport<RoleClient>,
{
let msg = expect_next_message(stream, context).await?;
let msg = expect_next_message(transport, context).await?;
match msg {
ServerJsonRpcMessage::Response(JsonRpcResponse { id, result, .. }) => Ok((result, id)),
@ -123,9 +122,7 @@ where
T: IntoTransport<RoleClient, E, A>,
E: std::error::Error + From<std::io::Error> + Send + Sync + 'static,
{
let (sink, stream) = transport.into_transport();
let mut sink = Box::pin(sink);
let mut stream = Box::pin(stream);
let mut transport = transport.into_transport();
let id_provider = <Arc<AtomicU32RequestIdProvider>>::default();
// Convert ClientError to std::io::Error, then to E
@ -143,13 +140,14 @@ where
params: service.get_info(),
extensions: Default::default(),
};
sink.send(ClientJsonRpcMessage::request(
ClientRequest::InitializeRequest(init_request),
id.clone(),
))
.await?;
transport
.send(ClientJsonRpcMessage::request(
ClientRequest::InitializeRequest(init_request),
id.clone(),
))
.await?;
let (response, response_id) = expect_response(&mut stream, "initialize response")
let (response, response_id) = expect_response(&mut transport, "initialize response")
.await
.map_err(handle_client_error)?;
@ -173,9 +171,9 @@ where
extensions: Default::default(),
}),
);
sink.send(notification).await?;
transport.send(notification).await?;
let (peer, peer_rx) = Peer::new(id_provider, initialize_result);
serve_inner(service, (sink, stream), peer, peer_rx, ct).await
serve_inner(service, transport, peer, peer_rx, ct).await
}
macro_rules! method {

View file

@ -1,4 +1,3 @@
use futures::{SinkExt, StreamExt};
use thiserror::Error;
use super::*;
@ -81,42 +80,42 @@ where
}
/// Helper function to get the next message from the stream
async fn expect_next_message<S>(
stream: &mut S,
async fn expect_next_message<T>(
transport: &mut T,
context: &str,
) -> Result<ClientJsonRpcMessage, ServerError>
where
S: StreamExt<Item = ClientJsonRpcMessage> + Unpin,
T: Transport<RoleServer>,
{
stream
.next()
transport
.receive()
.await
.ok_or_else(|| ServerError::ConnectionClosed(context.to_string()))
}
/// Helper function to expect a request from the stream
async fn expect_request<S>(
stream: &mut S,
async fn expect_request<T>(
transport: &mut T,
context: &str,
) -> Result<(ClientRequest, RequestId), ServerError>
where
S: StreamExt<Item = ClientJsonRpcMessage> + Unpin,
T: Transport<RoleServer>,
{
let msg = expect_next_message(stream, context).await?;
let msg = expect_next_message(transport, context).await?;
let msg_clone = msg.clone();
msg.into_request()
.ok_or(ServerError::ExpectedInitRequest(Some(msg_clone)))
}
/// Helper function to expect a notification from the stream
async fn expect_notification<S>(
stream: &mut S,
async fn expect_notification<T>(
transport: &mut T,
context: &str,
) -> Result<ClientNotification, ServerError>
where
S: StreamExt<Item = ClientJsonRpcMessage> + Unpin,
T: Transport<RoleServer>,
{
let msg = expect_next_message(stream, context).await?;
let msg = expect_next_message(transport, context).await?;
let msg_clone = msg.clone();
msg.into_notification()
.ok_or(ServerError::ExpectedInitNotification(Some(msg_clone)))
@ -132,9 +131,7 @@ where
T: IntoTransport<RoleServer, E, A>,
E: std::error::Error + From<std::io::Error> + Send + Sync + 'static,
{
let (sink, stream) = transport.into_transport();
let mut sink = Box::pin(sink);
let mut stream = Box::pin(stream);
let mut transport = transport.into_transport();
let id_provider = <Arc<AtomicU32RequestIdProvider>>::default();
// Convert ServerError to std::io::Error, then to E
@ -146,7 +143,7 @@ where
};
// Get initialize request
let (request, id) = expect_request(&mut stream, "initialized request")
let (request, id) = expect_request(&mut transport, "initialized request")
.await
.map_err(handle_server_error)?;
@ -173,7 +170,8 @@ where
));
}
Err(e) => {
sink.send(ServerJsonRpcMessage::error(e.clone(), id))
transport
.send(ServerJsonRpcMessage::error(e.clone(), id))
.await?;
return Err(handle_server_error(ServerError::InitializeFailed(e)));
}
@ -190,14 +188,15 @@ where
_ => init_response.protocol_version,
};
init_response.protocol_version = protocol_version;
sink.send(ServerJsonRpcMessage::response(
ServerResult::InitializeResult(init_response),
id,
))
.await?;
transport
.send(ServerJsonRpcMessage::response(
ServerResult::InitializeResult(init_response),
id,
))
.await?;
// Wait for initialize notification
let notification = expect_notification(&mut stream, "initialize notification")
let notification = expect_notification(&mut transport, "initialize notification")
.await
.map_err(handle_server_error)?;
let ClientNotification::InitializedNotification(_) = notification else {
@ -207,7 +206,7 @@ where
};
let _ = service.handle_notification(notification).await;
// Continue processing service
serve_inner(service, (sink, stream), peer, peer_rx, ct).await
serve_inner(service, transport, peer, peer_rx, ct).await
}
macro_rules! method {