use std::collections::HashMap;
use std::io;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::time::Duration;
use serde_json::Value;
use tokio::io::{AsyncBufRead, AsyncWrite};
use tokio::sync::{Mutex, oneshot};
use tokio::task::JoinHandle;
use tokio::time::timeout;
use crate::jsonrpc_framing::{decode_frame, encode_frame};
const WRITE_TIMEOUT: Duration = Duration::from_secs(10);
pub(crate) trait RpcErr: From<io::Error> + From<serde_json::Error> + Send + 'static {
fn connection_closed() -> Self;
fn timeout(duration: Duration) -> Self;
}
pub(crate) enum Incoming<E> {
Response { id: u64, result: Result<Value, E> },
Notify { key: String, body: Value },
ReverseRequest { ack: Value },
Ignore,
}
pub(crate) trait Protocol: 'static {
type Error: RpcErr;
fn name() -> &'static str;
fn build_request(id: u64, method: &str, params: Value) -> Value;
fn build_notification(id: u64, method: &str, params: Value) -> Value;
fn classify(msg: &Value) -> Incoming<Self::Error>;
}
type Pending<E> = HashMap<u64, oneshot::Sender<Result<Value, E>>>;
type Handler = Arc<dyn Fn(Value) + Send + Sync>;
pub(crate) struct Inner<E> {
pub(crate) next_id: AtomicU64,
pub(crate) pending: Mutex<Pending<E>>,
pub(crate) handlers: Mutex<HashMap<String, Handler>>,
pub(crate) writer: Mutex<Box<dyn AsyncWrite + Send + Unpin>>,
pub(crate) closed: AtomicBool,
}
pub(crate) fn new<P, R, W>(
reader: R,
writer: W,
) -> (Arc<Inner<P::Error>>, JoinHandle<io::Result<()>>)
where
P: Protocol,
R: AsyncBufRead + Send + Unpin + 'static,
W: AsyncWrite + Send + Unpin + 'static,
{
let inner = Arc::new(Inner::<P::Error> {
next_id: AtomicU64::new(1),
pending: Mutex::new(HashMap::new()),
handlers: Mutex::new(HashMap::new()),
writer: Mutex::new(Box::new(writer)),
closed: AtomicBool::new(false),
});
let task = tokio::spawn(read_loop::<P, R>(inner.clone(), reader));
(inner, task)
}
pub(crate) async fn request<P, Params, R>(
inner: &Inner<P::Error>,
method: &str,
params: Params,
request_timeout: Duration,
) -> Result<R, P::Error>
where
P: Protocol,
Params: serde::Serialize,
R: serde::de::DeserializeOwned,
{
if inner.closed.load(Ordering::SeqCst) {
return Err(P::Error::connection_closed());
}
let id = inner.next_id.fetch_add(1, Ordering::SeqCst);
let (tx, rx) = oneshot::channel();
inner.pending.lock().await.insert(id, tx);
let body = P::build_request(id, method, serde_json::to_value(params)?);
let bytes = serde_json::to_vec(&body)?;
let send_result = timeout(WRITE_TIMEOUT, async {
let mut writer = inner.writer.lock().await;
encode_frame(&mut *writer, &bytes).await
})
.await;
match send_result {
Ok(Ok(())) => {}
Ok(Err(e)) => {
inner.pending.lock().await.remove(&id);
return Err(P::Error::from(e));
}
Err(_) => {
inner.pending.lock().await.remove(&id);
return Err(P::Error::timeout(WRITE_TIMEOUT));
}
}
let value = match timeout(request_timeout, rx).await {
Ok(Ok(result)) => result?,
Ok(Err(_)) => {
inner.pending.lock().await.remove(&id);
return Err(P::Error::connection_closed());
}
Err(_) => {
inner.pending.lock().await.remove(&id);
return Err(P::Error::timeout(request_timeout));
}
};
Ok(serde_json::from_value(value)?)
}
pub(crate) async fn notify<P, Params>(
inner: &Inner<P::Error>,
method: &str,
params: Params,
) -> Result<(), P::Error>
where
P: Protocol,
Params: serde::Serialize,
{
if inner.closed.load(Ordering::SeqCst) {
return Err(P::Error::connection_closed());
}
let id = inner.next_id.fetch_add(1, Ordering::SeqCst);
let body = P::build_notification(id, method, serde_json::to_value(params)?);
let bytes = serde_json::to_vec(&body)?;
match timeout(WRITE_TIMEOUT, async {
let mut writer = inner.writer.lock().await;
encode_frame(&mut *writer, &bytes).await
})
.await
{
Ok(Ok(())) => Ok(()),
Ok(Err(e)) => Err(P::Error::from(e)),
Err(_) => Err(P::Error::timeout(WRITE_TIMEOUT)),
}
}
pub(crate) async fn register_notification<E>(inner: &Inner<E>, method: &str, handler: Handler) {
inner
.handlers
.lock()
.await
.insert(method.to_string(), handler);
}
pub(crate) async fn read_loop<P, R>(inner: Arc<Inner<P::Error>>, mut reader: R) -> io::Result<()>
where
P: Protocol,
R: AsyncBufRead + Send + Unpin,
{
let name = P::name();
let mut exit_err: Option<io::Error> = None;
loop {
let frame = match decode_frame(&mut reader).await {
Ok(b) => b,
Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => {
break;
}
Err(e) => {
tracing::warn!("{name}: read loop aborting on decode error: {e}");
exit_err = Some(e);
break;
}
};
let msg: Value = match serde_json::from_slice(&frame) {
Ok(v) => v,
Err(e) => {
tracing::warn!("{name}: skipping non-JSON frame: {e}");
continue;
}
};
dispatch::<P>(&inner, msg).await;
}
inner.closed.store(true, Ordering::SeqCst);
let mut pending = inner.pending.lock().await;
for (_, sender) in pending.drain() {
let _ = sender.send(Err(P::Error::connection_closed()));
}
drop(pending);
match exit_err {
Some(e) => Err(e),
None => Ok(()),
}
}
async fn dispatch<P: Protocol>(inner: &Arc<Inner<P::Error>>, msg: Value) {
match P::classify(&msg) {
Incoming::Response { id, result } => {
let sender = inner.pending.lock().await.remove(&id);
if let Some(sender) = sender {
let _ = sender.send(result);
}
}
Incoming::Notify { key, body } => {
let handler = inner.handlers.lock().await.get(&key).cloned();
if let Some(handler) = handler {
handler(body);
}
}
Incoming::ReverseRequest { ack } => {
if let Ok(bytes) = serde_json::to_vec(&ack) {
let mut writer = inner.writer.lock().await;
let _ = encode_frame(&mut *writer, &bytes).await;
}
}
Incoming::Ignore => {
tracing::warn!("{}: ignoring frame", P::name());
}
}
}