loona_h2/
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};
9
10pub use enumflags2;
11use enumflags2::{bitflags, BitFlags};
12
13pub use nom;
14
15use nom::{
16    combinator::map,
17    number::streaming::{be_u24, be_u32, be_u8},
18    sequence::tuple,
19    IResult,
20};
21
22use buffet::{Piece, Roll, RollMut};
23
24/// This is sent by h2 clients after negotiating over ALPN, or when doing h2c.
25pub const PREFACE: &[u8] = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
26
27pub fn preface(i: Roll) -> IResult<Roll, ()> {
28    let (i, _) = nom::bytes::streaming::tag(PREFACE)(i)?;
29    Ok((i, ()))
30}
31
32pub trait IntoPiece {
33    fn into_piece(self, scratch: &mut RollMut) -> std::io::Result<Piece>;
34}
35
36/// See <https://httpwg.org/specs/rfc9113.html#FrameTypes>
37#[derive(Debug, Clone, Copy, PartialEq, Eq)]
38pub enum RawFrameType {
39    Data = 0x00,
40    Headers = 0x01,
41    Priority = 0x02,
42    RstStream = 0x03,
43    Settings = 0x04,
44    PushPromise = 0x05,
45    Ping = 0x06,
46    GoAway = 0x07,
47    WindowUpdate = 0x08,
48    Continuation = 0x09,
49}
50
51impl RawFrameType {
52    pub fn repr(&self) -> u8 {
53        *self as u8
54    }
55
56    pub fn from_repr(value: u8) -> Option<Self> {
57        match value {
58            0x00 => Some(RawFrameType::Data),
59            0x01 => Some(RawFrameType::Headers),
60            0x02 => Some(RawFrameType::Priority),
61            0x03 => Some(RawFrameType::RstStream),
62            0x04 => Some(RawFrameType::Settings),
63            0x05 => Some(RawFrameType::PushPromise),
64            0x06 => Some(RawFrameType::Ping),
65            0x07 => Some(RawFrameType::GoAway),
66            0x08 => Some(RawFrameType::WindowUpdate),
67            0x09 => Some(RawFrameType::Continuation),
68            _ => None,
69        }
70    }
71}
72
73#[test]
74fn test_raw_frame_type_roundtrip() {
75    let variants = [
76        RawFrameType::Data,
77        RawFrameType::Headers,
78        RawFrameType::Priority,
79        RawFrameType::RstStream,
80        RawFrameType::Settings,
81        RawFrameType::PushPromise,
82        RawFrameType::Ping,
83        RawFrameType::GoAway,
84        RawFrameType::WindowUpdate,
85        RawFrameType::Continuation,
86    ];
87
88    for &variant in &variants {
89        let repr = variant as u8;
90        let roundtripped = RawFrameType::from_repr(repr).unwrap();
91        assert_eq!(variant, roundtripped, "Failed to roundtrip {:?}", variant);
92    }
93
94    // Test that an invalid repr returns None
95    assert_eq!(RawFrameType::from_repr(0xFF), None);
96}
97
98/// Typed flags for various frame types
99#[derive(Debug, Clone, Copy)]
100pub enum FrameType {
101    Data(BitFlags<DataFlags>),
102    Headers(BitFlags<HeadersFlags>),
103    Priority,
104    RstStream,
105    Settings(BitFlags<SettingsFlags>),
106    PushPromise,
107    Ping(BitFlags<PingFlags>),
108    GoAway,
109    WindowUpdate,
110    Continuation(BitFlags<ContinuationFlags>),
111    Unknown(EncodedFrameType),
112}
113
114impl FrameType {
115    /// Turn this [FrameType] into a [Frame]
116    pub fn into_frame(self, stream_id: StreamId) -> Frame {
117        Frame {
118            frame_type: self,
119            len: 0,
120            reserved: 0,
121            stream_id,
122        }
123    }
124}
125
126/// See <https://httpwg.org/specs/rfc9113.html#DATA>
127#[bitflags]
128#[repr(u8)]
129#[derive(Copy, Clone, Debug, PartialEq, Eq)]
130pub enum DataFlags {
131    Padded = 0x08,
132    EndStream = 0x01,
133}
134
135/// See <https://httpwg.org/specs/rfc9113.html#rfc.section.6.2>
136#[bitflags]
137#[repr(u8)]
138#[derive(Copy, Clone, Debug, PartialEq, Eq)]
139pub enum HeadersFlags {
140    Priority = 0x20,
141    Padded = 0x08,
142    EndHeaders = 0x04,
143    EndStream = 0x01,
144}
145
146/// See <https://httpwg.org/specs/rfc9113.html#SETTINGS>
147#[bitflags]
148#[repr(u8)]
149#[derive(Copy, Clone, Debug, PartialEq, Eq)]
150pub enum SettingsFlags {
151    Ack = 0x01,
152}
153
154/// See <https://httpwg.org/specs/rfc9113.html#PING>
155#[bitflags]
156#[repr(u8)]
157#[derive(Copy, Clone, Debug, PartialEq, Eq)]
158pub enum PingFlags {
159    Ack = 0x01,
160}
161
162/// See <https://httpwg.org/specs/rfc9113.html#CONTINUATION>
163#[bitflags]
164#[repr(u8)]
165#[derive(Copy, Clone, Debug, PartialEq, Eq)]
166pub enum ContinuationFlags {
167    EndHeaders = 0x04,
168}
169
170#[derive(Debug, Clone, Copy)]
171pub struct EncodedFrameType {
172    pub ty: u8,
173    pub flags: u8,
174}
175
176impl EncodedFrameType {
177    fn parse(i: Roll) -> IResult<Roll, Self> {
178        let (i, (ty, flags)) = tuple((be_u8, be_u8))(i)?;
179        Ok((i, Self { ty, flags }))
180    }
181}
182
183impl From<(RawFrameType, u8)> for EncodedFrameType {
184    fn from((ty, flags): (RawFrameType, u8)) -> Self {
185        Self {
186            ty: ty.repr(),
187            flags,
188        }
189    }
190}
191
192impl FrameType {
193    pub(crate) fn encode(self) -> EncodedFrameType {
194        match self {
195            FrameType::Data(f) => (RawFrameType::Data, f.bits()).into(),
196            FrameType::Headers(f) => (RawFrameType::Headers, f.bits()).into(),
197            FrameType::Priority => (RawFrameType::Priority, 0).into(),
198            FrameType::RstStream => (RawFrameType::RstStream, 0).into(),
199            FrameType::Settings(f) => (RawFrameType::Settings, f.bits()).into(),
200            FrameType::PushPromise => (RawFrameType::PushPromise, 0).into(),
201            FrameType::Ping(f) => (RawFrameType::Ping, f.bits()).into(),
202            FrameType::GoAway => (RawFrameType::GoAway, 0).into(),
203            FrameType::WindowUpdate => (RawFrameType::WindowUpdate, 0).into(),
204            FrameType::Continuation(f) => (RawFrameType::Continuation, f.bits()).into(),
205            FrameType::Unknown(ft) => ft,
206        }
207    }
208
209    fn decode(ft: EncodedFrameType) -> Self {
210        match RawFrameType::from_repr(ft.ty) {
211            Some(ty) => match ty {
212                RawFrameType::Data => {
213                    FrameType::Data(BitFlags::<DataFlags>::from_bits_truncate(ft.flags))
214                }
215                RawFrameType::Headers => {
216                    FrameType::Headers(BitFlags::<HeadersFlags>::from_bits_truncate(ft.flags))
217                }
218                RawFrameType::Priority => FrameType::Priority,
219                RawFrameType::RstStream => FrameType::RstStream,
220                RawFrameType::Settings => {
221                    FrameType::Settings(BitFlags::<SettingsFlags>::from_bits_truncate(ft.flags))
222                }
223                RawFrameType::PushPromise => FrameType::PushPromise,
224                RawFrameType::Ping => {
225                    FrameType::Ping(BitFlags::<PingFlags>::from_bits_truncate(ft.flags))
226                }
227                RawFrameType::GoAway => FrameType::GoAway,
228                RawFrameType::WindowUpdate => FrameType::WindowUpdate,
229                RawFrameType::Continuation => FrameType::Continuation(
230                    BitFlags::<ContinuationFlags>::from_bits_truncate(ft.flags),
231                ),
232            },
233            None => FrameType::Unknown(ft),
234        }
235    }
236}
237
238#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
239pub struct StreamId(pub u32);
240
241impl StreamId {
242    /// Stream ID used for connection control frames
243    pub const CONNECTION: Self = Self(0);
244
245    /// Server-initiated streams have even IDs
246    pub fn is_server_initiated(&self) -> bool {
247        self.0 % 2 == 0
248    }
249}
250
251#[derive(Debug, thiserror::Error)]
252#[error("invalid stream id: {0}")]
253pub struct StreamIdOutOfRange(u32);
254
255impl TryFrom<u32> for StreamId {
256    type Error = StreamIdOutOfRange;
257
258    fn try_from(value: u32) -> Result<Self, Self::Error> {
259        if value & 0x8000_0000 != 0 {
260            Err(StreamIdOutOfRange(value))
261        } else {
262            Ok(Self(value))
263        }
264    }
265}
266
267impl fmt::Debug for StreamId {
268    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
269        fmt::Debug::fmt(&self.0, f)
270    }
271}
272
273impl fmt::Display for StreamId {
274    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
275        fmt::Display::fmt(&self.0, f)
276    }
277}
278
279/// See <https://httpwg.org/specs/rfc9113.html#FrameHeader>
280#[derive(Clone, Copy)]
281pub struct Frame {
282    pub frame_type: FrameType,
283    pub reserved: u8,
284    pub stream_id: StreamId,
285    pub len: u32,
286}
287
288impl Default for Frame {
289    fn default() -> Self {
290        Self {
291            frame_type: FrameType::Unknown(EncodedFrameType {
292                ty: 0xff,
293                flags: 0xff,
294            }),
295            reserved: 0,
296            stream_id: StreamId::CONNECTION,
297            len: 0,
298        }
299    }
300}
301
302impl fmt::Debug for Frame {
303    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
304        if self.stream_id.0 == 0 {
305            write!(f, "Conn:")?;
306        } else {
307            write!(f, "#{}:", self.stream_id.0)?;
308        }
309
310        let name = match &self.frame_type {
311            FrameType::Data(_) => "Data",
312            FrameType::Headers(_) => "Headers",
313            FrameType::Priority => "Priority",
314            FrameType::RstStream => "RstStream",
315            FrameType::Settings(_) => "Settings",
316            FrameType::PushPromise => "PushPromise",
317            FrameType::Ping(_) => "Ping",
318            FrameType::GoAway => "GoAway",
319            FrameType::WindowUpdate => "WindowUpdate",
320            FrameType::Continuation(_) => "Continuation",
321            FrameType::Unknown(EncodedFrameType { ty, flags }) => {
322                return write!(f, "UnknownFrame({:#x}, {:#x}, len={})", ty, flags, self.len)
323            }
324        };
325        let mut s = f.debug_struct(name);
326
327        if self.reserved != 0 {
328            s.field("reserved", &self.reserved);
329        }
330        if self.len > 0 {
331            s.field("len", &self.len);
332        }
333
334        // now write flags with DisplayDebug
335        struct DisplayDebug<'a, D: fmt::Display>(&'a D);
336        impl<'a, D: fmt::Display> fmt::Debug for DisplayDebug<'a, D> {
337            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
338                fmt::Display::fmt(self.0, f)
339            }
340        }
341
342        // for all the variants with flags, add a flags field, of value
343        // &DisplayDebug(flags)
344        match &self.frame_type {
345            FrameType::Data(flags) => {
346                if !flags.is_empty() {
347                    s.field("flags", &DisplayDebug(flags));
348                }
349            }
350            FrameType::Headers(flags) => {
351                if !flags.is_empty() {
352                    s.field("flags", &DisplayDebug(flags));
353                }
354            }
355            FrameType::Settings(flags) => {
356                if !flags.is_empty() {
357                    s.field("flags", &DisplayDebug(flags));
358                }
359            }
360            FrameType::Ping(flags) => {
361                if !flags.is_empty() {
362                    s.field("flags", &DisplayDebug(flags));
363                }
364            }
365            FrameType::Continuation(flags) => {
366                if !flags.is_empty() {
367                    s.field("flags", &DisplayDebug(flags));
368                }
369            }
370            _ => {
371                // muffin
372            }
373        }
374
375        s.finish()
376    }
377}
378
379impl Frame {
380    /// Create a new frame with the given type and stream ID.
381    pub fn new(frame_type: FrameType, stream_id: StreamId) -> Self {
382        Self {
383            frame_type,
384            reserved: 0,
385            stream_id,
386            len: 0,
387        }
388    }
389
390    /// Set the frame's length.
391    pub fn with_len(mut self, len: u32) -> Self {
392        self.len = len;
393        self
394    }
395
396    /// Parse a frame from the given slice
397    pub fn parse(i: Roll) -> IResult<Roll, Self> {
398        let (i, (len, frame_type, (reserved, stream_id))) = tuple((
399            be_u24,
400            EncodedFrameType::parse,
401            parse_reserved_and_stream_id,
402        ))(i)?;
403
404        let frame = Frame {
405            frame_type: FrameType::decode(frame_type),
406            reserved,
407            stream_id,
408            len,
409        };
410        Ok((i, frame))
411    }
412
413    pub fn write_into(self, mut w: impl std::io::Write) -> std::io::Result<()> {
414        use byteorder::{BigEndian, WriteBytesExt};
415        w.write_u24::<BigEndian>(self.len as _)?;
416        let ft = self.frame_type.encode();
417        w.write_u8(ft.ty)?;
418        w.write_u8(ft.flags)?;
419        w.write_all(&pack_reserved_and_stream_id(self.reserved, self.stream_id))?;
420
421        Ok(())
422    }
423
424    /// Returns true if this frame is an ack
425    pub fn is_ack(&self) -> bool {
426        match self.frame_type {
427            FrameType::Settings(flags) => flags.contains(SettingsFlags::Ack),
428            FrameType::Ping(flags) => flags.contains(PingFlags::Ack),
429            _ => false,
430        }
431    }
432
433    /// Returns true if this frame has `EndHeaders` set
434    pub fn is_end_headers(&self) -> bool {
435        match self.frame_type {
436            FrameType::Headers(flags) => flags.contains(HeadersFlags::EndHeaders),
437            FrameType::Continuation(flags) => flags.contains(ContinuationFlags::EndHeaders),
438            _ => false,
439        }
440    }
441
442    /// Returns true if this frame has `EndStream` set
443    pub fn is_end_stream(&self) -> bool {
444        match self.frame_type {
445            FrameType::Data(flags) => flags.contains(DataFlags::EndStream),
446            FrameType::Headers(flags) => flags.contains(HeadersFlags::EndStream),
447            _ => false,
448        }
449    }
450}
451
452impl IntoPiece for Frame {
453    /// FIXME: not support happy about this returning an `std::io::Error`
454    /// really the only way this can fail is if we cannot allocate memory,
455    /// because we're never actually doing any I/O here.
456    fn into_piece(self, scratch: &mut RollMut) -> std::io::Result<Piece> {
457        debug_assert_eq!(scratch.len(), 0);
458        self.write_into(&mut *scratch)?;
459        Ok(scratch.take_all().into())
460    }
461}
462
463/// See <https://httpwg.org/specs/rfc9113.html#FrameHeader> - the first bit
464/// is reserved, and the rest is a 31-bit stream id
465pub fn parse_bit_and_u31(i: Roll) -> IResult<Roll, (u8, u32)> {
466    // first, parse a u32:
467    let (i, x) = be_u32(i)?;
468
469    let bit = (x >> 31) as u8;
470    let val = x & 0x7FFF_FFFF;
471
472    Ok((i, (bit, val)))
473}
474
475fn parse_reserved_and_stream_id(i: Roll) -> IResult<Roll, (u8, StreamId)> {
476    parse_bit_and_u31(i).map(|(i, (reserved, stream_id))| (i, (reserved, StreamId(stream_id))))
477}
478
479/// Pack a bit and a u31 into a 4-byte array (big-endian)
480pub fn pack_bit_and_u31(bit: u8, val: u32) -> [u8; 4] {
481    // assert val is in range
482    assert_eq!(val & 0x7FFF_FFFF, val, "val is too large: {val:x}");
483
484    // assert bit is in range
485    assert_eq!(bit & 0x1, bit, "bit should be 0 or 1: {bit:x}");
486
487    // pack
488    let mut bytes = val.to_be_bytes();
489    if bit != 0 {
490        bytes[0] |= 0x80;
491    }
492
493    bytes
494}
495
496pub fn pack_reserved_and_stream_id(reserved: u8, stream_id: StreamId) -> [u8; 4] {
497    pack_bit_and_u31(reserved, stream_id.0)
498}
499
500#[test]
501fn test_pack_and_parse_bit_and_u31() {
502    buffet::bufpool::initialize_allocator().unwrap();
503
504    // Test round-tripping through parse_bit_and_u31 and pack_bit_and_u31
505    let test_cases = [
506        (0, 0),
507        (1, 0),
508        (0, 1),
509        (1, 1),
510        (0, 0x7FFF_FFFF),
511        (1, 0x7FFF_FFFF),
512    ];
513
514    let mut roll = RollMut::alloc().unwrap();
515    for &(bit, number) in &test_cases {
516        let packed = pack_bit_and_u31(bit, number);
517        roll.reserve_at_least(4).unwrap();
518        roll.put(&packed[..]).unwrap();
519        let (_, (parsed_bit, parsed_number)) = parse_bit_and_u31(roll.take_all()).unwrap();
520        assert_eq!(dbg!(bit), dbg!(parsed_bit));
521        assert_eq!(dbg!(number), dbg!(parsed_number));
522    }
523}
524
525#[test]
526#[should_panic(expected = "bit should be 0 or 1: 2")]
527fn test_pack_bit_and_u31_panic_not_a_bit() {
528    pack_bit_and_u31(2, 0);
529}
530
531#[test]
532#[should_panic(expected = "val is too large: 80000000")]
533fn test_pack_bit_and_u31_panic_val_too_large() {
534    pack_bit_and_u31(0, 1 << 31);
535}
536
537// cf. <https://httpwg.org/specs/rfc9113.html#HEADERS>
538#[derive(Debug)]
539pub struct PrioritySpec {
540    pub exclusive: bool,
541    pub stream_dependency: StreamId,
542    // 0-255 => 1-256
543    pub weight: u8,
544}
545
546impl PrioritySpec {
547    pub fn parse(i: Roll) -> IResult<Roll, Self> {
548        map(
549            tuple((parse_reserved_and_stream_id, be_u8)),
550            |((exclusive, stream_dependency), weight)| Self {
551                exclusive: exclusive != 0,
552                stream_dependency,
553                weight,
554            },
555        )(i)
556    }
557}
558
559impl IntoPiece for PrioritySpec {
560    fn into_piece(self, scratch: &mut RollMut) -> std::io::Result<Piece> {
561        let roll = scratch
562            .put_to_roll(5, |mut slice| {
563                let reserved_and_stream_id =
564                    pack_reserved_and_stream_id(self.exclusive as u8, self.stream_dependency);
565                slice.write_all(&reserved_and_stream_id)?;
566                slice.write_u8(self.weight)?;
567                Ok(())
568            })
569            .unwrap();
570        Ok(roll.into())
571    }
572}
573
574#[derive(Clone, Copy)]
575pub struct ErrorCode(pub u32);
576
577impl ErrorCode {
578    /// Returns the underlying u32
579    pub fn as_repr(self) -> u32 {
580        self.0
581    }
582}
583
584impl fmt::Debug for ErrorCode {
585    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
586        match KnownErrorCode::from_repr(self.0) {
587            Some(e) => fmt::Debug::fmt(&e, f),
588            None => write!(f, "ErrorCode(0x{:02x})", self.0),
589        }
590    }
591}
592
593impl From<KnownErrorCode> for ErrorCode {
594    fn from(e: KnownErrorCode) -> Self {
595        Self(e as u32)
596    }
597}
598
599#[derive(Debug, Clone, Copy, PartialEq, Eq)]
600pub enum KnownErrorCode {
601    /// The associated condition is not a result of an error. For example, a
602    /// GOAWAY might include this code to indicate graceful shutdown of a
603    /// connection.
604    NoError = 0x00,
605
606    /// The endpoint detected an unspecific protocol error. This error is for
607    /// use when a more specific error code is not available.
608    ProtocolError = 0x01,
609
610    /// The endpoint encountered an unexpected internal error.
611    InternalError = 0x02,
612
613    /// The endpoint detected that its peer violated the flow-control protocol.
614    FlowControlError = 0x03,
615
616    /// The endpoint sent a SETTINGS frame but did not receive a response in a
617    /// timely manner. See Section 6.5.3 ("Settings Synchronization").
618    /// <https://httpwg.org/specs/rfc9113.html#SettingsSync>
619    SettingsTimeout = 0x04,
620
621    /// The endpoint received a frame after a stream was half-closed.
622    StreamClosed = 0x05,
623
624    /// The endpoint received a frame with an invalid size.
625    FrameSizeError = 0x06,
626
627    /// The endpoint refused the stream prior to performing any application
628    /// processing (see Section 8.7 for details).
629    /// <https://httpwg.org/specs/rfc9113.html#Reliability>
630    RefusedStream = 0x07,
631
632    /// The endpoint uses this error code to indicate that the stream is no
633    /// longer needed.
634    Cancel = 0x08,
635
636    /// The endpoint is unable to maintain the field section compression context
637    /// for the connection.
638    CompressionError = 0x09,
639
640    /// The connection established in response to a CONNECT request (Section
641    /// 8.5) was reset or abnormally closed.
642    /// <https://httpwg.org/specs/rfc9113.html#CONNECT>
643    ConnectError = 0x0a,
644
645    /// The endpoint detected that its peer is exhibiting a behavior that might
646    /// be generating excessive load.
647    EnhanceYourCalm = 0x0b,
648
649    /// The underlying transport has properties that do not meet minimum
650    /// security requirements (see Section 9.2).
651    /// <https://httpwg.org/specs/rfc9113.html#TLSUsage>
652    InadequateSecurity = 0x0c,
653
654    /// The endpoint requires that HTTP/1.1 be used instead of HTTP/2.
655    Http1_1Required = 0x0d,
656}
657
658impl KnownErrorCode {
659    pub fn from_repr(value: u32) -> Option<Self> {
660        match value {
661            0x00 => Some(KnownErrorCode::NoError),
662            0x01 => Some(KnownErrorCode::ProtocolError),
663            0x02 => Some(KnownErrorCode::InternalError),
664            0x03 => Some(KnownErrorCode::FlowControlError),
665            0x04 => Some(KnownErrorCode::SettingsTimeout),
666            0x05 => Some(KnownErrorCode::StreamClosed),
667            0x06 => Some(KnownErrorCode::FrameSizeError),
668            0x07 => Some(KnownErrorCode::RefusedStream),
669            0x08 => Some(KnownErrorCode::Cancel),
670            0x09 => Some(KnownErrorCode::CompressionError),
671            0x0a => Some(KnownErrorCode::ConnectError),
672            0x0b => Some(KnownErrorCode::EnhanceYourCalm),
673            0x0c => Some(KnownErrorCode::InadequateSecurity),
674            0x0d => Some(KnownErrorCode::Http1_1Required),
675            _ => None,
676        }
677    }
678
679    pub fn repr(&self) -> u32 {
680        *self as u32
681    }
682}
683
684#[test]
685fn test_known_error_code_roundtrip() {
686    let error_codes = [
687        KnownErrorCode::NoError,
688        KnownErrorCode::ProtocolError,
689        KnownErrorCode::InternalError,
690        KnownErrorCode::FlowControlError,
691        KnownErrorCode::SettingsTimeout,
692        KnownErrorCode::StreamClosed,
693        KnownErrorCode::FrameSizeError,
694        KnownErrorCode::RefusedStream,
695        KnownErrorCode::Cancel,
696        KnownErrorCode::CompressionError,
697        KnownErrorCode::ConnectError,
698        KnownErrorCode::EnhanceYourCalm,
699        KnownErrorCode::InadequateSecurity,
700        KnownErrorCode::Http1_1Required,
701    ];
702
703    for &original in &error_codes {
704        let repr = original.repr();
705        let roundtripped = KnownErrorCode::from_repr(repr).unwrap();
706        assert_eq!(original, roundtripped, "Failed to roundtrip {:?}", original);
707    }
708
709    // Test that an invalid repr returns None
710    assert_eq!(KnownErrorCode::from_repr(0xFF), None);
711}
712
713impl TryFrom<ErrorCode> for KnownErrorCode {
714    type Error = ();
715
716    fn try_from(e: ErrorCode) -> Result<Self, Self::Error> {
717        KnownErrorCode::from_repr(e.0).ok_or(())
718    }
719}
720
721/// cf. <https://httpwg.org/specs/rfc9113.html#SettingValues>
722#[derive(Clone, Copy, Debug)]
723pub struct Settings {
724    /// This setting allows the sender to inform the remote endpoint of the
725    /// maximum size of the compression table used to decode field blocks, in
726    /// units of octets. The encoder can select any size equal to or less than
727    /// this value by using signaling specific to the compression format inside
728    /// a field block (see COMPRESSION). The initial value is 4,096 octets.
729    pub header_table_size: u32,
730
731    /// This setting can be used to enable or disable server push. A server MUST
732    /// NOT send a PUSH_PROMISE frame if it receives this parameter set to a
733    /// value of 0; see Section 8.4. A client that has both set this parameter
734    /// to 0 and had it acknowledged MUST treat the receipt of a PUSH_PROMISE
735    /// frame as a connection error (Section 5.4.1) of type PROTOCOL_ERROR.
736    ///
737    /// The initial value of SETTINGS_ENABLE_PUSH is 1. For a client, this value
738    /// indicates that it is willing to receive PUSH_PROMISE frames. For a
739    /// server, this initial value has no effect, and is equivalent to the value
740    /// 0. Any value other than 0 or 1 MUST be treated as a connection error
741    /// (Section 5.4.1) of type PROTOCOL_ERROR.
742    ///
743    /// A server MUST NOT explicitly set this value to 1. A server MAY choose to
744    /// omit this setting when it sends a SETTINGS frame, but if a server does
745    /// include a value, it MUST be 0. A client MUST treat receipt of a SETTINGS
746    /// frame with SETTINGS_ENABLE_PUSH set to 1 as a connection error (Section
747    /// 5.4.1) of type PROTOCOL_ERROR.
748    pub enable_push: bool,
749
750    /// This setting indicates the maximum number of concurrent streams that the
751    /// sender will allow. This limit is directional: it applies to the number
752    /// of streams that the sender permits the receiver to create.
753    /// Initially, there is no limit to this value. It is recommended that
754    /// this value be no smaller than 100, so as to not unnecessarily limit
755    /// parallelism.
756    ///
757    /// A value of 0 for SETTINGS_MAX_CONCURRENT_STREAMS SHOULD NOT be treated
758    /// as special by endpoints. A zero value does prevent the creation of
759    /// new streams; however, this can also happen for any limit that is
760    /// exhausted with active streams. Servers SHOULD only set a zero value
761    /// for short durations; if a server does not wish to accept requests,
762    /// closing the connection is more appropriate.
763    pub max_concurrent_streams: Option<u32>,
764
765    /// This setting indicates the sender's initial window size (in units of
766    /// octets) for stream-level flow control. The initial value is 2^16-1
767    /// (65,535) octets.
768    ///
769    /// This setting affects the window size of all streams (see Section 6.9.2).
770    ///
771    /// Values above the maximum flow-control window size of 2^31-1 MUST be
772    /// treated as a connection error (Section 5.4.1) of type
773    /// FLOW_CONTROL_ERROR.
774    pub initial_window_size: u32,
775
776    /// This setting indicates the size of the largest frame payload that the
777    /// sender is willing to receive, in units of octets.
778    ///
779    /// The initial value is 2^14 (16,384) octets. The value advertised by an
780    /// endpoint MUST be between this initial value and the maximum allowed
781    /// frame size (2^24-1 or 16,777,215 octets), inclusive. Values outside
782    /// this range MUST be treated as a connection error (Section 5.4.1) of
783    /// type PROTOCOL_ERROR.
784    pub max_frame_size: u32,
785
786    /// This advisory setting informs a peer of the maximum field section size
787    /// that the sender is prepared to accept, in units of octets. The value is
788    /// based on the uncompressed size of field lines, including the length of
789    /// the name and value in units of octets plus an overhead of 32 octets for
790    /// each field line.
791    ///
792    /// For any given request, a lower limit than what is advertised MAY be
793    /// enforced. The initial value of this setting is unlimited.
794    pub max_header_list_size: u32,
795}
796
797impl Default for Settings {
798    fn default() -> Self {
799        // cf. <https://httpwg.org/specs/rfc9113.html#SettingValues>
800        Self {
801            header_table_size: 4096,
802            enable_push: false,
803            max_concurrent_streams: Some(100),
804            initial_window_size: (1 << 16) - 1,
805            max_frame_size: (1 << 14),
806            max_header_list_size: 0,
807        }
808    }
809}
810
811impl Settings {
812    /// Apply a setting to the current settings, returning an error if the
813    /// setting is invalid.
814    pub fn apply(&mut self, code: Setting, value: u32) -> Result<(), SettingsError> {
815        match code {
816            Setting::HeaderTableSize => {
817                self.header_table_size = value;
818            }
819            Setting::EnablePush => match value {
820                0 => self.enable_push = false,
821                1 => self.enable_push = true,
822                _ => return Err(SettingsError::InvalidEnablePushValue { actual: value }),
823            },
824            Setting::MaxConcurrentStreams => {
825                self.max_concurrent_streams = Some(value);
826            }
827            Setting::InitialWindowSize => {
828                if value > Self::MAX_INITIAL_WINDOW_SIZE {
829                    return Err(SettingsError::InitialWindowSizeTooLarge { actual: value });
830                }
831                self.initial_window_size = value;
832            }
833            Setting::MaxFrameSize => {
834                if !Self::MAX_FRAME_SIZE_ALLOWED_RANGE.contains(&value) {
835                    return Err(SettingsError::SettingsMaxFrameSizeInvalid { actual: value });
836                }
837                self.max_frame_size = value;
838            }
839            Setting::MaxHeaderListSize => {
840                self.max_header_list_size = value;
841            }
842        }
843
844        Ok(())
845    }
846}
847
848#[derive(thiserror::Error, Debug)]
849#[non_exhaustive]
850pub enum SettingsError {
851    #[error("ENABLE_PUSH setting is supposed to be either 0 or 1, got {actual}")]
852    InvalidEnablePushValue { actual: u32 },
853
854    #[error("bad INITIAL_WINDOW_SIZE value {actual}, should be than or equal to 2^31-1")]
855    InitialWindowSizeTooLarge { actual: u32 },
856
857    #[error(
858        "bad SETTINGS_MAX_FRAME_SIZE value {actual}, should be between 2^14 and 2^24-1 inclusive"
859    )]
860    SettingsMaxFrameSizeInvalid { actual: u32 },
861}
862
863#[derive(Debug, Clone, Copy, PartialEq, Eq)]
864pub enum Setting {
865    HeaderTableSize = 0x01,
866    EnablePush = 0x02,
867    MaxConcurrentStreams = 0x03,
868    InitialWindowSize = 0x04,
869    MaxFrameSize = 0x05,
870    MaxHeaderListSize = 0x06,
871}
872
873impl Setting {
874    pub fn repr(&self) -> u16 {
875        *self as u16
876    }
877
878    pub fn from_repr(value: u16) -> Option<Self> {
879        match value {
880            0x01 => Some(Setting::HeaderTableSize),
881            0x02 => Some(Setting::EnablePush),
882            0x03 => Some(Setting::MaxConcurrentStreams),
883            0x04 => Some(Setting::InitialWindowSize),
884            0x05 => Some(Setting::MaxFrameSize),
885            0x06 => Some(Setting::MaxHeaderListSize),
886            _ => None,
887        }
888    }
889}
890
891#[test]
892fn test_setting_roundtrip() {
893    let settings = [
894        Setting::HeaderTableSize,
895        Setting::EnablePush,
896        Setting::MaxConcurrentStreams,
897        Setting::InitialWindowSize,
898        Setting::MaxFrameSize,
899        Setting::MaxHeaderListSize,
900    ];
901
902    for &setting in &settings {
903        let repr = setting.repr();
904        let roundtripped = Setting::from_repr(repr).unwrap();
905        assert_eq!(setting, roundtripped, "Failed to roundtrip {:?}", setting);
906    }
907
908    // Test that an invalid repr returns None
909    assert_eq!(Setting::from_repr(0x07), None);
910}
911
912impl Settings {
913    pub const MAX_INITIAL_WINDOW_SIZE: u32 = (1 << 31) - 1;
914    pub const MAX_FRAME_SIZE_ALLOWED_RANGE: RangeInclusive<u32> = (1 << 14)..=((1 << 24) - 1);
915
916    /// Parse a series of settings from a buffer, calls the callback for each
917    /// known setting found.
918    ///
919    /// Unknown settings are ignored.
920    ///
921    /// Panics if the buf isn't a multiple of 6 bytes.
922    pub fn parse<E>(
923        buf: &[u8],
924        mut callback: impl FnMut(Setting, u32) -> Result<(), E>,
925    ) -> Result<(), E> {
926        assert!(
927            buf.len() % 6 == 0,
928            "buffer length must be a multiple of 6 bytes"
929        );
930
931        for chunk in buf.chunks_exact(6) {
932            let id = u16::from_be_bytes([chunk[0], chunk[1]]);
933            let value = u32::from_be_bytes([chunk[2], chunk[3], chunk[4], chunk[5]]);
934            match Setting::from_repr(id) {
935                None => {}
936                Some(id) => {
937                    callback(id, value)?;
938                }
939            }
940        }
941
942        Ok(())
943    }
944}
945
946pub struct SettingPairs<'a>(pub &'a [(Setting, u32)]);
947
948impl<'a> From<&'a [(Setting, u32)]> for SettingPairs<'a> {
949    fn from(value: &'a [(Setting, u32)]) -> Self {
950        Self(value)
951    }
952}
953
954impl<const N: usize> From<&'static [(Setting, u32); N]> for SettingPairs<'static> {
955    fn from(value: &'static [(Setting, u32); N]) -> Self {
956        Self(value)
957    }
958}
959
960impl<'a> IntoPiece for SettingPairs<'a> {
961    fn into_piece(self, scratch: &mut RollMut) -> std::io::Result<Piece> {
962        let roll = scratch
963            .put_to_roll(self.0.len() * 6, |mut slice| {
964                for (id, value) in self.0.iter() {
965                    slice.write_u16::<BigEndian>(*id as u16)?;
966                    slice.write_u32::<BigEndian>(*value)?;
967                }
968                Ok(())
969            })
970            .unwrap();
971        Ok(roll.into())
972    }
973}
974
975/// Payload for a GOAWAY frame
976pub struct GoAway {
977    pub last_stream_id: StreamId,
978    pub error_code: ErrorCode,
979    pub additional_debug_data: Piece,
980}
981
982impl IntoPiece for GoAway {
983    fn into_piece(self, scratch: &mut RollMut) -> std::io::Result<Piece> {
984        let roll = scratch
985            .put_to_roll(8 + self.additional_debug_data.len(), |mut slice| {
986                slice.write_u32::<BigEndian>(self.last_stream_id.0)?;
987                slice.write_u32::<BigEndian>(self.error_code.0)?;
988                slice.write_all(&self.additional_debug_data[..])?;
989
990                Ok(())
991            })
992            .unwrap();
993        Ok(roll.into())
994    }
995}
996
997impl GoAway {
998    pub fn parse(i: Roll) -> IResult<Roll, Self> {
999        let (rest, (last_stream_id, error_code)) = tuple((be_u32, be_u32))(i)?;
1000
1001        let i = Roll::empty();
1002        Ok((
1003            i,
1004            Self {
1005                last_stream_id: StreamId(last_stream_id),
1006                error_code: ErrorCode(error_code),
1007                additional_debug_data: rest.into(),
1008            },
1009        ))
1010    }
1011}
1012
1013/// Payload for a RST_STREAM frame
1014pub struct RstStream {
1015    pub error_code: ErrorCode,
1016}
1017
1018impl IntoPiece for RstStream {
1019    fn into_piece(self, scratch: &mut RollMut) -> std::io::Result<Piece> {
1020        let roll = scratch
1021            .put_to_roll(4, |mut slice| {
1022                slice.write_u32::<BigEndian>(self.error_code.0)?;
1023                Ok(())
1024            })
1025            .unwrap();
1026        Ok(roll.into())
1027    }
1028}
1029
1030impl RstStream {
1031    pub fn parse(i: Roll) -> IResult<Roll, Self> {
1032        let (rest, error_code) = be_u32(i)?;
1033        Ok((
1034            rest,
1035            Self {
1036                error_code: ErrorCode(error_code),
1037            },
1038        ))
1039    }
1040}
1041
1042/// Payload for a WINDOW_UPDATE frame
1043#[derive(Debug, Clone, Copy)]
1044pub struct WindowUpdate {
1045    pub reserved: u8,
1046    pub increment: u32,
1047}
1048
1049impl IntoPiece for WindowUpdate {
1050    fn into_piece(self, scratch: &mut RollMut) -> std::io::Result<Piece> {
1051        let roll = scratch
1052            .put_to_roll(4, |mut slice| {
1053                let packed = pack_bit_and_u31(self.reserved, self.increment);
1054                slice.write_all(&packed)?;
1055                Ok(())
1056            })
1057            .unwrap();
1058        Ok(roll.into())
1059    }
1060}
1061
1062impl WindowUpdate {
1063    pub fn parse(i: Roll) -> IResult<Roll, Self> {
1064        let (rest, (reserved, increment)) = parse_bit_and_u31(i)?;
1065        Ok((
1066            rest,
1067            Self {
1068                reserved,
1069                increment,
1070            },
1071        ))
1072    }
1073}
1074
1075impl<T> IntoPiece for T
1076where
1077    Piece: From<T>,
1078{
1079    fn into_piece(self, _scratch: &mut RollMut) -> std::io::Result<Piece> {
1080        Ok(self.into())
1081    }
1082}