use std::{fmt, io::Write, ops::RangeInclusive};
use byteorder::{BigEndian, WriteBytesExt};
pub use enumflags2;
use enumflags2::{bitflags, BitFlags};
pub use nom;
use nom::{
combinator::map,
number::streaming::{be_u24, be_u32, be_u8},
sequence::tuple,
IResult,
};
use buffet::{Piece, Roll, RollMut};
pub const PREFACE: &[u8] = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
pub fn preface(i: Roll) -> IResult<Roll, ()> {
let (i, _) = nom::bytes::streaming::tag(PREFACE)(i)?;
Ok((i, ()))
}
pub trait IntoPiece {
fn into_piece(self, scratch: &mut RollMut) -> std::io::Result<Piece>;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RawFrameType {
Data = 0x00,
Headers = 0x01,
Priority = 0x02,
RstStream = 0x03,
Settings = 0x04,
PushPromise = 0x05,
Ping = 0x06,
GoAway = 0x07,
WindowUpdate = 0x08,
Continuation = 0x09,
}
impl RawFrameType {
pub fn repr(&self) -> u8 {
*self as u8
}
pub fn from_repr(value: u8) -> Option<Self> {
match value {
0x00 => Some(RawFrameType::Data),
0x01 => Some(RawFrameType::Headers),
0x02 => Some(RawFrameType::Priority),
0x03 => Some(RawFrameType::RstStream),
0x04 => Some(RawFrameType::Settings),
0x05 => Some(RawFrameType::PushPromise),
0x06 => Some(RawFrameType::Ping),
0x07 => Some(RawFrameType::GoAway),
0x08 => Some(RawFrameType::WindowUpdate),
0x09 => Some(RawFrameType::Continuation),
_ => None,
}
}
}
#[test]
fn test_raw_frame_type_roundtrip() {
let variants = [
RawFrameType::Data,
RawFrameType::Headers,
RawFrameType::Priority,
RawFrameType::RstStream,
RawFrameType::Settings,
RawFrameType::PushPromise,
RawFrameType::Ping,
RawFrameType::GoAway,
RawFrameType::WindowUpdate,
RawFrameType::Continuation,
];
for &variant in &variants {
let repr = variant as u8;
let roundtripped = RawFrameType::from_repr(repr).unwrap();
assert_eq!(variant, roundtripped, "Failed to roundtrip {:?}", variant);
}
assert_eq!(RawFrameType::from_repr(0xFF), None);
}
#[derive(Debug, Clone, Copy)]
pub enum FrameType {
Data(BitFlags<DataFlags>),
Headers(BitFlags<HeadersFlags>),
Priority,
RstStream,
Settings(BitFlags<SettingsFlags>),
PushPromise,
Ping(BitFlags<PingFlags>),
GoAway,
WindowUpdate,
Continuation(BitFlags<ContinuationFlags>),
Unknown(EncodedFrameType),
}
impl FrameType {
pub fn into_frame(self, stream_id: StreamId) -> Frame {
Frame {
frame_type: self,
len: 0,
reserved: 0,
stream_id,
}
}
}
#[bitflags]
#[repr(u8)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum DataFlags {
Padded = 0x08,
EndStream = 0x01,
}
#[bitflags]
#[repr(u8)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum HeadersFlags {
Priority = 0x20,
Padded = 0x08,
EndHeaders = 0x04,
EndStream = 0x01,
}
#[bitflags]
#[repr(u8)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum SettingsFlags {
Ack = 0x01,
}
#[bitflags]
#[repr(u8)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum PingFlags {
Ack = 0x01,
}
#[bitflags]
#[repr(u8)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ContinuationFlags {
EndHeaders = 0x04,
}
#[derive(Debug, Clone, Copy)]
pub struct EncodedFrameType {
pub ty: u8,
pub flags: u8,
}
impl EncodedFrameType {
fn parse(i: Roll) -> IResult<Roll, Self> {
let (i, (ty, flags)) = tuple((be_u8, be_u8))(i)?;
Ok((i, Self { ty, flags }))
}
}
impl From<(RawFrameType, u8)> for EncodedFrameType {
fn from((ty, flags): (RawFrameType, u8)) -> Self {
Self {
ty: ty.repr(),
flags,
}
}
}
impl FrameType {
pub(crate) fn encode(self) -> EncodedFrameType {
match self {
FrameType::Data(f) => (RawFrameType::Data, f.bits()).into(),
FrameType::Headers(f) => (RawFrameType::Headers, f.bits()).into(),
FrameType::Priority => (RawFrameType::Priority, 0).into(),
FrameType::RstStream => (RawFrameType::RstStream, 0).into(),
FrameType::Settings(f) => (RawFrameType::Settings, f.bits()).into(),
FrameType::PushPromise => (RawFrameType::PushPromise, 0).into(),
FrameType::Ping(f) => (RawFrameType::Ping, f.bits()).into(),
FrameType::GoAway => (RawFrameType::GoAway, 0).into(),
FrameType::WindowUpdate => (RawFrameType::WindowUpdate, 0).into(),
FrameType::Continuation(f) => (RawFrameType::Continuation, f.bits()).into(),
FrameType::Unknown(ft) => ft,
}
}
fn decode(ft: EncodedFrameType) -> Self {
match RawFrameType::from_repr(ft.ty) {
Some(ty) => match ty {
RawFrameType::Data => {
FrameType::Data(BitFlags::<DataFlags>::from_bits_truncate(ft.flags))
}
RawFrameType::Headers => {
FrameType::Headers(BitFlags::<HeadersFlags>::from_bits_truncate(ft.flags))
}
RawFrameType::Priority => FrameType::Priority,
RawFrameType::RstStream => FrameType::RstStream,
RawFrameType::Settings => {
FrameType::Settings(BitFlags::<SettingsFlags>::from_bits_truncate(ft.flags))
}
RawFrameType::PushPromise => FrameType::PushPromise,
RawFrameType::Ping => {
FrameType::Ping(BitFlags::<PingFlags>::from_bits_truncate(ft.flags))
}
RawFrameType::GoAway => FrameType::GoAway,
RawFrameType::WindowUpdate => FrameType::WindowUpdate,
RawFrameType::Continuation => FrameType::Continuation(
BitFlags::<ContinuationFlags>::from_bits_truncate(ft.flags),
),
},
None => FrameType::Unknown(ft),
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct StreamId(pub u32);
impl StreamId {
pub const CONNECTION: Self = Self(0);
pub fn is_server_initiated(&self) -> bool {
self.0 % 2 == 0
}
}
#[derive(Debug, thiserror::Error)]
#[error("invalid stream id: {0}")]
pub struct StreamIdOutOfRange(u32);
impl TryFrom<u32> for StreamId {
type Error = StreamIdOutOfRange;
fn try_from(value: u32) -> Result<Self, Self::Error> {
if value & 0x8000_0000 != 0 {
Err(StreamIdOutOfRange(value))
} else {
Ok(Self(value))
}
}
}
impl fmt::Debug for StreamId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.0, f)
}
}
impl fmt::Display for StreamId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
#[derive(Clone, Copy)]
pub struct Frame {
pub frame_type: FrameType,
pub reserved: u8,
pub stream_id: StreamId,
pub len: u32,
}
impl Default for Frame {
fn default() -> Self {
Self {
frame_type: FrameType::Unknown(EncodedFrameType {
ty: 0xff,
flags: 0xff,
}),
reserved: 0,
stream_id: StreamId::CONNECTION,
len: 0,
}
}
}
impl fmt::Debug for Frame {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.stream_id.0 == 0 {
write!(f, "Conn:")?;
} else {
write!(f, "#{}:", self.stream_id.0)?;
}
let name = match &self.frame_type {
FrameType::Data(_) => "Data",
FrameType::Headers(_) => "Headers",
FrameType::Priority => "Priority",
FrameType::RstStream => "RstStream",
FrameType::Settings(_) => "Settings",
FrameType::PushPromise => "PushPromise",
FrameType::Ping(_) => "Ping",
FrameType::GoAway => "GoAway",
FrameType::WindowUpdate => "WindowUpdate",
FrameType::Continuation(_) => "Continuation",
FrameType::Unknown(EncodedFrameType { ty, flags }) => {
return write!(f, "UnknownFrame({:#x}, {:#x}, len={})", ty, flags, self.len)
}
};
let mut s = f.debug_struct(name);
if self.reserved != 0 {
s.field("reserved", &self.reserved);
}
if self.len > 0 {
s.field("len", &self.len);
}
struct DisplayDebug<'a, D: fmt::Display>(&'a D);
impl<'a, D: fmt::Display> fmt::Debug for DisplayDebug<'a, D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self.0, f)
}
}
match &self.frame_type {
FrameType::Data(flags) => {
if !flags.is_empty() {
s.field("flags", &DisplayDebug(flags));
}
}
FrameType::Headers(flags) => {
if !flags.is_empty() {
s.field("flags", &DisplayDebug(flags));
}
}
FrameType::Settings(flags) => {
if !flags.is_empty() {
s.field("flags", &DisplayDebug(flags));
}
}
FrameType::Ping(flags) => {
if !flags.is_empty() {
s.field("flags", &DisplayDebug(flags));
}
}
FrameType::Continuation(flags) => {
if !flags.is_empty() {
s.field("flags", &DisplayDebug(flags));
}
}
_ => {
}
}
s.finish()
}
}
impl Frame {
pub fn new(frame_type: FrameType, stream_id: StreamId) -> Self {
Self {
frame_type,
reserved: 0,
stream_id,
len: 0,
}
}
pub fn with_len(mut self, len: u32) -> Self {
self.len = len;
self
}
pub fn parse(i: Roll) -> IResult<Roll, Self> {
let (i, (len, frame_type, (reserved, stream_id))) = tuple((
be_u24,
EncodedFrameType::parse,
parse_reserved_and_stream_id,
))(i)?;
let frame = Frame {
frame_type: FrameType::decode(frame_type),
reserved,
stream_id,
len,
};
Ok((i, frame))
}
pub fn write_into(self, mut w: impl std::io::Write) -> std::io::Result<()> {
use byteorder::{BigEndian, WriteBytesExt};
w.write_u24::<BigEndian>(self.len as _)?;
let ft = self.frame_type.encode();
w.write_u8(ft.ty)?;
w.write_u8(ft.flags)?;
w.write_all(&pack_reserved_and_stream_id(self.reserved, self.stream_id))?;
Ok(())
}
pub fn is_ack(&self) -> bool {
match self.frame_type {
FrameType::Settings(flags) => flags.contains(SettingsFlags::Ack),
FrameType::Ping(flags) => flags.contains(PingFlags::Ack),
_ => false,
}
}
pub fn is_end_headers(&self) -> bool {
match self.frame_type {
FrameType::Headers(flags) => flags.contains(HeadersFlags::EndHeaders),
FrameType::Continuation(flags) => flags.contains(ContinuationFlags::EndHeaders),
_ => false,
}
}
pub fn is_end_stream(&self) -> bool {
match self.frame_type {
FrameType::Data(flags) => flags.contains(DataFlags::EndStream),
FrameType::Headers(flags) => flags.contains(HeadersFlags::EndStream),
_ => false,
}
}
}
impl IntoPiece for Frame {
fn into_piece(self, scratch: &mut RollMut) -> std::io::Result<Piece> {
debug_assert_eq!(scratch.len(), 0);
self.write_into(&mut *scratch)?;
Ok(scratch.take_all().into())
}
}
pub fn parse_bit_and_u31(i: Roll) -> IResult<Roll, (u8, u32)> {
let (i, x) = be_u32(i)?;
let bit = (x >> 31) as u8;
let val = x & 0x7FFF_FFFF;
Ok((i, (bit, val)))
}
fn parse_reserved_and_stream_id(i: Roll) -> IResult<Roll, (u8, StreamId)> {
parse_bit_and_u31(i).map(|(i, (reserved, stream_id))| (i, (reserved, StreamId(stream_id))))
}
pub fn pack_bit_and_u31(bit: u8, val: u32) -> [u8; 4] {
assert_eq!(val & 0x7FFF_FFFF, val, "val is too large: {val:x}");
assert_eq!(bit & 0x1, bit, "bit should be 0 or 1: {bit:x}");
let mut bytes = val.to_be_bytes();
if bit != 0 {
bytes[0] |= 0x80;
}
bytes
}
pub fn pack_reserved_and_stream_id(reserved: u8, stream_id: StreamId) -> [u8; 4] {
pack_bit_and_u31(reserved, stream_id.0)
}
#[test]
fn test_pack_and_parse_bit_and_u31() {
buffet::bufpool::initialize_allocator().unwrap();
let test_cases = [
(0, 0),
(1, 0),
(0, 1),
(1, 1),
(0, 0x7FFF_FFFF),
(1, 0x7FFF_FFFF),
];
let mut roll = RollMut::alloc().unwrap();
for &(bit, number) in &test_cases {
let packed = pack_bit_and_u31(bit, number);
roll.reserve_at_least(4).unwrap();
roll.put(&packed[..]).unwrap();
let (_, (parsed_bit, parsed_number)) = parse_bit_and_u31(roll.take_all()).unwrap();
assert_eq!(dbg!(bit), dbg!(parsed_bit));
assert_eq!(dbg!(number), dbg!(parsed_number));
}
}
#[test]
#[should_panic(expected = "bit should be 0 or 1: 2")]
fn test_pack_bit_and_u31_panic_not_a_bit() {
pack_bit_and_u31(2, 0);
}
#[test]
#[should_panic(expected = "val is too large: 80000000")]
fn test_pack_bit_and_u31_panic_val_too_large() {
pack_bit_and_u31(0, 1 << 31);
}
#[derive(Debug)]
pub struct PrioritySpec {
pub exclusive: bool,
pub stream_dependency: StreamId,
pub weight: u8,
}
impl PrioritySpec {
pub fn parse(i: Roll) -> IResult<Roll, Self> {
map(
tuple((parse_reserved_and_stream_id, be_u8)),
|((exclusive, stream_dependency), weight)| Self {
exclusive: exclusive != 0,
stream_dependency,
weight,
},
)(i)
}
}
impl IntoPiece for PrioritySpec {
fn into_piece(self, scratch: &mut RollMut) -> std::io::Result<Piece> {
let roll = scratch
.put_to_roll(5, |mut slice| {
let reserved_and_stream_id =
pack_reserved_and_stream_id(self.exclusive as u8, self.stream_dependency);
slice.write_all(&reserved_and_stream_id)?;
slice.write_u8(self.weight)?;
Ok(())
})
.unwrap();
Ok(roll.into())
}
}
#[derive(Clone, Copy)]
pub struct ErrorCode(pub u32);
impl ErrorCode {
pub fn as_repr(self) -> u32 {
self.0
}
}
impl fmt::Debug for ErrorCode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match KnownErrorCode::from_repr(self.0) {
Some(e) => fmt::Debug::fmt(&e, f),
None => write!(f, "ErrorCode(0x{:02x})", self.0),
}
}
}
impl From<KnownErrorCode> for ErrorCode {
fn from(e: KnownErrorCode) -> Self {
Self(e as u32)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum KnownErrorCode {
NoError = 0x00,
ProtocolError = 0x01,
InternalError = 0x02,
FlowControlError = 0x03,
SettingsTimeout = 0x04,
StreamClosed = 0x05,
FrameSizeError = 0x06,
RefusedStream = 0x07,
Cancel = 0x08,
CompressionError = 0x09,
ConnectError = 0x0a,
EnhanceYourCalm = 0x0b,
InadequateSecurity = 0x0c,
Http1_1Required = 0x0d,
}
impl KnownErrorCode {
pub fn from_repr(value: u32) -> Option<Self> {
match value {
0x00 => Some(KnownErrorCode::NoError),
0x01 => Some(KnownErrorCode::ProtocolError),
0x02 => Some(KnownErrorCode::InternalError),
0x03 => Some(KnownErrorCode::FlowControlError),
0x04 => Some(KnownErrorCode::SettingsTimeout),
0x05 => Some(KnownErrorCode::StreamClosed),
0x06 => Some(KnownErrorCode::FrameSizeError),
0x07 => Some(KnownErrorCode::RefusedStream),
0x08 => Some(KnownErrorCode::Cancel),
0x09 => Some(KnownErrorCode::CompressionError),
0x0a => Some(KnownErrorCode::ConnectError),
0x0b => Some(KnownErrorCode::EnhanceYourCalm),
0x0c => Some(KnownErrorCode::InadequateSecurity),
0x0d => Some(KnownErrorCode::Http1_1Required),
_ => None,
}
}
pub fn repr(&self) -> u32 {
*self as u32
}
}
#[test]
fn test_known_error_code_roundtrip() {
let error_codes = [
KnownErrorCode::NoError,
KnownErrorCode::ProtocolError,
KnownErrorCode::InternalError,
KnownErrorCode::FlowControlError,
KnownErrorCode::SettingsTimeout,
KnownErrorCode::StreamClosed,
KnownErrorCode::FrameSizeError,
KnownErrorCode::RefusedStream,
KnownErrorCode::Cancel,
KnownErrorCode::CompressionError,
KnownErrorCode::ConnectError,
KnownErrorCode::EnhanceYourCalm,
KnownErrorCode::InadequateSecurity,
KnownErrorCode::Http1_1Required,
];
for &original in &error_codes {
let repr = original.repr();
let roundtripped = KnownErrorCode::from_repr(repr).unwrap();
assert_eq!(original, roundtripped, "Failed to roundtrip {:?}", original);
}
assert_eq!(KnownErrorCode::from_repr(0xFF), None);
}
impl TryFrom<ErrorCode> for KnownErrorCode {
type Error = ();
fn try_from(e: ErrorCode) -> Result<Self, Self::Error> {
KnownErrorCode::from_repr(e.0).ok_or(())
}
}
#[derive(Clone, Copy, Debug)]
pub struct Settings {
pub header_table_size: u32,
pub enable_push: bool,
pub max_concurrent_streams: Option<u32>,
pub initial_window_size: u32,
pub max_frame_size: u32,
pub max_header_list_size: u32,
}
impl Default for Settings {
fn default() -> Self {
Self {
header_table_size: 4096,
enable_push: false,
max_concurrent_streams: Some(100),
initial_window_size: (1 << 16) - 1,
max_frame_size: (1 << 14),
max_header_list_size: 0,
}
}
}
impl Settings {
pub fn apply(&mut self, code: Setting, value: u32) -> Result<(), SettingsError> {
match code {
Setting::HeaderTableSize => {
self.header_table_size = value;
}
Setting::EnablePush => match value {
0 => self.enable_push = false,
1 => self.enable_push = true,
_ => return Err(SettingsError::InvalidEnablePushValue { actual: value }),
},
Setting::MaxConcurrentStreams => {
self.max_concurrent_streams = Some(value);
}
Setting::InitialWindowSize => {
if value > Self::MAX_INITIAL_WINDOW_SIZE {
return Err(SettingsError::InitialWindowSizeTooLarge { actual: value });
}
self.initial_window_size = value;
}
Setting::MaxFrameSize => {
if !Self::MAX_FRAME_SIZE_ALLOWED_RANGE.contains(&value) {
return Err(SettingsError::SettingsMaxFrameSizeInvalid { actual: value });
}
self.max_frame_size = value;
}
Setting::MaxHeaderListSize => {
self.max_header_list_size = value;
}
}
Ok(())
}
}
#[derive(thiserror::Error, Debug)]
#[non_exhaustive]
pub enum SettingsError {
#[error("ENABLE_PUSH setting is supposed to be either 0 or 1, got {actual}")]
InvalidEnablePushValue { actual: u32 },
#[error("bad INITIAL_WINDOW_SIZE value {actual}, should be than or equal to 2^31-1")]
InitialWindowSizeTooLarge { actual: u32 },
#[error(
"bad SETTINGS_MAX_FRAME_SIZE value {actual}, should be between 2^14 and 2^24-1 inclusive"
)]
SettingsMaxFrameSizeInvalid { actual: u32 },
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Setting {
HeaderTableSize = 0x01,
EnablePush = 0x02,
MaxConcurrentStreams = 0x03,
InitialWindowSize = 0x04,
MaxFrameSize = 0x05,
MaxHeaderListSize = 0x06,
}
impl Setting {
pub fn repr(&self) -> u16 {
*self as u16
}
pub fn from_repr(value: u16) -> Option<Self> {
match value {
0x01 => Some(Setting::HeaderTableSize),
0x02 => Some(Setting::EnablePush),
0x03 => Some(Setting::MaxConcurrentStreams),
0x04 => Some(Setting::InitialWindowSize),
0x05 => Some(Setting::MaxFrameSize),
0x06 => Some(Setting::MaxHeaderListSize),
_ => None,
}
}
}
#[test]
fn test_setting_roundtrip() {
let settings = [
Setting::HeaderTableSize,
Setting::EnablePush,
Setting::MaxConcurrentStreams,
Setting::InitialWindowSize,
Setting::MaxFrameSize,
Setting::MaxHeaderListSize,
];
for &setting in &settings {
let repr = setting.repr();
let roundtripped = Setting::from_repr(repr).unwrap();
assert_eq!(setting, roundtripped, "Failed to roundtrip {:?}", setting);
}
assert_eq!(Setting::from_repr(0x07), None);
}
impl Settings {
pub const MAX_INITIAL_WINDOW_SIZE: u32 = (1 << 31) - 1;
pub const MAX_FRAME_SIZE_ALLOWED_RANGE: RangeInclusive<u32> = (1 << 14)..=((1 << 24) - 1);
pub fn parse<E>(
buf: &[u8],
mut callback: impl FnMut(Setting, u32) -> Result<(), E>,
) -> Result<(), E> {
assert!(
buf.len() % 6 == 0,
"buffer length must be a multiple of 6 bytes"
);
for chunk in buf.chunks_exact(6) {
let id = u16::from_be_bytes([chunk[0], chunk[1]]);
let value = u32::from_be_bytes([chunk[2], chunk[3], chunk[4], chunk[5]]);
match Setting::from_repr(id) {
None => {}
Some(id) => {
callback(id, value)?;
}
}
}
Ok(())
}
}
pub struct SettingPairs<'a>(pub &'a [(Setting, u32)]);
impl<'a> From<&'a [(Setting, u32)]> for SettingPairs<'a> {
fn from(value: &'a [(Setting, u32)]) -> Self {
Self(value)
}
}
impl<const N: usize> From<&'static [(Setting, u32); N]> for SettingPairs<'static> {
fn from(value: &'static [(Setting, u32); N]) -> Self {
Self(value)
}
}
impl<'a> IntoPiece for SettingPairs<'a> {
fn into_piece(self, scratch: &mut RollMut) -> std::io::Result<Piece> {
let roll = scratch
.put_to_roll(self.0.len() * 6, |mut slice| {
for (id, value) in self.0.iter() {
slice.write_u16::<BigEndian>(*id as u16)?;
slice.write_u32::<BigEndian>(*value)?;
}
Ok(())
})
.unwrap();
Ok(roll.into())
}
}
pub struct GoAway {
pub last_stream_id: StreamId,
pub error_code: ErrorCode,
pub additional_debug_data: Piece,
}
impl IntoPiece for GoAway {
fn into_piece(self, scratch: &mut RollMut) -> std::io::Result<Piece> {
let roll = scratch
.put_to_roll(8 + self.additional_debug_data.len(), |mut slice| {
slice.write_u32::<BigEndian>(self.last_stream_id.0)?;
slice.write_u32::<BigEndian>(self.error_code.0)?;
slice.write_all(&self.additional_debug_data[..])?;
Ok(())
})
.unwrap();
Ok(roll.into())
}
}
impl GoAway {
pub fn parse(i: Roll) -> IResult<Roll, Self> {
let (rest, (last_stream_id, error_code)) = tuple((be_u32, be_u32))(i)?;
let i = Roll::empty();
Ok((
i,
Self {
last_stream_id: StreamId(last_stream_id),
error_code: ErrorCode(error_code),
additional_debug_data: rest.into(),
},
))
}
}
pub struct RstStream {
pub error_code: ErrorCode,
}
impl IntoPiece for RstStream {
fn into_piece(self, scratch: &mut RollMut) -> std::io::Result<Piece> {
let roll = scratch
.put_to_roll(4, |mut slice| {
slice.write_u32::<BigEndian>(self.error_code.0)?;
Ok(())
})
.unwrap();
Ok(roll.into())
}
}
impl RstStream {
pub fn parse(i: Roll) -> IResult<Roll, Self> {
let (rest, error_code) = be_u32(i)?;
Ok((
rest,
Self {
error_code: ErrorCode(error_code),
},
))
}
}
#[derive(Debug, Clone, Copy)]
pub struct WindowUpdate {
pub reserved: u8,
pub increment: u32,
}
impl IntoPiece for WindowUpdate {
fn into_piece(self, scratch: &mut RollMut) -> std::io::Result<Piece> {
let roll = scratch
.put_to_roll(4, |mut slice| {
let packed = pack_bit_and_u31(self.reserved, self.increment);
slice.write_all(&packed)?;
Ok(())
})
.unwrap();
Ok(roll.into())
}
}
impl WindowUpdate {
pub fn parse(i: Roll) -> IResult<Roll, Self> {
let (rest, (reserved, increment)) = parse_bit_and_u31(i)?;
Ok((
rest,
Self {
reserved,
increment,
},
))
}
}
impl<T> IntoPiece for T
where
Piece: From<T>,
{
fn into_piece(self, _scratch: &mut RollMut) -> std::io::Result<Piece> {
Ok(self.into())
}
}