diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rust/neqo-transport/src/frame.rs | |
parent | Initial commit. (diff) | |
download | firefox-esr-upstream.tar.xz firefox-esr-upstream.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
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.rs | 946 |
1 files changed, 946 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..8d56fd3000 --- /dev/null +++ b/third_party/rust/neqo-transport/src/frame.rs @@ -0,0 +1,946 @@ +// 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 neqo_common::{qtrace, Decoder}; + +use crate::cid::MAX_CONNECTION_ID_LEN; +use crate::packet::PacketType; +use crate::stream_id::{StreamId, StreamType}; +use crate::{AppError, ConnectionError, Error, Res, TransportError}; + +use std::convert::TryFrom; +use std::ops::RangeInclusive; + +#[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> { + 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)?; + 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 super::*; + use neqo_common::{Decoder, Encoder}; + + 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"); + } +} |