use std::{
collections::HashMap,
task::{Context, Poll},
};
use bytes::Bytes;
use http_body::Frame;
use crate::{
app::Wakeups,
h3::common::{H3App, H3Error, ReadState, StreamState},
quic::connection::{QuicScionConn, WeakConnectionHandle},
};
const READ_CHUNK: usize = 16 * 1024;
pub(crate) fn poll_read_frame<A: H3App>(
handle: &WeakConnectionHandle<A>,
stream_id: u64,
cx: &mut Context<'_>,
) -> Poll<Option<Result<Frame<Bytes>, H3Error>>> {
let Some(handle) = handle.upgrade() else {
return Poll::Ready(None);
};
let mut guard = handle.lock();
let QuicScionConn { inner, app, .. } = &mut *guard;
let (h3, streams) = app.h3_streams();
let Some(h3) = h3 else {
return Poll::Ready(None);
};
let mut buf = [0u8; READ_CHUNK];
match h3.recv_body(inner, stream_id, &mut buf) {
Ok(n) => {
let bytes = Bytes::copy_from_slice(&buf[..n]);
drop(guard);
handle.notify();
Poll::Ready(Some(Ok(Frame::data(bytes))))
}
Err(squiche::h3::Error::Done) => {
let st = streams.entry(stream_id).or_default();
match &mut st.read_state {
ReadState::Streaming => {
st.read_waker = Some(cx.waker().clone());
drop(guard);
handle.notify();
Poll::Pending
}
ReadState::Trailers(_) => {
let ReadState::Trailers(trailers) =
std::mem::replace(&mut st.read_state, ReadState::Eof)
else {
unreachable!("just matched Trailers")
};
Poll::Ready(Some(Ok(Frame::trailers(trailers))))
}
ReadState::Eof => Poll::Ready(None),
ReadState::Reset(code) => Poll::Ready(Some(Err(H3Error::Reset(*code)))),
}
}
Err(err) => Poll::Ready(Some(Err(H3Error::H3(err)))),
}
}
pub(crate) trait StreamReader: Send + Sync {
fn poll_read(
&self,
stream_id: u64,
cx: &mut Context<'_>,
) -> Poll<Option<Result<Frame<Bytes>, H3Error>>>;
}
pub(crate) struct ConnReader<A: H3App> {
pub(crate) handle: WeakConnectionHandle<A>,
}
impl<A: H3App + 'static + Send> StreamReader for ConnReader<A> {
fn poll_read(
&self,
stream_id: u64,
cx: &mut Context<'_>,
) -> Poll<Option<Result<Frame<Bytes>, H3Error>>> {
poll_read_frame(&self.handle, stream_id, cx)
}
}
pub(crate) fn on_data(
streams: &mut HashMap<u64, StreamState>,
stream_id: u64,
wakeups: &mut Wakeups,
) {
if let Some(st) = streams.get_mut(&stream_id)
&& let Some(w) = st.read_waker.take()
{
wakeups.schedule(w);
}
}
pub(crate) fn on_finished(
streams: &mut HashMap<u64, StreamState>,
stream_id: u64,
wakeups: &mut Wakeups,
) {
if let Some(st) = streams.get_mut(&stream_id) {
if matches!(st.read_state, ReadState::Streaming) {
st.read_state = ReadState::Eof;
}
if let Some(w) = st.read_waker.take() {
wakeups.schedule(w);
}
}
}
pub(crate) fn on_reset(
streams: &mut HashMap<u64, StreamState>,
stream_id: u64,
code: u64,
wakeups: &mut Wakeups,
) {
if let Some(st) = streams.get_mut(&stream_id) {
st.read_state = ReadState::Reset(code);
if let Some(w) = st.read_waker.take() {
wakeups.schedule(w);
}
if let Some(w) = st.write_waker.take() {
wakeups.schedule(w);
}
}
}
pub(crate) fn wake_writable(
conn: &mut squiche::Connection,
streams: &mut HashMap<u64, StreamState>,
wakeups: &mut Wakeups,
) {
for stream_id in conn.writable() {
if let Some(st) = streams.get_mut(&stream_id)
&& let Some(w) = st.write_waker.take()
{
wakeups.schedule(w);
}
}
}