use core::fmt;
use std::{
io,
pin::Pin,
task::{Context, Poll, ready},
};
use pin_project::pin_project;
use tokio::io::{AsyncRead, AsyncWrite};
#[cfg(feature = "client")]
use crate::client::pool::PoolableStream;
use crate::info::{self, HasConnectionInfo, HasTlsConnectionInfo};
#[cfg(feature = "server")]
use crate::server::conn::Accept;
#[derive(Default, Clone, PartialEq, Eq, Hash)]
pub struct DuplexAddr {
_priv: (),
}
impl fmt::Debug for DuplexAddr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "DuplexAddr")
}
}
impl DuplexAddr {
pub fn new() -> Self {
Self { _priv: () }
}
}
impl fmt::Display for DuplexAddr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "duplex")
}
}
type ConnectionInfo = info::ConnectionInfo<DuplexAddr>;
#[derive(Debug)]
#[pin_project]
pub struct DuplexStream {
#[pin]
inner: tokio::io::DuplexStream,
info: ConnectionInfo,
}
impl HasConnectionInfo for DuplexStream {
type Addr = DuplexAddr;
fn info(&self) -> ConnectionInfo {
self.info.clone()
}
}
impl HasTlsConnectionInfo for DuplexStream {
fn tls_info(&self) -> Option<&info::TlsConnectionInfo> {
None
}
}
impl DuplexStream {
pub fn new(max_buf_size: usize) -> (Self, Self) {
let (a, b) = tokio::io::duplex(max_buf_size);
let info = info::ConnectionInfo::duplex().map(|_| DuplexAddr::new());
(
DuplexStream {
inner: a,
info: info.clone(),
},
DuplexStream { inner: b, info },
)
}
}
#[cfg(feature = "client")]
impl PoolableStream for DuplexStream {
fn can_share(&self) -> bool {
false
}
}
impl AsyncRead for DuplexStream {
fn poll_read(
self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
buf: &mut tokio::io::ReadBuf<'_>,
) -> std::task::Poll<std::io::Result<()>> {
self.project().inner.poll_read(cx, buf)
}
}
impl AsyncWrite for DuplexStream {
fn poll_write(
self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
buf: &[u8],
) -> std::task::Poll<Result<usize, std::io::Error>> {
self.project().inner.poll_write(cx, buf)
}
fn poll_flush(
self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Result<(), std::io::Error>> {
self.project().inner.poll_flush(cx)
}
fn poll_shutdown(
self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Result<(), std::io::Error>> {
self.project().inner.poll_shutdown(cx)
}
}
#[derive(Debug, Clone)]
pub struct DuplexClient {
sender: tokio::sync::mpsc::Sender<DuplexConnectionRequest>,
}
impl DuplexClient {
pub async fn connect(&self, max_buf_size: usize) -> Result<DuplexStream, io::Error> {
let (tx, rx) = tokio::sync::oneshot::channel();
let request = DuplexConnectionRequest::new(tx, max_buf_size);
self.sender
.send(request)
.await
.map_err(|_| io::ErrorKind::ConnectionReset)?;
Ok(rx.await.map_err(|_| io::ErrorKind::ConnectionReset)?)
}
}
struct DuplexConnectionRequest {
ack: tokio::sync::oneshot::Sender<DuplexStream>,
max_buf_size: usize,
}
impl DuplexConnectionRequest {
fn new(ack: tokio::sync::oneshot::Sender<DuplexStream>, max_buf_size: usize) -> Self {
Self { ack, max_buf_size }
}
fn ack(self, max_buf_size: Option<usize>) -> Result<DuplexStream, io::Error> {
let max_buf_size = match max_buf_size {
Some(size) => std::cmp::min(size, self.max_buf_size),
None => self.max_buf_size,
};
let (tx, rx) = DuplexStream::new(max_buf_size);
self.ack
.send(tx)
.map_err(|_| io::ErrorKind::ConnectionReset)?;
Ok(rx)
}
}
#[derive(Debug)]
pub struct DuplexIncoming {
receiver: tokio::sync::mpsc::Receiver<DuplexConnectionRequest>,
max_buf_size: Option<usize>,
}
impl DuplexIncoming {
fn new(receiver: tokio::sync::mpsc::Receiver<DuplexConnectionRequest>) -> Self {
Self {
receiver,
max_buf_size: None,
}
}
pub fn with_max_buf_size(mut self, max_buf_size: usize) -> Self {
self.max_buf_size = Some(max_buf_size);
self
}
}
#[cfg(feature = "server")]
impl Accept for DuplexIncoming {
type Connection = DuplexStream;
type Error = io::Error;
fn poll_accept(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Result<Self::Connection, Self::Error>> {
if let Some(request) = ready!(self.receiver.poll_recv(cx)) {
let stream = request.ack(self.max_buf_size)?;
Poll::Ready(Ok(stream))
} else {
Poll::Ready(Err(io::ErrorKind::ConnectionReset.into()))
}
}
}
impl futures::Stream for DuplexIncoming {
type Item = Result<DuplexStream, io::Error>;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
if let Some(request) = ready!(self.receiver.poll_recv(cx)) {
let stream = request.ack(self.max_buf_size)?;
Poll::Ready(Some(Ok(stream)))
} else {
Poll::Ready(None)
}
}
}
pub fn pair() -> (DuplexClient, DuplexIncoming) {
let (sender, receiver) = tokio::sync::mpsc::channel(32);
(DuplexClient { sender }, DuplexIncoming::new(receiver))
}
#[cfg(test)]
mod test {
#[tokio::test]
async fn test_duplex() {
use super::*;
use futures::StreamExt;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
let (client, incoming) = pair();
let mut incoming = incoming.fuse();
let (mut client_stream, mut server_stream) =
tokio::try_join!(client.connect(1024), async {
incoming.next().await.unwrap()
})
.unwrap();
let mut buf = [0u8; 1024];
tokio::try_join!(
client_stream.write_all(b"hello"),
server_stream.read_exact(&mut buf[..5])
)
.unwrap();
assert_eq!(&buf[..5], b"hello");
tokio::try_join!(
server_stream.write_all(b"world"),
client_stream.read_exact(&mut buf[..5])
)
.unwrap();
assert_eq!(&buf[..5], b"world");
}
}