use std::io;
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
use tokio::sync::mpsc;
use crate::error::Error;
use crate::message::StreamHalf;
use crate::transport::OutboundCommand;
#[derive(Debug)]
pub struct UpgradedStream {
id: u64,
cmd_tx: mpsc::Sender<OutboundCommand>,
rx: mpsc::Receiver<Result<Vec<u8>, Error>>,
read_buf: Vec<u8>,
read_pos: usize,
eof: bool,
write_closed: bool,
closed: bool,
}
impl UpgradedStream {
pub(crate) fn new(
id: u64,
cmd_tx: mpsc::Sender<OutboundCommand>,
rx: mpsc::Receiver<Result<Vec<u8>, Error>>,
initial: Vec<u8>,
) -> Self {
Self {
id,
cmd_tx,
rx,
read_buf: initial,
read_pos: 0,
eof: false,
write_closed: false,
closed: false,
}
}
pub(crate) fn id(&self) -> u64 {
self.id
}
pub(crate) async fn recv_chunk(&mut self) -> Option<Result<Vec<u8>, Error>> {
self.rx.recv().await
}
pub(crate) fn prepend_read(&mut self, bytes: &[u8]) {
if bytes.is_empty() {
return;
}
let mut joined = Vec::with_capacity(bytes.len() + (self.read_buf.len() - self.read_pos));
joined.extend_from_slice(bytes);
joined.extend_from_slice(&self.read_buf[self.read_pos..]);
self.read_buf = joined;
self.read_pos = 0;
}
}
impl AsyncRead for UpgradedStream {
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
loop {
let available = self.read_buf.len() - self.read_pos;
if available > 0 {
let n = available.min(buf.remaining());
buf.put_slice(&self.read_buf[self.read_pos..self.read_pos + n]);
self.read_pos += n;
if self.read_pos == self.read_buf.len() {
self.read_buf.clear();
self.read_pos = 0;
}
return Poll::Ready(Ok(()));
}
if self.eof {
return Poll::Ready(Ok(()));
}
match self.rx.poll_recv(cx) {
Poll::Ready(Some(Ok(chunk))) => {
if chunk.is_empty() {
continue;
}
self.read_buf = chunk;
self.read_pos = 0;
}
Poll::Ready(Some(Err(e))) => {
return Poll::Ready(Err(io::Error::other(e.to_string())));
}
Poll::Ready(None) => {
self.eof = true;
return Poll::Ready(Ok(()));
}
Poll::Pending => return Poll::Pending,
}
}
}
}
impl AsyncWrite for UpgradedStream {
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
let this = self.get_mut();
if this.write_closed {
return Poll::Ready(Err(io::Error::new(
io::ErrorKind::BrokenPipe,
"write half closed",
)));
}
match this.cmd_tx.try_send(OutboundCommand::StreamData {
id: this.id,
payload: buf.to_vec(),
}) {
Ok(()) => Poll::Ready(Ok(buf.len())),
Err(mpsc::error::TrySendError::Full(_)) => {
cx.waker().wake_by_ref();
Poll::Pending
}
Err(mpsc::error::TrySendError::Closed(_)) => Poll::Ready(Err(io::Error::new(
io::ErrorKind::BrokenPipe,
"transport closed",
))),
}
}
fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<()>> {
Poll::Ready(Ok(()))
}
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
let this = self.get_mut();
if this.write_closed {
return Poll::Ready(Ok(()));
}
match this.cmd_tx.try_send(OutboundCommand::StreamClose {
id: this.id,
half: StreamHalf::Write,
}) {
Ok(()) => {
this.write_closed = true;
Poll::Ready(Ok(()))
}
Err(mpsc::error::TrySendError::Full(_)) => {
cx.waker().wake_by_ref();
Poll::Pending
}
Err(mpsc::error::TrySendError::Closed(_)) => {
this.write_closed = true;
Poll::Ready(Ok(()))
}
}
}
}
impl Drop for UpgradedStream {
fn drop(&mut self) {
if !self.closed {
self.closed = true;
let _ = self.cmd_tx.try_send(OutboundCommand::StreamClose {
id: self.id,
half: StreamHalf::Both,
});
}
}
}