|
@@ -1,72 +1,94 @@
|
|
|
//! Code which enables sending messages between processes in the blocktree system.
|
|
|
-use btlib::{
|
|
|
- Result,
|
|
|
- crypto::rand_array,
|
|
|
- BlockPath, Writecap,
|
|
|
+use btlib::{crypto::rand_array, error::BoxInIoErr, BlockPath, Result, Writecap};
|
|
|
+use btserde::{read_from, write_to};
|
|
|
+use bytes::{BufMut, BytesMut};
|
|
|
+use core::{
|
|
|
+ future::Future,
|
|
|
+ pin::Pin,
|
|
|
+ task::{Context, Poll},
|
|
|
};
|
|
|
-use std::{
|
|
|
- path::PathBuf,
|
|
|
- io::{Read, Write},
|
|
|
- os::unix::net::{UnixStream, UnixListener},
|
|
|
+use futures::{
|
|
|
+ sink::{Send, Sink},
|
|
|
+ stream::Stream,
|
|
|
+ SinkExt, StreamExt,
|
|
|
};
|
|
|
-use btserde::{read_from, write_to};
|
|
|
+use lazy_static::lazy_static;
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
+use std::{io, net::Shutdown, path::PathBuf};
|
|
|
+use tokio::{
|
|
|
+ io::{AsyncRead, AsyncWrite, ReadBuf},
|
|
|
+ net::UnixDatagram,
|
|
|
+};
|
|
|
+use tokio_util::codec::{Decoder, Encoder, FramedRead, FramedWrite};
|
|
|
use zerocopy::FromBytes;
|
|
|
|
|
|
pub use private::*;
|
|
|
|
|
|
mod private {
|
|
|
- use std::io;
|
|
|
|
|
|
use super::*;
|
|
|
|
|
|
- /// The directory where sockets are created.
|
|
|
- fn socket_dir() -> PathBuf {
|
|
|
- let home_dir: PathBuf = std::env::var("HOME").unwrap().into();
|
|
|
- home_dir.join(".btmsg")
|
|
|
+ lazy_static! {
|
|
|
+ /// The default directory in which to place blocktree sockets.
|
|
|
+ static ref SOCK_DIR: PathBuf = {
|
|
|
+ let mut path: PathBuf = std::env::var("HOME").unwrap().into();
|
|
|
+ path.push(".btmsg");
|
|
|
+ path
|
|
|
+ };
|
|
|
}
|
|
|
|
|
|
- pub fn socket_path(block_path: &BlockPath) -> PathBuf {
|
|
|
- socket_dir().join(block_path.to_string())
|
|
|
+ /// Appends the given Blocktree path to the path of the given directory.
|
|
|
+ fn socket_path(fs_path: &mut PathBuf, block_path: &BlockPath) {
|
|
|
+ fs_path.push(block_path.to_string());
|
|
|
}
|
|
|
|
|
|
#[derive(PartialEq, Eq, Serialize, Deserialize)]
|
|
|
- pub enum MsgError {}
|
|
|
+ pub enum MsgError {
|
|
|
+ Unknown,
|
|
|
+ }
|
|
|
|
|
|
+ /// The owned version of a message body. This is the body type for received messages. It's
|
|
|
+ /// very important that this enum's variants match those of [MsgBodyRef], otherwise runtime
|
|
|
+ /// deserialization errors will occur.
|
|
|
#[derive(Deserialize)]
|
|
|
pub enum MsgBodyOwned {
|
|
|
Success,
|
|
|
Fail(MsgError),
|
|
|
Ping,
|
|
|
Hello(Writecap),
|
|
|
- Read { offset: u64, size: u64, },
|
|
|
+ Read { offset: u64, size: u64 },
|
|
|
Write { offset: u64, buf: Vec<u8> },
|
|
|
Custom(Vec<u8>),
|
|
|
}
|
|
|
|
|
|
+ /// The reference version of a message body. This is the body type when sending messages. It's
|
|
|
+ /// very important that this enum's variants match those of [MsgBodyOwned], otherwise runtime
|
|
|
+ /// deserialization errors will occur.
|
|
|
#[derive(Serialize)]
|
|
|
pub enum MsgBodyRef<'a> {
|
|
|
Success,
|
|
|
Fail(&'a MsgError),
|
|
|
Ping,
|
|
|
Hello(&'a Writecap),
|
|
|
- Read { offset: u64, buf: &'a [u8] },
|
|
|
- Write { offset: u64, buf: &'a mut [u8] },
|
|
|
+ Read { offset: u64, size: u64 },
|
|
|
+ Write { offset: u64, buf: &'a [u8] },
|
|
|
Custom(&'a [u8]),
|
|
|
}
|
|
|
|
|
|
+ /// Trait which unifies owned and borrowed messages.
|
|
|
pub trait Msg {
|
|
|
type Body;
|
|
|
- fn from(&self) -> &BlockPath;
|
|
|
fn to(&self) -> &BlockPath;
|
|
|
+ fn from(&self) -> &BlockPath;
|
|
|
fn id(&self) -> u128;
|
|
|
fn body(&self) -> &Self::Body;
|
|
|
}
|
|
|
|
|
|
+ /// An owned message. This is the type which observed by the receiver.
|
|
|
#[derive(Deserialize)]
|
|
|
pub struct MsgOwned {
|
|
|
- from: BlockPath,
|
|
|
to: BlockPath,
|
|
|
+ from: BlockPath,
|
|
|
id: u128,
|
|
|
body: MsgBodyOwned,
|
|
|
}
|
|
@@ -74,14 +96,14 @@ mod private {
|
|
|
impl Msg for MsgOwned {
|
|
|
type Body = MsgBodyOwned;
|
|
|
|
|
|
- fn from(&self) -> &BlockPath {
|
|
|
- &self.from
|
|
|
- }
|
|
|
-
|
|
|
fn to(&self) -> &BlockPath {
|
|
|
&self.to
|
|
|
}
|
|
|
|
|
|
+ fn from(&self) -> &BlockPath {
|
|
|
+ &self.from
|
|
|
+ }
|
|
|
+
|
|
|
fn id(&self) -> u128 {
|
|
|
self.id
|
|
|
}
|
|
@@ -91,23 +113,30 @@ mod private {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /// A borrowed message. This is the type which is produced by the sender.
|
|
|
#[derive(Serialize)]
|
|
|
pub struct MsgRef<'a> {
|
|
|
- from: &'a BlockPath,
|
|
|
to: &'a BlockPath,
|
|
|
+ from: &'a BlockPath,
|
|
|
id: u128,
|
|
|
body: MsgBodyRef<'a>,
|
|
|
}
|
|
|
|
|
|
+ impl<'a> MsgRef<'a> {
|
|
|
+ pub fn new(to: &'a BlockPath, from: &'a BlockPath, id: u128, body: MsgBodyRef<'a>) -> Self {
|
|
|
+ Self { to, from, id, body }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
impl<'a> Msg for MsgRef<'a> {
|
|
|
type Body = MsgBodyRef<'a>;
|
|
|
|
|
|
fn to(&self) -> &BlockPath {
|
|
|
- &self.to
|
|
|
+ self.to
|
|
|
}
|
|
|
|
|
|
fn from(&self) -> &BlockPath {
|
|
|
- &self.from
|
|
|
+ self.from
|
|
|
}
|
|
|
|
|
|
fn id(&self) -> u128 {
|
|
@@ -119,185 +148,394 @@ mod private {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /// An owned message tagged with a version.
|
|
|
#[derive(Deserialize)]
|
|
|
- pub enum VerMsg {
|
|
|
+ enum VerMsgOwned {
|
|
|
V0(MsgOwned),
|
|
|
}
|
|
|
|
|
|
+ /// A borrowed message tagged with a version.
|
|
|
#[derive(Serialize)]
|
|
|
- pub enum VerMsgRef<'a> {
|
|
|
+ enum VerMsgRef<'a> {
|
|
|
V0(MsgRef<'a>),
|
|
|
}
|
|
|
|
|
|
- pub trait Sender: Send {
|
|
|
- fn send<'a>(&mut self, msg: MsgRef<'a>) -> Result<()>;
|
|
|
+ /// A type which can be used to send messages.
|
|
|
+ pub trait Sender<'a>: Sink<MsgRef<'a>, Error = btlib::Error> {
|
|
|
+ type SendFut: Future<Output = Result<()>> + std::marker::Send;
|
|
|
+
|
|
|
fn path(&self) -> &BlockPath;
|
|
|
|
|
|
+ /// Creates a new message with the given `from` and `body` fields and sends it to the peer
|
|
|
+ /// this [Sender] is associated with.
|
|
|
+ fn send_msg(&'a mut self, from: &'a BlockPath, body: MsgBodyRef<'a>) -> Self::SendFut;
|
|
|
+
|
|
|
/// Generates and returns a new message ID.
|
|
|
- fn gen_id(&mut self) -> Result<u128> {
|
|
|
+ fn gen_id() -> Result<u128> {
|
|
|
const LEN: usize = std::mem::size_of::<u128>();
|
|
|
let bytes = rand_array::<LEN>()?;
|
|
|
let option = u128::read_from(bytes.as_slice());
|
|
|
// Safety: because LEN == size_of::<u128>(), read_from should have returned Some.
|
|
|
Ok(option.unwrap())
|
|
|
}
|
|
|
+ }
|
|
|
+
|
|
|
+ /// A type which can be used to receive messages.
|
|
|
+ pub trait Receiver: Stream<Item = Result<MsgOwned>> {
|
|
|
+ fn path(&self) -> &BlockPath;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Encodes and decodes messages using [btserde].
|
|
|
+ struct MessageCodec;
|
|
|
+
|
|
|
+ impl<'a> Encoder<MsgRef<'a>> for MessageCodec {
|
|
|
+ type Error = btlib::Error;
|
|
|
+
|
|
|
+ fn encode(&mut self, item: MsgRef<'a>, dst: &mut BytesMut) -> Result<()> {
|
|
|
+ const U64_LEN: usize = std::mem::size_of::<u64>();
|
|
|
+ let payload = dst.split_off(U64_LEN);
|
|
|
+ let mut writer = payload.writer();
|
|
|
+ write_to(&VerMsgRef::V0(item), &mut writer)?;
|
|
|
+ let payload = writer.into_inner();
|
|
|
+ let payload_len = payload.len() as u64;
|
|
|
+ let mut writer = dst.writer();
|
|
|
+ write_to(&payload_len, &mut writer)?;
|
|
|
+ let dst = writer.into_inner();
|
|
|
+ dst.unsplit(payload);
|
|
|
+ Ok(())
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl Decoder for MessageCodec {
|
|
|
+ type Item = MsgOwned;
|
|
|
+ type Error = btlib::Error;
|
|
|
|
|
|
- fn send_new<'a>(&mut self, to: &BlockPath, from: &BlockPath, body: MsgBodyRef<'a>) -> Result<()> {
|
|
|
- let id = self.gen_id()?;
|
|
|
- let msg = MsgRef {
|
|
|
- to,
|
|
|
- from,
|
|
|
- id,
|
|
|
- body,
|
|
|
+ fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>> {
|
|
|
+ let mut slice: &[u8] = src.as_ref();
|
|
|
+ let payload_len: u64 = match read_from(&mut slice) {
|
|
|
+ Ok(payload_len) => payload_len,
|
|
|
+ Err(err) => {
|
|
|
+ if let btserde::Error::Eof = err {
|
|
|
+ return Ok(None);
|
|
|
+ }
|
|
|
+ return Err(err.into());
|
|
|
+ }
|
|
|
};
|
|
|
- self.send(msg)
|
|
|
+ let payload_len: usize = payload_len.try_into().box_err()?;
|
|
|
+ if slice.len() < payload_len {
|
|
|
+ src.reserve(payload_len - slice.len());
|
|
|
+ return Ok(None);
|
|
|
+ }
|
|
|
+ let VerMsgOwned::V0(msg) = read_from(&mut slice)?;
|
|
|
+ let _ = src.split_to(std::mem::size_of::<u64>() + payload_len);
|
|
|
+ Ok(Some(msg))
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- pub trait Receiver {
|
|
|
- fn receive(&mut self) -> Result<MsgOwned>;
|
|
|
- fn path(&self) -> &BlockPath;
|
|
|
+ /// Wraps a [UnixDatagram] and implements [AsyncRead] and [AsyncWrite] for it. Read operations
|
|
|
+ /// are translated to calls to `recv_from` and write operations are translated to `send`. Note
|
|
|
+ /// that this means that writes will fail unless the wrapped socket is connected to a peer.
|
|
|
+ struct DatagramAdapter {
|
|
|
+ socket: UnixDatagram,
|
|
|
}
|
|
|
|
|
|
- pub trait Channel: Sized + Receiver {
|
|
|
- type Sender: Sender;
|
|
|
+ impl DatagramAdapter {
|
|
|
+ fn new(socket: UnixDatagram) -> Self {
|
|
|
+ Self { socket }
|
|
|
+ }
|
|
|
+
|
|
|
+ fn get_ref(&self) -> &UnixDatagram {
|
|
|
+ &self.socket
|
|
|
+ }
|
|
|
|
|
|
- fn new(receiver_path: BlockPath) -> Result<Self>;
|
|
|
- fn connect_to(receiver_path: BlockPath) -> Result<Self::Sender>;
|
|
|
+ fn get_mut(&mut self) -> &mut UnixDatagram {
|
|
|
+ &mut self.socket
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- pub struct WriteSender<W> {
|
|
|
- write: W,
|
|
|
- path: BlockPath,
|
|
|
+ impl AsRef<UnixDatagram> for DatagramAdapter {
|
|
|
+ fn as_ref(&self) -> &UnixDatagram {
|
|
|
+ self.get_ref()
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- impl<W> WriteSender<W> {
|
|
|
- pub fn new(write: W, path: BlockPath) -> Self {
|
|
|
- Self { write, path }
|
|
|
+ impl AsMut<UnixDatagram> for DatagramAdapter {
|
|
|
+ fn as_mut(&mut self) -> &mut UnixDatagram {
|
|
|
+ self.get_mut()
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- impl<W: Write + Send> Sender for WriteSender<W> {
|
|
|
- fn send<'a>(&mut self, msg: MsgRef<'a>) -> Result<()> {
|
|
|
- Ok(write_to(&VerMsgRef::V0(msg), &mut self.write)?)
|
|
|
+ impl AsyncRead for DatagramAdapter {
|
|
|
+ fn poll_read(
|
|
|
+ self: Pin<&mut Self>,
|
|
|
+ cx: &mut Context<'_>,
|
|
|
+ buf: &mut ReadBuf<'_>,
|
|
|
+ ) -> Poll<io::Result<()>> {
|
|
|
+ self.socket.poll_recv(cx, buf)
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- fn path(&self) -> &BlockPath {
|
|
|
- &self.path
|
|
|
+ impl AsyncWrite for DatagramAdapter {
|
|
|
+ fn poll_write(
|
|
|
+ self: Pin<&mut Self>,
|
|
|
+ cx: &mut Context<'_>,
|
|
|
+ buf: &[u8],
|
|
|
+ ) -> Poll<io::Result<usize>> {
|
|
|
+ self.socket.poll_send(cx, buf)
|
|
|
+ }
|
|
|
+
|
|
|
+ fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
|
|
+ Poll::Ready(self.socket.shutdown(Shutdown::Write))
|
|
|
}
|
|
|
+
|
|
|
+ fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
|
|
+ Poll::Ready(Ok(()))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Returns a [Receiver] which can be used to receive messages addressed to the given path.
|
|
|
+ /// The `fs_path` argument specifies the filesystem directory under which the receiver's socket
|
|
|
+ /// will be stored.
|
|
|
+ pub fn local_receiver(fs_path: PathBuf, block_path: BlockPath) -> Result<impl Receiver> {
|
|
|
+ UnixReceiver::new(fs_path, block_path)
|
|
|
}
|
|
|
|
|
|
- pub struct ReadReceiver<R> {
|
|
|
- read: R,
|
|
|
+ /// An implementation of [Receiver] which uses a Unix datagram socket for receiving messages.
|
|
|
+ struct UnixReceiver {
|
|
|
path: BlockPath,
|
|
|
+ socket: FramedRead<DatagramAdapter, MessageCodec>,
|
|
|
}
|
|
|
|
|
|
- impl<R> ReadReceiver<R> {
|
|
|
- pub fn new(read: R, path: BlockPath) -> Self {
|
|
|
- Self { read, path }
|
|
|
+ impl UnixReceiver {
|
|
|
+ fn new(mut fs_path: PathBuf, block_path: BlockPath) -> Result<Self> {
|
|
|
+ socket_path(&mut fs_path, &block_path);
|
|
|
+ std::fs::create_dir_all(fs_path.parent().unwrap())?;
|
|
|
+ let socket = DatagramAdapter::new(UnixDatagram::bind(fs_path)?);
|
|
|
+ let socket = FramedRead::new(socket, MessageCodec);
|
|
|
+ Ok(Self {
|
|
|
+ path: block_path,
|
|
|
+ socket,
|
|
|
+ })
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- impl<R: Read> Receiver for ReadReceiver<R> {
|
|
|
- fn receive(&mut self) -> Result<MsgOwned> {
|
|
|
- let VerMsg::V0(msg) = read_from(&mut self.read)?;
|
|
|
- Ok(msg)
|
|
|
+ impl Stream for UnixReceiver {
|
|
|
+ type Item = Result<MsgOwned>;
|
|
|
+
|
|
|
+ fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
|
|
+ self.socket.poll_next_unpin(cx)
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
+ impl Receiver for UnixReceiver {
|
|
|
fn path(&self) -> &BlockPath {
|
|
|
&self.path
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- pub struct UnixChannel {
|
|
|
- receiver_path: BlockPath,
|
|
|
- listener: UnixListener,
|
|
|
- streams: Vec<ReadReceiver<UnixStream>>,
|
|
|
- next: usize,
|
|
|
+ /// Returns a [Sender] which can be used to send messages to the given Blocktree path.
|
|
|
+ /// The `fs_path` argument specifies the filesystem directory in which to locate the
|
|
|
+ /// socket of the recipient.
|
|
|
+ pub fn local_sender(
|
|
|
+ fs_path: PathBuf,
|
|
|
+ block_path: BlockPath,
|
|
|
+ ) -> Result<impl for<'a> Sender<'a>> {
|
|
|
+ UnixSender::new(fs_path, block_path)
|
|
|
}
|
|
|
|
|
|
- impl Receiver for UnixChannel {
|
|
|
- fn path(&self) -> &BlockPath {
|
|
|
- &self.receiver_path
|
|
|
+ /// An implementation of [Sender] which uses a Unix datagram socket to send messages.
|
|
|
+ struct UnixSender {
|
|
|
+ path: BlockPath,
|
|
|
+ socket: FramedWrite<DatagramAdapter, MessageCodec>,
|
|
|
+ }
|
|
|
+
|
|
|
+ impl UnixSender {
|
|
|
+ fn new(mut fs_path: PathBuf, block_path: BlockPath) -> Result<Self> {
|
|
|
+ let socket = UnixDatagram::unbound()?;
|
|
|
+ socket_path(&mut fs_path, &block_path);
|
|
|
+ socket.connect(fs_path)?;
|
|
|
+ let socket = FramedWrite::new(DatagramAdapter::new(socket), MessageCodec);
|
|
|
+ Ok(Self {
|
|
|
+ path: block_path,
|
|
|
+ socket,
|
|
|
+ })
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- fn receive(&mut self) -> Result<MsgOwned> {
|
|
|
- // Note that the listener is put in non-blocking mode when it is created.
|
|
|
- match self.listener.accept() {
|
|
|
- Ok((socket, ..)) => {
|
|
|
- self.streams.push(ReadReceiver::new(socket, self.receiver_path.clone()));
|
|
|
- }
|
|
|
- Err(err) => {
|
|
|
- // If the error is anything other than `WouldBlock`, then it is unexpected.
|
|
|
- if io::ErrorKind::WouldBlock != err.kind() {
|
|
|
- return Err(err.into());
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- let receiver = self.streams.get_mut(self.next).unwrap();
|
|
|
- self.next += 1;
|
|
|
- receiver.receive()
|
|
|
+ impl Sink<MsgRef<'_>> for UnixSender {
|
|
|
+ type Error = btlib::Error;
|
|
|
+
|
|
|
+ fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
|
|
|
+ self.socket.poll_ready_unpin(cx)
|
|
|
+ }
|
|
|
+
|
|
|
+ fn start_send(mut self: Pin<&mut Self>, item: MsgRef<'_>) -> Result<()> {
|
|
|
+ self.socket.start_send_unpin(item)
|
|
|
+ }
|
|
|
+
|
|
|
+ fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
|
|
|
+ self.socket.poll_flush_unpin(cx)
|
|
|
+ }
|
|
|
+
|
|
|
+ fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
|
|
|
+ self.socket.poll_close_unpin(cx)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- impl Channel for UnixChannel {
|
|
|
- type Sender = WriteSender<UnixStream>;
|
|
|
+ impl<'a> Sender<'a> for UnixSender {
|
|
|
+ type SendFut = Send<'a, FramedWrite<DatagramAdapter, MessageCodec>, MsgRef<'a>>;
|
|
|
|
|
|
- fn new(receiver_path: BlockPath) -> Result<Self> {
|
|
|
- let path = socket_path(&receiver_path);
|
|
|
- std::fs::create_dir_all(path.parent().unwrap())?;
|
|
|
- let listener = UnixListener::bind(&path)?;
|
|
|
- listener.set_nonblocking(true)?;
|
|
|
- Ok(UnixChannel { receiver_path, listener, streams: Vec::new(), next: 0 })
|
|
|
+ fn path(&self) -> &BlockPath {
|
|
|
+ &self.path
|
|
|
}
|
|
|
|
|
|
- fn connect_to(receiver_path: BlockPath) -> Result<Self::Sender> {
|
|
|
- let path = socket_path(&receiver_path);
|
|
|
- let stream = UnixStream::connect(&path)?;
|
|
|
- let sender = WriteSender::new(stream, receiver_path);
|
|
|
- Ok(sender)
|
|
|
+ fn send_msg(&'a mut self, from: &'a BlockPath, body: MsgBodyRef<'a>) -> Self::SendFut {
|
|
|
+ let id = Self::gen_id().unwrap();
|
|
|
+ let msg = MsgRef::new(&self.path, from, id, body);
|
|
|
+ self.socket.send(msg)
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ /// Returns a connected [Sender] and [Receiver].
|
|
|
+ pub fn local_pair(
|
|
|
+ dir: PathBuf,
|
|
|
+ block_path: BlockPath,
|
|
|
+ ) -> Result<(impl for<'a> Sender<'a>, impl Receiver)> {
|
|
|
+ let receiver = local_receiver(dir.clone(), block_path.clone())?;
|
|
|
+ let sender = local_sender(dir, block_path)?;
|
|
|
+ Ok((sender, receiver))
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
#[cfg(test)]
|
|
|
mod tests {
|
|
|
use super::*;
|
|
|
|
|
|
- //#[test]
|
|
|
- //fn ping_pong_via_pipes() {
|
|
|
- // let ping_path = test_helpers::make_path(vec!["ping"]);
|
|
|
- // let (mut ping_sender, mut ping_receiver) =
|
|
|
- // PipeChannel::new(ping_path.clone()).expect("failed to create a ping channel");
|
|
|
- // let pong_path = test_helpers::make_path(vec!["pong"]);
|
|
|
- // let (mut pong_sender, mut pong_receiver) =
|
|
|
- // PipeChannel::new(pong_path.clone()).expect("failed to create a pong channel");
|
|
|
- // let pong = std::thread::spawn(move || loop {
|
|
|
- // let msg = pong_receiver.receive().expect("pong receive failed");
|
|
|
- // assert_eq!(pong_receiver.path(), msg.to());
|
|
|
- // match msg.body() {
|
|
|
- // MsgBody::Ping => ping_sender
|
|
|
- // .send_new(ping_path.clone(), MsgBody::Success)
|
|
|
- // .expect("send to ping failed"),
|
|
|
- // MsgBody::Success => return,
|
|
|
- // _ => panic!("unexpected message received by pong"),
|
|
|
- // }
|
|
|
- // });
|
|
|
- // let mut iter = 5;
|
|
|
- // while iter > 0 {
|
|
|
- // pong_sender
|
|
|
- // .send_new(pong_path.clone(), MsgBody::Ping)
|
|
|
- // .expect("send to pong failed");
|
|
|
- // let msg = ping_receiver.receive().expect("ping receive failed");
|
|
|
- // assert_eq!(ping_receiver.path(), msg.to());
|
|
|
- // match msg.body() {
|
|
|
- // MsgBody::Success => iter -= 1,
|
|
|
- // _ => panic!("unexpected message received by ping"),
|
|
|
- // }
|
|
|
- // }
|
|
|
- // pong_sender
|
|
|
- // .send_new(pong_path.clone(), MsgBody::Success)
|
|
|
- // .expect("send success to pong failed");
|
|
|
- // pong.join().expect("join failed");
|
|
|
- //}
|
|
|
+ use tempdir::TempDir;
|
|
|
+
|
|
|
+ lazy_static! {
|
|
|
+ static ref BT_ROOT: BlockPath =
|
|
|
+ BlockPath::try_from("0!dSip4J0kurN5VhVo_aTipM-ywOOWrqJuRRVQ7aa-bew").unwrap();
|
|
|
+ }
|
|
|
+
|
|
|
+ fn block_path<'a, I: Iterator<Item = &'a str>>(components: I) -> BlockPath {
|
|
|
+ let mut path = BT_ROOT.clone();
|
|
|
+ for component in components {
|
|
|
+ path.push_component(component.to_string());
|
|
|
+ }
|
|
|
+ path
|
|
|
+ }
|
|
|
+
|
|
|
+ struct TestCase {
|
|
|
+ dir: TempDir,
|
|
|
+ }
|
|
|
+
|
|
|
+ impl TestCase {
|
|
|
+ fn new() -> TestCase {
|
|
|
+ Self {
|
|
|
+ dir: TempDir::new("btmsg").unwrap(),
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fn endpoint(&self, name: &str) -> (BlockPath, impl for<'a> Sender<'a>, impl Receiver) {
|
|
|
+ let block_path = block_path(["apps", name].into_iter());
|
|
|
+ let (sender, receiver) =
|
|
|
+ local_pair(self.dir.path().to_owned(), block_path.clone()).unwrap();
|
|
|
+ (block_path, sender, receiver)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ #[tokio::test]
|
|
|
+ async fn message_received_is_message_sent() {
|
|
|
+ let case = TestCase::new();
|
|
|
+ let (block_path, mut sender, mut receiver) = case.endpoint("social");
|
|
|
+
|
|
|
+ sender
|
|
|
+ .send_msg(&block_path, MsgBodyRef::Ping)
|
|
|
+ .await
|
|
|
+ .unwrap();
|
|
|
+ let actual = receiver.next().await.unwrap().unwrap();
|
|
|
+
|
|
|
+ let matched = if let MsgBodyOwned::Ping = actual.body() {
|
|
|
+ true
|
|
|
+ } else {
|
|
|
+ false
|
|
|
+ };
|
|
|
+ assert!(matched);
|
|
|
+ assert_eq!(&block_path, actual.to());
|
|
|
+ assert_eq!(&block_path, actual.from());
|
|
|
+ }
|
|
|
+
|
|
|
+ #[tokio::test]
|
|
|
+ async fn ping_pong() {
|
|
|
+ let case = TestCase::new();
|
|
|
+ let (block_path_one, mut sender_one, mut receiver_one) = case.endpoint("one");
|
|
|
+ let (block_path_two, mut sender_two, mut receiver_two) = case.endpoint("two");
|
|
|
+
|
|
|
+ let handle = tokio::spawn(async move {
|
|
|
+ let msg = receiver_one.next().await.unwrap().unwrap();
|
|
|
+ let reply_body = if let MsgBodyOwned::Ping = msg.body() {
|
|
|
+ MsgBodyRef::Success
|
|
|
+ } else {
|
|
|
+ MsgBodyRef::Fail(&MsgError::Unknown)
|
|
|
+ };
|
|
|
+ sender_two
|
|
|
+ .send_msg(&block_path_one, reply_body)
|
|
|
+ .await
|
|
|
+ .unwrap();
|
|
|
+ });
|
|
|
+
|
|
|
+ sender_one
|
|
|
+ .send_msg(&block_path_two, MsgBodyRef::Ping)
|
|
|
+ .await
|
|
|
+ .unwrap();
|
|
|
+ handle.await.unwrap();
|
|
|
+ let reply = receiver_two.next().await.unwrap().unwrap();
|
|
|
+ let matched = if let MsgBodyOwned::Success = reply.body() {
|
|
|
+ true
|
|
|
+ } else {
|
|
|
+ false
|
|
|
+ };
|
|
|
+ assert!(matched)
|
|
|
+ }
|
|
|
+
|
|
|
+ #[tokio::test]
|
|
|
+ async fn read_write() {
|
|
|
+ let case = TestCase::new();
|
|
|
+ let (block_path_one, mut sender_one, mut receiver_one) = case.endpoint("one");
|
|
|
+ let (block_path_two, mut sender_two, mut receiver_two) = case.endpoint("two");
|
|
|
+
|
|
|
+ let handle = tokio::spawn(async move {
|
|
|
+ let data: [u8; 8] = [0, 1, 2, 3, 4, 5, 6, 7];
|
|
|
+ let msg = receiver_one.next().await.unwrap().unwrap();
|
|
|
+ let reply_body = if let MsgBodyOwned::Read { offset, size } = msg.body() {
|
|
|
+ let offset: usize = (*offset).try_into().unwrap();
|
|
|
+ let size: usize = (*size).try_into().unwrap();
|
|
|
+ let end: usize = offset + size;
|
|
|
+ MsgBodyRef::Write {
|
|
|
+ offset: offset as u64,
|
|
|
+ buf: &data[offset..end],
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ MsgBodyRef::Fail(&MsgError::Unknown)
|
|
|
+ };
|
|
|
+ sender_two
|
|
|
+ .send_msg(&block_path_one, reply_body)
|
|
|
+ .await
|
|
|
+ .unwrap();
|
|
|
+ });
|
|
|
+
|
|
|
+ sender_one
|
|
|
+ .send_msg(&block_path_two, MsgBodyRef::Read { offset: 2, size: 2 })
|
|
|
+ .await
|
|
|
+ .unwrap();
|
|
|
+ handle.await.unwrap();
|
|
|
+ let reply = receiver_two.next().await.unwrap().unwrap();
|
|
|
+ if let MsgBodyOwned::Write { offset, buf } = reply.body() {
|
|
|
+ assert_eq!(2, *offset);
|
|
|
+ assert_eq!([2, 3].as_slice(), buf.as_slice());
|
|
|
+ } else {
|
|
|
+ panic!("replay was not the right type");
|
|
|
+ };
|
|
|
+ }
|
|
|
}
|