use core::fmt;
use super::super::codepoints::{Http3ErrorCode, Http3FrameType};
use super::super::enums::message::Http3HeadersKind;
use super::super::frame::Http3Frame;
use super::super::frame::Http3FramePayloadParseError;
use super::super::frame::{Http3Data, Http3Headers, Http3PushPromise};
use super::super::headers::Http3DecodedHeaderSection;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Http3MessageStreamKind {
Request,
Response,
Push,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Http3MessagePosition {
BeforeHeaders,
BeforeFinalResponse,
Content,
Trailers,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Http3MessageStreamError {
UnexpectedFrame {
frame_type: Http3FrameType,
position: Http3MessagePosition,
},
FramePayload {
frame_type: Http3FrameType,
error: Http3FramePayloadParseError,
},
InvalidHeaderSection {
stream_kind: Http3MessageStreamKind,
position: Http3MessagePosition,
section_kind: Http3HeadersKind,
},
IncompleteMessage {
stream_kind: Http3MessageStreamKind,
position: Http3MessagePosition,
},
}
impl Http3MessageStreamError {
pub const fn error_code(self) -> Http3ErrorCode {
match self {
Self::UnexpectedFrame { .. } => Http3ErrorCode::FRAME_UNEXPECTED,
Self::FramePayload { .. } => Http3ErrorCode::FRAME_ERROR,
Self::InvalidHeaderSection { .. } => Http3ErrorCode::MESSAGE_ERROR,
Self::IncompleteMessage {
stream_kind: Http3MessageStreamKind::Request,
..
} => Http3ErrorCode::REQUEST_INCOMPLETE,
Self::IncompleteMessage {
stream_kind: Http3MessageStreamKind::Response | Http3MessageStreamKind::Push,
..
} => Http3ErrorCode::MESSAGE_ERROR,
}
}
}
impl fmt::Display for Http3MessageStreamError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::UnexpectedFrame {
frame_type,
position,
} => write!(
f,
"HTTP/3 frame type {} is unexpected at message position {position:?}",
frame_type.value()
),
Self::FramePayload { frame_type, error } => write!(
f,
"HTTP/3 message frame type {} payload: {error}",
frame_type.value()
),
Self::InvalidHeaderSection {
stream_kind,
position,
section_kind,
} => write!(
f,
"HTTP/3 {section_kind:?} header section is invalid for {stream_kind:?} at message position {position:?}"
),
Self::IncompleteMessage {
stream_kind,
position,
} => write!(
f,
"HTTP/3 {stream_kind:?} message is incomplete at message position {position:?}"
),
}
}
}
impl core::error::Error for Http3MessageStreamError {
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
match self {
Self::FramePayload { error, .. } => Some(error),
Self::UnexpectedFrame { .. }
| Self::InvalidHeaderSection { .. }
| Self::IncompleteMessage { .. } => None,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Http3MessageStreamState {
kind: Http3MessageStreamKind,
position: Http3MessagePosition,
}
impl Http3MessageStreamState {
pub const fn new(kind: Http3MessageStreamKind) -> Self {
let position = match kind {
Http3MessageStreamKind::Request => Http3MessagePosition::BeforeHeaders,
Http3MessageStreamKind::Response | Http3MessageStreamKind::Push => {
Http3MessagePosition::BeforeFinalResponse
}
};
Self { kind, position }
}
pub const fn kind(self) -> Http3MessageStreamKind {
self.kind
}
pub const fn position(self) -> Http3MessagePosition {
self.position
}
pub fn receive<'state, 'wire>(
&'state mut self,
frame: Http3Frame<'wire>,
) -> Result<Http3MessageFrame<'state, 'wire>, Http3MessageStreamError> {
let frame_type = frame.frame_type();
let position = self.position;
match frame_type {
Http3FrameType::HEADERS => {
if position == Http3MessagePosition::Trailers {
return Err(unexpected_frame(frame_type, position));
}
let headers = Http3Headers::from_frame(frame)
.map_err(|error| frame_payload_error(frame_type, error))?;
Ok(Http3MessageFrame::Headers(Http3PendingHeaders {
state: self,
headers,
}))
}
Http3FrameType::DATA => {
if position != Http3MessagePosition::Content {
return Err(unexpected_frame(frame_type, position));
}
Http3Data::from_frame(frame)
.map(Http3MessageFrame::Data)
.map_err(|error| frame_payload_error(frame_type, error))
}
Http3FrameType::PUSH_PROMISE => {
if self.kind != Http3MessageStreamKind::Response {
return Err(unexpected_frame(frame_type, position));
}
Http3PushPromise::from_frame(frame)
.map(Http3MessageFrame::PushPromise)
.map_err(|error| frame_payload_error(frame_type, error))
}
Http3FrameType::CANCEL_PUSH
| Http3FrameType::SETTINGS
| Http3FrameType::GOAWAY
| Http3FrameType::MAX_PUSH_ID => Err(unexpected_frame(frame_type, position)),
frame_type if is_http2_reserved_frame_type(frame_type) => {
Err(unexpected_frame(frame_type, position))
}
_ => Ok(Http3MessageFrame::Unknown(frame)),
}
}
pub const fn finish(&self) -> Result<(), Http3MessageStreamError> {
match self.position {
Http3MessagePosition::Content | Http3MessagePosition::Trailers => Ok(()),
Http3MessagePosition::BeforeHeaders | Http3MessagePosition::BeforeFinalResponse => {
Err(Http3MessageStreamError::IncompleteMessage {
stream_kind: self.kind,
position: self.position,
})
}
}
}
}
#[derive(Debug)]
pub enum Http3MessageFrame<'state, 'wire> {
Data(Http3Data<'wire>),
Headers(Http3PendingHeaders<'state, 'wire>),
PushPromise(Http3PushPromise<'wire>),
Unknown(Http3Frame<'wire>),
}
#[derive(Debug)]
pub struct Http3PendingHeaders<'state, 'wire> {
state: &'state mut Http3MessageStreamState,
headers: Http3Headers<'wire>,
}
impl<'state, 'wire> Http3PendingHeaders<'state, 'wire> {
pub const fn headers(&self) -> Http3Headers<'wire> {
self.headers
}
pub fn accept(
self,
section: &Http3DecodedHeaderSection<'_>,
) -> Result<Http3Headers<'wire>, Http3MessageStreamError> {
let section_kind = section.kind();
let next_position = next_position(self.state.kind, self.state.position, section_kind)?;
self.state.position = next_position;
Ok(self.headers)
}
}
const fn next_position(
stream_kind: Http3MessageStreamKind,
position: Http3MessagePosition,
section_kind: Http3HeadersKind,
) -> Result<Http3MessagePosition, Http3MessageStreamError> {
match (stream_kind, position, section_kind) {
(
Http3MessageStreamKind::Request,
Http3MessagePosition::BeforeHeaders,
Http3HeadersKind::Request,
) => Ok(Http3MessagePosition::Content),
(
Http3MessageStreamKind::Request,
Http3MessagePosition::Content,
Http3HeadersKind::Trailers,
) => Ok(Http3MessagePosition::Trailers),
(
Http3MessageStreamKind::Response | Http3MessageStreamKind::Push,
Http3MessagePosition::BeforeFinalResponse,
Http3HeadersKind::InformationalResponse,
) => Ok(Http3MessagePosition::BeforeFinalResponse),
(
Http3MessageStreamKind::Response | Http3MessageStreamKind::Push,
Http3MessagePosition::BeforeFinalResponse,
Http3HeadersKind::FinalResponse,
) => Ok(Http3MessagePosition::Content),
(
Http3MessageStreamKind::Response | Http3MessageStreamKind::Push,
Http3MessagePosition::Content,
Http3HeadersKind::Trailers,
) => Ok(Http3MessagePosition::Trailers),
_ => Err(Http3MessageStreamError::InvalidHeaderSection {
stream_kind,
position,
section_kind,
}),
}
}
const fn unexpected_frame(
frame_type: Http3FrameType,
position: Http3MessagePosition,
) -> Http3MessageStreamError {
Http3MessageStreamError::UnexpectedFrame {
frame_type,
position,
}
}
const fn frame_payload_error(
frame_type: Http3FrameType,
error: Http3FramePayloadParseError,
) -> Http3MessageStreamError {
Http3MessageStreamError::FramePayload { frame_type, error }
}
const fn is_http2_reserved_frame_type(frame_type: Http3FrameType) -> bool {
matches!(frame_type.value(), 0x02 | 0x06 | 0x08 | 0x09)
}