use std::{
io,
sync::atomic::{AtomicBool, Ordering},
};
use futures_core::future::BoxFuture;
use tokio::io::{AsyncRead, AsyncWrite, BufReader};
use crate::protocol::{
Message, RequestId, Response, Server,
stdio::{read, write},
};
static TAKEN: AtomicBool = AtomicBool::new(false);
pub struct StdioServer {
incoming: BufReader<Box<dyn AsyncRead + Send + Unpin>>,
outgoing: Box<dyn AsyncWrite + Send + Unpin>,
}
impl StdioServer {
pub fn new(
incoming: impl AsyncRead + Send + Unpin + 'static,
outgoing: impl AsyncWrite + Send + Unpin + 'static,
) -> Self {
StdioServer {
incoming: BufReader::new(Box::new(incoming)),
outgoing: Box::new(outgoing),
}
}
pub fn stdio() -> anyhow::Result<Self> {
if TAKEN.swap(true, Ordering::SeqCst) {
anyhow::bail!("stdin and stdout are already the protocol's — there is one of each");
}
Ok(StdioServer::new(tokio::io::stdin(), tokio::io::stdout()))
}
}
impl Server for StdioServer {
fn recv(&mut self) -> BoxFuture<'_, io::Result<Option<Message>>> {
Box::pin(read(&mut self.incoming))
}
fn respond(&mut self, id: RequestId, result: Response) -> BoxFuture<'_, io::Result<()>> {
Box::pin(async move { write(&mut self.outgoing, &Message::Response { id, result }).await })
}
}
#[cfg(test)]
mod tests {
use std::{
io::Cursor,
pin::Pin,
sync::{Arc, Mutex},
task::{Context as TaskContext, Poll},
};
use super::*;
use crate::protocol::{Call, Error, ExecCall, InitCall, InitResp, Notification};
#[derive(Clone, Default)]
struct Sent(Arc<Mutex<Vec<u8>>>);
impl AsyncWrite for Sent {
fn poll_write(
self: Pin<&mut Self>,
_cx: &mut TaskContext<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
self.0.lock().unwrap().extend_from_slice(buf);
Poll::Ready(Ok(buf.len()))
}
fn poll_flush(self: Pin<&mut Self>, _cx: &mut TaskContext<'_>) -> Poll<io::Result<()>> {
Poll::Ready(Ok(()))
}
fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut TaskContext<'_>) -> Poll<io::Result<()>> {
Poll::Ready(Ok(()))
}
}
async fn framed(messages: &[Message]) -> Vec<u8> {
let mut bytes = Vec::new();
for message in messages {
write(&mut bytes, message).await.unwrap();
}
bytes
}
fn request(id: RequestId, call: Call) -> Message {
Message::Request { id, call }
}
#[tokio::test]
async fn every_message_arrives_as_it_was_sent() {
let sent = vec![
request(0, Call::Init(InitCall::default())),
Message::Notification(Notification::Start),
request(
1,
Call::Exec(ExecCall {
cmd: vec!["echo".into(), "hi".into()],
..ExecCall::default()
}),
),
Message::Notification(Notification::Stop),
Message::Response {
id: 99,
result: Response::Init(InitResp::default()),
},
Message::Notification(Notification::Quit),
];
let mut server = StdioServer::new(Cursor::new(framed(&sent).await), Sent::default());
for message in &sent {
assert_eq!(server.recv().await.unwrap().as_ref(), Some(message));
}
assert!(server.recv().await.unwrap().is_none());
}
#[tokio::test]
async fn a_response_carries_the_id_it_was_given() {
let sent = Sent::default();
let mut server = StdioServer::new(tokio::io::empty(), sent.clone());
server
.respond(7, Response::Init(InitResp::default()))
.await
.unwrap();
server
.respond(9, Response::Error(Error::new(Error::TIMED_OUT, "too slow")))
.await
.unwrap();
let bytes = sent.0.lock().unwrap().clone();
let mut reader = bytes.as_slice();
let mut written = Vec::new();
while let Some(message) = read(&mut reader).await.unwrap() {
written.push(message);
}
assert_eq!(
written,
[
Message::Response {
id: 7,
result: Response::Init(InitResp::default()),
},
Message::Response {
id: 9,
result: Response::Error(Error::new(Error::TIMED_OUT, "too slow")),
},
]
);
}
#[tokio::test]
async fn a_malformed_frame_is_an_error_and_not_an_end() {
let payload = br#"{"jsonrpc":"2.0","method":"nonsense"}"#;
let mut bytes = (payload.len() as u32).to_be_bytes().to_vec();
bytes.extend_from_slice(payload);
let mut server = StdioServer::new(Cursor::new(bytes), Sent::default());
let error = server.recv().await.unwrap_err();
assert_eq!(error.kind(), io::ErrorKind::InvalidData);
}
}