pub mod stream;
use std::{
pin::{Pin, pin},
task::{Context, Poll},
};
use bytes::{Buf, Bytes};
use futures::{Sink, SinkExt, Stream, TryStream, TryStreamExt};
use tokio::io::{self, AsyncBufRead, AsyncRead, AsyncWrite, ReadBuf};
use crate::{
buflist::BufList,
codec::{
DecodeExt, DecodeFrom, DecodeStreamError, EncodeError, EncodeExt, EncodeInto,
EncodeStreamError, FixedLengthReader, StreamReader,
},
connection::StreamError,
error::H3FrameDecodeError,
quic::{self, GetStreamId, StopStream},
varint::{self, VARINT_MAX, VarInt},
};
pin_project_lite::pin_project! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Frame<P: ?Sized> {
r#type: VarInt,
length: VarInt,
#[pin]
payload: P,
}
}
impl Frame<()> {
pub const DATA_FRAME_TYPE: VarInt = VarInt::from_u32(0x00);
pub const HEADERS_FRAME_TYPE: VarInt = VarInt::from_u32(0x01);
pub const CANCEL_PUSH_FRAME_TYPE: VarInt = VarInt::from_u32(0x03);
pub const SETTINGS_FRAME_TYPE: VarInt = VarInt::from_u32(0x04);
pub const PUSH_PROMISE_FRAME_TYPE: VarInt = VarInt::from_u32(0x05);
pub const GOAWAY_FRAME_TYPE: VarInt = VarInt::from_u32(0x07);
pub const MAX_PUSH_ID_FRAME_TYPE: VarInt = VarInt::from_u32(0x0d);
}
impl<P: ?Sized> Frame<P> {
pub fn new(r#type: VarInt, payload: P) -> Result<Self, varint::err::Overflow>
where
P: Buf + Sized,
{
let length = VarInt::try_from(payload.remaining())?;
Ok(Self {
r#type,
length,
payload,
})
}
pub const fn r#type(&self) -> VarInt {
self.r#type
}
pub const fn is_reserved_frame(&self) -> bool {
(self.r#type.into_inner() >= 0x21) && (self.r#type.into_inner() - 0x21).is_multiple_of(0x1f)
}
pub const fn length(&self) -> VarInt {
self.length
}
pub const fn payload(&self) -> &P {
&self.payload
}
pub fn into_payload(self) -> P
where
P: Sized,
{
self.payload
}
pub fn map<U>(self, map: impl FnOnce(P) -> U) -> Frame<U>
where
P: Sized,
{
Frame {
r#type: self.r#type,
length: self.length,
payload: map(self.payload),
}
}
}
impl<P: AsyncWrite + ?Sized> AsyncWrite for Frame<P> {
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
let project = self.project();
match project.length.into_inner().checked_add(buf.len() as u64) {
Some(new_length) if new_length > VARINT_MAX => {
return Poll::Ready(Err(EncodeError::FramePayloadTooLarge.into()));
}
Some(new_length) => {
*project.length =
VarInt::from_u64(new_length).expect("length checked to be within VarInt range");
}
None => return Poll::Ready(Err(EncodeError::FramePayloadTooLarge.into())),
}
project.payload.poll_write(cx, buf)
}
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
self.project().payload.poll_flush(cx)
}
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
self.project().payload.poll_shutdown(cx)
}
}
impl<P: Sink<B> + ?Sized, B: Buf> Sink<B> for Frame<P>
where
EncodeStreamError: From<P::Error>,
{
type Error = EncodeStreamError;
fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.project()
.payload
.poll_ready(cx)
.map_err(EncodeStreamError::from)
}
fn start_send(self: Pin<&mut Self>, item: B) -> Result<(), Self::Error> {
let project = self.project();
let len = item.remaining() as u64;
match project.length.into_inner().checked_add(len) {
Some(new_length) if new_length > VARINT_MAX => {
return Err(EncodeError::FramePayloadTooLarge.into());
}
Some(new_length) => {
*project.length =
VarInt::from_u64(new_length).expect("length checked to be within VarInt range");
}
None => return Err(EncodeError::FramePayloadTooLarge.into()),
}
project.payload.start_send(item)?;
Ok(())
}
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.project()
.payload
.poll_flush(cx)
.map_err(EncodeStreamError::from)
}
fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.project()
.payload
.poll_close(cx)
.map_err(EncodeStreamError::from)
}
}
impl<P: Buf + Send, S: AsyncWrite + Sink<Bytes, Error = quic::StreamError> + Send> EncodeInto<S>
for Frame<P>
{
type Output = ();
type Error = quic::StreamError;
async fn encode_into(self, stream: S) -> Result<Self::Output, Self::Error> {
let Frame {
r#type,
length,
mut payload,
} = self;
let mut stream = pin!(stream);
stream.as_mut().encode(r#type).await?;
stream.as_mut().encode(length).await?;
while payload.has_remaining() {
let bytes = payload.copy_to_bytes(payload.chunk().len());
stream.as_mut().feed(bytes).await?;
}
Ok(())
}
}
impl<P1, P> DecodeFrom<Frame<P>> for Frame<P1>
where
P1: DecodeFrom<P> + Send,
P: Send,
{
type Error = <P1 as DecodeFrom<P>>::Error;
async fn decode_from(stream: Frame<P>) -> Result<Self, Self::Error> {
let Frame {
r#type,
length,
payload,
} = stream;
let payload = P1::decode_from(payload).await?;
Ok(Frame {
r#type,
length,
payload,
})
}
}
impl<S> DecodeFrom<&mut StreamReader<S>> for Frame<BufList>
where
S: TryStream<Ok = Bytes, Error = quic::StreamError> + Unpin + Send,
{
type Error = StreamError;
async fn decode_from(stream: &mut StreamReader<S>) -> Result<Self, Self::Error> {
let decode = async move {
let r#type = stream.decode_one::<VarInt>().await?;
let length = stream.decode_one::<VarInt>().await?;
let mut payload = BufList::new();
let mut reader = FixedLengthReader::new(stream, length.into_inner());
while let Some(bytes) = reader.try_next().await? {
payload.write(bytes);
}
Ok(Frame {
r#type,
length,
payload,
})
};
decode.await.map_err(|error: DecodeStreamError| {
error.map_decode_error(|decode_error| {
H3FrameDecodeError {
source: decode_error,
}
.into()
})
})
}
}
impl<P: AsyncRead + ?Sized> AsyncRead for Frame<P> {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
self.project().payload.poll_read(cx, buf)
}
}
impl<P: AsyncBufRead + ?Sized> AsyncBufRead for Frame<P> {
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
self.project().payload.poll_fill_buf(cx)
}
fn consume(self: Pin<&mut Self>, amt: usize) {
self.project().payload.consume(amt);
}
}
impl<P: TryStream<Ok = Bytes, Error = DecodeStreamError> + ?Sized> Stream for Frame<P> {
type Item = Result<Bytes, DecodeStreamError>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
self.project().payload.try_poll_next(cx)
}
}
impl<P: GetStreamId + ?Sized> GetStreamId for Frame<P> {
fn poll_stream_id(
self: Pin<&mut Self>,
cx: &mut Context,
) -> Poll<Result<VarInt, quic::StreamError>> {
self.project().payload.poll_stream_id(cx)
}
}
impl<P: StopStream + ?Sized> StopStream for Frame<P> {
fn poll_stop(
self: Pin<&mut Self>,
cx: &mut Context,
code: VarInt,
) -> Poll<Result<(), quic::StreamError>> {
self.project().payload.poll_stop(cx, code)
}
}
#[cfg(test)]
mod tests {
use futures::stream::{self, StreamExt};
use tokio::io::AsyncReadExt;
use super::*;
use crate::{
codec::{DecodeError, EncodeExt, SinkWriter},
dhttp::frame::stream::FrameStream,
error::Code,
};
fn to_pre_byte_stream(
data: impl IntoIterator<Item = u8>,
) -> impl Stream<Item = Result<Bytes, quic::StreamError>> {
stream::iter(
data.into_iter()
.map(|byte| vec![byte])
.map(Bytes::from_owner)
.map(Ok),
)
}
#[test]
fn new_frame() {
Frame::new(Frame::DATA_FRAME_TYPE, Bytes::new()).unwrap();
}
#[tokio::test]
async fn test_data_frames() {
let stream = StreamReader::new(to_pre_byte_stream(vec![
0x00, 0x05, b'H', b'e', b'l', b'l', b'o', 0x00, 0x05, b'W', b'o', b'r', b'l', b'd', ]));
let mut stream = pin!(FrameStream::new(stream));
let mut frame1 = stream.as_mut().next_frame().await.unwrap().unwrap();
assert_eq!(frame1.r#type().into_inner(), 0);
assert_eq!(frame1.length().into_inner(), 5);
let mut payload = vec![];
frame1.read_to_end(&mut payload).await.unwrap();
assert_eq!(&payload[..], b"Hello");
let mut frame2 = stream.next_frame().await.unwrap().unwrap();
let mut payload = vec![];
frame2.read_to_end(&mut payload).await.unwrap();
assert_eq!(&payload[..], b"World");
assert_eq!(frame2.length().into_inner(), 5);
assert_eq!(frame2.r#type().into_inner(), 0);
}
#[tokio::test]
async fn incomplete_type() {
let mut stream = StreamReader::new(to_pre_byte_stream(vec![
]));
assert!(matches!(
stream.decode_one::<Frame<_>>().await,
Err(StreamError::Code { source }) if source.code() == Code::H3_FRAME_ERROR
));
}
#[tokio::test]
async fn incomplete_length() {
let mut stream = StreamReader::new(to_pre_byte_stream(vec![
0x00, ]));
assert!(matches!(
stream.decode_one::<Frame<_>>().await,
Err(StreamError::Code { source }) if source.code() == Code::H3_FRAME_ERROR
));
}
#[tokio::test]
async fn incomplete_payload() {
let stream = StreamReader::new(to_pre_byte_stream(vec![
0x00, 0x05, b'H', b'e', b'l', b'l', ]));
let stream = pin!(FrameStream::new(stream));
let mut frame1 = stream.next_frame().await.unwrap().unwrap();
assert_eq!(frame1.r#type().into_inner(), 0);
assert_eq!(frame1.length().into_inner(), 5);
let mut payload = vec![];
assert!(matches!(
DecodeStreamError::from(frame1.read_to_end(&mut payload).await.unwrap_err()),
DecodeStreamError::Decode {
source: DecodeError::Incomplete
}
));
assert_eq!(payload.as_slice(), b"Hell")
}
fn channel() -> (
impl Sink<Bytes, Error = quic::StreamError>,
impl Stream<Item = Result<Bytes, quic::StreamError>>,
) {
let (sink, stream) = futures::channel::mpsc::channel(8);
(sink.sink_map_err(|_e| unreachable!()), stream.map(Ok))
}
#[tokio::test]
async fn encode_and_decode() {
let (sink, stream) = channel();
let decode = tokio::spawn(async move {
let stream = StreamReader::new(stream);
let mut stream = pin!(FrameStream::new(stream));
let mut frame1 = stream.as_mut().next_frame().await.unwrap().unwrap();
assert_eq!(frame1.r#type().into_inner(), 0);
assert_eq!(frame1.length().into_inner(), 5);
let mut payload = vec![];
frame1.read_to_end(&mut payload).await.unwrap();
assert_eq!(&payload[..], b"Hello");
let mut frame2 = stream.next_frame().await.unwrap().unwrap();
let mut payload = vec![];
frame2.read_to_end(&mut payload).await.unwrap();
assert_eq!(&payload[..], b"World");
assert_eq!(frame2.length().into_inner(), 5);
assert_eq!(frame2.r#type().into_inner(), 0);
});
let encode = tokio::spawn(async move {
let mut sink = SinkWriter::new(sink);
let frame1 = Frame::new(VarInt::from_u32(0), Bytes::from_static(b"Hello")).unwrap();
assert!(frame1.r#type().into_inner() == 0);
assert!(frame1.length().into_inner() == 5);
assert!(frame1.payload().as_ref() == b"Hello");
sink.encode_one(frame1).await.unwrap();
let frame2 = Frame::new(VarInt::from_u32(0), Bytes::from_static(b"World")).unwrap();
assert!(frame2.r#type().into_inner() == 0);
assert!(frame2.length().into_inner() == 5);
assert!(frame2.payload().as_ref() == b"World");
sink.encode_one(frame2).await.unwrap();
Pin::new(&mut sink).flush_buffer().await.unwrap();
});
tokio::try_join!(encode, decode).unwrap();
}
}