Skip to main content

quiche/h3/
frame.rs

1// Copyright (C) 2019, Cloudflare, Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8//     * Redistributions of source code must retain the above copyright notice,
9//       this list of conditions and the following disclaimer.
10//
11//     * Redistributions in binary form must reproduce the above copyright
12//       notice, this list of conditions and the following disclaimer in the
13//       documentation and/or other materials provided with the distribution.
14//
15// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
16// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
19// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27use super::Result;
28
29#[cfg(feature = "qlog")]
30use qlog::events::http3::Http3Frame;
31
32pub const DATA_FRAME_TYPE_ID: u64 = 0x0;
33pub const HEADERS_FRAME_TYPE_ID: u64 = 0x1;
34pub const CANCEL_PUSH_FRAME_TYPE_ID: u64 = 0x3;
35pub const SETTINGS_FRAME_TYPE_ID: u64 = 0x4;
36pub const PUSH_PROMISE_FRAME_TYPE_ID: u64 = 0x5;
37pub const GOAWAY_FRAME_TYPE_ID: u64 = 0x7;
38pub const MAX_PUSH_FRAME_TYPE_ID: u64 = 0xD;
39pub const PRIORITY_UPDATE_FRAME_REQUEST_TYPE_ID: u64 = 0xF0700;
40pub const PRIORITY_UPDATE_FRAME_PUSH_TYPE_ID: u64 = 0xF0701;
41
42pub const SETTINGS_QPACK_MAX_TABLE_CAPACITY: u64 = 0x1;
43pub const SETTINGS_MAX_FIELD_SECTION_SIZE: u64 = 0x6;
44pub const SETTINGS_QPACK_BLOCKED_STREAMS: u64 = 0x7;
45pub const SETTINGS_ENABLE_CONNECT_PROTOCOL: u64 = 0x8;
46pub const SETTINGS_H3_DATAGRAM_00: u64 = 0x276;
47pub const SETTINGS_H3_DATAGRAM: u64 = 0x33;
48
49// Permit between 16 maximally-encoded and 128 minimally-encoded SETTINGS.
50pub(crate) const MAX_SETTINGS_PAYLOAD_SIZE: usize = 256;
51
52#[derive(Clone, PartialEq, Eq)]
53pub enum Frame {
54    Data {
55        payload: Vec<u8>,
56    },
57
58    Headers {
59        header_block: Vec<u8>,
60    },
61
62    CancelPush {
63        push_id: u64,
64    },
65
66    Settings {
67        max_field_section_size: Option<u64>,
68        qpack_max_table_capacity: Option<u64>,
69        qpack_blocked_streams: Option<u64>,
70        connect_protocol_enabled: Option<u64>,
71        h3_datagram: Option<u64>,
72        grease: Option<(u64, u64)>,
73        additional_settings: Option<Vec<(u64, u64)>>,
74        raw: Option<Vec<(u64, u64)>>,
75    },
76
77    PushPromise {
78        push_id: u64,
79        header_block: Vec<u8>,
80    },
81
82    GoAway {
83        id: u64,
84    },
85
86    MaxPushId {
87        push_id: u64,
88    },
89
90    PriorityUpdateRequest {
91        prioritized_element_id: u64,
92        priority_field_value: Vec<u8>,
93    },
94
95    PriorityUpdatePush {
96        prioritized_element_id: u64,
97        priority_field_value: Vec<u8>,
98    },
99
100    Unknown {
101        raw_type: u64,
102        payload: Vec<u8>,
103    },
104}
105
106impl Frame {
107    pub fn from_bytes(
108        frame_type: u64, payload_length: u64, bytes: &[u8],
109    ) -> Result<Frame> {
110        let mut b = octets::Octets::with_slice(bytes);
111
112        // TODO: handling of 0-length frames
113        let frame = match frame_type {
114            DATA_FRAME_TYPE_ID => Frame::Data {
115                payload: b.get_bytes(payload_length as usize)?.to_vec(),
116            },
117
118            HEADERS_FRAME_TYPE_ID => Frame::Headers {
119                header_block: b.get_bytes(payload_length as usize)?.to_vec(),
120            },
121
122            CANCEL_PUSH_FRAME_TYPE_ID => Frame::CancelPush {
123                push_id: b.get_varint()?,
124            },
125
126            SETTINGS_FRAME_TYPE_ID =>
127                parse_settings_frame(&mut b, payload_length as usize)?,
128
129            PUSH_PROMISE_FRAME_TYPE_ID =>
130                parse_push_promise(payload_length, &mut b)?,
131
132            GOAWAY_FRAME_TYPE_ID => Frame::GoAway {
133                id: b.get_varint()?,
134            },
135
136            MAX_PUSH_FRAME_TYPE_ID => Frame::MaxPushId {
137                push_id: b.get_varint()?,
138            },
139
140            PRIORITY_UPDATE_FRAME_REQUEST_TYPE_ID |
141            PRIORITY_UPDATE_FRAME_PUSH_TYPE_ID =>
142                parse_priority_update(frame_type, payload_length, &mut b)?,
143
144            _ => Frame::Unknown {
145                raw_type: frame_type,
146                payload: b.get_bytes(payload_length as usize)?.to_vec(),
147            },
148        };
149
150        Ok(frame)
151    }
152
153    pub fn to_bytes(&self, b: &mut octets::OctetsMut) -> Result<usize> {
154        let before = b.cap();
155
156        match self {
157            Frame::Data { payload } => {
158                b.put_varint(DATA_FRAME_TYPE_ID)?;
159                b.put_varint(payload.len() as u64)?;
160
161                b.put_bytes(payload.as_ref())?;
162            },
163
164            Frame::Headers { header_block } => {
165                b.put_varint(HEADERS_FRAME_TYPE_ID)?;
166                b.put_varint(header_block.len() as u64)?;
167
168                b.put_bytes(header_block.as_ref())?;
169            },
170
171            Frame::CancelPush { push_id } => {
172                b.put_varint(CANCEL_PUSH_FRAME_TYPE_ID)?;
173                b.put_varint(octets::varint_len(*push_id) as u64)?;
174
175                b.put_varint(*push_id)?;
176            },
177
178            Frame::Settings {
179                max_field_section_size,
180                qpack_max_table_capacity,
181                qpack_blocked_streams,
182                connect_protocol_enabled,
183                h3_datagram,
184                grease,
185                additional_settings,
186                ..
187            } => {
188                let mut len = 0;
189
190                if let Some(val) = max_field_section_size {
191                    len += octets::varint_len(SETTINGS_MAX_FIELD_SECTION_SIZE);
192                    len += octets::varint_len(*val);
193                }
194
195                if let Some(val) = qpack_max_table_capacity {
196                    len += octets::varint_len(SETTINGS_QPACK_MAX_TABLE_CAPACITY);
197                    len += octets::varint_len(*val);
198                }
199
200                if let Some(val) = qpack_blocked_streams {
201                    len += octets::varint_len(SETTINGS_QPACK_BLOCKED_STREAMS);
202                    len += octets::varint_len(*val);
203                }
204
205                if let Some(val) = connect_protocol_enabled {
206                    len += octets::varint_len(SETTINGS_ENABLE_CONNECT_PROTOCOL);
207                    len += octets::varint_len(*val);
208                }
209
210                if let Some(val) = h3_datagram {
211                    len += octets::varint_len(SETTINGS_H3_DATAGRAM_00);
212                    len += octets::varint_len(*val);
213                    len += octets::varint_len(SETTINGS_H3_DATAGRAM);
214                    len += octets::varint_len(*val);
215                }
216
217                if let Some(val) = grease {
218                    len += octets::varint_len(val.0);
219                    len += octets::varint_len(val.1);
220                }
221
222                if let Some(vals) = additional_settings {
223                    for val in vals {
224                        len += octets::varint_len(val.0);
225                        len += octets::varint_len(val.1);
226                    }
227                }
228
229                b.put_varint(SETTINGS_FRAME_TYPE_ID)?;
230                b.put_varint(len as u64)?;
231
232                if let Some(val) = max_field_section_size {
233                    b.put_varint(SETTINGS_MAX_FIELD_SECTION_SIZE)?;
234                    b.put_varint(*val)?;
235                }
236
237                if let Some(val) = qpack_max_table_capacity {
238                    b.put_varint(SETTINGS_QPACK_MAX_TABLE_CAPACITY)?;
239                    b.put_varint(*val)?;
240                }
241
242                if let Some(val) = qpack_blocked_streams {
243                    b.put_varint(SETTINGS_QPACK_BLOCKED_STREAMS)?;
244                    b.put_varint(*val)?;
245                }
246
247                if let Some(val) = connect_protocol_enabled {
248                    b.put_varint(SETTINGS_ENABLE_CONNECT_PROTOCOL)?;
249                    b.put_varint(*val)?;
250                }
251
252                if let Some(val) = h3_datagram {
253                    b.put_varint(SETTINGS_H3_DATAGRAM_00)?;
254                    b.put_varint(*val)?;
255                    b.put_varint(SETTINGS_H3_DATAGRAM)?;
256                    b.put_varint(*val)?;
257                }
258
259                if let Some(val) = grease {
260                    b.put_varint(val.0)?;
261                    b.put_varint(val.1)?;
262                }
263
264                if let Some(vals) = additional_settings {
265                    for val in vals {
266                        b.put_varint(val.0)?;
267                        b.put_varint(val.1)?;
268                    }
269                }
270            },
271
272            Frame::PushPromise {
273                push_id,
274                header_block,
275            } => {
276                let len = octets::varint_len(*push_id) + header_block.len();
277                b.put_varint(PUSH_PROMISE_FRAME_TYPE_ID)?;
278                b.put_varint(len as u64)?;
279
280                b.put_varint(*push_id)?;
281                b.put_bytes(header_block.as_ref())?;
282            },
283
284            Frame::GoAway { id } => {
285                b.put_varint(GOAWAY_FRAME_TYPE_ID)?;
286                b.put_varint(octets::varint_len(*id) as u64)?;
287
288                b.put_varint(*id)?;
289            },
290
291            Frame::MaxPushId { push_id } => {
292                b.put_varint(MAX_PUSH_FRAME_TYPE_ID)?;
293                b.put_varint(octets::varint_len(*push_id) as u64)?;
294
295                b.put_varint(*push_id)?;
296            },
297
298            Frame::PriorityUpdateRequest {
299                prioritized_element_id,
300                priority_field_value,
301            } => {
302                let len = octets::varint_len(*prioritized_element_id) +
303                    priority_field_value.len();
304
305                b.put_varint(PRIORITY_UPDATE_FRAME_REQUEST_TYPE_ID)?;
306                b.put_varint(len as u64)?;
307
308                b.put_varint(*prioritized_element_id)?;
309                b.put_bytes(priority_field_value)?;
310            },
311
312            Frame::PriorityUpdatePush {
313                prioritized_element_id,
314                priority_field_value,
315            } => {
316                let len = octets::varint_len(*prioritized_element_id) +
317                    priority_field_value.len();
318
319                b.put_varint(PRIORITY_UPDATE_FRAME_PUSH_TYPE_ID)?;
320                b.put_varint(len as u64)?;
321
322                b.put_varint(*prioritized_element_id)?;
323                b.put_bytes(priority_field_value)?;
324            },
325
326            Frame::Unknown { raw_type, payload } => {
327                b.put_varint(*raw_type)?;
328                b.put_varint(payload.len() as u64)?;
329
330                b.put_bytes(payload.as_ref())?;
331            },
332        }
333
334        Ok(before - b.cap())
335    }
336
337    #[cfg(feature = "qlog")]
338    pub fn to_qlog(&self) -> Http3Frame {
339        use qlog::events::RawInfo;
340
341        match self {
342            Frame::Data { .. } => Http3Frame::Data { raw: None },
343
344            // Qlog expects the `headers` to be represented as an array of
345            // name:value pairs. At this stage, we only have the qpack block, so
346            // populate the field with an empty vec.
347            Frame::Headers { .. } => Http3Frame::Headers {
348                headers: vec![],
349                raw: None,
350            },
351
352            Frame::CancelPush { push_id } => Http3Frame::CancelPush {
353                push_id: *push_id,
354                raw: None,
355            },
356
357            Frame::Settings {
358                max_field_section_size,
359                qpack_max_table_capacity,
360                qpack_blocked_streams,
361                connect_protocol_enabled,
362                h3_datagram,
363                grease,
364                additional_settings,
365                ..
366            } => {
367                let mut settings = vec![];
368
369                if let Some(v) = max_field_section_size {
370                    settings.push(qlog::events::http3::Setting {
371                        name: Some("MAX_FIELD_SECTION_SIZE".to_string()),
372                        name_bytes: None,
373                        value: *v,
374                    });
375                }
376
377                if let Some(v) = qpack_max_table_capacity {
378                    settings.push(qlog::events::http3::Setting {
379                        name: Some("QPACK_MAX_TABLE_CAPACITY".to_string()),
380                        name_bytes: None,
381                        value: *v,
382                    });
383                }
384
385                if let Some(v) = qpack_blocked_streams {
386                    settings.push(qlog::events::http3::Setting {
387                        name: Some("QPACK_BLOCKED_STREAMS".to_string()),
388                        name_bytes: None,
389                        value: *v,
390                    });
391                }
392
393                if let Some(v) = connect_protocol_enabled {
394                    settings.push(qlog::events::http3::Setting {
395                        name: Some(
396                            "SETTINGS_ENABLE_CONNECT_PROTOCOL".to_string(),
397                        ),
398                        name_bytes: None,
399                        value: *v,
400                    });
401                }
402
403                if let Some(v) = h3_datagram {
404                    settings.push(qlog::events::http3::Setting {
405                        name: Some("H3_DATAGRAM".to_string()),
406                        name_bytes: None,
407                        value: *v,
408                    });
409                }
410
411                if let Some((k, v)) = grease {
412                    settings.push(qlog::events::http3::Setting {
413                        name: Some(k.to_string()),
414                        name_bytes: None,
415                        value: *v,
416                    });
417                }
418
419                if let Some(additional_settings) = additional_settings {
420                    for (k, v) in additional_settings {
421                        settings.push(qlog::events::http3::Setting {
422                            name: Some(k.to_string()),
423                            name_bytes: None,
424                            value: *v,
425                        });
426                    }
427                }
428
429                Http3Frame::Settings {
430                    settings,
431                    raw: None,
432                }
433            },
434
435            // Qlog expects the `headers` to be represented as an array of
436            // name:value pairs. At this stage, we only have the qpack block, so
437            // populate the field with an empty vec.
438            Frame::PushPromise { push_id, .. } => Http3Frame::PushPromise {
439                push_id: *push_id,
440                headers: vec![],
441                raw: None,
442            },
443
444            Frame::GoAway { id } => Http3Frame::Goaway { id: *id, raw: None },
445
446            Frame::MaxPushId { push_id } => Http3Frame::MaxPushId {
447                push_id: *push_id,
448                raw: None,
449            },
450
451            Frame::PriorityUpdateRequest {
452                prioritized_element_id,
453                priority_field_value,
454            } => Http3Frame::PriorityUpdate {
455                stream_id: Some(*prioritized_element_id),
456                push_id: None,
457                priority_field_value: String::from_utf8_lossy(
458                    priority_field_value,
459                )
460                .into_owned(),
461                raw: None,
462            },
463
464            Frame::PriorityUpdatePush {
465                prioritized_element_id,
466                priority_field_value,
467            } => Http3Frame::PriorityUpdate {
468                stream_id: None,
469                push_id: Some(*prioritized_element_id),
470                priority_field_value: String::from_utf8_lossy(
471                    priority_field_value,
472                )
473                .into_owned(),
474                raw: None,
475            },
476
477            Frame::Unknown { raw_type, payload } => Http3Frame::Unknown {
478                frame_type_bytes: *raw_type,
479                raw: Some(RawInfo {
480                    data: None,
481                    payload_length: Some(payload.len() as u64),
482                    length: None,
483                }),
484            },
485        }
486    }
487}
488
489impl std::fmt::Debug for Frame {
490    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
491        match self {
492            Frame::Data { .. } => {
493                write!(f, "DATA")?;
494            },
495
496            Frame::Headers { .. } => {
497                write!(f, "HEADERS")?;
498            },
499
500            Frame::CancelPush { push_id } => {
501                write!(f, "CANCEL_PUSH push_id={push_id}")?;
502            },
503
504            Frame::Settings {
505                max_field_section_size,
506                qpack_max_table_capacity,
507                qpack_blocked_streams,
508                additional_settings,
509                raw,
510                ..
511            } => {
512                write!(f, "SETTINGS max_field_section={max_field_section_size:?}, qpack_max_table={qpack_max_table_capacity:?}, qpack_blocked={qpack_blocked_streams:?} raw={raw:?}, additional_settings={additional_settings:?}")?;
513            },
514
515            Frame::PushPromise {
516                push_id,
517                header_block,
518            } => {
519                write!(
520                    f,
521                    "PUSH_PROMISE push_id={} len={}",
522                    push_id,
523                    header_block.len()
524                )?;
525            },
526
527            Frame::GoAway { id } => {
528                write!(f, "GOAWAY id={id}")?;
529            },
530
531            Frame::MaxPushId { push_id } => {
532                write!(f, "MAX_PUSH_ID push_id={push_id}")?;
533            },
534
535            Frame::PriorityUpdateRequest {
536                prioritized_element_id,
537                priority_field_value,
538            } => {
539                write!(
540                    f,
541                    "PRIORITY_UPDATE request_stream_id={}, priority_field_len={}",
542                    prioritized_element_id,
543                    priority_field_value.len()
544                )?;
545            },
546
547            Frame::PriorityUpdatePush {
548                prioritized_element_id,
549                priority_field_value,
550            } => {
551                write!(
552                    f,
553                    "PRIORITY_UPDATE push_id={}, priority_field_len={}",
554                    prioritized_element_id,
555                    priority_field_value.len()
556                )?;
557            },
558
559            Frame::Unknown { raw_type, .. } => {
560                write!(f, "UNKNOWN raw_type={raw_type}",)?;
561            },
562        }
563
564        Ok(())
565    }
566}
567
568fn parse_settings_frame(
569    b: &mut octets::Octets, settings_length: usize,
570) -> Result<Frame> {
571    let mut max_field_section_size = None;
572    let mut qpack_max_table_capacity = None;
573    let mut qpack_blocked_streams = None;
574    let mut connect_protocol_enabled = None;
575    let mut h3_datagram = None;
576    let mut raw = Vec::new();
577    let mut additional_settings: Option<Vec<(u64, u64)>> = None;
578
579    // Reject SETTINGS frames that are too long.
580    if settings_length > MAX_SETTINGS_PAYLOAD_SIZE {
581        return Err(super::Error::ExcessiveLoad);
582    }
583
584    while b.off() < settings_length {
585        let identifier = b.get_varint()?;
586        let value = b.get_varint()?;
587
588        // MAX_SETTINGS_PAYLOAD_SIZE protects us from storing too many raw
589        // settings.
590        raw.push((identifier, value));
591
592        match identifier {
593            SETTINGS_QPACK_MAX_TABLE_CAPACITY => {
594                qpack_max_table_capacity = Some(value);
595            },
596
597            SETTINGS_MAX_FIELD_SECTION_SIZE => {
598                max_field_section_size = Some(value);
599            },
600
601            SETTINGS_QPACK_BLOCKED_STREAMS => {
602                qpack_blocked_streams = Some(value);
603            },
604
605            SETTINGS_ENABLE_CONNECT_PROTOCOL => {
606                if value > 1 {
607                    return Err(super::Error::SettingsError);
608                }
609
610                connect_protocol_enabled = Some(value);
611            },
612
613            SETTINGS_H3_DATAGRAM_00 | SETTINGS_H3_DATAGRAM => {
614                if value > 1 {
615                    return Err(super::Error::SettingsError);
616                }
617
618                h3_datagram = Some(value);
619            },
620
621            // Reserved values overlap with HTTP/2 and MUST be rejected
622            0x0 | 0x2 | 0x3 | 0x4 | 0x5 =>
623                return Err(super::Error::SettingsError),
624
625            // Unknown Settings parameters go into additional_settings.
626            _ => {
627                let s: &mut Vec<(u64, u64)> =
628                    additional_settings.get_or_insert(vec![]);
629                s.push((identifier, value));
630            },
631        }
632    }
633
634    Ok(Frame::Settings {
635        max_field_section_size,
636        qpack_max_table_capacity,
637        qpack_blocked_streams,
638        connect_protocol_enabled,
639        h3_datagram,
640        grease: None,
641        raw: Some(raw),
642        additional_settings,
643    })
644}
645
646fn parse_push_promise(
647    payload_length: u64, b: &mut octets::Octets,
648) -> Result<Frame> {
649    let before = b.off();
650    let push_id = b.get_varint()?;
651    let header_block_length = payload_length - (b.off() - before) as u64;
652    let header_block = b.get_bytes(header_block_length as usize)?.to_vec();
653
654    Ok(Frame::PushPromise {
655        push_id,
656        header_block,
657    })
658}
659
660fn parse_priority_update(
661    frame_type: u64, payload_length: u64, b: &mut octets::Octets,
662) -> Result<Frame> {
663    let before = b.off();
664    let prioritized_element_id = b.get_varint()?;
665    let priority_field_value_length = payload_length - (b.off() - before) as u64;
666    let priority_field_value =
667        b.get_bytes(priority_field_value_length as usize)?.to_vec();
668
669    match frame_type {
670        PRIORITY_UPDATE_FRAME_REQUEST_TYPE_ID =>
671            Ok(Frame::PriorityUpdateRequest {
672                prioritized_element_id,
673                priority_field_value,
674            }),
675
676        PRIORITY_UPDATE_FRAME_PUSH_TYPE_ID => Ok(Frame::PriorityUpdatePush {
677            prioritized_element_id,
678            priority_field_value,
679        }),
680
681        _ => unreachable!(),
682    }
683}
684
685#[cfg(test)]
686mod tests {
687    use super::*;
688
689    #[test]
690    fn data() {
691        let mut d = [42; 128];
692
693        let payload = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
694        let frame_payload_len = payload.len();
695        let frame_header_len = 2;
696
697        let frame = Frame::Data { payload };
698
699        let wire_len = {
700            let mut b = octets::OctetsMut::with_slice(&mut d);
701            frame.to_bytes(&mut b).unwrap()
702        };
703
704        assert_eq!(wire_len, frame_header_len + frame_payload_len);
705
706        assert_eq!(
707            Frame::from_bytes(
708                DATA_FRAME_TYPE_ID,
709                frame_payload_len as u64,
710                &d[frame_header_len..]
711            )
712            .unwrap(),
713            frame
714        );
715    }
716
717    #[test]
718    fn headers() {
719        let mut d = [42; 128];
720
721        let header_block = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
722        let frame_payload_len = header_block.len();
723        let frame_header_len = 2;
724
725        let frame = Frame::Headers { header_block };
726
727        let wire_len = {
728            let mut b = octets::OctetsMut::with_slice(&mut d);
729            frame.to_bytes(&mut b).unwrap()
730        };
731
732        assert_eq!(wire_len, frame_header_len + frame_payload_len);
733
734        assert_eq!(
735            Frame::from_bytes(
736                HEADERS_FRAME_TYPE_ID,
737                frame_payload_len as u64,
738                &d[frame_header_len..]
739            )
740            .unwrap(),
741            frame
742        );
743    }
744
745    #[test]
746    fn cancel_push() {
747        let mut d = [42; 128];
748
749        let frame = Frame::CancelPush { push_id: 0 };
750
751        let frame_payload_len = 1;
752        let frame_header_len = 2;
753
754        let wire_len = {
755            let mut b = octets::OctetsMut::with_slice(&mut d);
756            frame.to_bytes(&mut b).unwrap()
757        };
758
759        assert_eq!(wire_len, frame_header_len + frame_payload_len);
760
761        assert_eq!(
762            Frame::from_bytes(
763                CANCEL_PUSH_FRAME_TYPE_ID,
764                frame_payload_len as u64,
765                &d[frame_header_len..]
766            )
767            .unwrap(),
768            frame
769        );
770    }
771
772    #[test]
773    fn settings_all_no_grease() {
774        let mut d = [42; 128];
775
776        let raw_settings = vec![
777            (SETTINGS_MAX_FIELD_SECTION_SIZE, 0),
778            (SETTINGS_QPACK_MAX_TABLE_CAPACITY, 0),
779            (SETTINGS_QPACK_BLOCKED_STREAMS, 0),
780            (SETTINGS_ENABLE_CONNECT_PROTOCOL, 0),
781            (SETTINGS_H3_DATAGRAM_00, 0),
782            (SETTINGS_H3_DATAGRAM, 0),
783        ];
784
785        let frame = Frame::Settings {
786            max_field_section_size: Some(0),
787            qpack_max_table_capacity: Some(0),
788            qpack_blocked_streams: Some(0),
789            connect_protocol_enabled: Some(0),
790            h3_datagram: Some(0),
791            grease: None,
792            raw: Some(raw_settings),
793            additional_settings: None,
794        };
795
796        let frame_payload_len = 13;
797        let frame_header_len = 2;
798
799        let wire_len = {
800            let mut b = octets::OctetsMut::with_slice(&mut d);
801            frame.to_bytes(&mut b).unwrap()
802        };
803
804        assert_eq!(wire_len, frame_header_len + frame_payload_len);
805
806        assert_eq!(
807            Frame::from_bytes(
808                SETTINGS_FRAME_TYPE_ID,
809                frame_payload_len as u64,
810                &d[frame_header_len..]
811            )
812            .unwrap(),
813            frame
814        );
815    }
816
817    #[test]
818    fn settings_all_grease() {
819        let mut d = [42; 128];
820
821        let frame = Frame::Settings {
822            max_field_section_size: Some(0),
823            qpack_max_table_capacity: Some(0),
824            qpack_blocked_streams: Some(0),
825            connect_protocol_enabled: Some(0),
826            h3_datagram: Some(0),
827            grease: Some((33, 33)),
828            raw: Default::default(),
829            additional_settings: None,
830        };
831
832        let raw_settings = vec![
833            (SETTINGS_MAX_FIELD_SECTION_SIZE, 0),
834            (SETTINGS_QPACK_MAX_TABLE_CAPACITY, 0),
835            (SETTINGS_QPACK_BLOCKED_STREAMS, 0),
836            (SETTINGS_ENABLE_CONNECT_PROTOCOL, 0),
837            (SETTINGS_H3_DATAGRAM_00, 0),
838            (SETTINGS_H3_DATAGRAM, 0),
839            (33, 33),
840        ];
841
842        // Frame parsing will not populate GREASE property but will be in the
843        // raw info.
844        let frame_parsed = Frame::Settings {
845            max_field_section_size: Some(0),
846            qpack_max_table_capacity: Some(0),
847            qpack_blocked_streams: Some(0),
848            connect_protocol_enabled: Some(0),
849            h3_datagram: Some(0),
850            grease: None,
851            raw: Some(raw_settings),
852            additional_settings: Some(vec![(33, 33)]),
853        };
854
855        let frame_payload_len = 15;
856        let frame_header_len = 2;
857
858        let wire_len = {
859            let mut b = octets::OctetsMut::with_slice(&mut d);
860            frame.to_bytes(&mut b).unwrap()
861        };
862
863        assert_eq!(wire_len, frame_header_len + frame_payload_len);
864
865        assert_eq!(
866            Frame::from_bytes(
867                SETTINGS_FRAME_TYPE_ID,
868                frame_payload_len as u64,
869                &d[frame_header_len..]
870            )
871            .unwrap(),
872            frame_parsed
873        );
874    }
875
876    #[test]
877    fn settings_h3_only() {
878        let mut d = [42; 128];
879
880        let raw_settings = vec![(SETTINGS_MAX_FIELD_SECTION_SIZE, 1024)];
881
882        let frame = Frame::Settings {
883            max_field_section_size: Some(1024),
884            qpack_max_table_capacity: None,
885            qpack_blocked_streams: None,
886            connect_protocol_enabled: None,
887            h3_datagram: None,
888            grease: None,
889            raw: Some(raw_settings),
890            additional_settings: None,
891        };
892
893        let frame_payload_len = 3;
894        let frame_header_len = 2;
895
896        let wire_len = {
897            let mut b = octets::OctetsMut::with_slice(&mut d);
898            frame.to_bytes(&mut b).unwrap()
899        };
900
901        assert_eq!(wire_len, frame_header_len + frame_payload_len);
902
903        assert_eq!(
904            Frame::from_bytes(
905                SETTINGS_FRAME_TYPE_ID,
906                frame_payload_len as u64,
907                &d[frame_header_len..]
908            )
909            .unwrap(),
910            frame
911        );
912    }
913
914    #[test]
915    fn settings_h3_connect_protocol_enabled() {
916        let mut d = [42; 128];
917
918        let raw_settings = vec![(SETTINGS_ENABLE_CONNECT_PROTOCOL, 1)];
919
920        let frame = Frame::Settings {
921            max_field_section_size: None,
922            qpack_max_table_capacity: None,
923            qpack_blocked_streams: None,
924            connect_protocol_enabled: Some(1),
925            h3_datagram: None,
926            grease: None,
927            raw: Some(raw_settings),
928            additional_settings: None,
929        };
930
931        let frame_payload_len = 2;
932        let frame_header_len = 2;
933
934        let wire_len = {
935            let mut b = octets::OctetsMut::with_slice(&mut d);
936            frame.to_bytes(&mut b).unwrap()
937        };
938
939        assert_eq!(wire_len, frame_header_len + frame_payload_len);
940
941        assert_eq!(
942            Frame::from_bytes(
943                SETTINGS_FRAME_TYPE_ID,
944                frame_payload_len as u64,
945                &d[frame_header_len..]
946            )
947            .unwrap(),
948            frame
949        );
950    }
951
952    #[test]
953    fn settings_h3_connect_protocol_enabled_bad() {
954        let mut d = [42; 128];
955
956        let raw_settings = vec![(SETTINGS_ENABLE_CONNECT_PROTOCOL, 9)];
957
958        let frame = Frame::Settings {
959            max_field_section_size: None,
960            qpack_max_table_capacity: None,
961            qpack_blocked_streams: None,
962            connect_protocol_enabled: Some(9),
963            h3_datagram: None,
964            grease: None,
965            raw: Some(raw_settings),
966            additional_settings: None,
967        };
968
969        let frame_payload_len = 2;
970        let frame_header_len = 2;
971
972        let wire_len = {
973            let mut b = octets::OctetsMut::with_slice(&mut d);
974            frame.to_bytes(&mut b).unwrap()
975        };
976
977        assert_eq!(wire_len, frame_header_len + frame_payload_len);
978
979        assert_eq!(
980            Frame::from_bytes(
981                SETTINGS_FRAME_TYPE_ID,
982                frame_payload_len as u64,
983                &d[frame_header_len..]
984            ),
985            Err(crate::h3::Error::SettingsError)
986        );
987    }
988
989    #[test]
990    fn settings_h3_dgram_only() {
991        let mut d = [42; 128];
992
993        let raw_settings =
994            vec![(SETTINGS_H3_DATAGRAM_00, 1), (SETTINGS_H3_DATAGRAM, 1)];
995
996        let frame = Frame::Settings {
997            max_field_section_size: None,
998            qpack_max_table_capacity: None,
999            qpack_blocked_streams: None,
1000            connect_protocol_enabled: None,
1001            h3_datagram: Some(1),
1002            grease: None,
1003            raw: Some(raw_settings),
1004            additional_settings: None,
1005        };
1006
1007        let frame_payload_len = 5;
1008        let frame_header_len = 2;
1009
1010        let wire_len = {
1011            let mut b = octets::OctetsMut::with_slice(&mut d);
1012            frame.to_bytes(&mut b).unwrap()
1013        };
1014
1015        assert_eq!(wire_len, frame_header_len + frame_payload_len);
1016
1017        assert_eq!(
1018            Frame::from_bytes(
1019                SETTINGS_FRAME_TYPE_ID,
1020                frame_payload_len as u64,
1021                &d[frame_header_len..]
1022            )
1023            .unwrap(),
1024            frame
1025        );
1026    }
1027
1028    #[test]
1029    fn settings_h3_dgram_bad() {
1030        let mut d = [42; 128];
1031
1032        let frame = Frame::Settings {
1033            max_field_section_size: None,
1034            qpack_max_table_capacity: None,
1035            qpack_blocked_streams: None,
1036            connect_protocol_enabled: None,
1037            h3_datagram: Some(5),
1038            grease: None,
1039            raw: Default::default(),
1040            additional_settings: None,
1041        };
1042
1043        let frame_payload_len = 5;
1044        let frame_header_len = 2;
1045
1046        let wire_len = {
1047            let mut b = octets::OctetsMut::with_slice(&mut d);
1048            frame.to_bytes(&mut b).unwrap()
1049        };
1050
1051        assert_eq!(wire_len, frame_header_len + frame_payload_len);
1052
1053        assert_eq!(
1054            Frame::from_bytes(
1055                SETTINGS_FRAME_TYPE_ID,
1056                frame_payload_len as u64,
1057                &d[frame_header_len..]
1058            ),
1059            Err(crate::h3::Error::SettingsError)
1060        );
1061    }
1062
1063    #[test]
1064    fn settings_qpack_only() {
1065        let mut d = [42; 128];
1066
1067        let raw_settings = vec![
1068            (SETTINGS_QPACK_MAX_TABLE_CAPACITY, 0),
1069            (SETTINGS_QPACK_BLOCKED_STREAMS, 0),
1070        ];
1071
1072        let frame = Frame::Settings {
1073            max_field_section_size: None,
1074            qpack_max_table_capacity: Some(0),
1075            qpack_blocked_streams: Some(0),
1076            connect_protocol_enabled: None,
1077            h3_datagram: None,
1078            grease: None,
1079            raw: Some(raw_settings),
1080            additional_settings: None,
1081        };
1082
1083        let frame_payload_len = 4;
1084        let frame_header_len = 2;
1085
1086        let wire_len = {
1087            let mut b = octets::OctetsMut::with_slice(&mut d);
1088            frame.to_bytes(&mut b).unwrap()
1089        };
1090
1091        assert_eq!(wire_len, frame_header_len + frame_payload_len);
1092
1093        assert_eq!(
1094            Frame::from_bytes(
1095                SETTINGS_FRAME_TYPE_ID,
1096                frame_payload_len as u64,
1097                &d[frame_header_len..]
1098            )
1099            .unwrap(),
1100            frame
1101        );
1102    }
1103
1104    #[test]
1105    fn settings_h2_prohibited() {
1106        // Test prohibited values manually because the quiche API cannot create
1107        // them. `d[frame_header_len]` contains the SETTINGS type.
1108        let frame_payload_len = 2u64;
1109        let frame_header_len = 2;
1110        let mut d = [
1111            SETTINGS_FRAME_TYPE_ID as u8,
1112            frame_payload_len as u8,
1113            0x0,
1114            1,
1115        ];
1116
1117        assert_eq!(
1118            Frame::from_bytes(
1119                SETTINGS_FRAME_TYPE_ID,
1120                frame_payload_len,
1121                &d[frame_header_len..]
1122            ),
1123            Err(crate::h3::Error::SettingsError)
1124        );
1125
1126        d[frame_header_len] = 0x2;
1127
1128        assert_eq!(
1129            Frame::from_bytes(
1130                SETTINGS_FRAME_TYPE_ID,
1131                frame_payload_len,
1132                &d[frame_header_len..]
1133            ),
1134            Err(crate::h3::Error::SettingsError)
1135        );
1136
1137        d[frame_header_len] = 0x3;
1138
1139        assert_eq!(
1140            Frame::from_bytes(
1141                SETTINGS_FRAME_TYPE_ID,
1142                frame_payload_len,
1143                &d[frame_header_len..]
1144            ),
1145            Err(crate::h3::Error::SettingsError)
1146        );
1147
1148        d[frame_header_len] = 0x4;
1149
1150        assert_eq!(
1151            Frame::from_bytes(
1152                SETTINGS_FRAME_TYPE_ID,
1153                frame_payload_len,
1154                &d[frame_header_len..]
1155            ),
1156            Err(crate::h3::Error::SettingsError)
1157        );
1158
1159        d[frame_header_len] = 0x5;
1160
1161        assert_eq!(
1162            Frame::from_bytes(
1163                SETTINGS_FRAME_TYPE_ID,
1164                frame_payload_len,
1165                &d[frame_header_len..]
1166            ),
1167            Err(crate::h3::Error::SettingsError)
1168        );
1169    }
1170
1171    #[test]
1172    fn settings_too_big() {
1173        // We need to test a SETTINGS frame that exceeds
1174        // MAX_SETTINGS_PAYLOAD_SIZE, so just craft a special buffer that look
1175        // likes the frame. The payload content doesn't matter since quiche
1176        // should abort before then.
1177        let frame_payload_len = MAX_SETTINGS_PAYLOAD_SIZE + 1;
1178        let frame_header_len = 2;
1179        let d = [
1180            SETTINGS_FRAME_TYPE_ID as u8,
1181            frame_payload_len as u8,
1182            0x1,
1183            1,
1184        ];
1185
1186        assert_eq!(
1187            Frame::from_bytes(
1188                SETTINGS_FRAME_TYPE_ID,
1189                frame_payload_len as u64,
1190                &d[frame_header_len..]
1191            ),
1192            Err(crate::h3::Error::ExcessiveLoad)
1193        );
1194    }
1195
1196    #[test]
1197    fn push_promise() {
1198        let mut d = [42; 128];
1199
1200        let header_block = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
1201        let frame_payload_len = 1 + header_block.len();
1202        let frame_header_len = 2;
1203
1204        let frame = Frame::PushPromise {
1205            push_id: 0,
1206            header_block,
1207        };
1208
1209        let wire_len = {
1210            let mut b = octets::OctetsMut::with_slice(&mut d);
1211            frame.to_bytes(&mut b).unwrap()
1212        };
1213
1214        assert_eq!(wire_len, frame_header_len + frame_payload_len);
1215
1216        assert_eq!(
1217            Frame::from_bytes(
1218                PUSH_PROMISE_FRAME_TYPE_ID,
1219                frame_payload_len as u64,
1220                &d[frame_header_len..]
1221            )
1222            .unwrap(),
1223            frame
1224        );
1225    }
1226
1227    #[test]
1228    fn push_promise_non_minimal_varint() {
1229        // A regression test for a bug handling varints in frame processing.
1230
1231        // Value 37 minimally encodes as 1 byte [0x25]. Non-minimal
1232        // 2-byte encoding: [0x40, 0x25] (2-byte varint prefix 0b01).
1233        let header_block = b"\x00\x01\x02";
1234        let mut payload = vec![0x40u8, 0x25]; // non-minimal push_id = 37
1235        payload.extend_from_slice(header_block);
1236        let payload_length = payload.len() as u64;
1237        let frame = Frame::from_bytes(
1238            PUSH_PROMISE_FRAME_TYPE_ID,
1239            payload_length,
1240            &payload,
1241        )
1242        .expect("non-minimal varint was parsed correctly");
1243        assert_eq!(frame, Frame::PushPromise {
1244            push_id: 37,
1245            header_block: header_block.to_vec(),
1246        });
1247    }
1248
1249    #[test]
1250    fn goaway() {
1251        let mut d = [42; 128];
1252
1253        let frame = Frame::GoAway { id: 32 };
1254
1255        let frame_payload_len = 1;
1256        let frame_header_len = 2;
1257
1258        let wire_len = {
1259            let mut b = octets::OctetsMut::with_slice(&mut d);
1260            frame.to_bytes(&mut b).unwrap()
1261        };
1262
1263        assert_eq!(wire_len, frame_header_len + frame_payload_len);
1264
1265        assert_eq!(
1266            Frame::from_bytes(
1267                GOAWAY_FRAME_TYPE_ID,
1268                frame_payload_len as u64,
1269                &d[frame_header_len..]
1270            )
1271            .unwrap(),
1272            frame
1273        );
1274    }
1275
1276    #[test]
1277    fn max_push_id() {
1278        let mut d = [42; 128];
1279
1280        let frame = Frame::MaxPushId { push_id: 128 };
1281
1282        let frame_payload_len = 2;
1283        let frame_header_len = 2;
1284
1285        let wire_len = {
1286            let mut b = octets::OctetsMut::with_slice(&mut d);
1287            frame.to_bytes(&mut b).unwrap()
1288        };
1289
1290        assert_eq!(wire_len, frame_header_len + frame_payload_len);
1291
1292        assert_eq!(
1293            Frame::from_bytes(
1294                MAX_PUSH_FRAME_TYPE_ID,
1295                frame_payload_len as u64,
1296                &d[frame_header_len..]
1297            )
1298            .unwrap(),
1299            frame
1300        );
1301    }
1302
1303    #[test]
1304    fn priority_update_request() {
1305        let mut d = [42; 128];
1306
1307        let prioritized_element_id = 4;
1308        let priority_field_value = b"abcdefghijklm".to_vec();
1309        let frame_payload_len = 1 + priority_field_value.len();
1310        let frame_header_len = 5;
1311
1312        let frame = Frame::PriorityUpdateRequest {
1313            prioritized_element_id,
1314            priority_field_value,
1315        };
1316
1317        let wire_len = {
1318            let mut b = octets::OctetsMut::with_slice(&mut d);
1319            frame.to_bytes(&mut b).unwrap()
1320        };
1321
1322        assert_eq!(wire_len, frame_header_len + frame_payload_len);
1323
1324        assert_eq!(
1325            Frame::from_bytes(
1326                PRIORITY_UPDATE_FRAME_REQUEST_TYPE_ID,
1327                frame_payload_len as u64,
1328                &d[frame_header_len..]
1329            )
1330            .unwrap(),
1331            frame
1332        );
1333    }
1334
1335    #[test]
1336    fn priority_update_push() {
1337        let mut d = [42; 128];
1338
1339        let prioritized_element_id = 6;
1340        let priority_field_value = b"abcdefghijklm".to_vec();
1341        let frame_payload_len = 1 + priority_field_value.len();
1342        let frame_header_len = 5;
1343
1344        let frame = Frame::PriorityUpdatePush {
1345            prioritized_element_id,
1346            priority_field_value,
1347        };
1348
1349        let wire_len = {
1350            let mut b = octets::OctetsMut::with_slice(&mut d);
1351            frame.to_bytes(&mut b).unwrap()
1352        };
1353
1354        assert_eq!(wire_len, frame_header_len + frame_payload_len);
1355
1356        assert_eq!(
1357            Frame::from_bytes(
1358                PRIORITY_UPDATE_FRAME_PUSH_TYPE_ID,
1359                frame_payload_len as u64,
1360                &d[frame_header_len..]
1361            )
1362            .unwrap(),
1363            frame
1364        );
1365    }
1366
1367    #[test]
1368    fn priority_update_non_minimal_varint() {
1369        // A regression test for a bug handling varints in frame processing.
1370
1371        // Value 37 minimally encodes as 1 byte [0x25]. Non-minimal
1372        // 2-byte encoding: [0x40, 0x25] (2-byte varint prefix 0b01).
1373        let priority_field = b"u=3";
1374        let mut payload = vec![0x40u8, 0x25];
1375        payload.extend_from_slice(priority_field);
1376        let payload_length = payload.len() as u64;
1377
1378        let frame = Frame::from_bytes(
1379            PRIORITY_UPDATE_FRAME_REQUEST_TYPE_ID,
1380            payload_length,
1381            &payload,
1382        )
1383        .expect("non-minimal varint was parsed correctly");
1384
1385        assert_eq!(frame, Frame::PriorityUpdateRequest {
1386            prioritized_element_id: 37,
1387            priority_field_value: priority_field.to_vec(),
1388        });
1389    }
1390
1391    #[test]
1392    fn unknown_type() {
1393        let d = [42; 12];
1394
1395        assert_eq!(
1396            Frame::from_bytes(255, 12, &d[..]),
1397            Ok(Frame::Unknown {
1398                raw_type: 255,
1399                payload: vec![42; 12]
1400            })
1401        );
1402    }
1403}