use std::collections::VecDeque;
use std::io::{Read, Write};
use std::net::{TcpListener, TcpStream};
use crate::error::{Error, Result};
use crate::wire::{Envelope, WireLimits};
pub mod connect;
#[cfg(feature = "quic")]
pub mod quic;
pub mod shm;
pub const MAX_FRAME_BYTES: usize = 64 << 20;
pub trait Transport {
fn name(&self) -> &'static str;
fn send(&mut self, envelope: &Envelope) -> Result<()>;
fn recv(&mut self) -> Result<Option<Envelope>>;
fn bytes_written(&self) -> u64 {
0
}
}
#[derive(Default)]
struct Mailboxes {
a_to_b: VecDeque<Envelope>,
b_to_a: VecDeque<Envelope>,
}
#[derive(Clone, Default)]
pub struct InProcessTransport {
shared: Option<std::rc::Rc<std::cell::RefCell<Mailboxes>>>,
is_a: bool,
local: VecDeque<Envelope>,
}
impl std::fmt::Debug for InProcessTransport {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("InProcessTransport")
.field("paired", &self.shared.is_some())
.finish()
}
}
impl InProcessTransport {
pub fn pair() -> (InProcessTransport, InProcessTransport) {
let shared = std::rc::Rc::new(std::cell::RefCell::new(Mailboxes::default()));
(
InProcessTransport {
shared: Some(std::rc::Rc::clone(&shared)),
is_a: true,
local: VecDeque::new(),
},
InProcessTransport {
shared: Some(shared),
is_a: false,
local: VecDeque::new(),
},
)
}
pub fn queue() -> InProcessTransport {
InProcessTransport::default()
}
}
impl Transport for InProcessTransport {
fn name(&self) -> &'static str {
"in-process"
}
fn send(&mut self, envelope: &Envelope) -> Result<()> {
match &self.shared {
Some(shared) => {
let mut shared = shared.borrow_mut();
if self.is_a {
shared.a_to_b.push_back(envelope.clone());
} else {
shared.b_to_a.push_back(envelope.clone());
}
}
None => self.local.push_back(envelope.clone()),
}
Ok(())
}
fn recv(&mut self) -> Result<Option<Envelope>> {
match &self.shared {
Some(shared) => {
let mut shared = shared.borrow_mut();
Ok(if self.is_a {
shared.b_to_a.pop_front()
} else {
shared.a_to_b.pop_front()
})
}
None => Ok(self.local.pop_front()),
}
}
fn bytes_written(&self) -> u64 {
0
}
}
#[derive(Debug)]
pub struct TcpTransport {
stream: TcpStream,
inbox: VecDeque<Envelope>,
written: u64,
limits: WireLimits,
}
impl TcpTransport {
pub fn new(stream: TcpStream) -> Result<Self> {
stream
.set_nodelay(true)
.map_err(|error| Error::Transport(error.to_string()))?;
Ok(Self {
stream,
inbox: VecDeque::new(),
written: 0,
limits: WireLimits::default(),
})
}
pub fn with_limits(mut self, limits: WireLimits) -> Self {
self.limits = limits;
self
}
pub fn connect(address: &str) -> Result<Self> {
let stream = TcpStream::connect(address)
.map_err(|error| Error::Transport(format!("connect: {error}")))?;
Self::new(stream)
}
}
impl Transport for TcpTransport {
fn name(&self) -> &'static str {
"tcp"
}
fn send(&mut self, envelope: &Envelope) -> Result<()> {
let bytes = envelope.encode()?;
write_frame(&mut self.stream, &bytes)?;
self.written += (bytes.len() + 4) as u64;
Ok(())
}
fn recv(&mut self) -> Result<Option<Envelope>> {
if let Some(envelope) = self.inbox.pop_front() {
return Ok(Some(envelope));
}
let Some(body) = read_frame(&mut self.stream, &self.limits)? else {
return Ok(None);
};
Ok(Some(Envelope::decode_with(&body, self.limits)?))
}
fn bytes_written(&self) -> u64 {
self.written
}
}
pub fn frame(len: usize) -> Result<[u8; 4]> {
if len > MAX_FRAME_BYTES {
return Err(Error::Wire("frame exceeds the transport ceiling"));
}
Ok((len as u32).to_le_bytes())
}
pub fn serve_connection<H>(mut transport: TcpTransport, mut handler: H) -> Result<u64>
where
H: FnMut(Envelope) -> Envelope,
{
let mut served = 0u64;
loop {
match transport.recv() {
Ok(Some(envelope)) => {
let response = handler(envelope);
transport.send(&response)?;
served += 1;
}
Ok(None) => return Ok(served),
Err(error) => {
let reply = Envelope::error(0, &error);
let _ = transport.send(&reply);
return Err(error);
}
}
}
}
pub fn bind_loopback() -> Result<(TcpListener, String)> {
let listener =
TcpListener::bind("127.0.0.1:0").map_err(|error| Error::Transport(error.to_string()))?;
let address = listener
.local_addr()
.map_err(|error| Error::Transport(error.to_string()))?
.to_string();
Ok((listener, address))
}
pub fn read_frame<R: Read>(reader: &mut R, limits: &WireLimits) -> Result<Option<Vec<u8>>> {
let mut header = [0u8; 4];
match reader.read_exact(&mut header) {
Ok(()) => {}
Err(error) if error.kind() == std::io::ErrorKind::UnexpectedEof => return Ok(None),
Err(error) => return Err(Error::Transport(error.to_string())),
}
let len = u32::from_le_bytes(header) as usize;
if len > limits.max_bytes {
return Err(Error::Wire("frame exceeds the size ceiling"));
}
let mut body = vec![0u8; len];
reader
.read_exact(&mut body)
.map_err(|error| Error::Transport(error.to_string()))?;
Ok(Some(body))
}
pub fn write_frame<W: Write>(writer: &mut W, body: &[u8]) -> Result<()> {
writer
.write_all(&frame(body.len())?)
.and_then(|_| writer.write_all(body))
.and_then(|_| writer.flush())
.map_err(|error| Error::Transport(error.to_string()))
}