fluke_h2_parse/
lib.rs

1//! HTTP/2 parser
2//!
3//! HTTP/2 <https://httpwg.org/specs/rfc9113.html>
4//! HTTP semantics <https://httpwg.org/specs/rfc9110.html>
5
6use std::{fmt, io::Write, ops::RangeInclusive};
7
8use byteorder::{BigEndian, WriteBytesExt};
9use enum_repr::EnumRepr;
10
11pub use enumflags2;
12use enumflags2::{bitflags, BitFlags};
13
14pub use nom;
15
16use nom::{
17    combinator::map,
18    number::streaming::{be_u16, be_u24, be_u32, be_u8},
19    sequence::tuple,
20    IResult,
21};
22
23use fluke_buffet::{Piece, Roll, RollMut};
24
25/// This is sent by h2 clients after negotiating over ALPN, or when doing h2c.
26pub const PREFACE: &[u8] = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
27
28pub fn preface(i: Roll) -> IResult<Roll, ()> {
29    let (i, _) = nom::bytes::streaming::tag(PREFACE)(i)?;
30    Ok((i, ()))
31}
32
33pub trait IntoPiece {
34    fn into_piece(self, scratch: &mut RollMut) -> std::io::Result<Piece>;
35}
36
37/// See https://httpwg.org/specs/rfc9113.html#FrameTypes
38#[EnumRepr(type = "u8")]
39#[derive(Debug, Clone, Copy)]
40pub enum RawFrameType {
41    Data = 0x00,
42    Headers = 0x01,
43    Priority = 0x02,
44    RstStream = 0x03,
45    Settings = 0x04,
46    PushPromise = 0x05,
47    Ping = 0x06,
48    GoAway = 0x07,
49    WindowUpdate = 0x08,
50    Continuation = 0x09,
51}
52
53/// Typed flags for various frame types
54#[derive(Debug, Clone, Copy)]
55pub enum FrameType {
56    Data(BitFlags<DataFlags>),
57    Headers(BitFlags<HeadersFlags>),
58    Priority,
59    RstStream,
60    Settings(BitFlags<SettingsFlags>),
61    PushPromise,
62    Ping(BitFlags<PingFlags>),
63    GoAway,
64    WindowUpdate,
65    Continuation(BitFlags<ContinuationFlags>),
66    Unknown(EncodedFrameType),
67}
68
69impl FrameType {
70    /// Turn this [FrameType] into a [Frame]
71    pub fn into_frame(self, stream_id: StreamId) -> Frame {
72        Frame {
73            frame_type: self,
74            len: 0,
75            reserved: 0,
76            stream_id,
77        }
78    }
79}
80
81/// See https://httpwg.org/specs/rfc9113.html#DATA
82#[bitflags]
83#[repr(u8)]
84#[derive(Copy, Clone, Debug, PartialEq, Eq)]
85pub enum DataFlags {
86    Padded = 0x08,
87    EndStream = 0x01,
88}
89
90/// See https://httpwg.org/specs/rfc9113.html#rfc.section.6.2
91#[bitflags]
92#[repr(u8)]
93#[derive(Copy, Clone, Debug, PartialEq, Eq)]
94pub enum HeadersFlags {
95    Priority = 0x20,
96    Padded = 0x08,
97    EndHeaders = 0x04,
98    EndStream = 0x01,
99}
100
101/// See https://httpwg.org/specs/rfc9113.html#SETTINGS
102#[bitflags]
103#[repr(u8)]
104#[derive(Copy, Clone, Debug, PartialEq, Eq)]
105pub enum SettingsFlags {
106    Ack = 0x01,
107}
108
109/// See https://httpwg.org/specs/rfc9113.html#PING
110#[bitflags]
111#[repr(u8)]
112#[derive(Copy, Clone, Debug, PartialEq, Eq)]
113pub enum PingFlags {
114    Ack = 0x01,
115}
116
117/// See https://httpwg.org/specs/rfc9113.html#CONTINUATION
118#[bitflags]
119#[repr(u8)]
120#[derive(Copy, Clone, Debug, PartialEq, Eq)]
121pub enum ContinuationFlags {
122    EndHeaders = 0x04,
123}
124
125#[derive(Debug, Clone, Copy)]
126pub struct EncodedFrameType {
127    pub ty: u8,
128    pub flags: u8,
129}
130
131impl EncodedFrameType {
132    fn parse(i: Roll) -> IResult<Roll, Self> {
133        let (i, (ty, flags)) = tuple((be_u8, be_u8))(i)?;
134        Ok((i, Self { ty, flags }))
135    }
136}
137
138impl From<(RawFrameType, u8)> for EncodedFrameType {
139    fn from((ty, flags): (RawFrameType, u8)) -> Self {
140        Self {
141            ty: ty.repr(),
142            flags,
143        }
144    }
145}
146
147impl FrameType {
148    pub(crate) fn encode(self) -> EncodedFrameType {
149        match self {
150            FrameType::Data(f) => (RawFrameType::Data, f.bits()).into(),
151            FrameType::Headers(f) => (RawFrameType::Headers, f.bits()).into(),
152            FrameType::Priority => (RawFrameType::Priority, 0).into(),
153            FrameType::RstStream => (RawFrameType::RstStream, 0).into(),
154            FrameType::Settings(f) => (RawFrameType::Settings, f.bits()).into(),
155            FrameType::PushPromise => (RawFrameType::PushPromise, 0).into(),
156            FrameType::Ping(f) => (RawFrameType::Ping, f.bits()).into(),
157            FrameType::GoAway => (RawFrameType::GoAway, 0).into(),
158            FrameType::WindowUpdate => (RawFrameType::WindowUpdate, 0).into(),
159            FrameType::Continuation(f) => (RawFrameType::Continuation, f.bits()).into(),
160            FrameType::Unknown(ft) => ft,
161        }
162    }
163
164    fn decode(ft: EncodedFrameType) -> Self {
165        match RawFrameType::from_repr(ft.ty) {
166            Some(ty) => match ty {
167                RawFrameType::Data => {
168                    FrameType::Data(BitFlags::<DataFlags>::from_bits_truncate(ft.flags))
169                }
170                RawFrameType::Headers => {
171                    FrameType::Headers(BitFlags::<HeadersFlags>::from_bits_truncate(ft.flags))
172                }
173                RawFrameType::Priority => FrameType::Priority,
174                RawFrameType::RstStream => FrameType::RstStream,
175                RawFrameType::Settings => {
176                    FrameType::Settings(BitFlags::<SettingsFlags>::from_bits_truncate(ft.flags))
177                }
178                RawFrameType::PushPromise => FrameType::PushPromise,
179                RawFrameType::Ping => {
180                    FrameType::Ping(BitFlags::<PingFlags>::from_bits_truncate(ft.flags))
181                }
182                RawFrameType::GoAway => FrameType::GoAway,
183                RawFrameType::WindowUpdate => FrameType::WindowUpdate,
184                RawFrameType::Continuation => FrameType::Continuation(
185                    BitFlags::<ContinuationFlags>::from_bits_truncate(ft.flags),
186                ),
187            },
188            None => FrameType::Unknown(ft),
189        }
190    }
191}
192
193#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
194pub struct StreamId(pub u32);
195
196impl StreamId {
197    /// Stream ID used for connection control frames
198    pub const CONNECTION: Self = Self(0);
199
200    /// Server-initiated streams have even IDs
201    pub fn is_server_initiated(&self) -> bool {
202        self.0 % 2 == 0
203    }
204}
205
206#[derive(Debug, thiserror::Error)]
207#[error("invalid stream id: {0}")]
208pub struct StreamIdOutOfRange(u32);
209
210impl TryFrom<u32> for StreamId {
211    type Error = StreamIdOutOfRange;
212
213    fn try_from(value: u32) -> Result<Self, Self::Error> {
214        if value & 0x8000_0000 != 0 {
215            Err(StreamIdOutOfRange(value))
216        } else {
217            Ok(Self(value))
218        }
219    }
220}
221
222impl fmt::Debug for StreamId {
223    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
224        fmt::Debug::fmt(&self.0, f)
225    }
226}
227
228impl fmt::Display for StreamId {
229    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
230        fmt::Display::fmt(&self.0, f)
231    }
232}
233
234/// See https://httpwg.org/specs/rfc9113.html#FrameHeader
235pub struct Frame {
236    pub frame_type: FrameType,
237    pub reserved: u8,
238    pub stream_id: StreamId,
239    pub len: u32,
240}
241
242impl fmt::Debug for Frame {
243    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
244        if self.stream_id.0 == 0 {
245            write!(f, "Conn:")?;
246        } else {
247            write!(f, "#{}:", self.stream_id.0)?;
248        }
249
250        let name = match &self.frame_type {
251            FrameType::Data(_) => "Data",
252            FrameType::Headers(_) => "Headers",
253            FrameType::Priority => "Priority",
254            FrameType::RstStream => "RstStream",
255            FrameType::Settings(_) => "Settings",
256            FrameType::PushPromise => "PushPromise",
257            FrameType::Ping(_) => "Ping",
258            FrameType::GoAway => "GoAway",
259            FrameType::WindowUpdate => "WindowUpdate",
260            FrameType::Continuation(_) => "Continuation",
261            FrameType::Unknown(EncodedFrameType { ty, flags }) => {
262                return write!(f, "UnknownFrame({:#x}, {:#x})", ty, flags)
263            }
264        };
265        let mut s = f.debug_struct(name);
266
267        if self.reserved != 0 {
268            s.field("reserved", &self.reserved);
269        }
270        if self.len > 0 {
271            s.field("len", &self.len);
272        }
273
274        // now write flags with DisplayDebug
275        struct DisplayDebug<'a, D: fmt::Display>(&'a D);
276        impl<'a, D: fmt::Display> fmt::Debug for DisplayDebug<'a, D> {
277            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
278                fmt::Display::fmt(self.0, f)
279            }
280        }
281
282        // for all the variants with flags, add a flags field, of value
283        // &DisplayDebug(flags)
284        match &self.frame_type {
285            FrameType::Data(flags) => {
286                if !flags.is_empty() {
287                    s.field("flags", &DisplayDebug(flags));
288                }
289            }
290            FrameType::Headers(flags) => {
291                if !flags.is_empty() {
292                    s.field("flags", &DisplayDebug(flags));
293                }
294            }
295            FrameType::Settings(flags) => {
296                if !flags.is_empty() {
297                    s.field("flags", &DisplayDebug(flags));
298                }
299            }
300            FrameType::Ping(flags) => {
301                if !flags.is_empty() {
302                    s.field("flags", &DisplayDebug(flags));
303                }
304            }
305            FrameType::Continuation(flags) => {
306                if !flags.is_empty() {
307                    s.field("flags", &DisplayDebug(flags));
308                }
309            }
310            _ => {
311                // muffin
312            }
313        }
314
315        s.finish()
316    }
317}
318
319impl Frame {
320    /// Create a new frame with the given type and stream ID.
321    pub fn new(frame_type: FrameType, stream_id: StreamId) -> Self {
322        Self {
323            frame_type,
324            reserved: 0,
325            stream_id,
326            len: 0,
327        }
328    }
329
330    /// Set the frame's length.
331    pub fn with_len(mut self, len: u32) -> Self {
332        self.len = len;
333        self
334    }
335
336    /// Parse a frame from the given slice
337    pub fn parse(i: Roll) -> IResult<Roll, Self> {
338        let (i, (len, frame_type, (reserved, stream_id))) = tuple((
339            be_u24,
340            EncodedFrameType::parse,
341            parse_reserved_and_stream_id,
342        ))(i)?;
343
344        let frame = Frame {
345            frame_type: FrameType::decode(frame_type),
346            reserved,
347            stream_id,
348            len,
349        };
350        Ok((i, frame))
351    }
352
353    pub fn write_into(self, mut w: impl std::io::Write) -> std::io::Result<()> {
354        use byteorder::{BigEndian, WriteBytesExt};
355        w.write_u24::<BigEndian>(self.len as _)?;
356        let ft = self.frame_type.encode();
357        w.write_u8(ft.ty)?;
358        w.write_u8(ft.flags)?;
359        w.write_all(&pack_reserved_and_stream_id(self.reserved, self.stream_id))?;
360
361        Ok(())
362    }
363
364    /// Returns true if this  frame is an ack
365    pub fn is_ack(self) -> bool {
366        match self.frame_type {
367            FrameType::Data(_) => false,
368            FrameType::Headers(_) => false,
369            FrameType::Priority => false,
370            FrameType::RstStream => false,
371            FrameType::Settings(flags) => flags.contains(SettingsFlags::Ack),
372            FrameType::PushPromise => false,
373            FrameType::Ping(flags) => flags.contains(PingFlags::Ack),
374            FrameType::GoAway => false,
375            FrameType::WindowUpdate => false,
376            FrameType::Continuation(_) => false,
377            FrameType::Unknown(_) => false,
378        }
379    }
380}
381
382impl IntoPiece for Frame {
383    fn into_piece(self, mut scratch: &mut RollMut) -> std::io::Result<Piece> {
384        debug_assert_eq!(scratch.len(), 0);
385        self.write_into(&mut scratch)?;
386        Ok(scratch.take_all().into())
387    }
388}
389
390/// See https://httpwg.org/specs/rfc9113.html#FrameHeader - the first bit
391/// is reserved, and the rest is a 31-bit stream id
392pub fn parse_reserved_and_u31(i: Roll) -> IResult<Roll, (u8, u32)> {
393    fn reserved(i: (Roll, usize)) -> IResult<(Roll, usize), u8> {
394        nom::bits::streaming::take(1_usize)(i)
395    }
396
397    fn stream_id(i: (Roll, usize)) -> IResult<(Roll, usize), u32> {
398        nom::bits::streaming::take(31_usize)(i)
399    }
400
401    nom::bits::bits(tuple((reserved, stream_id)))(i)
402}
403
404fn parse_reserved_and_stream_id(i: Roll) -> IResult<Roll, (u8, StreamId)> {
405    parse_reserved_and_u31(i).map(|(i, (reserved, stream_id))| (i, (reserved, StreamId(stream_id))))
406}
407
408/// Pack `reserved` into the first bit of a u32 and write it out as big-endian
409fn pack_reserved_and_stream_id(reserved: u8, stream_id: StreamId) -> [u8; 4] {
410    let mut bytes = stream_id.0.to_be_bytes();
411    if reserved != 0 {
412        bytes[0] |= 0b1000_0000;
413    }
414    bytes
415}
416
417// cf. https://httpwg.org/specs/rfc9113.html#HEADERS
418#[derive(Debug)]
419pub struct PrioritySpec {
420    pub exclusive: bool,
421    pub stream_dependency: StreamId,
422    // 0-255 => 1-256
423    pub weight: u8,
424}
425
426impl PrioritySpec {
427    pub fn parse(i: Roll) -> IResult<Roll, Self> {
428        map(
429            tuple((parse_reserved_and_stream_id, be_u8)),
430            |((exclusive, stream_dependency), weight)| Self {
431                exclusive: exclusive != 0,
432                stream_dependency,
433                weight,
434            },
435        )(i)
436    }
437}
438
439#[derive(Clone, Copy)]
440pub struct ErrorCode(u32);
441
442impl ErrorCode {
443    /// Returns the underlying u32
444    pub fn as_repr(self) -> u32 {
445        self.0
446    }
447}
448
449impl fmt::Debug for ErrorCode {
450    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
451        match KnownErrorCode::from_repr(self.0) {
452            Some(e) => fmt::Debug::fmt(&e, f),
453            None => write!(f, "ErrorCode(0x{:02x})", self.0),
454        }
455    }
456}
457
458impl From<KnownErrorCode> for ErrorCode {
459    fn from(e: KnownErrorCode) -> Self {
460        Self(e as u32)
461    }
462}
463
464#[EnumRepr(type = "u32")]
465#[derive(Debug, Clone, Copy)]
466pub enum KnownErrorCode {
467    /// The associated condition is not a result of an error. For example, a
468    /// GOAWAY might include this code to indicate graceful shutdown of a
469    /// connection.
470    NoError = 0x00,
471
472    /// The endpoint detected an unspecific protocol error. This error is for
473    /// use when a more specific error code is not available.
474    ProtocolError = 0x01,
475
476    /// The endpoint encountered an unexpected internal error.
477    InternalError = 0x02,
478
479    /// The endpoint detected that its peer violated the flow-control protocol.
480    FlowControlError = 0x03,
481
482    /// The endpoint sent a SETTINGS frame but did not receive a response in a
483    /// timely manner. See Section 6.5.3 ("Settings Synchronization").
484    /// https://httpwg.org/specs/rfc9113.html#SettingsSync
485    SettingsTimeout = 0x04,
486
487    /// The endpoint received a frame after a stream was half-closed.
488    StreamClosed = 0x05,
489
490    /// The endpoint received a frame with an invalid size.
491    FrameSizeError = 0x06,
492
493    /// The endpoint refused the stream prior to performing any application
494    /// processing (see Section 8.7 for details).
495    /// https://httpwg.org/specs/rfc9113.html#Reliability
496    RefusedStream = 0x07,
497
498    /// The endpoint uses this error code to indicate that the stream is no
499    /// longer needed.
500    Cancel = 0x08,
501
502    /// The endpoint is unable to maintain the field section compression context
503    /// for the connection.
504    CompressionError = 0x09,
505
506    /// The connection established in response to a CONNECT request (Section
507    /// 8.5) was reset or abnormally closed.
508    /// https://httpwg.org/specs/rfc9113.html#CONNECT
509    ConnectError = 0x0a,
510
511    /// The endpoint detected that its peer is exhibiting a behavior that might
512    /// be generating excessive load.
513    EnhanceYourCalm = 0x0b,
514
515    /// The underlying transport has properties that do not meet minimum
516    /// security requirements (see Section 9.2).
517    /// https://httpwg.org/specs/rfc9113.html#TLSUsage
518    InadequateSecurity = 0x0c,
519
520    /// The endpoint requires that HTTP/1.1 be used instead of HTTP/2.
521    Http1_1Required = 0x0d,
522}
523
524impl TryFrom<ErrorCode> for KnownErrorCode {
525    type Error = ();
526
527    fn try_from(e: ErrorCode) -> Result<Self, Self::Error> {
528        KnownErrorCode::from_repr(e.0).ok_or(())
529    }
530}
531
532/// cf. https://httpwg.org/specs/rfc9113.html#SettingValues
533#[derive(Clone, Copy, Debug)]
534pub struct Settings {
535    /// This setting allows the sender to inform the remote endpoint of the
536    /// maximum size of the compression table used to decode field blocks, in
537    /// units of octets. The encoder can select any size equal to or less than
538    /// this value by using signaling specific to the compression format inside
539    /// a field block (see [COMPRESSION]). The initial value is 4,096 octets.
540    pub header_table_size: u32,
541
542    /// This setting can be used to enable or disable server push. A server MUST
543    /// NOT send a PUSH_PROMISE frame if it receives this parameter set to a
544    /// value of 0; see Section 8.4. A client that has both set this parameter
545    /// to 0 and had it acknowledged MUST treat the receipt of a PUSH_PROMISE
546    /// frame as a connection error (Section 5.4.1) of type PROTOCOL_ERROR.
547    ///
548    /// The initial value of SETTINGS_ENABLE_PUSH is 1. For a client, this value
549    /// indicates that it is willing to receive PUSH_PROMISE frames. For a
550    /// server, this initial value has no effect, and is equivalent to the value
551    /// 0. Any value other than 0 or 1 MUST be treated as a connection error
552    /// (Section 5.4.1) of type PROTOCOL_ERROR.
553    ///
554    /// A server MUST NOT explicitly set this value to 1. A server MAY choose to
555    /// omit this setting when it sends a SETTINGS frame, but if a server does
556    /// include a value, it MUST be 0. A client MUST treat receipt of a SETTINGS
557    /// frame with SETTINGS_ENABLE_PUSH set to 1 as a connection error (Section
558    /// 5.4.1) of type PROTOCOL_ERROR.
559    pub enable_push: bool,
560
561    /// This setting indicates the maximum number of concurrent streams that the
562    /// sender will allow. This limit is directional: it applies to the number of
563    /// streams that the sender permits the receiver to create. Initially, there is
564    /// no limit to this value. It is recommended that this value be no smaller than
565    /// 100, so as to not unnecessarily limit parallelism.
566    ///
567    /// A value of 0 for SETTINGS_MAX_CONCURRENT_STREAMS SHOULD NOT be treated as
568    /// special by endpoints. A zero value does prevent the creation of new streams;
569    /// however, this can also happen for any limit that is exhausted with active
570    /// streams. Servers SHOULD only set a zero value for short durations; if a
571    /// server does not wish to accept requests, closing the connection is more
572    /// appropriate.
573    pub max_concurrent_streams: u32,
574
575    /// This setting indicates the sender's initial window size (in units of
576    /// octets) for stream-level flow control. The initial value is 2^16-1
577    /// (65,535) octets.
578    ///
579    /// This setting affects the window size of all streams (see Section 6.9.2).
580    ///
581    /// Values above the maximum flow-control window size of 2^31-1 MUST be
582    /// treated as a connection error (Section 5.4.1) of type
583    /// FLOW_CONTROL_ERROR.
584    pub initial_window_size: u32,
585
586    /// This setting indicates the size of the largest frame payload that the
587    /// sender is willing to receive, in units of octets.
588    ///
589    /// The initial value is 2^14 (16,384) octets. The value advertised by an
590    /// endpoint MUST be between this initial value and the maximum allowed frame
591    /// size (2^24-1 or 16,777,215 octets), inclusive. Values outside this range MUST
592    /// be treated as a connection error (Section 5.4.1) of type PROTOCOL_ERROR.
593    pub max_frame_size: u32,
594
595    /// This advisory setting informs a peer of the maximum field section size
596    /// that the sender is prepared to accept, in units of octets. The value is
597    /// based on the uncompressed size of field lines, including the length of
598    /// the name and value in units of octets plus an overhead of 32 octets for
599    /// each field line.
600    ///
601    /// For any given request, a lower limit than what is advertised MAY be
602    /// enforced. The initial value of this setting is unlimited.
603    pub max_header_list_size: u32,
604}
605
606impl Default for Settings {
607    fn default() -> Self {
608        // cf. https://httpwg.org/specs/rfc9113.html#SettingValues
609        Self {
610            header_table_size: 4096,
611            enable_push: false,
612            max_concurrent_streams: 100,
613            initial_window_size: (1 << 16) - 1,
614            max_frame_size: (1 << 14),
615            max_header_list_size: 0,
616        }
617    }
618}
619
620#[EnumRepr(type = "u16")]
621#[derive(Debug, Clone, Copy)]
622enum SettingIdentifier {
623    HeaderTableSize = 0x01,
624    EnablePush = 0x02,
625    MaxConcurrentStreams = 0x03,
626    InitialWindowSize = 0x04,
627    MaxFrameSize = 0x05,
628    MaxHeaderListSize = 0x06,
629}
630
631impl Settings {
632    const MAX_INITIAL_WINDOW_SIZE: u32 = (1 << 31) - 1;
633    const MAX_FRAME_SIZE_ALLOWED_RANGE: RangeInclusive<u32> = (1 << 14)..=((1 << 24) - 1);
634
635    pub fn parse(mut i: Roll) -> IResult<Roll, Self> {
636        tracing::trace!("parsing settings frame, roll length: {}", i.len());
637        let mut settings = Self::default();
638
639        while !i.is_empty() {
640            let (rest, (id, value)) = tuple((be_u16, be_u32))(i)?;
641            tracing::trace!(%id, %value, "Got setting pair");
642            match SettingIdentifier::from_repr(id) {
643                None => {
644                    // ignore unknown settings
645                }
646                Some(id) => match id {
647                    SettingIdentifier::HeaderTableSize => {
648                        settings.header_table_size = value;
649                    }
650                    SettingIdentifier::EnablePush => {
651                        settings.enable_push = match value {
652                            0 => false,
653                            1 => true,
654                            _ => {
655                                return Err(nom::Err::Error(nom::error::Error::new(
656                                    rest,
657                                    nom::error::ErrorKind::Digit,
658                                )));
659                            }
660                        }
661                    }
662                    SettingIdentifier::MaxConcurrentStreams => {
663                        settings.max_concurrent_streams = value;
664                    }
665                    SettingIdentifier::InitialWindowSize => {
666                        if value > Self::MAX_INITIAL_WINDOW_SIZE {
667                            return Err(nom::Err::Error(nom::error::Error::new(
668                                rest,
669                                nom::error::ErrorKind::Digit,
670                            )));
671                        }
672                        settings.initial_window_size = value;
673                    }
674                    SettingIdentifier::MaxFrameSize => {
675                        if !Self::MAX_FRAME_SIZE_ALLOWED_RANGE.contains(&value) {
676                            return Err(nom::Err::Error(nom::error::Error::new(
677                                rest,
678                                // FIXME: this isn't really representative of
679                                // the quality error handling we're striving for
680                                nom::error::ErrorKind::Digit,
681                            )));
682                        }
683                        settings.max_frame_size = value;
684                    }
685                    SettingIdentifier::MaxHeaderListSize => {
686                        settings.max_header_list_size = value;
687                    }
688                },
689            }
690            i = rest;
691        }
692
693        Ok((i, settings))
694    }
695
696    /// Iterates over pairs of id/values
697    pub fn pairs(&self) -> impl Iterator<Item = (u16, u32)> {
698        [
699            (
700                SettingIdentifier::HeaderTableSize as u16,
701                self.header_table_size,
702            ),
703            (
704                // note: as a server, we're free to omit this, but it doesn't
705                // hurt to send 0 there I guess.
706                SettingIdentifier::EnablePush as u16,
707                self.enable_push as u32,
708            ),
709            (
710                SettingIdentifier::MaxConcurrentStreams as u16,
711                self.max_concurrent_streams,
712            ),
713            (
714                SettingIdentifier::InitialWindowSize as u16,
715                self.initial_window_size,
716            ),
717            (SettingIdentifier::MaxFrameSize as u16, self.max_frame_size),
718            (
719                SettingIdentifier::MaxHeaderListSize as u16,
720                self.max_header_list_size,
721            ),
722        ]
723        .into_iter()
724    }
725
726    /// Encode these settings into (u16, u32) pairs as specified in
727    /// https://httpwg.org/specs/rfc9113.html#SETTINGS
728    pub fn write_into(self, mut w: impl std::io::Write) -> std::io::Result<()> {
729        use byteorder::{BigEndian, WriteBytesExt};
730
731        for (id, value) in self.pairs() {
732            w.write_u16::<BigEndian>(id)?;
733            w.write_u32::<BigEndian>(value)?;
734        }
735
736        Ok(())
737    }
738}
739
740impl IntoPiece for Settings {
741    fn into_piece(self, mut scratch: &mut RollMut) -> std::io::Result<Piece> {
742        debug_assert_eq!(scratch.len(), 0);
743        self.write_into(&mut scratch)?;
744        Ok(scratch.take_all().into())
745    }
746}
747
748/// Payload for a GOAWAY frame
749pub struct GoAway {
750    pub last_stream_id: StreamId,
751    pub error_code: ErrorCode,
752    pub additional_debug_data: Piece,
753}
754
755impl IntoPiece for GoAway {
756    fn into_piece(self, scratch: &mut RollMut) -> std::io::Result<Piece> {
757        let roll = scratch
758            .put_to_roll(8 + self.additional_debug_data.len(), |mut slice| {
759                slice.write_u32::<BigEndian>(self.last_stream_id.0)?;
760                slice.write_u32::<BigEndian>(self.error_code.0)?;
761                slice.write_all(&self.additional_debug_data[..])?;
762
763                Ok(())
764            })
765            .unwrap();
766        Ok(roll.into())
767    }
768}
769
770impl GoAway {
771    pub fn parse(i: Roll) -> IResult<Roll, Self> {
772        let (rest, (last_stream_id, error_code)) = tuple((be_u32, be_u32))(i)?;
773
774        let i = Roll::empty();
775        Ok((
776            i,
777            Self {
778                last_stream_id: StreamId(last_stream_id),
779                error_code: ErrorCode(error_code),
780                additional_debug_data: rest.into(),
781            },
782        ))
783    }
784}
785
786/// Payload for a RST_STREAM frame
787pub struct RstStream {
788    pub error_code: ErrorCode,
789}
790
791impl IntoPiece for RstStream {
792    fn into_piece(self, scratch: &mut RollMut) -> std::io::Result<Piece> {
793        let roll = scratch
794            .put_to_roll(4, |mut slice| {
795                slice.write_u32::<BigEndian>(self.error_code.0)?;
796                Ok(())
797            })
798            .unwrap();
799        Ok(roll.into())
800    }
801}
802
803impl RstStream {
804    pub fn parse(i: Roll) -> IResult<Roll, Self> {
805        let (rest, error_code) = be_u32(i)?;
806        Ok((
807            rest,
808            Self {
809                error_code: ErrorCode(error_code),
810            },
811        ))
812    }
813}
814
815impl<T> IntoPiece for T
816where
817    Piece: From<T>,
818{
819    fn into_piece(self, _scratch: &mut RollMut) -> std::io::Result<Piece> {
820        Ok(self.into())
821    }
822}