pallas/pallas-network/src/plexer.rs

317 lines
8.9 KiB
Rust

use pallas_codec::{minicbor, Fragment};
use thiserror::Error;
use tokio::sync::mpsc::error::SendError;
use tokio::{select, time::Instant};
use tracing::{debug, error, trace};
use crate::bearer::{Bearer, Payload, Protocol, SegmentBuffer};
#[derive(Error, Debug)]
pub enum Error {
#[error("failure to encode channel message")]
Decoding(String),
#[error("failure to decode channel message")]
Encoding(String),
#[error("agent failed to enqueue chunk for protocol {0}")]
AgentEnqueue(Protocol, Payload),
#[error("agent failed to dequeue chunk")]
AgentDequeue,
#[error("plexer failed to dumux chunk for protocol {0}")]
PlexerDemux(Protocol, Payload),
#[error("plexer failed to mux chunk")]
PlexerMux,
#[error("bearer IO error")]
Bearer(tokio::io::Error),
}
pub struct AgentChannel {
enqueue_protocol: crate::bearer::Protocol,
dequeue_protocol: crate::bearer::Protocol,
to_plexer: tokio::sync::mpsc::Sender<(Protocol, Payload)>,
from_plexer: tokio::sync::broadcast::Receiver<(Protocol, Payload)>,
}
impl AgentChannel {
fn for_client(protocol: crate::bearer::Protocol, ingress: &Ingress, egress: &Egress) -> Self {
Self {
enqueue_protocol: protocol,
dequeue_protocol: protocol ^ 0x8000,
to_plexer: ingress.0.clone(),
from_plexer: egress.0.subscribe(),
}
}
fn for_server(protocol: crate::bearer::Protocol, ingress: &Ingress, egress: &Egress) -> Self {
Self {
enqueue_protocol: protocol ^ 0x8000,
dequeue_protocol: protocol,
to_plexer: ingress.0.clone(),
from_plexer: egress.0.subscribe(),
}
}
pub async fn enqueue_chunk(&mut self, chunk: Payload) -> Result<(), Error> {
self.to_plexer
.send((self.enqueue_protocol, chunk))
.await
.map_err(|SendError((protocol, payload))| Error::AgentEnqueue(protocol, payload))
}
pub async fn dequeue_chunk(&mut self) -> Result<Payload, Error> {
loop {
let (protocol, payload) = self
.from_plexer
.recv()
.await
.map_err(|_| Error::AgentDequeue)?;
if protocol == self.dequeue_protocol {
trace!(protocol, "message for our protocol");
break Ok(payload);
}
}
}
}
type Ingress = (
tokio::sync::mpsc::Sender<(Protocol, Payload)>,
tokio::sync::mpsc::Receiver<(Protocol, Payload)>,
);
type Egress = (
tokio::sync::broadcast::Sender<(Protocol, Payload)>,
tokio::sync::broadcast::Receiver<(Protocol, Payload)>,
);
pub struct Plexer {
clock: Instant,
bearer: SegmentBuffer,
ingress: Ingress,
egress: Egress,
}
impl Plexer {
pub fn new(bearer: Bearer) -> Self {
Self {
clock: Instant::now(),
bearer: SegmentBuffer::new(bearer),
ingress: tokio::sync::mpsc::channel(100), // TODO: define buffer
egress: tokio::sync::broadcast::channel(100),
}
}
async fn mux(&mut self, msg: (Protocol, Payload)) -> tokio::io::Result<()> {
self.bearer
.write_segment(msg.0, &self.clock, &msg.1)
.await?;
if tracing::event_enabled!(tracing::Level::TRACE) {
trace!(
protocol = msg.0,
data = hex::encode(&msg.1),
"write to bearer"
);
}
Ok(())
}
async fn demux(&mut self, protocol: Protocol, payload: Payload) -> tokio::io::Result<()> {
if tracing::event_enabled!(tracing::Level::TRACE) {
trace!(protocol, data = hex::encode(&payload), "read from bearer");
}
self.egress.0.send((protocol, payload)).unwrap();
Ok(())
}
pub fn subscribe_client(&mut self, protocol: Protocol) -> AgentChannel {
AgentChannel::for_client(protocol, &self.ingress, &self.egress)
}
pub fn subscribe_server(&mut self, protocol: Protocol) -> AgentChannel {
AgentChannel::for_server(protocol, &self.ingress, &self.egress)
}
pub async fn run(&mut self) -> tokio::io::Result<()> {
loop {
trace!("selecting");
select! {
Ok(x) = self.bearer.read_segment() => {
trace!("demux selected");
self.demux(x.0, x.1).await?
},
Some(x) = self.ingress.1.recv() => {
trace!("mux selected");
self.mux(x).await?
},
_ = tokio::time::sleep(tokio::time::Duration::from_secs(5)) => {
trace!("idle plexer");
}
else => {
error!("something else happened");
}
}
}
}
}
/// Protocol value that defines max segment length
pub const MAX_SEGMENT_PAYLOAD_LENGTH: usize = 65535;
fn try_decode_message<M>(buffer: &mut Vec<u8>) -> Result<Option<M>, Error>
where
M: Fragment,
{
let mut decoder = minicbor::Decoder::new(buffer);
let maybe_msg = decoder.decode();
match maybe_msg {
Ok(msg) => {
let pos = decoder.position();
buffer.drain(0..pos);
Ok(Some(msg))
}
Err(err) if err.is_end_of_input() => Ok(None),
Err(err) => {
error!(?err);
trace!("{}", hex::encode(buffer));
Err(Error::Decoding(err.to_string()))
}
}
}
/// A channel abstraction to hide the complexity of partial payloads
pub struct ChannelBuffer {
channel: AgentChannel,
temp: Vec<u8>,
}
impl ChannelBuffer {
pub fn new(channel: AgentChannel) -> Self {
Self {
channel,
temp: Vec::new(),
}
}
/// Enqueues a msg as a sequence payload chunks
pub async fn send_msg_chunks<M>(&mut self, msg: &M) -> Result<(), Error>
where
M: Fragment,
{
let mut payload = Vec::new();
minicbor::encode(msg, &mut payload).map_err(|err| Error::Encoding(err.to_string()))?;
let chunks = payload.chunks(MAX_SEGMENT_PAYLOAD_LENGTH);
for chunk in chunks {
self.channel.enqueue_chunk(Vec::from(chunk)).await?;
}
Ok(())
}
/// Reads from the channel until a complete message is found
pub async fn recv_full_msg<M>(&mut self) -> Result<M, Error>
where
M: Fragment,
{
trace!(len = self.temp.len(), "waiting for full message");
if !self.temp.is_empty() {
trace!("buffer has data from previous payload");
if let Some(msg) = try_decode_message::<M>(&mut self.temp)? {
debug!("decoding done");
return Ok(msg);
}
}
loop {
let chunk = self.channel.dequeue_chunk().await?;
self.temp.extend(chunk);
if let Some(msg) = try_decode_message::<M>(&mut self.temp)? {
debug!("decoding done");
return Ok(msg);
}
trace!("not enough data");
}
}
pub fn unwrap(self) -> AgentChannel {
self.channel
}
}
impl From<AgentChannel> for ChannelBuffer {
fn from(channel: AgentChannel) -> Self {
ChannelBuffer::new(channel)
}
}
#[cfg(test)]
mod tests {
use super::*;
use pallas_codec::minicbor;
#[tokio::test]
async fn multiple_messages_in_same_payload() {
let mut input = Vec::new();
let in_part1 = (1u8, 2u8, 3u8);
let in_part2 = (6u8, 5u8, 4u8);
minicbor::encode(in_part1, &mut input).unwrap();
minicbor::encode(in_part2, &mut input).unwrap();
let ingress = tokio::sync::mpsc::channel(100);
let egress = tokio::sync::broadcast::channel(100);
let channel = AgentChannel::for_client(0, &ingress, &egress);
egress.0.send((0 ^ 0x8000, input)).unwrap();
let mut buf = ChannelBuffer::new(channel);
let out_part1 = buf.recv_full_msg::<(u8, u8, u8)>().await.unwrap();
let out_part2 = buf.recv_full_msg::<(u8, u8, u8)>().await.unwrap();
assert_eq!(in_part1, out_part1);
assert_eq!(in_part2, out_part2);
}
#[tokio::test]
async fn fragmented_message_in_multiple_payloads() {
let mut input = Vec::new();
let msg = (11u8, 12u8, 13u8, 14u8, 15u8, 16u8, 17u8);
minicbor::encode(msg, &mut input).unwrap();
let ingress = tokio::sync::mpsc::channel(100);
let egress = tokio::sync::broadcast::channel(100);
let channel = AgentChannel::for_client(0, &ingress, &egress);
while !input.is_empty() {
let chunk = Vec::from(input.drain(0..2).as_slice());
egress.0.send((0 ^ 0x8000, chunk)).unwrap();
}
let mut buf = ChannelBuffer::new(channel);
let out_msg = buf
.recv_full_msg::<(u8, u8, u8, u8, u8, u8, u8)>()
.await
.unwrap();
assert_eq!(msg, out_msg);
}
}