#![allow(dead_code)]
use alloc::vec::Vec;
use super::varint;
use crate::tls::Error;
pub(crate) const MAX_STREAMS_LIMIT: u64 = 1u64 << 60;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub(crate) enum StreamDir {
Bidi,
Uni,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
pub(crate) struct EcnCounts {
pub ect0: u64,
pub ect1: u64,
pub ce: u64,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum Frame<'a> {
Padding(usize),
Ping,
Ack {
largest: u64,
ack_delay: u64,
ranges_raw: &'a [u8],
first_range: u64,
ecn: Option<EcnCounts>,
},
ResetStream {
id: u64,
code: u64,
final_size: u64,
},
StopSending {
id: u64,
code: u64,
},
Crypto {
offset: u64,
data: &'a [u8],
},
NewToken {
token: &'a [u8],
},
Stream {
id: u64,
offset: u64,
fin: bool,
data: &'a [u8],
},
MaxData(u64),
MaxStreamData {
id: u64,
limit: u64,
},
MaxStreams {
dir: StreamDir,
limit: u64,
},
DataBlocked(u64),
StreamDataBlocked {
id: u64,
limit: u64,
},
StreamsBlocked {
dir: StreamDir,
limit: u64,
},
NewConnectionId {
seq: u64,
retire_prior_to: u64,
cid: &'a [u8],
reset_token: [u8; 16],
},
RetireConnectionId {
seq: u64,
},
PathChallenge([u8; 8]),
PathResponse([u8; 8]),
ConnectionClose {
error: u64,
frame_type: Option<u64>,
reason: &'a [u8],
},
HandshakeDone,
Datagram {
data: &'a [u8],
},
}
impl<'a> Frame<'a> {
pub(crate) fn ack_range_iter(&self) -> AckRangeIter<'a> {
match *self {
Frame::Ack { ranges_raw, .. } => AckRangeIter { buf: ranges_raw },
_ => AckRangeIter { buf: &[] },
}
}
pub(crate) fn decode(buf: &'a [u8]) -> Result<(Frame<'a>, usize), Error> {
if buf.is_empty() {
return Err(Error::Decode);
}
let t = buf[0];
let mut p = 1usize;
match t {
0x00 => {
let mut n = 1;
while p < buf.len() && buf[p] == 0x00 {
p += 1;
n += 1;
}
Ok((Frame::Padding(n), p))
}
0x01 => Ok((Frame::Ping, 1)),
0x02 | 0x03 => {
let (largest, n) = varint::decode(&buf[p..])?;
p += n;
let (ack_delay, n) = varint::decode(&buf[p..])?;
p += n;
let (range_count, n) = varint::decode(&buf[p..])?;
p += n;
let (first_range, n) = varint::decode(&buf[p..])?;
p += n;
let raw_start = p;
for _ in 0..range_count {
let (_, n) = varint::decode(&buf[p..])?;
p += n;
let (_, n) = varint::decode(&buf[p..])?;
p += n;
}
let raw_end = p;
let ecn = if t == 0x03 {
let (ect0, n) = varint::decode(&buf[p..])?;
p += n;
let (ect1, n) = varint::decode(&buf[p..])?;
p += n;
let (ce, n) = varint::decode(&buf[p..])?;
p += n;
Some(EcnCounts { ect0, ect1, ce })
} else {
None
};
Ok((
Frame::Ack {
largest,
ack_delay,
ranges_raw: &buf[raw_start..raw_end],
first_range,
ecn,
},
p,
))
}
0x04 => {
let (id, n) = varint::decode(&buf[p..])?;
p += n;
let (code, n) = varint::decode(&buf[p..])?;
p += n;
let (final_size, n) = varint::decode(&buf[p..])?;
p += n;
Ok((
Frame::ResetStream {
id,
code,
final_size,
},
p,
))
}
0x05 => {
let (id, n) = varint::decode(&buf[p..])?;
p += n;
let (code, n) = varint::decode(&buf[p..])?;
p += n;
Ok((Frame::StopSending { id, code }, p))
}
0x06 => {
let (offset, n) = varint::decode(&buf[p..])?;
p += n;
let (length, n) = varint::decode(&buf[p..])?;
p += n;
match offset.checked_add(length) {
Some(end) if end <= varint::MAX => {}
_ => return Err(Error::Decode),
}
let length = length as usize;
if buf.len() - p < length {
return Err(Error::Decode);
}
let data = &buf[p..p + length];
p += length;
Ok((Frame::Crypto { offset, data }, p))
}
0x07 => {
let (length, n) = varint::decode(&buf[p..])?;
p += n;
let length = length as usize;
if buf.len() - p < length {
return Err(Error::Decode);
}
let token = &buf[p..p + length];
p += length;
Ok((Frame::NewToken { token }, p))
}
0x08..=0x0F => {
let fin = (t & 0x01) != 0;
let has_len = (t & 0x02) != 0;
let has_off = (t & 0x04) != 0;
let (id, n) = varint::decode(&buf[p..])?;
p += n;
let offset = if has_off {
let (off, n) = varint::decode(&buf[p..])?;
p += n;
off
} else {
0
};
let data = if has_len {
let (length, n) = varint::decode(&buf[p..])?;
p += n;
let length = length as usize;
if buf.len() - p < length {
return Err(Error::Decode);
}
let d = &buf[p..p + length];
p += length;
d
} else {
let d = &buf[p..];
p = buf.len();
d
};
Ok((
Frame::Stream {
id,
offset,
fin,
data,
},
p,
))
}
0x10 => {
let (v, n) = varint::decode(&buf[p..])?;
p += n;
Ok((Frame::MaxData(v), p))
}
0x11 => {
let (id, n) = varint::decode(&buf[p..])?;
p += n;
let (limit, n) = varint::decode(&buf[p..])?;
p += n;
Ok((Frame::MaxStreamData { id, limit }, p))
}
0x12 | 0x13 => {
let (limit, n) = varint::decode(&buf[p..])?;
p += n;
if limit > MAX_STREAMS_LIMIT {
return Err(Error::Decode);
}
let dir = if t == 0x12 {
StreamDir::Bidi
} else {
StreamDir::Uni
};
Ok((Frame::MaxStreams { dir, limit }, p))
}
0x14 => {
let (v, n) = varint::decode(&buf[p..])?;
p += n;
Ok((Frame::DataBlocked(v), p))
}
0x15 => {
let (id, n) = varint::decode(&buf[p..])?;
p += n;
let (limit, n) = varint::decode(&buf[p..])?;
p += n;
Ok((Frame::StreamDataBlocked { id, limit }, p))
}
0x16 | 0x17 => {
let (limit, n) = varint::decode(&buf[p..])?;
p += n;
if limit > MAX_STREAMS_LIMIT {
return Err(Error::Decode);
}
let dir = if t == 0x16 {
StreamDir::Bidi
} else {
StreamDir::Uni
};
Ok((Frame::StreamsBlocked { dir, limit }, p))
}
0x18 => {
let (seq, n) = varint::decode(&buf[p..])?;
p += n;
let (retire_prior_to, n) = varint::decode(&buf[p..])?;
p += n;
if buf.len() - p < 1 {
return Err(Error::Decode);
}
let cid_len = buf[p] as usize;
p += 1;
if cid_len == 0 || cid_len > 20 {
return Err(Error::Decode);
}
if buf.len() - p < cid_len + 16 {
return Err(Error::Decode);
}
let cid = &buf[p..p + cid_len];
p += cid_len;
let mut reset_token = [0u8; 16];
reset_token.copy_from_slice(&buf[p..p + 16]);
p += 16;
Ok((
Frame::NewConnectionId {
seq,
retire_prior_to,
cid,
reset_token,
},
p,
))
}
0x19 => {
let (seq, n) = varint::decode(&buf[p..])?;
p += n;
Ok((Frame::RetireConnectionId { seq }, p))
}
0x1A => {
if buf.len() - p < 8 {
return Err(Error::Decode);
}
let mut data = [0u8; 8];
data.copy_from_slice(&buf[p..p + 8]);
p += 8;
Ok((Frame::PathChallenge(data), p))
}
0x1B => {
if buf.len() - p < 8 {
return Err(Error::Decode);
}
let mut data = [0u8; 8];
data.copy_from_slice(&buf[p..p + 8]);
p += 8;
Ok((Frame::PathResponse(data), p))
}
0x1C | 0x1D => {
let (error, n) = varint::decode(&buf[p..])?;
p += n;
let frame_type = if t == 0x1C {
let (ft, n) = varint::decode(&buf[p..])?;
p += n;
Some(ft)
} else {
None
};
let (reason_len, n) = varint::decode(&buf[p..])?;
p += n;
let reason_len = reason_len as usize;
if buf.len() - p < reason_len {
return Err(Error::Decode);
}
let reason = &buf[p..p + reason_len];
p += reason_len;
Ok((
Frame::ConnectionClose {
error,
frame_type,
reason,
},
p,
))
}
0x1E => Ok((Frame::HandshakeDone, 1)),
0x30 => {
let data = &buf[p..];
Ok((Frame::Datagram { data }, buf.len()))
}
0x31 => {
let (length, n) = varint::decode(&buf[p..])?;
p += n;
let length = length as usize;
if buf.len() - p < length {
return Err(Error::Decode);
}
let data = &buf[p..p + length];
p += length;
Ok((Frame::Datagram { data }, p))
}
_ => Err(Error::Decode),
}
}
pub(crate) fn encode(&self, out: &mut Vec<u8>) {
match *self {
Frame::Padding(n) => {
for _ in 0..n {
out.push(0x00);
}
}
Frame::Ping => out.push(0x01),
Frame::Ack {
largest,
ack_delay,
ranges_raw,
first_range,
ecn,
} => {
out.push(if ecn.is_some() { 0x03 } else { 0x02 });
varint::encode(largest, out);
varint::encode(ack_delay, out);
let mut count = 0u64;
{
let mut p = 0;
while p < ranges_raw.len() {
let (_, n) = varint::decode(&ranges_raw[p..])
.expect("ack ranges_raw must be well-formed");
p += n;
let (_, n) = varint::decode(&ranges_raw[p..])
.expect("ack ranges_raw must be well-formed");
p += n;
count += 1;
}
}
varint::encode(count, out);
varint::encode(first_range, out);
out.extend_from_slice(ranges_raw);
if let Some(c) = ecn {
varint::encode(c.ect0, out);
varint::encode(c.ect1, out);
varint::encode(c.ce, out);
}
}
Frame::ResetStream {
id,
code,
final_size,
} => {
out.push(0x04);
varint::encode(id, out);
varint::encode(code, out);
varint::encode(final_size, out);
}
Frame::StopSending { id, code } => {
out.push(0x05);
varint::encode(id, out);
varint::encode(code, out);
}
Frame::Crypto { offset, data } => {
out.push(0x06);
varint::encode(offset, out);
varint::encode(data.len() as u64, out);
out.extend_from_slice(data);
}
Frame::NewToken { token } => {
out.push(0x07);
varint::encode(token.len() as u64, out);
out.extend_from_slice(token);
}
Frame::Stream {
id,
offset,
fin,
data,
} => {
let mut t = 0x08u8;
if offset != 0 {
t |= 0x04;
}
t |= 0x02; if fin {
t |= 0x01;
}
out.push(t);
varint::encode(id, out);
if offset != 0 {
varint::encode(offset, out);
}
varint::encode(data.len() as u64, out);
out.extend_from_slice(data);
}
Frame::MaxData(v) => {
out.push(0x10);
varint::encode(v, out);
}
Frame::MaxStreamData { id, limit } => {
out.push(0x11);
varint::encode(id, out);
varint::encode(limit, out);
}
Frame::MaxStreams { dir, limit } => {
out.push(match dir {
StreamDir::Bidi => 0x12,
StreamDir::Uni => 0x13,
});
varint::encode(limit, out);
}
Frame::DataBlocked(v) => {
out.push(0x14);
varint::encode(v, out);
}
Frame::StreamDataBlocked { id, limit } => {
out.push(0x15);
varint::encode(id, out);
varint::encode(limit, out);
}
Frame::StreamsBlocked { dir, limit } => {
out.push(match dir {
StreamDir::Bidi => 0x16,
StreamDir::Uni => 0x17,
});
varint::encode(limit, out);
}
Frame::NewConnectionId {
seq,
retire_prior_to,
cid,
reset_token,
} => {
out.push(0x18);
varint::encode(seq, out);
varint::encode(retire_prior_to, out);
assert!(cid.len() <= 20, "QUIC connection ID exceeds 20 bytes");
out.push(cid.len() as u8);
out.extend_from_slice(cid);
out.extend_from_slice(&reset_token);
}
Frame::RetireConnectionId { seq } => {
out.push(0x19);
varint::encode(seq, out);
}
Frame::PathChallenge(data) => {
out.push(0x1A);
out.extend_from_slice(&data);
}
Frame::PathResponse(data) => {
out.push(0x1B);
out.extend_from_slice(&data);
}
Frame::ConnectionClose {
error,
frame_type,
reason,
} => {
out.push(if frame_type.is_some() { 0x1C } else { 0x1D });
varint::encode(error, out);
if let Some(ft) = frame_type {
varint::encode(ft, out);
}
varint::encode(reason.len() as u64, out);
out.extend_from_slice(reason);
}
Frame::HandshakeDone => out.push(0x1E),
Frame::Datagram { data } => {
out.push(0x31);
varint::encode(data.len() as u64, out);
out.extend_from_slice(data);
}
}
}
}
#[derive(Debug, Clone)]
pub(crate) struct AckRangeIter<'a> {
buf: &'a [u8],
}
impl<'a> AckRangeIter<'a> {
pub(crate) fn from_raw(buf: &'a [u8]) -> Self {
Self { buf }
}
}
impl<'a> Iterator for AckRangeIter<'a> {
type Item = Result<(u64, u64), Error>;
fn next(&mut self) -> Option<Self::Item> {
if self.buf.is_empty() {
return None;
}
let (gap, n) = match varint::decode(self.buf) {
Ok(v) => v,
Err(e) => return Some(Err(e)),
};
self.buf = &self.buf[n..];
let (range_length, n) = match varint::decode(self.buf) {
Ok(v) => v,
Err(e) => return Some(Err(e)),
};
self.buf = &self.buf[n..];
Some(Ok((gap, range_length)))
}
}
#[derive(Debug, Clone)]
pub(crate) struct FrameIter<'a> {
buf: &'a [u8],
}
impl<'a> FrameIter<'a> {
pub(crate) fn new(buf: &'a [u8]) -> Self {
Self { buf }
}
}
impl<'a> Iterator for FrameIter<'a> {
type Item = Result<Frame<'a>, Error>;
fn next(&mut self) -> Option<Self::Item> {
if self.buf.is_empty() {
return None;
}
match Frame::decode(self.buf) {
Ok((f, n)) => {
self.buf = &self.buf[n..];
Some(Ok(f))
}
Err(e) => {
self.buf = &[];
Some(Err(e))
}
}
}
}
pub(crate) fn build_ack_ranges_raw(ranges: &super::pn::AckRanges) -> Option<(u64, u64, Vec<u8>)> {
let stored = ranges.ranges();
if stored.is_empty() {
return None;
}
let largest = *stored[0].end();
let first_range = largest - *stored[0].start();
let mut raw = Vec::new();
for i in 1..stored.len() {
let prev_start = *stored[i - 1].start();
let cur_end = *stored[i].end();
let cur_start = *stored[i].start();
let gap = prev_start - cur_end - 2;
let range_length = cur_end - cur_start;
varint::encode(gap, &mut raw);
varint::encode(range_length, &mut raw);
}
Some((largest, first_range, raw))
}
pub(crate) fn parse_ack_ranges(
largest: u64,
first_range: u64,
ranges_raw: &[u8],
) -> Result<super::pn::AckRanges, Error> {
let mut out = super::pn::AckRanges::new();
if first_range > largest {
return Err(Error::Decode);
}
let mut smallest_in_block = largest - first_range;
for pn in smallest_in_block..=largest {
out.insert(pn);
}
let it = AckRangeIter { buf: ranges_raw };
for pair in it {
let (gap, range_length) = pair?;
let gap_plus_two = gap.checked_add(2).ok_or(Error::Decode)?;
if smallest_in_block < gap_plus_two {
return Err(Error::Decode);
}
let next_largest = smallest_in_block - gap_plus_two;
if range_length > next_largest {
return Err(Error::Decode);
}
let next_smallest = next_largest - range_length;
for pn in next_smallest..=next_largest {
out.insert(pn);
}
smallest_in_block = next_smallest;
}
Ok(out)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::quic::pn::AckRanges;
#[test]
fn roundtrip_every_variant() {
let cases: Vec<Frame<'_>> = alloc::vec![
Frame::Padding(3),
Frame::Ping,
Frame::Ack {
largest: 42,
ack_delay: 7,
ranges_raw: &[],
first_range: 0,
ecn: None,
},
Frame::Ack {
largest: 42,
ack_delay: 7,
ranges_raw: &[],
first_range: 0,
ecn: Some(EcnCounts {
ect0: 1,
ect1: 2,
ce: 3,
}),
},
Frame::ResetStream {
id: 5,
code: 7,
final_size: 100,
},
Frame::StopSending { id: 5, code: 7 },
Frame::Crypto {
offset: 0,
data: b"hi",
},
Frame::NewToken { token: b"tok" },
Frame::Stream {
id: 4,
offset: 0,
fin: true,
data: b"hi",
},
Frame::Stream {
id: 8,
offset: 64,
fin: false,
data: b"abc",
},
Frame::MaxData(1024),
Frame::MaxStreamData { id: 0, limit: 8192 },
Frame::MaxStreams {
dir: StreamDir::Bidi,
limit: 100,
},
Frame::MaxStreams {
dir: StreamDir::Uni,
limit: 100,
},
Frame::DataBlocked(500),
Frame::StreamDataBlocked { id: 1, limit: 200 },
Frame::StreamsBlocked {
dir: StreamDir::Bidi,
limit: 3,
},
Frame::StreamsBlocked {
dir: StreamDir::Uni,
limit: 4,
},
Frame::NewConnectionId {
seq: 1,
retire_prior_to: 0,
cid: &[1, 2, 3, 4],
reset_token: [9u8; 16],
},
Frame::RetireConnectionId { seq: 2 },
Frame::PathChallenge([1, 2, 3, 4, 5, 6, 7, 8]),
Frame::PathResponse([8, 7, 6, 5, 4, 3, 2, 1]),
Frame::ConnectionClose {
error: 0,
frame_type: Some(0x08),
reason: b"bye",
},
Frame::ConnectionClose {
error: 1,
frame_type: None,
reason: b"app",
},
Frame::HandshakeDone,
Frame::Datagram { data: b"hello" },
];
for frame in &cases {
let mut buf = Vec::new();
frame.encode(&mut buf);
let (decoded, used) = Frame::decode(&buf).expect("decode");
assert_eq!(
used,
buf.len(),
"frame {:?} consumed {} of {}",
frame,
used,
buf.len()
);
assert_eq!(&decoded, frame, "frame {:?} roundtrip", frame);
}
}
#[test]
fn stream_off_len_fin_bit_decoding() {
let frame = Frame::Stream {
id: 4,
offset: 0,
fin: true,
data: b"hi",
};
let mut buf = Vec::new();
frame.encode(&mut buf);
assert_eq!(buf[0], 0x0B);
let frame = Frame::Stream {
id: 4,
offset: 16,
fin: false,
data: b"hi",
};
let mut buf = Vec::new();
frame.encode(&mut buf);
assert_eq!(buf[0], 0x0E);
}
#[test]
fn stream_no_length_extends_to_end() {
let buf = [0x08u8, 0x04, b'a', b'b', b'c', b'd'];
let (frame, used) = Frame::decode(&buf).expect("decode");
assert_eq!(used, buf.len());
match frame {
Frame::Stream {
id,
offset,
fin,
data,
} => {
assert_eq!(id, 4);
assert_eq!(offset, 0);
assert!(!fin);
assert_eq!(data, b"abcd");
}
other => panic!("expected STREAM, got {other:?}"),
}
}
#[test]
fn crypto_offset_plus_length_past_varint_max_rejected() {
let mut buf = alloc::vec![0x06u8];
varint::encode(varint::MAX, &mut buf);
varint::encode(1, &mut buf);
buf.push(b'x');
assert!(matches!(Frame::decode(&buf), Err(Error::Decode)));
let mut ok = alloc::vec![0x06u8];
varint::encode(varint::MAX - 1, &mut ok);
varint::encode(1, &mut ok);
ok.push(b'x');
let (frame, _used) = Frame::decode(&ok).expect("decode at limit");
match frame {
Frame::Crypto { offset, data } => {
assert_eq!(offset, varint::MAX - 1);
assert_eq!(data, b"x");
}
other => panic!("expected CRYPTO, got {other:?}"),
}
}
#[test]
fn new_connection_id_zero_length_cid_rejected() {
let mut buf = alloc::vec![0x18u8, 0x01, 0x00, 0x00];
buf.extend_from_slice(&[0u8; 16]);
assert!(matches!(Frame::decode(&buf), Err(Error::Decode)));
let mut ok = alloc::vec![0x18u8, 0x01, 0x00, 0x01, 0xAA];
ok.extend_from_slice(&[0u8; 16]);
let (frame, used) = Frame::decode(&ok).expect("decode 1-byte cid");
assert_eq!(used, ok.len());
match frame {
Frame::NewConnectionId { cid, .. } => assert_eq!(cid, &[0xAA]),
other => panic!("expected NEW_CONNECTION_ID, got {other:?}"),
}
}
#[test]
fn max_streams_and_streams_blocked_over_2_pow_60_rejected() {
for t in [0x12u8, 0x13, 0x16, 0x17] {
let mut bad = alloc::vec![t];
varint::encode(MAX_STREAMS_LIMIT + 1, &mut bad);
assert!(
matches!(Frame::decode(&bad), Err(Error::Decode)),
"type {t:#x} accepted a count > 2^60"
);
let mut ok = alloc::vec![t];
varint::encode(MAX_STREAMS_LIMIT, &mut ok);
let (frame, used) = Frame::decode(&ok).expect("decode at 2^60");
assert_eq!(used, ok.len());
match frame {
Frame::MaxStreams { limit, .. } | Frame::StreamsBlocked { limit, .. } => {
assert_eq!(limit, MAX_STREAMS_LIMIT, "type {t:#x}");
}
other => panic!("type {t:#x}: unexpected frame {other:?}"),
}
}
}
fn ack_ranges_from(pns: &[u64]) -> AckRanges {
let mut r = AckRanges::new();
for &p in pns {
r.insert(p);
}
r
}
#[test]
fn ack_ranges_roundtrip_via_iter() {
let cases: &[&[u64]] = &[
&[5], &[5, 6], &[5, 9], &[1, 2, 5, 6], &[0], &[0, 1, 2, 5, 6, 10, 20], ];
for pns in cases {
let ranges = ack_ranges_from(pns);
let (largest, first_range, raw) = build_ack_ranges_raw(&ranges).expect("non-empty");
let parsed = parse_ack_ranges(largest, first_range, &raw).expect("parse");
assert_eq!(
parsed, ranges,
"ack-ranges roundtrip failed for {pns:?} (raw={raw:?})"
);
}
}
#[test]
fn padding_run_length() {
let mut buf = Vec::new();
Frame::Padding(7).encode(&mut buf);
assert_eq!(buf, alloc::vec![0u8; 7]);
let (frame, used) = Frame::decode(&buf).expect("decode");
assert_eq!(used, buf.len());
assert_eq!(frame, Frame::Padding(7));
}
#[test]
fn connection_close_app_vs_transport() {
let f1 = Frame::ConnectionClose {
error: 0,
frame_type: Some(0x06),
reason: b"bad CRYPTO",
};
let mut buf = Vec::new();
f1.encode(&mut buf);
assert_eq!(buf[0], 0x1C);
let (decoded, _) = Frame::decode(&buf).expect("decode");
assert_eq!(decoded, f1);
let f2 = Frame::ConnectionClose {
error: 42,
frame_type: None,
reason: b"app close",
};
let mut buf = Vec::new();
f2.encode(&mut buf);
assert_eq!(buf[0], 0x1D);
let (decoded, _) = Frame::decode(&buf).expect("decode");
assert_eq!(decoded, f2);
}
#[test]
fn frame_iter_walks_packet() {
let mut buf = Vec::new();
Frame::Ping.encode(&mut buf);
Frame::MaxData(1024).encode(&mut buf);
Frame::HandshakeDone.encode(&mut buf);
let mut it = FrameIter::new(&buf);
assert_eq!(it.next().unwrap().unwrap(), Frame::Ping);
assert_eq!(it.next().unwrap().unwrap(), Frame::MaxData(1024));
assert_eq!(it.next().unwrap().unwrap(), Frame::HandshakeDone);
assert!(it.next().is_none());
}
}