use futures::StreamExt as _;
use futures::channel::mpsc;
use crate::jsonrpc::protocol_compat::ProtocolCompat;
use crate::jsonrpc::{OutgoingMessage, PendingReplies, RawJsonRpcMessage};
use crate::schema::v1::RequestId;
pub type OutgoingMessageTx = mpsc::UnboundedSender<OutgoingMessage>;
pub(crate) fn send_raw_message(
tx: &OutgoingMessageTx,
message: OutgoingMessage,
) -> Result<(), crate::Error> {
tracing::debug!(?message, ?tx, "send_raw_message");
tx.unbounded_send(message)
.map_err(crate::util::internal_error)
}
pub(super) async fn outgoing_protocol_actor(
mut outgoing_rx: mpsc::UnboundedReceiver<OutgoingMessage>,
pending_replies: PendingReplies,
transport_tx: mpsc::UnboundedSender<Result<RawJsonRpcMessage, crate::Error>>,
protocol_compat: ProtocolCompat,
) -> Result<(), crate::Error> {
let mut drain_waiters = Vec::new();
while let Some(message) = outgoing_rx.next().await {
tracing::debug!(?message, "outgoing_protocol_actor");
let json_rpc_message = match message {
OutgoingMessage::CloseAfterDraining { done } => {
outgoing_rx.close();
drain_waiters.push(done);
continue;
}
OutgoingMessage::Request {
id,
method,
untyped,
} => {
if !pending_replies.contains(&id) {
continue;
}
let request = match protocol_compat
.outgoing_message(untyped)
.and_then(|untyped| untyped.into_raw_jsonrpc_message(Some(id.clone())))
{
Ok(request) => request,
Err(error) => {
tracing::warn!(?id, %method, ?error, "Failed to prepare outgoing request");
if let Some(pending_reply) = pending_replies.remove(&id) {
pending_reply.fail(error);
}
continue;
}
};
if !pending_replies.contains(&id) {
continue;
}
if let Err(error) = transport_tx.unbounded_send(Ok(request)) {
let error = crate::Error::into_internal_error(error);
if let Some(pending_reply) = pending_replies.remove(&id) {
pending_reply.fail(error.clone());
}
return Err(error);
}
continue;
}
OutgoingMessage::Notification { untyped } => {
let messages = match protocol_compat.outgoing_notification(untyped) {
Ok(messages) => messages,
Err(error) => {
tracing::warn!(
?error,
"Dropping outgoing notification after preparation failed"
);
continue;
}
};
for untyped in messages {
let message = match untyped.into_raw_jsonrpc_message(None) {
Ok(message) => message,
Err(error) => {
tracing::warn!(
?error,
"Dropping outgoing notification after serialization failed"
);
continue;
}
};
transport_tx
.unbounded_send(Ok(message))
.map_err(crate::Error::into_internal_error)?;
}
continue;
}
OutgoingMessage::Response {
id,
method,
response,
} => match protocol_compat.outgoing_response(&method, response) {
Ok(value) => {
tracing::debug!(?id, "Sending success response");
RawJsonRpcMessage::response(id, Ok(value))
}
Err(error) => {
tracing::warn!(?id, %method, ?error, "Sending error response");
RawJsonRpcMessage::response(id, Err(error))
}
},
OutgoingMessage::Error { error } => {
RawJsonRpcMessage::response(RequestId::Null, Err(error))
}
};
transport_tx
.unbounded_send(Ok(json_rpc_message))
.map_err(crate::Error::into_internal_error)?;
}
drop(transport_tx);
for done in drain_waiters {
let _ = done.send(());
}
Ok(())
}