feat: throw initialize error detail (#192)
This commit is contained in:
parent
4dc0cad499
commit
817e62d895
6 changed files with 115 additions and 93 deletions
|
|
@ -119,6 +119,13 @@ impl Default for ProtocolVersion {
|
||||||
Self::LATEST
|
Self::LATEST
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for ProtocolVersion {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
self.0.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ProtocolVersion {
|
impl ProtocolVersion {
|
||||||
pub const V_2025_03_26: Self = Self(Cow::Borrowed("2025-03-26"));
|
pub const V_2025_03_26: Self = Self(Cow::Borrowed("2025-03-26"));
|
||||||
pub const V_2024_11_05: Self = Self(Cow::Borrowed("2024-11-05"));
|
pub const V_2024_11_05: Self = Self(Cow::Borrowed("2024-11-05"));
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,7 @@ pub trait ServiceRole: std::fmt::Debug + Send + Sync + 'static + Copy + Clone {
|
||||||
type PeerNot: TryInto<CancelledNotification, Error = Self::PeerNot>
|
type PeerNot: TryInto<CancelledNotification, Error = Self::PeerNot>
|
||||||
+ From<CancelledNotification>
|
+ From<CancelledNotification>
|
||||||
+ TransferObject;
|
+ TransferObject;
|
||||||
|
type InitializeError<E>;
|
||||||
const IS_CLIENT: bool;
|
const IS_CLIENT: bool;
|
||||||
type Info: TransferObject;
|
type Info: TransferObject;
|
||||||
type PeerInfo: TransferObject;
|
type PeerInfo: TransferObject;
|
||||||
|
|
@ -113,7 +114,7 @@ pub trait ServiceExt<R: ServiceRole>: Service<R> + Sized {
|
||||||
fn serve<T, E, A>(
|
fn serve<T, E, A>(
|
||||||
self,
|
self,
|
||||||
transport: T,
|
transport: T,
|
||||||
) -> impl Future<Output = Result<RunningService<R, Self>, E>> + Send
|
) -> impl Future<Output = Result<RunningService<R, Self>, R::InitializeError<E>>> + Send
|
||||||
where
|
where
|
||||||
T: IntoTransport<R, E, A>,
|
T: IntoTransport<R, E, A>,
|
||||||
E: std::error::Error + From<std::io::Error> + Send + Sync + 'static,
|
E: std::error::Error + From<std::io::Error> + Send + Sync + 'static,
|
||||||
|
|
@ -125,7 +126,7 @@ pub trait ServiceExt<R: ServiceRole>: Service<R> + Sized {
|
||||||
self,
|
self,
|
||||||
transport: T,
|
transport: T,
|
||||||
ct: CancellationToken,
|
ct: CancellationToken,
|
||||||
) -> impl Future<Output = Result<RunningService<R, Self>, E>> + Send
|
) -> impl Future<Output = Result<RunningService<R, Self>, R::InitializeError<E>>> + Send
|
||||||
where
|
where
|
||||||
T: IntoTransport<R, E, A>,
|
T: IntoTransport<R, E, A>,
|
||||||
E: std::error::Error + From<std::io::Error> + Send + Sync + 'static,
|
E: std::error::Error + From<std::io::Error> + Send + Sync + 'static,
|
||||||
|
|
@ -469,7 +470,7 @@ pub async fn serve_directly<R, S, T, E, A>(
|
||||||
service: S,
|
service: S,
|
||||||
transport: T,
|
transport: T,
|
||||||
peer_info: R::PeerInfo,
|
peer_info: R::PeerInfo,
|
||||||
) -> Result<RunningService<R, S>, E>
|
) -> RunningService<R, S>
|
||||||
where
|
where
|
||||||
R: ServiceRole,
|
R: ServiceRole,
|
||||||
S: Service<R>,
|
S: Service<R>,
|
||||||
|
|
@ -485,7 +486,7 @@ pub async fn serve_directly_with_ct<R, S, T, E, A>(
|
||||||
transport: T,
|
transport: T,
|
||||||
peer_info: R::PeerInfo,
|
peer_info: R::PeerInfo,
|
||||||
ct: CancellationToken,
|
ct: CancellationToken,
|
||||||
) -> Result<RunningService<R, S>, E>
|
) -> RunningService<R, S>
|
||||||
where
|
where
|
||||||
R: ServiceRole,
|
R: ServiceRole,
|
||||||
S: Service<R>,
|
S: Service<R>,
|
||||||
|
|
@ -503,7 +504,7 @@ async fn serve_inner<R, S, T, E, A>(
|
||||||
peer: Peer<R>,
|
peer: Peer<R>,
|
||||||
mut peer_rx: tokio::sync::mpsc::Receiver<PeerSinkMessage<R>>,
|
mut peer_rx: tokio::sync::mpsc::Receiver<PeerSinkMessage<R>>,
|
||||||
ct: CancellationToken,
|
ct: CancellationToken,
|
||||||
) -> Result<RunningService<R, S>, E>
|
) -> RunningService<R, S>
|
||||||
where
|
where
|
||||||
R: ServiceRole,
|
R: ServiceRole,
|
||||||
S: Service<R>,
|
S: Service<R>,
|
||||||
|
|
@ -788,10 +789,10 @@ where
|
||||||
tracing::info!(?quit_reason, "serve finished");
|
tracing::info!(?quit_reason, "serve finished");
|
||||||
quit_reason
|
quit_reason
|
||||||
});
|
});
|
||||||
Ok(RunningService {
|
RunningService {
|
||||||
service,
|
service,
|
||||||
peer: peer_return,
|
peer: peer_return,
|
||||||
handle,
|
handle,
|
||||||
dg: ct.drop_guard(),
|
dg: ct.drop_guard(),
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
@ -19,7 +21,7 @@ use crate::model::{
|
||||||
///
|
///
|
||||||
/// if you want to handle the error, you can use `serve_client_with_ct` or `serve_client` with `Result<RunningService<RoleClient, S>, ClientError>`
|
/// if you want to handle the error, you can use `serve_client_with_ct` or `serve_client` with `Result<RunningService<RoleClient, S>, ClientError>`
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum ClientError {
|
pub enum ClientInitializeError<E> {
|
||||||
#[error("expect initialized response, but received: {0:?}")]
|
#[error("expect initialized response, but received: {0:?}")]
|
||||||
ExpectedInitResponse(Option<ServerJsonRpcMessage>),
|
ExpectedInitResponse(Option<ServerJsonRpcMessage>),
|
||||||
|
|
||||||
|
|
@ -32,30 +34,32 @@ pub enum ClientError {
|
||||||
#[error("connection closed: {0}")]
|
#[error("connection closed: {0}")]
|
||||||
ConnectionClosed(String),
|
ConnectionClosed(String),
|
||||||
|
|
||||||
#[error("IO error: {0}")]
|
#[error("Send message error {error}, when {context}")]
|
||||||
Io(#[from] std::io::Error),
|
TransportError {
|
||||||
|
error: E,
|
||||||
|
context: Cow<'static, str>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper function to get the next message from the stream
|
/// Helper function to get the next message from the stream
|
||||||
async fn expect_next_message<T>(
|
async fn expect_next_message<T, E>(
|
||||||
transport: &mut T,
|
transport: &mut T,
|
||||||
context: &str,
|
context: &str,
|
||||||
) -> Result<ServerJsonRpcMessage, ClientError>
|
) -> Result<ServerJsonRpcMessage, ClientInitializeError<E>>
|
||||||
where
|
where
|
||||||
T: Transport<RoleClient>,
|
T: Transport<RoleClient>,
|
||||||
{
|
{
|
||||||
transport
|
transport
|
||||||
.receive()
|
.receive()
|
||||||
.await
|
.await
|
||||||
.ok_or_else(|| ClientError::ConnectionClosed(context.to_string()))
|
.ok_or_else(|| ClientInitializeError::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
|
/// Helper function to expect a response from the stream
|
||||||
async fn expect_response<T>(
|
async fn expect_response<T, E>(
|
||||||
transport: &mut T,
|
transport: &mut T,
|
||||||
context: &str,
|
context: &str,
|
||||||
) -> Result<(ServerResult, RequestId), ClientError>
|
) -> Result<(ServerResult, RequestId), ClientInitializeError<E>>
|
||||||
where
|
where
|
||||||
T: Transport<RoleClient>,
|
T: Transport<RoleClient>,
|
||||||
{
|
{
|
||||||
|
|
@ -63,7 +67,7 @@ where
|
||||||
|
|
||||||
match msg {
|
match msg {
|
||||||
ServerJsonRpcMessage::Response(JsonRpcResponse { id, result, .. }) => Ok((result, id)),
|
ServerJsonRpcMessage::Response(JsonRpcResponse { id, result, .. }) => Ok((result, id)),
|
||||||
_ => Err(ClientError::ExpectedInitResponse(Some(msg))),
|
_ => Err(ClientInitializeError::ExpectedInitResponse(Some(msg))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -79,7 +83,7 @@ impl ServiceRole for RoleClient {
|
||||||
type PeerNot = ServerNotification;
|
type PeerNot = ServerNotification;
|
||||||
type Info = ClientInfo;
|
type Info = ClientInfo;
|
||||||
type PeerInfo = ServerInfo;
|
type PeerInfo = ServerInfo;
|
||||||
|
type InitializeError<E> = ClientInitializeError<E>;
|
||||||
const IS_CLIENT: bool = true;
|
const IS_CLIENT: bool = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -90,7 +94,7 @@ impl<S: Service<RoleClient>> ServiceExt<RoleClient> for S {
|
||||||
self,
|
self,
|
||||||
transport: T,
|
transport: T,
|
||||||
ct: CancellationToken,
|
ct: CancellationToken,
|
||||||
) -> impl Future<Output = Result<RunningService<RoleClient, Self>, E>> + Send
|
) -> impl Future<Output = Result<RunningService<RoleClient, Self>, ClientInitializeError<E>>> + Send
|
||||||
where
|
where
|
||||||
T: IntoTransport<RoleClient, E, A>,
|
T: IntoTransport<RoleClient, E, A>,
|
||||||
E: std::error::Error + From<std::io::Error> + Send + Sync + 'static,
|
E: std::error::Error + From<std::io::Error> + Send + Sync + 'static,
|
||||||
|
|
@ -103,7 +107,7 @@ impl<S: Service<RoleClient>> ServiceExt<RoleClient> for S {
|
||||||
pub async fn serve_client<S, T, E, A>(
|
pub async fn serve_client<S, T, E, A>(
|
||||||
service: S,
|
service: S,
|
||||||
transport: T,
|
transport: T,
|
||||||
) -> Result<RunningService<RoleClient, S>, E>
|
) -> Result<RunningService<RoleClient, S>, ClientInitializeError<E>>
|
||||||
where
|
where
|
||||||
S: Service<RoleClient>,
|
S: Service<RoleClient>,
|
||||||
T: IntoTransport<RoleClient, E, A>,
|
T: IntoTransport<RoleClient, E, A>,
|
||||||
|
|
@ -116,7 +120,7 @@ pub async fn serve_client_with_ct<S, T, E, A>(
|
||||||
service: S,
|
service: S,
|
||||||
transport: T,
|
transport: T,
|
||||||
ct: CancellationToken,
|
ct: CancellationToken,
|
||||||
) -> Result<RunningService<RoleClient, S>, E>
|
) -> Result<RunningService<RoleClient, S>, ClientInitializeError<E>>
|
||||||
where
|
where
|
||||||
S: Service<RoleClient>,
|
S: Service<RoleClient>,
|
||||||
T: IntoTransport<RoleClient, E, A>,
|
T: IntoTransport<RoleClient, E, A>,
|
||||||
|
|
@ -125,14 +129,6 @@ where
|
||||||
let mut transport = transport.into_transport();
|
let mut transport = transport.into_transport();
|
||||||
let id_provider = <Arc<AtomicU32RequestIdProvider>>::default();
|
let id_provider = <Arc<AtomicU32RequestIdProvider>>::default();
|
||||||
|
|
||||||
// Convert ClientError to std::io::Error, then to E
|
|
||||||
let handle_client_error = |e: ClientError| -> E {
|
|
||||||
match e {
|
|
||||||
ClientError::Io(io_err) => io_err.into(),
|
|
||||||
other => std::io::Error::new(std::io::ErrorKind::Other, format!("{}", other)).into(),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// service
|
// service
|
||||||
let id = id_provider.next_request_id();
|
let id = id_provider.next_request_id();
|
||||||
let init_request = InitializeRequest {
|
let init_request = InitializeRequest {
|
||||||
|
|
@ -145,23 +141,23 @@ where
|
||||||
ClientRequest::InitializeRequest(init_request),
|
ClientRequest::InitializeRequest(init_request),
|
||||||
id.clone(),
|
id.clone(),
|
||||||
))
|
))
|
||||||
.await?;
|
|
||||||
|
|
||||||
let (response, response_id) = expect_response(&mut transport, "initialize response")
|
|
||||||
.await
|
.await
|
||||||
.map_err(handle_client_error)?;
|
.map_err(|error| ClientInitializeError::TransportError {
|
||||||
|
error,
|
||||||
|
context: "send initialize request".into(),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let (response, response_id) = expect_response(&mut transport, "initialize response").await?;
|
||||||
|
|
||||||
if id != response_id {
|
if id != response_id {
|
||||||
return Err(handle_client_error(ClientError::ConflictInitResponseId(
|
return Err(ClientInitializeError::ConflictInitResponseId(
|
||||||
id,
|
id,
|
||||||
response_id,
|
response_id,
|
||||||
)));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let ServerResult::InitializeResult(initialize_result) = response else {
|
let ServerResult::InitializeResult(initialize_result) = response else {
|
||||||
return Err(handle_client_error(ClientError::ExpectedInitResult(Some(
|
return Err(ClientInitializeError::ExpectedInitResult(Some(response)));
|
||||||
response,
|
|
||||||
))));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// send notification
|
// send notification
|
||||||
|
|
@ -171,9 +167,15 @@ where
|
||||||
extensions: Default::default(),
|
extensions: Default::default(),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
transport.send(notification).await?;
|
transport
|
||||||
|
.send(notification)
|
||||||
|
.await
|
||||||
|
.map_err(|error| ClientInitializeError::TransportError {
|
||||||
|
error,
|
||||||
|
context: "send initialized notification".into(),
|
||||||
|
})?;
|
||||||
let (peer, peer_rx) = Peer::new(id_provider, initialize_result);
|
let (peer, peer_rx) = Peer::new(id_provider, initialize_result);
|
||||||
serve_inner(service, transport, peer, peer_rx, ct).await
|
Ok(serve_inner(service, transport, peer, peer_rx, ct).await)
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! method {
|
macro_rules! method {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
@ -6,9 +8,9 @@ use crate::model::{
|
||||||
ClientNotification, ClientRequest, ClientResult, CreateMessageRequest,
|
ClientNotification, ClientRequest, ClientResult, CreateMessageRequest,
|
||||||
CreateMessageRequestParam, CreateMessageResult, ErrorData, ListRootsRequest, ListRootsResult,
|
CreateMessageRequestParam, CreateMessageResult, ErrorData, ListRootsRequest, ListRootsResult,
|
||||||
LoggingMessageNotification, LoggingMessageNotificationParam, ProgressNotification,
|
LoggingMessageNotification, LoggingMessageNotificationParam, ProgressNotification,
|
||||||
ProgressNotificationParam, PromptListChangedNotification, ResourceListChangedNotification,
|
ProgressNotificationParam, PromptListChangedNotification, ProtocolVersion,
|
||||||
ResourceUpdatedNotification, ResourceUpdatedNotificationParam, ServerInfo, ServerNotification,
|
ResourceListChangedNotification, ResourceUpdatedNotification, ResourceUpdatedNotificationParam,
|
||||||
ServerRequest, ServerResult, ToolListChangedNotification,
|
ServerInfo, ServerNotification, ServerRequest, ServerResult, ToolListChangedNotification,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
||||||
|
|
@ -23,6 +25,8 @@ impl ServiceRole for RoleServer {
|
||||||
type PeerNot = ClientNotification;
|
type PeerNot = ClientNotification;
|
||||||
type Info = ServerInfo;
|
type Info = ServerInfo;
|
||||||
type PeerInfo = ClientInfo;
|
type PeerInfo = ClientInfo;
|
||||||
|
|
||||||
|
type InitializeError<E> = ServerInitializeError<E>;
|
||||||
const IS_CLIENT: bool = false;
|
const IS_CLIENT: bool = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -30,12 +34,12 @@ impl ServiceRole for RoleServer {
|
||||||
///
|
///
|
||||||
/// if you want to handle the error, you can use `serve_server_with_ct` or `serve_server` with `Result<RunningService<RoleServer, S>, ServerError>`
|
/// if you want to handle the error, you can use `serve_server_with_ct` or `serve_server` with `Result<RunningService<RoleServer, S>, ServerError>`
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum ServerError {
|
pub enum ServerInitializeError<E> {
|
||||||
#[error("expect initialized request, but received: {0:?}")]
|
#[error("expect initialized request, but received: {0:?}")]
|
||||||
ExpectedInitRequest(Option<ClientJsonRpcMessage>),
|
ExpectedInitializeRequest(Option<ClientJsonRpcMessage>),
|
||||||
|
|
||||||
#[error("expect initialized notification, but received: {0:?}")]
|
#[error("expect initialized notification, but received: {0:?}")]
|
||||||
ExpectedInitNotification(Option<ClientJsonRpcMessage>),
|
ExpectedInitializedNotification(Option<ClientJsonRpcMessage>),
|
||||||
|
|
||||||
#[error("connection closed: {0}")]
|
#[error("connection closed: {0}")]
|
||||||
ConnectionClosed(String),
|
ConnectionClosed(String),
|
||||||
|
|
@ -46,8 +50,14 @@ pub enum ServerError {
|
||||||
#[error("initialize failed: {0}")]
|
#[error("initialize failed: {0}")]
|
||||||
InitializeFailed(ErrorData),
|
InitializeFailed(ErrorData),
|
||||||
|
|
||||||
#[error("IO error: {0}")]
|
#[error("unsupported protocol version: {0}")]
|
||||||
Io(#[from] std::io::Error),
|
UnsupportedProtocolVersion(ProtocolVersion),
|
||||||
|
|
||||||
|
#[error("Send message error {error}, when {context}")]
|
||||||
|
TransportError {
|
||||||
|
error: E,
|
||||||
|
context: Cow<'static, str>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type ClientSink = Peer<RoleServer>;
|
pub type ClientSink = Peer<RoleServer>;
|
||||||
|
|
@ -57,7 +67,7 @@ impl<S: Service<RoleServer>> ServiceExt<RoleServer> for S {
|
||||||
self,
|
self,
|
||||||
transport: T,
|
transport: T,
|
||||||
ct: CancellationToken,
|
ct: CancellationToken,
|
||||||
) -> impl Future<Output = Result<RunningService<RoleServer, Self>, E>> + Send
|
) -> impl Future<Output = Result<RunningService<RoleServer, Self>, ServerInitializeError<E>>> + Send
|
||||||
where
|
where
|
||||||
T: IntoTransport<RoleServer, E, A>,
|
T: IntoTransport<RoleServer, E, A>,
|
||||||
E: std::error::Error + From<std::io::Error> + Send + Sync + 'static,
|
E: std::error::Error + From<std::io::Error> + Send + Sync + 'static,
|
||||||
|
|
@ -70,7 +80,7 @@ impl<S: Service<RoleServer>> ServiceExt<RoleServer> for S {
|
||||||
pub async fn serve_server<S, T, E, A>(
|
pub async fn serve_server<S, T, E, A>(
|
||||||
service: S,
|
service: S,
|
||||||
transport: T,
|
transport: T,
|
||||||
) -> Result<RunningService<RoleServer, S>, E>
|
) -> Result<RunningService<RoleServer, S>, ServerInitializeError<E>>
|
||||||
where
|
where
|
||||||
S: Service<RoleServer>,
|
S: Service<RoleServer>,
|
||||||
T: IntoTransport<RoleServer, E, A>,
|
T: IntoTransport<RoleServer, E, A>,
|
||||||
|
|
@ -80,52 +90,56 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper function to get the next message from the stream
|
/// Helper function to get the next message from the stream
|
||||||
async fn expect_next_message<T>(
|
async fn expect_next_message<T, E>(
|
||||||
transport: &mut T,
|
transport: &mut T,
|
||||||
context: &str,
|
context: &str,
|
||||||
) -> Result<ClientJsonRpcMessage, ServerError>
|
) -> Result<ClientJsonRpcMessage, ServerInitializeError<E>>
|
||||||
where
|
where
|
||||||
T: Transport<RoleServer>,
|
T: Transport<RoleServer>,
|
||||||
{
|
{
|
||||||
transport
|
transport
|
||||||
.receive()
|
.receive()
|
||||||
.await
|
.await
|
||||||
.ok_or_else(|| ServerError::ConnectionClosed(context.to_string()))
|
.ok_or_else(|| ServerInitializeError::ConnectionClosed(context.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper function to expect a request from the stream
|
/// Helper function to expect a request from the stream
|
||||||
async fn expect_request<T>(
|
async fn expect_request<T, E>(
|
||||||
transport: &mut T,
|
transport: &mut T,
|
||||||
context: &str,
|
context: &str,
|
||||||
) -> Result<(ClientRequest, RequestId), ServerError>
|
) -> Result<(ClientRequest, RequestId), ServerInitializeError<E>>
|
||||||
where
|
where
|
||||||
T: Transport<RoleServer>,
|
T: Transport<RoleServer>,
|
||||||
{
|
{
|
||||||
let msg = expect_next_message(transport, context).await?;
|
let msg = expect_next_message(transport, context).await?;
|
||||||
let msg_clone = msg.clone();
|
let msg_clone = msg.clone();
|
||||||
msg.into_request()
|
msg.into_request()
|
||||||
.ok_or(ServerError::ExpectedInitRequest(Some(msg_clone)))
|
.ok_or(ServerInitializeError::ExpectedInitializeRequest(Some(
|
||||||
|
msg_clone,
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper function to expect a notification from the stream
|
/// Helper function to expect a notification from the stream
|
||||||
async fn expect_notification<T>(
|
async fn expect_notification<T, E>(
|
||||||
transport: &mut T,
|
transport: &mut T,
|
||||||
context: &str,
|
context: &str,
|
||||||
) -> Result<ClientNotification, ServerError>
|
) -> Result<ClientNotification, ServerInitializeError<E>>
|
||||||
where
|
where
|
||||||
T: Transport<RoleServer>,
|
T: Transport<RoleServer>,
|
||||||
{
|
{
|
||||||
let msg = expect_next_message(transport, context).await?;
|
let msg = expect_next_message(transport, context).await?;
|
||||||
let msg_clone = msg.clone();
|
let msg_clone = msg.clone();
|
||||||
msg.into_notification()
|
msg.into_notification()
|
||||||
.ok_or(ServerError::ExpectedInitNotification(Some(msg_clone)))
|
.ok_or(ServerInitializeError::ExpectedInitializedNotification(
|
||||||
|
Some(msg_clone),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn serve_server_with_ct<S, T, E, A>(
|
pub async fn serve_server_with_ct<S, T, E, A>(
|
||||||
service: S,
|
service: S,
|
||||||
transport: T,
|
transport: T,
|
||||||
ct: CancellationToken,
|
ct: CancellationToken,
|
||||||
) -> Result<RunningService<RoleServer, S>, E>
|
) -> Result<RunningService<RoleServer, S>, ServerInitializeError<E>>
|
||||||
where
|
where
|
||||||
S: Service<RoleServer>,
|
S: Service<RoleServer>,
|
||||||
T: IntoTransport<RoleServer, E, A>,
|
T: IntoTransport<RoleServer, E, A>,
|
||||||
|
|
@ -134,23 +148,13 @@ where
|
||||||
let mut transport = transport.into_transport();
|
let mut transport = transport.into_transport();
|
||||||
let id_provider = <Arc<AtomicU32RequestIdProvider>>::default();
|
let id_provider = <Arc<AtomicU32RequestIdProvider>>::default();
|
||||||
|
|
||||||
// Convert ServerError to std::io::Error, then to E
|
|
||||||
let handle_server_error = |e: ServerError| -> E {
|
|
||||||
match e {
|
|
||||||
ServerError::Io(io_err) => io_err.into(),
|
|
||||||
other => std::io::Error::new(std::io::ErrorKind::Other, format!("{}", other)).into(),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Get initialize request
|
// Get initialize request
|
||||||
let (request, id) = expect_request(&mut transport, "initialized request")
|
let (request, id) = expect_request(&mut transport, "initialized request").await?;
|
||||||
.await
|
|
||||||
.map_err(handle_server_error)?;
|
|
||||||
|
|
||||||
let ClientRequest::InitializeRequest(peer_info) = &request else {
|
let ClientRequest::InitializeRequest(peer_info) = &request else {
|
||||||
return Err(handle_server_error(ServerError::ExpectedInitRequest(Some(
|
return Err(ServerInitializeError::ExpectedInitializeRequest(Some(
|
||||||
ClientJsonRpcMessage::request(request, id),
|
ClientJsonRpcMessage::request(request, id),
|
||||||
))));
|
)));
|
||||||
};
|
};
|
||||||
let (peer, peer_rx) = Peer::new(id_provider, peer_info.params.clone());
|
let (peer, peer_rx) = Peer::new(id_provider, peer_info.params.clone());
|
||||||
let context = RequestContext {
|
let context = RequestContext {
|
||||||
|
|
@ -165,24 +169,24 @@ where
|
||||||
let mut init_response = match init_response {
|
let mut init_response = match init_response {
|
||||||
Ok(ServerResult::InitializeResult(init_response)) => init_response,
|
Ok(ServerResult::InitializeResult(init_response)) => init_response,
|
||||||
Ok(result) => {
|
Ok(result) => {
|
||||||
return Err(handle_server_error(
|
return Err(ServerInitializeError::UnexpectedInitializeResponse(result));
|
||||||
ServerError::UnexpectedInitializeResponse(result),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
transport
|
transport
|
||||||
.send(ServerJsonRpcMessage::error(e.clone(), id))
|
.send(ServerJsonRpcMessage::error(e.clone(), id))
|
||||||
.await?;
|
.await
|
||||||
return Err(handle_server_error(ServerError::InitializeFailed(e)));
|
.map_err(|error| ServerInitializeError::TransportError {
|
||||||
|
error,
|
||||||
|
context: "sending error response".into(),
|
||||||
|
})?;
|
||||||
|
return Err(ServerInitializeError::InitializeFailed(e));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let protocol_version = match peer_info
|
let peer_protocol_version = peer_info.params.protocol_version.clone();
|
||||||
.params
|
let protocol_version = match peer_protocol_version
|
||||||
.protocol_version
|
|
||||||
.partial_cmp(&init_response.protocol_version)
|
.partial_cmp(&init_response.protocol_version)
|
||||||
.ok_or(std::io::Error::new(
|
.ok_or(ServerInitializeError::UnsupportedProtocolVersion(
|
||||||
std::io::ErrorKind::InvalidData,
|
peer_protocol_version,
|
||||||
"unsupported protocol version",
|
|
||||||
))? {
|
))? {
|
||||||
std::cmp::Ordering::Less => peer_info.params.protocol_version.clone(),
|
std::cmp::Ordering::Less => peer_info.params.protocol_version.clone(),
|
||||||
_ => init_response.protocol_version,
|
_ => init_response.protocol_version,
|
||||||
|
|
@ -193,20 +197,22 @@ where
|
||||||
ServerResult::InitializeResult(init_response),
|
ServerResult::InitializeResult(init_response),
|
||||||
id,
|
id,
|
||||||
))
|
))
|
||||||
.await?;
|
.await
|
||||||
|
.map_err(|error| ServerInitializeError::TransportError {
|
||||||
|
error,
|
||||||
|
context: "sending initialize response".into(),
|
||||||
|
})?;
|
||||||
|
|
||||||
// Wait for initialize notification
|
// Wait for initialize notification
|
||||||
let notification = expect_notification(&mut transport, "initialize notification")
|
let notification = expect_notification(&mut transport, "initialize notification").await?;
|
||||||
.await
|
|
||||||
.map_err(handle_server_error)?;
|
|
||||||
let ClientNotification::InitializedNotification(_) = notification else {
|
let ClientNotification::InitializedNotification(_) = notification else {
|
||||||
return Err(handle_server_error(ServerError::ExpectedInitNotification(
|
return Err(ServerInitializeError::ExpectedInitializedNotification(
|
||||||
Some(ClientJsonRpcMessage::notification(notification)),
|
Some(ClientJsonRpcMessage::notification(notification)),
|
||||||
)));
|
));
|
||||||
};
|
};
|
||||||
let _ = service.handle_notification(notification).await;
|
let _ = service.handle_notification(notification).await;
|
||||||
// Continue processing service
|
// Continue processing service
|
||||||
serve_inner(service, transport, peer, peer_rx, ct).await
|
Ok(serve_inner(service, transport, peer, peer_rx, ct).await)
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! method {
|
macro_rules! method {
|
||||||
|
|
|
||||||
|
|
@ -288,7 +288,10 @@ impl SseServer {
|
||||||
let service = service_provider();
|
let service = service_provider();
|
||||||
let ct = self.config.ct.child_token();
|
let ct = self.config.ct.child_token();
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let server = service.serve_with_ct(transport, ct).await?;
|
let server = service
|
||||||
|
.serve_with_ct(transport, ct)
|
||||||
|
.await
|
||||||
|
.map_err(std::io::Error::other)?;
|
||||||
server.waiting().await?;
|
server.waiting().await?;
|
||||||
tokio::io::Result::Ok(())
|
tokio::io::Result::Ok(())
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -327,7 +327,10 @@ impl StreamableHttpServer {
|
||||||
let service = service_provider();
|
let service = service_provider();
|
||||||
let ct = self.config.ct.child_token();
|
let ct = self.config.ct.child_token();
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let server = service.serve_with_ct(transport, ct).await?;
|
let server = service
|
||||||
|
.serve_with_ct(transport, ct)
|
||||||
|
.await
|
||||||
|
.map_err(tokio::io::Error::other)?;
|
||||||
server.waiting().await?;
|
server.waiting().await?;
|
||||||
tokio::io::Result::Ok(())
|
tokio::io::Result::Ok(())
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue