summaryrefslogtreecommitdiffstats
path: root/third_party/rust/neqo-transport/src/frame.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /third_party/rust/neqo-transport/src/frame.rs
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/neqo-transport/src/frame.rs')
-rw-r--r--third_party/rust/neqo-transport/src/frame.rs977
1 files changed, 977 insertions, 0 deletions
diff --git a/third_party/rust/neqo-transport/src/frame.rs b/third_party/rust/neqo-transport/src/frame.rs
new file mode 100644
index 0000000000..f3d567ac7c
--- /dev/null
+++ b/third_party/rust/neqo-transport/src/frame.rs
@@ -0,0 +1,977 @@
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Directly relating to QUIC frames.
+
+use std::{convert::TryFrom, ops::RangeInclusive};
+
+use neqo_common::{qtrace, Decoder};
+
+use crate::{
+ cid::MAX_CONNECTION_ID_LEN,
+ packet::PacketType,
+ stream_id::{StreamId, StreamType},
+ AppError, ConnectionError, Error, Res, TransportError,
+};
+
+#[allow(clippy::module_name_repetitions)]
+pub type FrameType = u64;
+
+const FRAME_TYPE_PADDING: FrameType = 0x0;
+pub const FRAME_TYPE_PING: FrameType = 0x1;
+pub const FRAME_TYPE_ACK: FrameType = 0x2;
+const FRAME_TYPE_ACK_ECN: FrameType = 0x3;
+pub const FRAME_TYPE_RESET_STREAM: FrameType = 0x4;
+pub const FRAME_TYPE_STOP_SENDING: FrameType = 0x5;
+pub const FRAME_TYPE_CRYPTO: FrameType = 0x6;
+pub const FRAME_TYPE_NEW_TOKEN: FrameType = 0x7;
+const FRAME_TYPE_STREAM: FrameType = 0x8;
+const FRAME_TYPE_STREAM_MAX: FrameType = 0xf;
+pub const FRAME_TYPE_MAX_DATA: FrameType = 0x10;
+pub const FRAME_TYPE_MAX_STREAM_DATA: FrameType = 0x11;
+pub const FRAME_TYPE_MAX_STREAMS_BIDI: FrameType = 0x12;
+pub const FRAME_TYPE_MAX_STREAMS_UNIDI: FrameType = 0x13;
+pub const FRAME_TYPE_DATA_BLOCKED: FrameType = 0x14;
+pub const FRAME_TYPE_STREAM_DATA_BLOCKED: FrameType = 0x15;
+pub const FRAME_TYPE_STREAMS_BLOCKED_BIDI: FrameType = 0x16;
+pub const FRAME_TYPE_STREAMS_BLOCKED_UNIDI: FrameType = 0x17;
+pub const FRAME_TYPE_NEW_CONNECTION_ID: FrameType = 0x18;
+pub const FRAME_TYPE_RETIRE_CONNECTION_ID: FrameType = 0x19;
+pub const FRAME_TYPE_PATH_CHALLENGE: FrameType = 0x1a;
+pub const FRAME_TYPE_PATH_RESPONSE: FrameType = 0x1b;
+pub const FRAME_TYPE_CONNECTION_CLOSE_TRANSPORT: FrameType = 0x1c;
+pub const FRAME_TYPE_CONNECTION_CLOSE_APPLICATION: FrameType = 0x1d;
+pub const FRAME_TYPE_HANDSHAKE_DONE: FrameType = 0x1e;
+// draft-ietf-quic-ack-delay
+pub const FRAME_TYPE_ACK_FREQUENCY: FrameType = 0xaf;
+// draft-ietf-quic-datagram
+pub const FRAME_TYPE_DATAGRAM: FrameType = 0x30;
+pub const FRAME_TYPE_DATAGRAM_WITH_LEN: FrameType = 0x31;
+const DATAGRAM_FRAME_BIT_LEN: u64 = 0x01;
+
+const STREAM_FRAME_BIT_FIN: u64 = 0x01;
+const STREAM_FRAME_BIT_LEN: u64 = 0x02;
+const STREAM_FRAME_BIT_OFF: u64 = 0x04;
+
+#[derive(PartialEq, Eq, Debug, PartialOrd, Ord, Clone, Copy)]
+pub enum CloseError {
+ Transport(TransportError),
+ Application(AppError),
+}
+
+impl CloseError {
+ fn frame_type_bit(self) -> u64 {
+ match self {
+ Self::Transport(_) => 0,
+ Self::Application(_) => 1,
+ }
+ }
+
+ fn from_type_bit(bit: u64, code: u64) -> Self {
+ if (bit & 0x01) == 0 {
+ Self::Transport(code)
+ } else {
+ Self::Application(code)
+ }
+ }
+
+ pub fn code(&self) -> u64 {
+ match self {
+ Self::Transport(c) | Self::Application(c) => *c,
+ }
+ }
+}
+
+impl From<ConnectionError> for CloseError {
+ fn from(err: ConnectionError) -> Self {
+ match err {
+ ConnectionError::Transport(c) => Self::Transport(c.code()),
+ ConnectionError::Application(c) => Self::Application(c),
+ }
+ }
+}
+
+#[derive(PartialEq, Eq, Debug, Default, Clone)]
+pub struct AckRange {
+ pub(crate) gap: u64,
+ pub(crate) range: u64,
+}
+
+#[derive(PartialEq, Eq, Debug, Clone)]
+pub enum Frame<'a> {
+ Padding,
+ Ping,
+ Ack {
+ largest_acknowledged: u64,
+ ack_delay: u64,
+ first_ack_range: u64,
+ ack_ranges: Vec<AckRange>,
+ },
+ ResetStream {
+ stream_id: StreamId,
+ application_error_code: AppError,
+ final_size: u64,
+ },
+ StopSending {
+ stream_id: StreamId,
+ application_error_code: AppError,
+ },
+ Crypto {
+ offset: u64,
+ data: &'a [u8],
+ },
+ NewToken {
+ token: &'a [u8],
+ },
+ Stream {
+ stream_id: StreamId,
+ offset: u64,
+ data: &'a [u8],
+ fin: bool,
+ fill: bool,
+ },
+ MaxData {
+ maximum_data: u64,
+ },
+ MaxStreamData {
+ stream_id: StreamId,
+ maximum_stream_data: u64,
+ },
+ MaxStreams {
+ stream_type: StreamType,
+ maximum_streams: u64,
+ },
+ DataBlocked {
+ data_limit: u64,
+ },
+ StreamDataBlocked {
+ stream_id: StreamId,
+ stream_data_limit: u64,
+ },
+ StreamsBlocked {
+ stream_type: StreamType,
+ stream_limit: u64,
+ },
+ NewConnectionId {
+ sequence_number: u64,
+ retire_prior: u64,
+ connection_id: &'a [u8],
+ stateless_reset_token: &'a [u8; 16],
+ },
+ RetireConnectionId {
+ sequence_number: u64,
+ },
+ PathChallenge {
+ data: [u8; 8],
+ },
+ PathResponse {
+ data: [u8; 8],
+ },
+ ConnectionClose {
+ error_code: CloseError,
+ frame_type: u64,
+ // Not a reference as we use this to hold the value.
+ // This is not used in optimized builds anyway.
+ reason_phrase: Vec<u8>,
+ },
+ HandshakeDone,
+ AckFrequency {
+ /// The current ACK frequency sequence number.
+ seqno: u64,
+ /// The number of contiguous packets that can be received without
+ /// acknowledging immediately.
+ tolerance: u64,
+ /// The time to delay after receiving the first packet that is
+ /// not immediately acknowledged.
+ delay: u64,
+ /// Ignore reordering when deciding to immediately acknowledge.
+ ignore_order: bool,
+ },
+ Datagram {
+ data: &'a [u8],
+ fill: bool,
+ },
+}
+
+impl<'a> Frame<'a> {
+ fn get_stream_type_bit(stream_type: StreamType) -> u64 {
+ match stream_type {
+ StreamType::BiDi => 0,
+ StreamType::UniDi => 1,
+ }
+ }
+
+ fn stream_type_from_bit(bit: u64) -> StreamType {
+ if (bit & 0x01) == 0 {
+ StreamType::BiDi
+ } else {
+ StreamType::UniDi
+ }
+ }
+
+ pub fn get_type(&self) -> FrameType {
+ match self {
+ Self::Padding => FRAME_TYPE_PADDING,
+ Self::Ping => FRAME_TYPE_PING,
+ Self::Ack { .. } => FRAME_TYPE_ACK, // We don't do ACK ECN.
+ Self::ResetStream { .. } => FRAME_TYPE_RESET_STREAM,
+ Self::StopSending { .. } => FRAME_TYPE_STOP_SENDING,
+ Self::Crypto { .. } => FRAME_TYPE_CRYPTO,
+ Self::NewToken { .. } => FRAME_TYPE_NEW_TOKEN,
+ Self::Stream {
+ fin, offset, fill, ..
+ } => Self::stream_type(*fin, *offset > 0, *fill),
+ Self::MaxData { .. } => FRAME_TYPE_MAX_DATA,
+ Self::MaxStreamData { .. } => FRAME_TYPE_MAX_STREAM_DATA,
+ Self::MaxStreams { stream_type, .. } => {
+ FRAME_TYPE_MAX_STREAMS_BIDI + Self::get_stream_type_bit(*stream_type)
+ }
+ Self::DataBlocked { .. } => FRAME_TYPE_DATA_BLOCKED,
+ Self::StreamDataBlocked { .. } => FRAME_TYPE_STREAM_DATA_BLOCKED,
+ Self::StreamsBlocked { stream_type, .. } => {
+ FRAME_TYPE_STREAMS_BLOCKED_BIDI + Self::get_stream_type_bit(*stream_type)
+ }
+ Self::NewConnectionId { .. } => FRAME_TYPE_NEW_CONNECTION_ID,
+ Self::RetireConnectionId { .. } => FRAME_TYPE_RETIRE_CONNECTION_ID,
+ Self::PathChallenge { .. } => FRAME_TYPE_PATH_CHALLENGE,
+ Self::PathResponse { .. } => FRAME_TYPE_PATH_RESPONSE,
+ Self::ConnectionClose { error_code, .. } => {
+ FRAME_TYPE_CONNECTION_CLOSE_TRANSPORT + error_code.frame_type_bit()
+ }
+ Self::HandshakeDone => FRAME_TYPE_HANDSHAKE_DONE,
+ Self::AckFrequency { .. } => FRAME_TYPE_ACK_FREQUENCY,
+ Self::Datagram { fill, .. } => {
+ if *fill {
+ FRAME_TYPE_DATAGRAM
+ } else {
+ FRAME_TYPE_DATAGRAM_WITH_LEN
+ }
+ }
+ }
+ }
+
+ pub fn is_stream(&self) -> bool {
+ matches!(
+ self,
+ Self::ResetStream { .. }
+ | Self::StopSending { .. }
+ | Self::Stream { .. }
+ | Self::MaxData { .. }
+ | Self::MaxStreamData { .. }
+ | Self::MaxStreams { .. }
+ | Self::DataBlocked { .. }
+ | Self::StreamDataBlocked { .. }
+ | Self::StreamsBlocked { .. }
+ )
+ }
+
+ pub fn stream_type(fin: bool, nonzero_offset: bool, fill: bool) -> u64 {
+ let mut t = FRAME_TYPE_STREAM;
+ if fin {
+ t |= STREAM_FRAME_BIT_FIN;
+ }
+ if nonzero_offset {
+ t |= STREAM_FRAME_BIT_OFF;
+ }
+ if !fill {
+ t |= STREAM_FRAME_BIT_LEN;
+ }
+ t
+ }
+
+ /// If the frame causes a recipient to generate an ACK within its
+ /// advertised maximum acknowledgement delay.
+ pub fn ack_eliciting(&self) -> bool {
+ !matches!(
+ self,
+ Self::Ack { .. } | Self::Padding | Self::ConnectionClose { .. }
+ )
+ }
+
+ /// If the frame can be sent in a path probe
+ /// without initiating migration to that path.
+ pub fn path_probing(&self) -> bool {
+ matches!(
+ self,
+ Self::Padding
+ | Self::NewConnectionId { .. }
+ | Self::PathChallenge { .. }
+ | Self::PathResponse { .. }
+ )
+ }
+
+ /// Converts AckRanges as encoded in a ACK frame (see -transport
+ /// 19.3.1) into ranges of acked packets (end, start), inclusive of
+ /// start and end values.
+ pub fn decode_ack_frame(
+ largest_acked: u64,
+ first_ack_range: u64,
+ ack_ranges: &[AckRange],
+ ) -> Res<Vec<RangeInclusive<u64>>> {
+ let mut acked_ranges = Vec::with_capacity(ack_ranges.len() + 1);
+
+ if largest_acked < first_ack_range {
+ return Err(Error::FrameEncodingError);
+ }
+ acked_ranges.push((largest_acked - first_ack_range)..=largest_acked);
+ if !ack_ranges.is_empty() && largest_acked < first_ack_range + 1 {
+ return Err(Error::FrameEncodingError);
+ }
+ let mut cur = if ack_ranges.is_empty() {
+ 0
+ } else {
+ largest_acked - first_ack_range - 1
+ };
+ for r in ack_ranges {
+ if cur < r.gap + 1 {
+ return Err(Error::FrameEncodingError);
+ }
+ cur = cur - r.gap - 1;
+
+ if cur < r.range {
+ return Err(Error::FrameEncodingError);
+ }
+ acked_ranges.push((cur - r.range)..=cur);
+
+ if cur > r.range + 1 {
+ cur -= r.range + 1;
+ } else {
+ cur -= r.range;
+ }
+ }
+
+ Ok(acked_ranges)
+ }
+
+ pub fn dump(&self) -> Option<String> {
+ match self {
+ Self::Crypto { offset, data } => Some(format!(
+ "Crypto {{ offset: {}, len: {} }}",
+ offset,
+ data.len()
+ )),
+ Self::Stream {
+ stream_id,
+ offset,
+ fill,
+ data,
+ fin,
+ } => Some(format!(
+ "Stream {{ stream_id: {}, offset: {}, len: {}{}, fin: {} }}",
+ stream_id.as_u64(),
+ offset,
+ if *fill { ">>" } else { "" },
+ data.len(),
+ fin,
+ )),
+ Self::Padding => None,
+ Self::Datagram { data, .. } => Some(format!("Datagram {{ len: {} }}", data.len())),
+ _ => Some(format!("{self:?}")),
+ }
+ }
+
+ pub fn is_allowed(&self, pt: PacketType) -> bool {
+ match self {
+ Self::Padding | Self::Ping => true,
+ Self::Crypto { .. }
+ | Self::Ack { .. }
+ | Self::ConnectionClose {
+ error_code: CloseError::Transport(_),
+ ..
+ } => pt != PacketType::ZeroRtt,
+ Self::NewToken { .. } | Self::ConnectionClose { .. } => pt == PacketType::Short,
+ _ => pt == PacketType::ZeroRtt || pt == PacketType::Short,
+ }
+ }
+
+ pub fn decode(dec: &mut Decoder<'a>) -> Res<Self> {
+ /// Maximum ACK Range Count in ACK Frame
+ ///
+ /// Given a max UDP datagram size of 64k bytes and a minimum ACK Range size of 2
+ /// bytes (2 QUIC varints), a single datagram can at most contain 32k ACK
+ /// Ranges.
+ ///
+ /// Note that the maximum (jumbogram) Ethernet MTU of 9216 or on the
+ /// Internet the regular Ethernet MTU of 1518 are more realistically to
+ /// be the limiting factor. Though for simplicity the higher limit is chosen.
+ const MAX_ACK_RANGE_COUNT: u64 = 32 * 1024;
+
+ fn d<T>(v: Option<T>) -> Res<T> {
+ v.ok_or(Error::NoMoreData)
+ }
+ fn dv(dec: &mut Decoder) -> Res<u64> {
+ d(dec.decode_varint())
+ }
+
+ // TODO(ekr@rtfm.com): check for minimal encoding
+ let t = d(dec.decode_varint())?;
+ match t {
+ FRAME_TYPE_PADDING => Ok(Self::Padding),
+ FRAME_TYPE_PING => Ok(Self::Ping),
+ FRAME_TYPE_RESET_STREAM => Ok(Self::ResetStream {
+ stream_id: StreamId::from(dv(dec)?),
+ application_error_code: d(dec.decode_varint())?,
+ final_size: match dec.decode_varint() {
+ Some(v) => v,
+ _ => return Err(Error::NoMoreData),
+ },
+ }),
+ FRAME_TYPE_ACK | FRAME_TYPE_ACK_ECN => {
+ let la = dv(dec)?;
+ let ad = dv(dec)?;
+ let nr = dv(dec).and_then(|nr| {
+ if nr < MAX_ACK_RANGE_COUNT {
+ Ok(nr)
+ } else {
+ Err(Error::TooMuchData)
+ }
+ })?;
+ let fa = dv(dec)?;
+ let mut arr: Vec<AckRange> = Vec::with_capacity(nr as usize);
+ for _ in 0..nr {
+ let ar = AckRange {
+ gap: dv(dec)?,
+ range: dv(dec)?,
+ };
+ arr.push(ar);
+ }
+
+ // Now check for the values for ACK_ECN.
+ if t == FRAME_TYPE_ACK_ECN {
+ dv(dec)?;
+ dv(dec)?;
+ dv(dec)?;
+ }
+
+ Ok(Self::Ack {
+ largest_acknowledged: la,
+ ack_delay: ad,
+ first_ack_range: fa,
+ ack_ranges: arr,
+ })
+ }
+ FRAME_TYPE_STOP_SENDING => Ok(Self::StopSending {
+ stream_id: StreamId::from(dv(dec)?),
+ application_error_code: d(dec.decode_varint())?,
+ }),
+ FRAME_TYPE_CRYPTO => {
+ let offset = dv(dec)?;
+ let data = d(dec.decode_vvec())?;
+ if offset + u64::try_from(data.len()).unwrap() > ((1 << 62) - 1) {
+ return Err(Error::FrameEncodingError);
+ }
+ Ok(Self::Crypto { offset, data })
+ }
+ FRAME_TYPE_NEW_TOKEN => {
+ let token = d(dec.decode_vvec())?;
+ if token.is_empty() {
+ return Err(Error::FrameEncodingError);
+ }
+ Ok(Self::NewToken { token })
+ }
+ FRAME_TYPE_STREAM..=FRAME_TYPE_STREAM_MAX => {
+ let s = dv(dec)?;
+ let o = if t & STREAM_FRAME_BIT_OFF == 0 {
+ 0
+ } else {
+ dv(dec)?
+ };
+ let fill = (t & STREAM_FRAME_BIT_LEN) == 0;
+ let data = if fill {
+ qtrace!("STREAM frame, extends to the end of the packet");
+ dec.decode_remainder()
+ } else {
+ qtrace!("STREAM frame, with length");
+ d(dec.decode_vvec())?
+ };
+ if o + u64::try_from(data.len()).unwrap() > ((1 << 62) - 1) {
+ return Err(Error::FrameEncodingError);
+ }
+ Ok(Self::Stream {
+ fin: (t & STREAM_FRAME_BIT_FIN) != 0,
+ stream_id: StreamId::from(s),
+ offset: o,
+ data,
+ fill,
+ })
+ }
+ FRAME_TYPE_MAX_DATA => Ok(Self::MaxData {
+ maximum_data: dv(dec)?,
+ }),
+ FRAME_TYPE_MAX_STREAM_DATA => Ok(Self::MaxStreamData {
+ stream_id: StreamId::from(dv(dec)?),
+ maximum_stream_data: dv(dec)?,
+ }),
+ FRAME_TYPE_MAX_STREAMS_BIDI | FRAME_TYPE_MAX_STREAMS_UNIDI => {
+ let m = dv(dec)?;
+ if m > (1 << 60) {
+ return Err(Error::StreamLimitError);
+ }
+ Ok(Self::MaxStreams {
+ stream_type: Self::stream_type_from_bit(t),
+ maximum_streams: m,
+ })
+ }
+ FRAME_TYPE_DATA_BLOCKED => Ok(Self::DataBlocked {
+ data_limit: dv(dec)?,
+ }),
+ FRAME_TYPE_STREAM_DATA_BLOCKED => Ok(Self::StreamDataBlocked {
+ stream_id: dv(dec)?.into(),
+ stream_data_limit: dv(dec)?,
+ }),
+ FRAME_TYPE_STREAMS_BLOCKED_BIDI | FRAME_TYPE_STREAMS_BLOCKED_UNIDI => {
+ Ok(Self::StreamsBlocked {
+ stream_type: Self::stream_type_from_bit(t),
+ stream_limit: dv(dec)?,
+ })
+ }
+ FRAME_TYPE_NEW_CONNECTION_ID => {
+ let sequence_number = dv(dec)?;
+ let retire_prior = dv(dec)?;
+ let connection_id = d(dec.decode_vec(1))?;
+ if connection_id.len() > MAX_CONNECTION_ID_LEN {
+ return Err(Error::DecodingFrame);
+ }
+ let srt = d(dec.decode(16))?;
+ let stateless_reset_token = <&[_; 16]>::try_from(srt).unwrap();
+
+ Ok(Self::NewConnectionId {
+ sequence_number,
+ retire_prior,
+ connection_id,
+ stateless_reset_token,
+ })
+ }
+ FRAME_TYPE_RETIRE_CONNECTION_ID => Ok(Self::RetireConnectionId {
+ sequence_number: dv(dec)?,
+ }),
+ FRAME_TYPE_PATH_CHALLENGE => {
+ let data = d(dec.decode(8))?;
+ let mut datav: [u8; 8] = [0; 8];
+ datav.copy_from_slice(data);
+ Ok(Self::PathChallenge { data: datav })
+ }
+ FRAME_TYPE_PATH_RESPONSE => {
+ let data = d(dec.decode(8))?;
+ let mut datav: [u8; 8] = [0; 8];
+ datav.copy_from_slice(data);
+ Ok(Self::PathResponse { data: datav })
+ }
+ FRAME_TYPE_CONNECTION_CLOSE_TRANSPORT | FRAME_TYPE_CONNECTION_CLOSE_APPLICATION => {
+ let error_code = CloseError::from_type_bit(t, d(dec.decode_varint())?);
+ let frame_type = if t == FRAME_TYPE_CONNECTION_CLOSE_TRANSPORT {
+ dv(dec)?
+ } else {
+ 0
+ };
+ // We can tolerate this copy for now.
+ let reason_phrase = d(dec.decode_vvec())?.to_vec();
+ Ok(Self::ConnectionClose {
+ error_code,
+ frame_type,
+ reason_phrase,
+ })
+ }
+ FRAME_TYPE_HANDSHAKE_DONE => Ok(Self::HandshakeDone),
+ FRAME_TYPE_ACK_FREQUENCY => {
+ let seqno = dv(dec)?;
+ let tolerance = dv(dec)?;
+ if tolerance == 0 {
+ return Err(Error::FrameEncodingError);
+ }
+ let delay = dv(dec)?;
+ let ignore_order = match d(dec.decode_uint(1))? {
+ 0 => false,
+ 1 => true,
+ _ => return Err(Error::FrameEncodingError),
+ };
+ Ok(Self::AckFrequency {
+ seqno,
+ tolerance,
+ delay,
+ ignore_order,
+ })
+ }
+ FRAME_TYPE_DATAGRAM | FRAME_TYPE_DATAGRAM_WITH_LEN => {
+ let fill = (t & DATAGRAM_FRAME_BIT_LEN) == 0;
+ let data = if fill {
+ qtrace!("DATAGRAM frame, extends to the end of the packet");
+ dec.decode_remainder()
+ } else {
+ qtrace!("DATAGRAM frame, with length");
+ d(dec.decode_vvec())?
+ };
+ Ok(Self::Datagram { data, fill })
+ }
+ _ => Err(Error::UnknownFrameType),
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use neqo_common::{Decoder, Encoder};
+
+ use super::*;
+
+ fn just_dec(f: &Frame, s: &str) {
+ let encoded = Encoder::from_hex(s);
+ let decoded = Frame::decode(&mut encoded.as_decoder()).unwrap();
+ assert_eq!(*f, decoded);
+ }
+
+ #[test]
+ fn padding() {
+ let f = Frame::Padding;
+ just_dec(&f, "00");
+ }
+
+ #[test]
+ fn ping() {
+ let f = Frame::Ping;
+ just_dec(&f, "01");
+ }
+
+ #[test]
+ fn ack() {
+ let ar = vec![AckRange { gap: 1, range: 2 }, AckRange { gap: 3, range: 4 }];
+
+ let f = Frame::Ack {
+ largest_acknowledged: 0x1234,
+ ack_delay: 0x1235,
+ first_ack_range: 0x1236,
+ ack_ranges: ar,
+ };
+
+ just_dec(&f, "025234523502523601020304");
+
+ // Try to parse ACK_ECN without ECN values
+ let enc = Encoder::from_hex("035234523502523601020304");
+ let mut dec = enc.as_decoder();
+ assert_eq!(Frame::decode(&mut dec).unwrap_err(), Error::NoMoreData);
+
+ // Try to parse ACK_ECN without ECN values
+ let enc = Encoder::from_hex("035234523502523601020304010203");
+ let mut dec = enc.as_decoder();
+ assert_eq!(Frame::decode(&mut dec).unwrap(), f);
+ }
+
+ #[test]
+ fn reset_stream() {
+ let f = Frame::ResetStream {
+ stream_id: StreamId::from(0x1234),
+ application_error_code: 0x77,
+ final_size: 0x3456,
+ };
+
+ just_dec(&f, "04523440777456");
+ }
+
+ #[test]
+ fn stop_sending() {
+ let f = Frame::StopSending {
+ stream_id: StreamId::from(63),
+ application_error_code: 0x77,
+ };
+
+ just_dec(&f, "053F4077");
+ }
+
+ #[test]
+ fn crypto() {
+ let f = Frame::Crypto {
+ offset: 1,
+ data: &[1, 2, 3],
+ };
+
+ just_dec(&f, "060103010203");
+ }
+
+ #[test]
+ fn new_token() {
+ let f = Frame::NewToken {
+ token: &[0x12, 0x34, 0x56],
+ };
+
+ just_dec(&f, "0703123456");
+ }
+
+ #[test]
+ fn empty_new_token() {
+ let mut dec = Decoder::from(&[0x07, 0x00][..]);
+ assert_eq!(
+ Frame::decode(&mut dec).unwrap_err(),
+ Error::FrameEncodingError
+ );
+ }
+
+ #[test]
+ fn stream() {
+ // First, just set the length bit.
+ let f = Frame::Stream {
+ fin: false,
+ stream_id: StreamId::from(5),
+ offset: 0,
+ data: &[1, 2, 3],
+ fill: false,
+ };
+
+ just_dec(&f, "0a0503010203");
+
+ // Now with offset != 0 and FIN
+ let f = Frame::Stream {
+ fin: true,
+ stream_id: StreamId::from(5),
+ offset: 1,
+ data: &[1, 2, 3],
+ fill: false,
+ };
+ just_dec(&f, "0f050103010203");
+
+ // Now to fill the packet.
+ let f = Frame::Stream {
+ fin: true,
+ stream_id: StreamId::from(5),
+ offset: 0,
+ data: &[1, 2, 3],
+ fill: true,
+ };
+ just_dec(&f, "0905010203");
+ }
+
+ #[test]
+ fn max_data() {
+ let f = Frame::MaxData {
+ maximum_data: 0x1234,
+ };
+
+ just_dec(&f, "105234");
+ }
+
+ #[test]
+ fn max_stream_data() {
+ let f = Frame::MaxStreamData {
+ stream_id: StreamId::from(5),
+ maximum_stream_data: 0x1234,
+ };
+
+ just_dec(&f, "11055234");
+ }
+
+ #[test]
+ fn max_streams() {
+ let mut f = Frame::MaxStreams {
+ stream_type: StreamType::BiDi,
+ maximum_streams: 0x1234,
+ };
+
+ just_dec(&f, "125234");
+
+ f = Frame::MaxStreams {
+ stream_type: StreamType::UniDi,
+ maximum_streams: 0x1234,
+ };
+
+ just_dec(&f, "135234");
+ }
+
+ #[test]
+ fn data_blocked() {
+ let f = Frame::DataBlocked { data_limit: 0x1234 };
+
+ just_dec(&f, "145234");
+ }
+
+ #[test]
+ fn stream_data_blocked() {
+ let f = Frame::StreamDataBlocked {
+ stream_id: StreamId::from(5),
+ stream_data_limit: 0x1234,
+ };
+
+ just_dec(&f, "15055234");
+ }
+
+ #[test]
+ fn streams_blocked() {
+ let mut f = Frame::StreamsBlocked {
+ stream_type: StreamType::BiDi,
+ stream_limit: 0x1234,
+ };
+
+ just_dec(&f, "165234");
+
+ f = Frame::StreamsBlocked {
+ stream_type: StreamType::UniDi,
+ stream_limit: 0x1234,
+ };
+
+ just_dec(&f, "175234");
+ }
+
+ #[test]
+ fn new_connection_id() {
+ let f = Frame::NewConnectionId {
+ sequence_number: 0x1234,
+ retire_prior: 0,
+ connection_id: &[0x01, 0x02],
+ stateless_reset_token: &[9; 16],
+ };
+
+ just_dec(&f, "1852340002010209090909090909090909090909090909");
+ }
+
+ #[test]
+ fn too_large_new_connection_id() {
+ let mut enc = Encoder::from_hex("18523400"); // up to the CID
+ enc.encode_vvec(&[0x0c; MAX_CONNECTION_ID_LEN + 10]);
+ enc.encode(&[0x11; 16][..]);
+ assert_eq!(
+ Frame::decode(&mut enc.as_decoder()).unwrap_err(),
+ Error::DecodingFrame
+ );
+ }
+
+ #[test]
+ fn retire_connection_id() {
+ let f = Frame::RetireConnectionId {
+ sequence_number: 0x1234,
+ };
+
+ just_dec(&f, "195234");
+ }
+
+ #[test]
+ fn path_challenge() {
+ let f = Frame::PathChallenge { data: [9; 8] };
+
+ just_dec(&f, "1a0909090909090909");
+ }
+
+ #[test]
+ fn path_response() {
+ let f = Frame::PathResponse { data: [9; 8] };
+
+ just_dec(&f, "1b0909090909090909");
+ }
+
+ #[test]
+ fn connection_close_transport() {
+ let f = Frame::ConnectionClose {
+ error_code: CloseError::Transport(0x5678),
+ frame_type: 0x1234,
+ reason_phrase: vec![0x01, 0x02, 0x03],
+ };
+
+ just_dec(&f, "1c80005678523403010203");
+ }
+
+ #[test]
+ fn connection_close_application() {
+ let f = Frame::ConnectionClose {
+ error_code: CloseError::Application(0x5678),
+ frame_type: 0,
+ reason_phrase: vec![0x01, 0x02, 0x03],
+ };
+
+ just_dec(&f, "1d8000567803010203");
+ }
+
+ #[test]
+ fn test_compare() {
+ let f1 = Frame::Padding;
+ let f2 = Frame::Padding;
+ let f3 = Frame::Crypto {
+ offset: 0,
+ data: &[1, 2, 3],
+ };
+ let f4 = Frame::Crypto {
+ offset: 0,
+ data: &[1, 2, 3],
+ };
+ let f5 = Frame::Crypto {
+ offset: 1,
+ data: &[1, 2, 3],
+ };
+ let f6 = Frame::Crypto {
+ offset: 0,
+ data: &[1, 2, 4],
+ };
+
+ assert_eq!(f1, f2);
+ assert_ne!(f1, f3);
+ assert_eq!(f3, f4);
+ assert_ne!(f3, f5);
+ assert_ne!(f3, f6);
+ }
+
+ #[test]
+ fn decode_ack_frame() {
+ let res = Frame::decode_ack_frame(7, 2, &[AckRange { gap: 0, range: 3 }]);
+ assert!(res.is_ok());
+ assert_eq!(res.unwrap(), vec![5..=7, 0..=3]);
+ }
+
+ #[test]
+ fn ack_frequency() {
+ let f = Frame::AckFrequency {
+ seqno: 10,
+ tolerance: 5,
+ delay: 2000,
+ ignore_order: true,
+ };
+ just_dec(&f, "40af0a0547d001");
+ }
+
+ #[test]
+ fn ack_frequency_ignore_error_error() {
+ let enc = Encoder::from_hex("40af0a0547d003"); // ignore_order of 3
+ assert_eq!(
+ Frame::decode(&mut enc.as_decoder()).unwrap_err(),
+ Error::FrameEncodingError
+ );
+ }
+
+ /// Hopefully this test is eventually redundant.
+ #[test]
+ fn ack_frequency_zero_packets() {
+ let enc = Encoder::from_hex("40af0a000101"); // packets of 0
+ assert_eq!(
+ Frame::decode(&mut enc.as_decoder()).unwrap_err(),
+ Error::FrameEncodingError
+ );
+ }
+
+ #[test]
+ fn datagram() {
+ // Without the length bit.
+ let f = Frame::Datagram {
+ data: &[1, 2, 3],
+ fill: true,
+ };
+
+ just_dec(&f, "4030010203");
+
+ // With the length bit.
+ let f = Frame::Datagram {
+ data: &[1, 2, 3],
+ fill: false,
+ };
+ just_dec(&f, "403103010203");
+ }
+
+ #[test]
+ fn frame_decode_enforces_bound_on_ack_range() {
+ let mut e = Encoder::new();
+
+ e.encode_varint(FRAME_TYPE_ACK);
+ e.encode_varint(0u64); // largest acknowledged
+ e.encode_varint(0u64); // ACK delay
+ e.encode_varint(u32::MAX); // ACK range count = huge, but maybe available for allocation
+
+ assert_eq!(Err(Error::TooMuchData), Frame::decode(&mut e.as_decoder()));
+ }
+}