use std::{
future::Future,
pin::Pin,
sync::Arc,
task::{Context, Poll},
};
use bytes::{Buf, Bytes};
use futures_util::FutureExt;
use tokio::io::{AsyncWrite, ReadBuf};
use tokio::sync::Mutex;
use crate::h3::h3_stream_error_to_io;
use crate::h3::stream::{RequestStream, StreamError};
#[inline]
pub(super) fn pair(inner: Arc<Mutex<RequestStream>>) -> (H3Upgraded, UpgradedSendStreamTask) {
let (tx, rx) = kanal::bounded_async(1);
let (error_tx, error_rx) = oneshot::async_channel();
(
H3Upgraded {
send_stream: UpgradedSendStreamBridge { tx, error_rx },
inner: inner.clone(),
buf: Bytes::new(),
},
UpgradedSendStreamTask {
inner,
send_fut: None,
rx,
error_tx: Some(error_tx),
},
)
}
pub(super) struct H3Upgraded {
send_stream: UpgradedSendStreamBridge,
inner: Arc<Mutex<RequestStream>>,
buf: Bytes,
}
impl tokio::io::AsyncRead for H3Upgraded {
#[inline]
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
read_buf: &mut ReadBuf<'_>,
) -> Poll<Result<(), std::io::Error>> {
if self.buf.is_empty() {
self.buf = loop {
let mut guard = match std::pin::pin!(self.inner.lock()).poll_unpin(cx) {
Poll::Ready(guard) => guard,
Poll::Pending => return Poll::Pending,
};
match guard.poll_recv_data(cx) {
Poll::Ready(Ok(None)) => return Poll::Ready(Ok(())),
Poll::Ready(Ok(Some(buf))) if !buf.has_remaining() => continue,
Poll::Ready(Ok(Some(mut buf))) => {
break buf.copy_to_bytes(buf.remaining());
}
Poll::Ready(Err(e)) => {
return Poll::Ready(Err(h3_stream_error_to_io(e)));
}
Poll::Pending => return Poll::Pending,
}
};
}
let cnt = std::cmp::min(self.buf.len(), read_buf.remaining());
read_buf.put_slice(&self.buf[..cnt]);
self.buf.advance(cnt);
Poll::Ready(Ok(()))
}
}
struct UpgradedSendStreamBridge {
tx: kanal::AsyncSender<Box<[u8]>>,
error_rx: oneshot::AsyncReceiver<std::io::Error>,
}
impl AsyncWrite for H3Upgraded {
#[inline]
fn poll_write(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<Result<usize, std::io::Error>> {
if buf.is_empty() {
return Poll::Ready(Ok(0));
}
let n = buf.len();
match std::pin::pin!(self.send_stream.tx.send(buf.into())).poll(cx) {
Poll::Ready(Ok(())) => return Poll::Ready(Ok(n)),
Poll::Pending => return Poll::Pending,
Poll::Ready(Err(_task_dropped)) => {
}
};
match Pin::new(&mut self.send_stream.error_rx).poll(cx) {
Poll::Ready(Ok(reason)) => Poll::Ready(Err(std::io::Error::other(reason))),
Poll::Ready(Err(_task_dropped)) => {
Poll::Ready(Err(std::io::ErrorKind::BrokenPipe.into()))
}
Poll::Pending => Poll::Pending,
}
}
#[inline]
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<(), std::io::Error>> {
Poll::Ready(Ok(()))
}
#[inline]
fn poll_shutdown(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Result<(), std::io::Error>> {
let _ = self.send_stream.tx.close();
match Pin::new(&mut self.send_stream.error_rx).poll(cx) {
Poll::Ready(Ok(reason)) => Poll::Ready(Err(std::io::Error::other(reason))),
Poll::Ready(Err(_task_dropped)) => Poll::Ready(Ok(())),
Poll::Pending => Poll::Pending,
}
}
}
pub(super) struct UpgradedSendStreamTask {
inner: Arc<Mutex<RequestStream>>,
#[allow(clippy::type_complexity)]
send_fut: Option<Pin<Box<dyn Future<Output = Result<(), StreamError>>>>>,
rx: kanal::AsyncReceiver<Box<[u8]>>,
error_tx: Option<oneshot::Sender<std::io::Error>>,
}
impl UpgradedSendStreamTask {
#[inline]
async fn send_data(inner: Arc<Mutex<RequestStream>>, data: Bytes) -> Result<(), StreamError> {
let mut guard = inner.lock().await;
std::future::poll_fn(move |cx| guard.poll_send_data(cx, data.clone())).await
}
#[inline]
async fn finish(inner: Arc<Mutex<RequestStream>>) -> Result<(), StreamError> {
let mut guard = inner.lock().await;
std::future::poll_fn(|cx| guard.poll_finish(cx)).await
}
#[inline]
fn tick(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), std::io::Error>> {
let this = self.get_mut();
loop {
if let Some(mut fut) = this.send_fut.take() {
match fut.as_mut().poll(cx) {
Poll::Ready(Ok(())) => {}
Poll::Ready(Err(e)) => {
return Poll::Ready(Err(h3_stream_error_to_io(e)));
}
Poll::Pending => {
this.send_fut = Some(fut);
return Poll::Pending;
}
}
}
match std::pin::pin!(this.rx.recv()).poll(cx) {
Poll::Ready(Ok(cursor)) => {
this.send_fut = Some(Box::pin(Self::send_data(
this.inner.clone(),
Bytes::from_owner(cursor),
)));
}
Poll::Ready(Err(_)) => {
this.send_fut = Some(Box::pin(Self::finish(this.inner.clone())));
}
Poll::Pending => {
return Poll::Pending;
}
}
}
}
}
impl Future for UpgradedSendStreamTask {
type Output = ();
#[inline]
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
match self.as_mut().tick(cx) {
Poll::Ready(Ok(())) => Poll::Ready(()),
Poll::Ready(Err(err)) => {
if let Some(tx) = self.error_tx.take() {
let _ = tx.send(err);
}
Poll::Ready(())
}
Poll::Pending => Poll::Pending,
}
}
}