use std::{
io::IoSlice,
ops::ControlFlow,
pin::Pin,
sync::Arc,
task::{Context, Poll},
};
use bytes::Bytes;
use futures::{Stream, future::BoxFuture};
use tokio::{io::AsyncWrite, net::unix::OwnedReadHalf};
use tokio_util::codec::FramedRead;
use super::codec::{
CONTROL_MAX_LEN, Frame, PUSH_HEADER_MAX_LEN, StreamCodec, encode_push_header,
encode_varint_to_slice,
};
use crate::{
quic::{self, ConnectionError, StreamError},
varint::VarInt,
};
pub(super) enum Step<T> {
Done(T),
Pending,
Transition(Transition),
}
impl<T> Step<T> {
pub fn map<U>(self, f: impl FnOnce(T) -> U) -> Step<U> {
match self {
Step::Done(v) => Step::Done(f(v)),
Step::Pending => Step::Pending,
Step::Transition(t) => Step::Transition(t),
}
}
pub fn and_then<U>(self, f: impl FnOnce(T) -> Step<U>) -> Step<U> {
match self {
Step::Done(v) => f(v),
Step::Pending => Step::Pending,
Step::Transition(t) => Step::Transition(t),
}
}
}
pub(super) enum Transition {
Reset(VarInt),
Finish,
ConnDied(Arc<dyn quic::DynLifecycle>),
}
pub(super) enum PipeState<L> {
Live(L),
Dying(BoxFuture<'static, ConnectionError>),
Dead(Result<(), StreamError>),
}
impl<L> PipeState<L> {
pub fn apply(&mut self, transition: Transition, cx: &mut Context<'_>) {
match transition {
Transition::Reset(code) => {
*self = PipeState::Dead(Err(StreamError::Reset { code }));
}
Transition::Finish => {
*self = PipeState::Dead(Ok(()));
}
Transition::ConnDied(lifecycle) => {
if let Err(e) = lifecycle.check() {
*self = PipeState::Dead(Err(StreamError::Connection { source: e }));
return;
}
let mut fut: BoxFuture<'static, ConnectionError> =
Box::pin(async move { lifecycle.closed().await });
match fut.as_mut().poll(cx) {
Poll::Ready(e) => {
*self = PipeState::Dead(Err(StreamError::Connection { source: e }));
}
Poll::Pending => {
*self = PipeState::Dying(fut);
}
}
}
}
}
pub fn poll_non_live(&mut self, cx: &mut Context<'_>) -> Option<Poll<Result<(), StreamError>>> {
loop {
match self {
PipeState::Live(_) => return None,
PipeState::Dying(fut) => {
let e = match fut.as_mut().poll(cx) {
Poll::Ready(e) => e,
Poll::Pending => return Some(Poll::Pending),
};
*self = PipeState::Dead(Err(StreamError::Connection { source: e }));
continue;
}
PipeState::Dead(Ok(())) => return Some(Poll::Ready(Ok(()))),
PipeState::Dead(Err(e)) => return Some(Poll::Ready(Err(e.clone()))),
}
}
}
pub fn live_mut(&mut self) -> Option<&mut L> {
match self {
PipeState::Live(l) => Some(l),
_ => None,
}
}
}
pub(super) enum DrainOutcome {
Drained,
Break(Transition),
ReadClosed,
}
impl DrainOutcome {
pub fn resolve(self, on_read_closed: impl FnOnce() -> Step<()>) -> Step<()> {
match self {
DrainOutcome::Drained => Step::Done(()),
DrainOutcome::Break(t) => Step::Transition(t),
DrainOutcome::ReadClosed => on_read_closed(),
}
}
}
pub(super) fn drain(
read: &mut FramedRead<OwnedReadHalf, StreamCodec>,
cx: &mut Context<'_>,
mut on_frame: impl FnMut(Frame) -> ControlFlow<Transition>,
) -> DrainOutcome {
loop {
match Pin::new(&mut *read).poll_next(cx) {
Poll::Ready(Some(Ok(frame))) => match on_frame(frame) {
ControlFlow::Continue(()) => continue,
ControlFlow::Break(t) => return DrainOutcome::Break(t),
},
Poll::Ready(Some(Err(_))) | Poll::Ready(None) => {
return DrainOutcome::ReadClosed;
}
Poll::Pending => return DrainOutcome::Drained,
}
}
}
pub(super) struct PendingPush {
header: [u8; PUSH_HEADER_MAX_LEN],
header_len: usize,
header_off: usize,
body: Bytes,
body_off: usize,
}
impl PendingPush {
pub fn new(body: Bytes) -> Result<Self, StreamError> {
let (header, header_len) =
encode_push_header(body.len()).map_err(|_| StreamError::Reset {
code: VarInt::default(),
})?;
Ok(Self {
header,
header_len,
header_off: 0,
body,
body_off: 0,
})
}
fn header_remaining(&self) -> &[u8] {
&self.header[self.header_off..self.header_len]
}
fn body_remaining(&self) -> &[u8] {
&self.body[self.body_off..]
}
fn is_done(&self) -> bool {
self.header_off == self.header_len && self.body_off == self.body.len()
}
fn advance(&mut self, mut written: usize) {
let header_left = self.header_len - self.header_off;
let take_header = written.min(header_left);
self.header_off += take_header;
written -= take_header;
self.body_off += written;
}
}
pub(super) fn flush_pending(
write: &mut (impl AsyncWrite + Unpin),
lifecycle: &Arc<dyn quic::DynLifecycle>,
pending: &mut Option<PendingPush>,
cx: &mut Context<'_>,
) -> Step<()> {
loop {
let Some(p) = pending.as_ref() else {
return Step::Done(());
};
let header_remaining = p.header_remaining();
let body_remaining = p.body_remaining();
let bufs = [IoSlice::new(header_remaining), IoSlice::new(body_remaining)];
let bufs: &[IoSlice<'_>] = if header_remaining.is_empty() {
&bufs[1..]
} else if body_remaining.is_empty() {
&bufs[..1]
} else {
&bufs
};
let written = match Pin::new(&mut *write).poll_write_vectored(cx, bufs) {
Poll::Ready(Ok(0)) => {
return check_lifecycle(lifecycle, Step::Transition(Transition::Finish));
}
Poll::Ready(Ok(n)) => n,
Poll::Ready(Err(e)) => {
tracing::debug!(%e, "pipe write error during PUSH flush");
return check_lifecycle(lifecycle, Step::Transition(Transition::Finish));
}
Poll::Pending => return Step::Pending,
};
let p = pending.as_mut().unwrap();
p.advance(written);
if p.is_done() {
*pending = None;
}
}
}
pub(super) fn check_lifecycle(
lifecycle: &Arc<dyn quic::DynLifecycle>,
on_alive: Step<()>,
) -> Step<()> {
if lifecycle.check().is_err() {
Step::Transition(Transition::ConnDied(lifecycle.clone()))
} else {
on_alive
}
}
pub(super) fn encode_control(tag: u8, code: VarInt) -> ([u8; CONTROL_MAX_LEN], usize) {
let mut buf = [0u8; CONTROL_MAX_LEN];
buf[0] = tag;
let vi_len = encode_varint_to_slice(&mut buf[1..], code);
(buf, 1 + vi_len)
}