h3: support frame and stream varints (draft 19)
diff --git a/examples/http3-client.rs b/examples/http3-client.rs
index 7124755..086b751 100644
--- a/examples/http3-client.rs
+++ b/examples/http3-client.rs
@@ -90,7 +90,7 @@
config.verify_peer(true);
- config.set_application_protos(b"\x05h3-18")?;
+ config.set_application_protos(b"\x05h3-19")?;
config.set_idle_timeout(5000);
config.set_max_packet_size(MAX_DATAGRAM_SIZE as u64);
@@ -182,7 +182,7 @@
}
if conn.is_established() && http3_conn.is_none() {
- if conn.application_proto() != b"h3-18" {
+ if conn.application_proto() != b"h3-19" {
// TODO a better error code?
conn.close(false, 0x0, b"I don't support your ALPNs")?;
break;
diff --git a/examples/http3-server.rs b/examples/http3-server.rs
index 667cc6f..09452eb 100644
--- a/examples/http3-server.rs
+++ b/examples/http3-server.rs
@@ -91,7 +91,7 @@
config.load_cert_chain_from_pem_file(args.get_str("--cert"))?;
config.load_priv_key_from_pem_file(args.get_str("--key"))?;
- config.set_application_protos(b"\x05h3-18")?;
+ config.set_application_protos(b"\x05h3-19")?;
config.set_idle_timeout(5000);
config.set_max_packet_size(MAX_DATAGRAM_SIZE as u64);
@@ -252,7 +252,7 @@
debug!("{} processed {} bytes", client.conn.trace_id(), read);
if client.conn.is_established() && client.http3_conn.is_none() {
- if client.conn.application_proto() != b"h3-18" {
+ if client.conn.application_proto() != b"h3-19" {
// TODO a better error code?
client.conn.close(
false,
diff --git a/src/h3/frame.rs b/src/h3/frame.rs
index 4754ae8..25f2343 100644
--- a/src/h3/frame.rs
+++ b/src/h3/frame.rs
@@ -24,27 +24,25 @@
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-use std::mem;
-
use super::Error;
use super::Result;
use crate::octets;
-pub const DATA_FRAME_TYPE_ID: u8 = 0x0;
-pub const HEADERS_FRAME_TYPE_ID: u8 = 0x1;
-pub const _PRIORITY_FRAME_TYPE_ID: u8 = 0x2;
-pub const CANCEL_PUSH_FRAME_TYPE_ID: u8 = 0x3;
-pub const SETTINGS_FRAME_TYPE_ID: u8 = 0x4;
-pub const PUSH_PROMISE_FRAME_TYPE_ID: u8 = 0x5;
-pub const GOAWAY_FRAME_TYPE_ID: u8 = 0x6;
-pub const MAX_PUSH_FRAME_TYPE_ID: u8 = 0xD;
-pub const DUPLICATE_PUSH_FRAME_TYPE_ID: u8 = 0xE;
+pub const DATA_FRAME_TYPE_ID: u64 = 0x0;
+pub const HEADERS_FRAME_TYPE_ID: u64 = 0x1;
+pub const PRIORITY_FRAME_TYPE_ID: u64 = 0x2;
+pub const CANCEL_PUSH_FRAME_TYPE_ID: u64 = 0x3;
+pub const SETTINGS_FRAME_TYPE_ID: u64 = 0x4;
+pub const PUSH_PROMISE_FRAME_TYPE_ID: u64 = 0x5;
+pub const GOAWAY_FRAME_TYPE_ID: u64 = 0x6;
+pub const MAX_PUSH_FRAME_TYPE_ID: u64 = 0xD;
+pub const DUPLICATE_PUSH_FRAME_TYPE_ID: u64 = 0xE;
-const SETTINGS_QPACK_MAX_TABLE_CAPACITY: u16 = 0x1;
-const SETTINGS_MAX_HEADER_LIST_SIZE: u16 = 0x6;
-const SETTINGS_QPACK_BLOCKED_STREAMS: u16 = 0x7;
-const SETTINGS_NUM_PLACEHOLDERS: u16 = 0x8;
+const SETTINGS_QPACK_MAX_TABLE_CAPACITY: u64 = 0x1;
+const SETTINGS_MAX_HEADER_LIST_SIZE: u64 = 0x6;
+const SETTINGS_QPACK_BLOCKED_STREAMS: u64 = 0x7;
+const SETTINGS_NUM_PLACEHOLDERS: u64 = 0x8;
#[derive(Clone, PartialEq)]
pub enum Frame {
@@ -65,6 +63,7 @@
max_header_list_size: Option<u64>,
qpack_max_table_capacity: Option<u64>,
qpack_blocked_streams: Option<u64>,
+ grease: Option<(u64, u64)>,
},
PushPromise {
@@ -87,7 +86,7 @@
impl Frame {
pub fn from_bytes(
- frame_type: u8, payload_length: u64, bytes: &mut [u8],
+ frame_type: u64, payload_length: u64, bytes: &mut [u8],
) -> Result<Frame> {
let mut b = octets::Octets::with_slice(bytes);
@@ -134,22 +133,22 @@
match self {
Frame::Data { payload } => {
+ b.put_varint(DATA_FRAME_TYPE_ID)?;
b.put_varint(payload.len() as u64)?;
- b.put_u8(DATA_FRAME_TYPE_ID)?;
b.put_bytes(payload.as_ref())?;
},
Frame::Headers { header_block } => {
+ b.put_varint(HEADERS_FRAME_TYPE_ID)?;
b.put_varint(header_block.len() as u64)?;
- b.put_u8(HEADERS_FRAME_TYPE_ID)?;
b.put_bytes(header_block.as_ref())?;
},
Frame::CancelPush { push_id } => {
+ b.put_varint(CANCEL_PUSH_FRAME_TYPE_ID)?;
b.put_varint(octets::varint_len(*push_id) as u64)?;
- b.put_u8(CANCEL_PUSH_FRAME_TYPE_ID)?;
b.put_varint(*push_id)?;
},
@@ -159,51 +158,62 @@
max_header_list_size,
qpack_max_table_capacity,
qpack_blocked_streams,
+ grease,
} => {
let mut len = 0;
if let Some(val) = num_placeholders {
- len += mem::size_of::<u16>();
+ len += octets::varint_len(SETTINGS_NUM_PLACEHOLDERS);
len += octets::varint_len(*val);
}
if let Some(val) = max_header_list_size {
- len += mem::size_of::<u16>();
+ len += octets::varint_len(SETTINGS_MAX_HEADER_LIST_SIZE);
len += octets::varint_len(*val);
}
if let Some(val) = qpack_max_table_capacity {
- len += mem::size_of::<u16>();
+ len += octets::varint_len(SETTINGS_QPACK_MAX_TABLE_CAPACITY);
len += octets::varint_len(*val);
}
if let Some(val) = qpack_blocked_streams {
- len += mem::size_of::<u16>();
+ len += octets::varint_len(SETTINGS_QPACK_BLOCKED_STREAMS);
len += octets::varint_len(*val);
}
+ if let Some(val) = grease {
+ len += octets::varint_len(val.0);
+ len += octets::varint_len(val.1);
+ }
+
+ b.put_varint(SETTINGS_FRAME_TYPE_ID)?;
b.put_varint(len as u64)?;
- b.put_u8(SETTINGS_FRAME_TYPE_ID)?;
if let Some(val) = num_placeholders {
- b.put_u16(0x8)?;
+ b.put_varint(SETTINGS_NUM_PLACEHOLDERS)?;
b.put_varint(*val as u64)?;
}
if let Some(val) = max_header_list_size {
- b.put_u16(0x6)?;
+ b.put_varint(SETTINGS_MAX_HEADER_LIST_SIZE)?;
b.put_varint(*val as u64)?;
}
if let Some(val) = qpack_max_table_capacity {
- b.put_u16(0x1)?;
+ b.put_varint(SETTINGS_QPACK_MAX_TABLE_CAPACITY)?;
b.put_varint(*val as u64)?;
}
if let Some(val) = qpack_blocked_streams {
- b.put_u16(0x7)?;
+ b.put_varint(SETTINGS_QPACK_BLOCKED_STREAMS)?;
b.put_varint(*val as u64)?;
}
+
+ if let Some(val) = grease {
+ b.put_varint(val.0)?;
+ b.put_varint(val.1)?;
+ }
},
Frame::PushPromise {
@@ -211,30 +221,30 @@
header_block,
} => {
let len = octets::varint_len(*push_id) + header_block.len();
+ b.put_varint(PUSH_PROMISE_FRAME_TYPE_ID)?;
b.put_varint(len as u64)?;
- b.put_u8(PUSH_PROMISE_FRAME_TYPE_ID)?;
b.put_varint(*push_id)?;
b.put_bytes(header_block.as_ref())?;
},
Frame::GoAway { stream_id } => {
+ b.put_varint(GOAWAY_FRAME_TYPE_ID)?;
b.put_varint(octets::varint_len(*stream_id) as u64)?;
- b.put_u8(GOAWAY_FRAME_TYPE_ID)?;
b.put_varint(*stream_id)?;
},
Frame::MaxPushId { push_id } => {
+ b.put_varint(MAX_PUSH_FRAME_TYPE_ID)?;
b.put_varint(octets::varint_len(*push_id) as u64)?;
- b.put_u8(MAX_PUSH_FRAME_TYPE_ID)?;
b.put_varint(*push_id)?;
},
Frame::DuplicatePush { push_id } => {
+ b.put_varint(DUPLICATE_PUSH_FRAME_TYPE_ID)?;
b.put_varint(octets::varint_len(*push_id) as u64)?;
- b.put_u8(DUPLICATE_PUSH_FRAME_TYPE_ID)?;
b.put_varint(*push_id)?;
},
@@ -264,6 +274,7 @@
max_header_list_size,
qpack_max_table_capacity,
qpack_blocked_streams,
+ ..
} => {
write!(f, "SETTINGS placeholders={:?}, max_headers={:?}, qpack_max_table={:?}, qpack_blocked={:?} ", num_placeholders, max_header_list_size, qpack_max_table_capacity, qpack_blocked_streams)?;
},
@@ -306,23 +317,24 @@
let mut qpack_blocked_streams = None;
while b.off() < settings_length {
- let setting = b.get_u16()?;
+ let setting_ty = b.get_varint()?;
+ let settings_val = b.get_varint()?;
- match setting {
+ match setting_ty {
SETTINGS_QPACK_MAX_TABLE_CAPACITY => {
- qpack_max_table_capacity = Some(b.get_varint()?);
+ qpack_max_table_capacity = Some(settings_val);
},
SETTINGS_MAX_HEADER_LIST_SIZE => {
- max_header_list_size = Some(b.get_varint()?);
+ max_header_list_size = Some(settings_val);
},
SETTINGS_QPACK_BLOCKED_STREAMS => {
- qpack_blocked_streams = Some(b.get_varint()?);
+ qpack_blocked_streams = Some(settings_val);
},
SETTINGS_NUM_PLACEHOLDERS => {
- num_placeholders = Some(b.get_varint()?);
+ num_placeholders = Some(settings_val);
},
// Unknown Settings parameters must be ignored.
@@ -335,6 +347,7 @@
max_header_list_size,
qpack_max_table_capacity,
qpack_blocked_streams,
+ grease: None,
})
}
@@ -439,7 +452,7 @@
}
#[test]
- fn settings_all() {
+ fn settings_all_no_grease() {
let mut d = [42; 128];
let frame = Frame::Settings {
@@ -447,9 +460,10 @@
max_header_list_size: Some(0),
qpack_max_table_capacity: Some(0),
qpack_blocked_streams: Some(0),
+ grease: None,
};
- let frame_payload_len = 12;
+ let frame_payload_len = 8;
let frame_header_len = 2;
let wire_len = {
@@ -471,6 +485,48 @@
}
#[test]
+ fn settings_all_grease() {
+ let mut d = [42; 128];
+
+ let frame = Frame::Settings {
+ num_placeholders: Some(0),
+ max_header_list_size: Some(0),
+ qpack_max_table_capacity: Some(0),
+ qpack_blocked_streams: Some(0),
+ grease: Some((33, 33)),
+ };
+
+ // Frame parsing will always ignore GREASE values.
+ let frame_parsed = Frame::Settings {
+ num_placeholders: Some(0),
+ max_header_list_size: Some(0),
+ qpack_max_table_capacity: Some(0),
+ qpack_blocked_streams: Some(0),
+ grease: None,
+ };
+
+ let frame_payload_len = 10;
+ let frame_header_len = 2;
+
+ let wire_len = {
+ let mut b = octets::Octets::with_slice(&mut d);
+ frame.to_bytes(&mut b).unwrap()
+ };
+
+ assert_eq!(wire_len, frame_header_len + frame_payload_len);
+
+ assert_eq!(
+ Frame::from_bytes(
+ SETTINGS_FRAME_TYPE_ID,
+ frame_payload_len as u64,
+ &mut d[frame_header_len..]
+ )
+ .unwrap(),
+ frame_parsed
+ );
+ }
+
+ #[test]
fn settings_h3_only() {
let mut d = [42; 128];
@@ -479,9 +535,10 @@
max_header_list_size: Some(1024),
qpack_max_table_capacity: None,
qpack_blocked_streams: None,
+ grease: None,
};
- let frame_payload_len = 7;
+ let frame_payload_len = 5;
let frame_header_len = 2;
let wire_len = {
@@ -511,9 +568,10 @@
max_header_list_size: None,
qpack_max_table_capacity: Some(0),
qpack_blocked_streams: Some(0),
+ grease: None,
};
- let frame_payload_len = 6;
+ let frame_payload_len = 4;
let frame_header_len = 2;
let wire_len = {
diff --git a/src/h3/mod.rs b/src/h3/mod.rs
index 4d90abb..427cec8 100644
--- a/src/h3/mod.rs
+++ b/src/h3/mod.rs
@@ -550,6 +550,10 @@
http3_conn.send_settings(conn)?;
http3_conn.open_qpack_streams(conn)?;
+ if conn.grease {
+ http3_conn.open_grease_stream(conn)?;
+ }
+
Ok(http3_conn)
}
@@ -598,8 +602,8 @@
header_block.truncate(len);
let mut b = octets::Octets::with_slice(&mut d);
+ b.put_varint(frame::HEADERS_FRAME_TYPE_ID)?;
b.put_varint(len as u64)?;
- b.put_u8(frame::HEADERS_FRAME_TYPE_ID)?;
let off = b.off();
@@ -631,8 +635,8 @@
let mut d = [42; 10];
let mut b = octets::Octets::with_slice(&mut d);
+ b.put_varint(frame::DATA_FRAME_TYPE_ID)?;
b.put_varint(body.len() as u64)?;
- b.put_u8(frame::DATA_FRAME_TYPE_ID)?;
let off = b.off();
@@ -684,7 +688,12 @@
for (stream_id, stream) in self.streams.iter_mut() {
if let Some(frame) = stream.get_frame() {
- trace!("{} rx frm {:?}", conn.trace_id(), frame);
+ trace!(
+ "{} rx frm {:?} on stream {}",
+ conn.trace_id(),
+ frame,
+ stream_id
+ );
match frame {
frame::Frame::Settings {
@@ -692,8 +701,15 @@
max_header_list_size,
qpack_max_table_capacity,
qpack_blocked_streams,
+ ..
} => {
if self.is_server && num_placeholders.is_some() {
+ conn.close(
+ true,
+ Error::WrongSettingDirection.to_wire(),
+ b"Num placeholder setting received by server.",
+ )?;
+
return Err(Error::WrongSettingDirection);
}
@@ -706,6 +722,13 @@
},
frame::Frame::Headers { mut header_block } => {
+ if Some(*stream_id) == self.peer_control_stream_id {
+ Connection::close_unexpected_frame(
+ conn,
+ b"HEADERS received on control stream",
+ )?;
+ }
+
let headers = self
.qpack_decoder
.decode(&mut header_block[..])
@@ -714,19 +737,84 @@
},
frame::Frame::Data { payload } => {
+ if Some(*stream_id) == self.peer_control_stream_id {
+ Connection::close_unexpected_frame(
+ conn,
+ b"DATA received on control stream",
+ )?;
+ }
+
return Ok((*stream_id, Event::Data(payload)));
},
- // TODO: implement GOAWAY
- frame::Frame::GoAway { .. } => {},
- // TODO: implement MAX_PUSH_ID
- frame::Frame::MaxPushId { .. } => {},
- // TODO: implement PUSH_PROMISE
- frame::Frame::PushPromise { .. } => {},
- // TODO: implement DUPLICATE_PUSH
- frame::Frame::DuplicatePush { .. } => {},
- // TODO: implement CANCEL_PUSH frame
- frame::Frame::CancelPush { .. } => {},
+ frame::Frame::GoAway { .. } => {
+ if Some(*stream_id) != self.peer_control_stream_id {
+ Connection::close_unexpected_frame(
+ conn,
+ b"GOAWAY received on non-control stream",
+ )?;
+ }
+
+ if self.is_server {
+ Connection::close_unexpected_frame(
+ conn,
+ b"GOWAY received on server",
+ )?;
+ }
+
+ // TODO: implement GOAWAY
+ },
+
+ frame::Frame::MaxPushId { .. } => {
+ if Some(*stream_id) != self.peer_control_stream_id {
+ Connection::close_unexpected_frame(
+ conn,
+ b"MAX_PUSH_ID received on non-control stream",
+ )?;
+ }
+
+ if !self.is_server {
+ Connection::close_unexpected_frame(
+ conn,
+ b"MAX_PUSH_ID received by client",
+ )?;
+ }
+
+ // TODO: implement MAX_PUSH_ID
+ },
+
+ frame::Frame::PushPromise { .. } => {
+ if stream_id % 4 != 0 {
+ Connection::close_unexpected_frame(
+ conn,
+ b"PUSH_PROMISE received on non-request stream",
+ )?;
+ }
+
+ // TODO: implement PUSH_PROMISE
+ },
+
+ frame::Frame::DuplicatePush { .. } => {
+ if stream_id % 4 != 0 {
+ Connection::close_unexpected_frame(
+ conn,
+ b"DUPLICATE_PUSH received on non-request stream",
+ )?;
+ }
+
+ // TODO: implement DUPLICATE_PUSH
+ },
+
+ frame::Frame::CancelPush { .. } => {
+ if Some(*stream_id) != self.peer_control_stream_id {
+ Connection::close_unexpected_frame(
+ conn,
+ b"CANCEL_PUSH received on non-control stream",
+ )?;
+ }
+
+ // TODO: implement CANCEL_PUSH frame
+ },
}
}
}
@@ -734,6 +822,22 @@
Err(Error::Done)
}
+ fn close_unexpected_frame(
+ conn: &mut super::Connection, reason: &[u8],
+ ) -> Result<()> {
+ conn.close(true, Error::UnexpectedFrame.to_wire(), reason)?;
+
+ Err(Error::UnexpectedFrame)
+ }
+
+ fn close_wrong_stream_count(
+ conn: &mut super::Connection, reason: &[u8],
+ ) -> Result<()> {
+ conn.close(true, Error::WrongStreamCount.to_wire(), reason)?;
+
+ Err(Error::WrongStreamCount)
+ }
+
/// Allocates a new request stream ID for the local endpoint to use.
fn get_available_request_stream(&mut self) -> Result<u64> {
if self.highest_request_stream_id < std::u64::MAX {
@@ -762,11 +866,13 @@
) -> Result<()> {
if self.control_stream_id.is_none() {
let stream_id = self.get_available_uni_stream()?;
- conn.stream_send(
- stream_id,
- &stream::HTTP3_CONTROL_STREAM_TYPE_ID.to_be_bytes(),
- false,
- )?;
+
+ let mut d = [42; 8];
+ let mut b = octets::Octets::with_slice(&mut d);
+ b.put_varint(stream::HTTP3_CONTROL_STREAM_TYPE_ID)?;
+ let off = b.off();
+
+ conn.stream_send(stream_id, &d[..off], false)?;
self.control_stream_id = Some(stream_id);
}
@@ -778,22 +884,26 @@
fn open_qpack_streams(&mut self, conn: &mut super::Connection) -> Result<()> {
if self.local_qpack_streams.encoder_stream_id.is_none() {
let stream_id = self.get_available_uni_stream()?;
- conn.stream_send(
- stream_id,
- &stream::QPACK_ENCODER_STREAM_TYPE_ID.to_be_bytes(),
- false,
- )?;
+
+ let mut d = [0; 8];
+ let mut b = octets::Octets::with_slice(&mut d);
+ b.put_varint(stream::QPACK_ENCODER_STREAM_TYPE_ID)?;
+ let off = b.off();
+
+ conn.stream_send(stream_id, &d[..off], false)?;
self.local_qpack_streams.encoder_stream_id = Some(stream_id);
}
if self.local_qpack_streams.decoder_stream_id.is_none() {
let stream_id = self.get_available_uni_stream()?;
- conn.stream_send(
- stream_id,
- &stream::QPACK_DECODER_STREAM_TYPE_ID.to_be_bytes(),
- false,
- )?;
+
+ let mut d = [0; 8];
+ let mut b = octets::Octets::with_slice(&mut d);
+ b.put_varint(stream::QPACK_DECODER_STREAM_TYPE_ID)?;
+ let off = b.off();
+
+ conn.stream_send(stream_id, &d[..off], false)?;
self.local_qpack_streams.decoder_stream_id = Some(stream_id);
}
@@ -801,6 +911,12 @@
Ok(())
}
+ /// Generate am HTTP/3 GREASE variable length integer.
+ fn grease_value() -> u64 {
+ let n = std::cmp::min(super::rand::rand_u64(), 148_764_065_110_560_899);
+ 31 * n + 33
+ }
+
/// Send GREASE frames on the provided stream ID.
fn send_grease_frames(
&mut self, conn: &mut super::Connection, stream_id: u64,
@@ -810,15 +926,15 @@
let mut b = octets::Octets::with_slice(&mut d);
// Empty GREASE frame.
+ b.put_varint(Connection::grease_value())?;
b.put_varint(0)?;
- b.put_u8(0xb)?;
// GREASE frame with payload.
+ b.put_varint(Connection::grease_value())?;
b.put_varint(18)?;
- b.put_u8(0x2a)?;
trace!(
- "{} sending GREASE frames on stream {}",
+ "{} sending GREASE frames on stream id {}",
conn.trace_id(),
stream_id
);
@@ -831,6 +947,43 @@
Ok(())
}
+ /// Opens a new unidirectional stream with a GREASE type and sends some
+ /// unframed payload.
+ fn open_grease_stream(&mut self, conn: &mut super::Connection) -> Result<()> {
+ let stream_id = self.get_available_uni_stream()?;
+
+ let mut d = [0; 8];
+ let mut b = octets::Octets::with_slice(&mut d);
+ b.put_varint(Connection::grease_value())?;
+
+ let off = b.off();
+
+ match conn.stream_send(stream_id, &d[..off], false) {
+ Ok(_v) => {
+ trace!(
+ "{} sending GREASE stream on stream id {}",
+ conn.trace_id(),
+ stream_id
+ );
+ conn.stream_send(stream_id, b"GREASE is the word", false)?;
+ },
+ Err(super::Error::StreamLimit) => {
+ trace!(
+ "{} sending GREASE stream was blocked on stream id {}",
+ conn.trace_id(),
+ stream_id
+ );
+
+ return Ok(());
+ },
+ Err(e) => {
+ return Err(Error::from(e));
+ },
+ };
+
+ Ok(())
+ }
+
/// Sends SETTINGS frame based on HTTP/3 configuration.
fn send_settings(&mut self, conn: &mut super::Connection) -> Result<()> {
let mut d = [42; 128];
@@ -844,6 +997,12 @@
None
};
+ let grease = if conn.grease {
+ Some((Connection::grease_value(), Connection::grease_value()))
+ } else {
+ None
+ };
+
let frame = frame::Frame::Settings {
num_placeholders,
max_header_list_size: self.local_settings.max_header_list_size,
@@ -851,6 +1010,7 @@
.local_settings
.qpack_max_table_capacity,
qpack_blocked_streams: self.local_settings.qpack_blocked_streams,
+ grease,
};
let mut b = octets::Octets::with_slice(&mut d);
@@ -889,12 +1049,14 @@
while stream.more() {
match stream.state() {
stream::State::StreamTypeLen => {
- let varint_len = 1;
+ let varint_byte = stream.buf_bytes(1)?[0];
+ stream.set_next_varint_len(octets::varint_parse_len(
+ varint_byte,
+ ))?;
+ },
- stream.set_stream_type_len(varint_len)?;
-
- let varint_bytes = stream.buf_bytes(varint_len as usize)?;
- let varint = varint_bytes[0];
+ stream::State::StreamType => {
+ let varint = stream.get_varint()?;
let ty = stream::Type::deserialize(varint)?;
@@ -904,7 +1066,10 @@
stream::Type::Control => {
// Only one control stream allowed.
if self.peer_control_stream_id.is_some() {
- return Err(Error::WrongStreamCount);
+ Connection::close_wrong_stream_count(
+ conn,
+ b"Received multiple control streams",
+ )?;
}
trace!(
@@ -919,6 +1084,12 @@
stream::Type::Push => {
// Only clients can receive push stream.
if self.is_server {
+ conn.close(
+ true,
+ Error::WrongStreamDirection.to_wire(),
+ b"Server received push stream.",
+ )?;
+
return Err(Error::WrongStreamDirection);
}
},
@@ -927,7 +1098,10 @@
// Only one qpack encoder stream allowed.
if self.peer_qpack_streams.encoder_stream_id.is_some()
{
- return Err(Error::WrongStreamCount);
+ Connection::close_wrong_stream_count(
+ conn,
+ b"Received multiple QPACK encoder streams",
+ )?;
}
self.peer_qpack_streams.encoder_stream_id =
@@ -938,22 +1112,25 @@
// Only one qpack decoder allowed.
if self.peer_qpack_streams.decoder_stream_id.is_some()
{
- return Err(Error::WrongStreamCount);
+ Connection::close_wrong_stream_count(
+ conn,
+ b"Received multiple QPACK decoder streams",
+ )?;
}
self.peer_qpack_streams.decoder_stream_id =
Some(stream_id);
},
- // TODO: enable GREASE streamsget_varint
+ stream::Type::Unknown => {
+ // Unknown stream types are ignored.
+ // TODO: we MAY send STOP_SENDING
+ },
+
stream::Type::Request => unreachable!(),
}
},
- stream::State::StreamType => {
- // TODO: populate this in draft 18+
- },
-
stream::State::FramePayloadLenLen => {
let varint_byte = stream.buf_bytes(1)?[0];
stream.set_next_varint_len(octets::varint_parse_len(
@@ -974,7 +1151,7 @@
},
stream::State::FrameType => {
- let varint = stream.get_u8()?;
+ let varint = stream.get_varint()?;
stream.set_frame_type(varint)?;
},
@@ -986,6 +1163,10 @@
return Err(Error::Done);
},
+ stream::State::Done => {
+ return Err(Error::Done);
+ },
+
_ => (),
}
}
@@ -1001,6 +1182,11 @@
use crate::testing;
#[test]
+ fn grease_value_in_varint_limit() {
+ assert!(Connection::grease_value() < 2u64.pow(62) - 1);
+ }
+
+ #[test]
fn simple_request() {
let mut buf = [0; 65535];
@@ -1063,6 +1249,7 @@
let ev = h3_cln.poll(&mut pipe.client).unwrap();
assert_eq!(ev, (stream, Event::Headers(resp.to_vec())));
}
+
}
mod ffi;
diff --git a/src/h3/stream.rs b/src/h3/stream.rs
index 1d9192f..56c9be2 100644
--- a/src/h3/stream.rs
+++ b/src/h3/stream.rs
@@ -33,10 +33,10 @@
use super::frame;
-pub const HTTP3_CONTROL_STREAM_TYPE_ID: u8 = 0x43;
-pub const HTTP3_PUSH_STREAM_TYPE_ID: u8 = 0x50;
-pub const QPACK_ENCODER_STREAM_TYPE_ID: u8 = 0x48;
-pub const QPACK_DECODER_STREAM_TYPE_ID: u8 = 0x68;
+pub const HTTP3_CONTROL_STREAM_TYPE_ID: u64 = 0x0;
+pub const HTTP3_PUSH_STREAM_TYPE_ID: u64 = 0x1;
+pub const QPACK_ENCODER_STREAM_TYPE_ID: u64 = 0x2;
+pub const QPACK_DECODER_STREAM_TYPE_ID: u64 = 0x3;
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Type {
@@ -45,7 +45,7 @@
Push,
QpackEncoder,
QpackDecoder,
- // TODO: enable GREASE streams
+ Unknown,
}
#[derive(Clone, Copy, Debug, PartialEq)]
@@ -60,18 +60,18 @@
PushIdLen,
PushId,
QpackInstruction,
+ Done,
}
impl Type {
- pub fn deserialize(v: u8) -> Result<Type> {
+ pub fn deserialize(v: u64) -> Result<Type> {
match v {
HTTP3_CONTROL_STREAM_TYPE_ID => Ok(Type::Control),
HTTP3_PUSH_STREAM_TYPE_ID => Ok(Type::Push),
QPACK_ENCODER_STREAM_TYPE_ID => Ok(Type::QpackEncoder),
QPACK_DECODER_STREAM_TYPE_ID => Ok(Type::QpackDecoder),
- // TODO: parse grease stream
- _ => Err(Error::UnknownStreamType),
+ _ => Ok(Type::Unknown),
}
}
}
@@ -83,7 +83,6 @@
ty: Option<Type>,
is_local: bool,
initialized: bool,
- ty_len: u8,
state: State,
stream_offset: u64,
buf: Vec<u8>,
@@ -91,7 +90,7 @@
buf_end_pos: u64,
next_varint_len: usize,
frame_payload_len: u64,
- frame_type: Option<u8>,
+ frame_type: Option<u64>,
frames: VecDeque<frame::Frame>,
}
@@ -104,7 +103,7 @@
if crate::stream::is_bidi(id) {
ty = Some(Type::Request);
initialized = true;
- state = State::FramePayloadLenLen;
+ state = State::FrameTypeLen;
};
Stream {
@@ -112,7 +111,6 @@
ty,
is_local,
initialized,
- ty_len: 0,
state,
stream_offset: 0,
// TODO: need a more elegant approach to buffer management.
@@ -155,17 +153,6 @@
Ok(())
}
- pub fn set_stream_type_len(&mut self, len: u8) -> Result<()> {
- if self.state != State::StreamTypeLen {
- return Err(Error::InternalError);
- }
-
- self.ty_len = len;
- self.state = State::StreamType;
-
- Ok(())
- }
-
pub fn set_stream_type(&mut self, ty: Type) -> Result<()> {
if self.state != State::StreamType {
return Err(Error::InternalError);
@@ -173,13 +160,10 @@
self.ty = Some(ty);
- self.stream_offset += u64::from(self.ty_len);
- self.buf_read_off += u64::from(self.ty_len);
-
match ty {
- Type::Request => self.state = State::FramePayloadLenLen,
+ Type::Request => self.state = State::FrameTypeLen,
- Type::Control => self.state = State::FramePayloadLenLen,
+ Type::Control => self.state = State::FrameTypeLen,
Type::Push => self.state = State::PushIdLen,
@@ -187,28 +171,42 @@
self.state = State::QpackInstruction;
self.initialized = true;
},
+
+ Type::Unknown => {
+ self.state = State::Done;
+ },
}
Ok(())
}
pub fn set_next_varint_len(&mut self, len: usize) -> Result<()> {
- self.next_varint_len = len;
-
match self.state {
+ State::StreamTypeLen => self.state = State::StreamType,
+
State::FramePayloadLenLen => self.state = State::FramePayloadLen,
State::FrameTypeLen => self.state = State::FrameType,
State::PushIdLen => self.state = State::PushId,
- _ => (),
+ State::PushId => self.state = State::FrameTypeLen,
+
+ _ => {
+ return Err(Error::InternalError);
+ },
}
+ self.next_varint_len = len;
+
Ok(())
}
pub fn get_varint(&mut self) -> Result<u64> {
+ if self.next_varint_len == 0 {
+ return Err(Error::Done);
+ }
+
if self.buf.len() - self.buf_read_off as usize >=
self.next_varint_len as usize
{
@@ -221,37 +219,44 @@
self.stream_offset += self.next_varint_len as u64;
self.buf_read_off += self.next_varint_len as u64;
+ // reset next_varint_len so we avoid incorrect multiple calls
+ self.next_varint_len = 0;
+
+ // if processing push, progress the state machine appropriately
+ if self.state == State::PushId {
+ self.state = State::FrameTypeLen;
+ }
+
return Ok(varint);
}
Err(Error::Done)
}
- pub fn get_u8(&mut self) -> Result<(u8)> {
- let ret = self.buf_bytes(1)?[0];
-
- self.stream_offset += 1;
- self.buf_read_off += 1;
-
- Ok(ret)
- }
-
pub fn set_frame_payload_len(&mut self, len: u64) -> Result<()> {
+ if self.state != State::FramePayloadLen {
+ return Err(Error::InternalError);
+ }
+
// Only expect frames on Control, Request and Push streams.
if self.ty == Some(Type::Control) ||
self.ty == Some(Type::Request) ||
self.ty == Some(Type::Push)
{
self.frame_payload_len = len;
- self.state = State::FrameTypeLen;
+ self.state = State::FramePayload;
return Ok(());
}
- Err(Error::UnexpectedFrame)
+ Err(Error::InternalError)
}
- pub fn set_frame_type(&mut self, ty: u8) -> Result<()> {
+ pub fn set_frame_type(&mut self, ty: u64) -> Result<()> {
+ if self.state != State::FrameType {
+ return Err(Error::InternalError);
+ }
+
// Only expect frames on Control, Request and Push streams.
match self.ty {
Some(Type::Control) => {
@@ -269,13 +274,21 @@
(_, false) => return Err(Error::MissingSettings),
+ (frame::GOAWAY_FRAME_TYPE_ID, true) => (),
+
+ (frame::MAX_PUSH_FRAME_TYPE_ID, true) => (),
+
+ (frame::CANCEL_PUSH_FRAME_TYPE_ID, true) => (),
+
+ (frame::PRIORITY_FRAME_TYPE_ID, true) => (),
+
(_, true) => return Err(Error::UnexpectedFrame),
}
- self.state = State::FramePayload;
+ self.state = State::FramePayloadLenLen;
},
- Some(Type::Request) => self.state = State::FramePayload,
+ Some(Type::Request) => self.state = State::FramePayloadLenLen,
Some(Type::Push) => self.state = State::FramePayloadLenLen,
@@ -291,7 +304,9 @@
// Parse the whole frame payload but only if there is enough data in
// the stream buffer. stream.buf_bytes() returns an error if we don't
// have enough.
- assert!(self.frame_type.is_some());
+ if self.frame_type.is_none() {
+ return Err(Error::InternalError);
+ }
if let Ok(frame) = frame::Frame::from_bytes(
self.frame_type.unwrap(),
@@ -309,7 +324,8 @@
// bytes were seen by the application layer.
self.stream_offset += self.frame_payload_len;
- self.state = State::FramePayloadLenLen;
+ // Set state to parse next frame
+ self.state = State::FrameTypeLen;
Ok(())
}
@@ -319,3 +335,449 @@
rem_bytes > 0
}
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn inbound_control_stream_good() {
+ let mut stream = Stream::new(3, false);
+ assert_eq!(*stream.state(), State::StreamTypeLen);
+
+ let mut d = [42; 40];
+ let mut b = octets::Octets::with_slice(&mut d);
+
+ let frame = frame::Frame::Settings {
+ num_placeholders: Some(0),
+ max_header_list_size: Some(0),
+ qpack_max_table_capacity: Some(0),
+ qpack_blocked_streams: Some(0),
+ grease: None,
+ };
+
+ b.put_varint(HTTP3_CONTROL_STREAM_TYPE_ID).unwrap();
+ frame.to_bytes(&mut b).unwrap();
+ let off = b.off();
+
+ stream.push(&mut d[..off]).unwrap();
+
+ // parse stream type
+ assert_eq!(stream.more(), true);
+ let stream_ty_len =
+ octets::varint_parse_len(stream.buf_bytes(1).unwrap()[0]);
+ assert_eq!(stream_ty_len, 1);
+ stream.set_next_varint_len(stream_ty_len).unwrap();
+ assert_eq!(*stream.state(), State::StreamType);
+
+ let stream_ty = stream.get_varint().unwrap();
+ assert_eq!(stream_ty, HTTP3_CONTROL_STREAM_TYPE_ID);
+ stream
+ .set_stream_type(Type::deserialize(stream_ty).unwrap())
+ .unwrap();
+ assert_eq!(*stream.state(), State::FrameTypeLen);
+
+ // parse the SETTINGS frame
+
+ assert_eq!(stream.more(), true);
+ let frame_ty_len =
+ octets::varint_parse_len(stream.buf_bytes(1).unwrap()[0]);
+ assert_eq!(frame_ty_len, 1);
+
+ stream.set_next_varint_len(frame_ty_len).unwrap();
+ assert_eq!(*stream.state(), State::FrameType);
+
+ let frame_ty = stream.get_varint().unwrap();
+ assert_eq!(frame_ty, frame::SETTINGS_FRAME_TYPE_ID);
+
+ stream.set_frame_type(frame_ty).unwrap();
+ assert_eq!(*stream.state(), State::FramePayloadLenLen);
+
+ let frame_payload_len_len =
+ octets::varint_parse_len(stream.buf_bytes(1).unwrap()[0]);
+ assert_eq!(frame_payload_len_len, 1);
+ stream.set_next_varint_len(frame_payload_len_len).unwrap();
+ assert_eq!(*stream.state(), State::FramePayloadLen);
+
+ let frame_payload_len = stream.get_varint().unwrap();
+ assert_eq!(frame_payload_len, 8);
+ stream.set_frame_payload_len(frame_payload_len).unwrap();
+ assert_eq!(*stream.state(), State::FramePayload);
+
+ assert_eq!(stream.parse_frame(), Ok(()));
+ assert_eq!(*stream.state(), State::FrameTypeLen);
+
+ assert_eq!(stream.get_frame(), Some(frame));
+
+ assert_eq!(stream.more(), false);
+ }
+
+ #[test]
+ fn inbound_control_multiple_settings() {
+ let mut stream = Stream::new(3, false);
+ assert_eq!(*stream.state(), State::StreamTypeLen);
+
+ let mut d = [42; 40];
+ let mut b = octets::Octets::with_slice(&mut d);
+
+ let frame = frame::Frame::Settings {
+ num_placeholders: Some(0),
+ max_header_list_size: Some(0),
+ qpack_max_table_capacity: Some(0),
+ qpack_blocked_streams: Some(0),
+ grease: None,
+ };
+
+ b.put_varint(HTTP3_CONTROL_STREAM_TYPE_ID).unwrap();
+ frame.to_bytes(&mut b).unwrap();
+ frame.to_bytes(&mut b).unwrap();
+ let off = b.off();
+
+ stream.push(&mut d[..off]).unwrap();
+
+ // parse stream type
+ let stream_ty_len =
+ octets::varint_parse_len(stream.buf_bytes(1).unwrap()[0]);
+ stream.set_next_varint_len(stream_ty_len).unwrap();
+ let stream_ty = stream.get_varint().unwrap();
+ stream
+ .set_stream_type(Type::deserialize(stream_ty).unwrap())
+ .unwrap();
+
+ // parse first SETTINGS frame
+ let frame_ty_len =
+ octets::varint_parse_len(stream.buf_bytes(1).unwrap()[0]);
+ stream.set_next_varint_len(frame_ty_len).unwrap();
+
+ let frame_ty = stream.get_varint().unwrap();
+ stream.set_frame_type(frame_ty).unwrap();
+
+ let frame_payload_len_len =
+ octets::varint_parse_len(stream.buf_bytes(1).unwrap()[0]);
+ stream.set_next_varint_len(frame_payload_len_len).unwrap();
+
+ let frame_payload_len = stream.get_varint().unwrap();
+ stream.set_frame_payload_len(frame_payload_len).unwrap();
+ assert_eq!(stream.parse_frame(), Ok(()));
+ stream.get_frame();
+
+ assert_eq!(stream.more(), true);
+
+ // parse second SETTINGS frame
+ let frame_ty_len =
+ octets::varint_parse_len(stream.buf_bytes(1).unwrap()[0]);
+ stream.set_next_varint_len(frame_ty_len).unwrap();
+
+ let frame_ty = stream.get_varint().unwrap();
+ assert_eq!(stream.set_frame_type(frame_ty), Err(Error::UnexpectedFrame));
+ }
+
+ #[test]
+ fn inbound_control_late_settings() {
+ let mut stream = Stream::new(3, false);
+ assert_eq!(*stream.state(), State::StreamTypeLen);
+
+ let mut d = [42; 40];
+ let mut b = octets::Octets::with_slice(&mut d);
+
+ let goaway = frame::Frame::GoAway { stream_id: 0 };
+
+ let settings = frame::Frame::Settings {
+ num_placeholders: Some(0),
+ max_header_list_size: Some(0),
+ qpack_max_table_capacity: Some(0),
+ qpack_blocked_streams: Some(0),
+ grease: None,
+ };
+
+ b.put_varint(HTTP3_CONTROL_STREAM_TYPE_ID).unwrap();
+ goaway.to_bytes(&mut b).unwrap();
+ settings.to_bytes(&mut b).unwrap();
+ let off = b.off();
+
+ stream.push(&mut d[..off]).unwrap();
+
+ // parse stream type
+ let stream_ty_len =
+ octets::varint_parse_len(stream.buf_bytes(1).unwrap()[0]);
+ stream.set_next_varint_len(stream_ty_len).unwrap();
+ let stream_ty = stream.get_varint().unwrap();
+ stream
+ .set_stream_type(Type::deserialize(stream_ty).unwrap())
+ .unwrap();
+
+ // parse GOAWAY
+ let frame_ty_len =
+ octets::varint_parse_len(stream.buf_bytes(1).unwrap()[0]);
+ stream.set_next_varint_len(frame_ty_len).unwrap();
+
+ let frame_ty = stream.get_varint().unwrap();
+ assert_eq!(stream.set_frame_type(frame_ty), Err(Error::MissingSettings));
+ }
+
+ #[test]
+ fn inbound_control_bad_frame() {
+ let mut stream = Stream::new(3, false);
+ assert_eq!(*stream.state(), State::StreamTypeLen);
+
+ let mut d = [42; 40];
+ let mut b = octets::Octets::with_slice(&mut d);
+
+ let header_block = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
+ let hdrs = frame::Frame::Headers { header_block };
+
+ let settings = frame::Frame::Settings {
+ num_placeholders: Some(0),
+ max_header_list_size: Some(0),
+ qpack_max_table_capacity: Some(0),
+ qpack_blocked_streams: Some(0),
+ grease: None,
+ };
+
+ b.put_varint(HTTP3_CONTROL_STREAM_TYPE_ID).unwrap();
+ settings.to_bytes(&mut b).unwrap();
+ hdrs.to_bytes(&mut b).unwrap();
+ let off = b.off();
+
+ stream.push(&mut d[..off]).unwrap();
+
+ // parse stream type
+ let stream_ty_len =
+ octets::varint_parse_len(stream.buf_bytes(1).unwrap()[0]);
+ stream.set_next_varint_len(stream_ty_len).unwrap();
+ let stream_ty = stream.get_varint().unwrap();
+ stream
+ .set_stream_type(Type::deserialize(stream_ty).unwrap())
+ .unwrap();
+
+ // parse first SETTINGS frame
+ let frame_ty_len =
+ octets::varint_parse_len(stream.buf_bytes(1).unwrap()[0]);
+ stream.set_next_varint_len(frame_ty_len).unwrap();
+
+ let frame_ty = stream.get_varint().unwrap();
+ stream.set_frame_type(frame_ty).unwrap();
+
+ let frame_payload_len_len =
+ octets::varint_parse_len(stream.buf_bytes(1).unwrap()[0]);
+ stream.set_next_varint_len(frame_payload_len_len).unwrap();
+
+ let frame_payload_len = stream.get_varint().unwrap();
+ stream.set_frame_payload_len(frame_payload_len).unwrap();
+ assert_eq!(stream.parse_frame(), Ok(()));
+ stream.get_frame();
+
+ // parse HEADERS
+ let frame_ty_len =
+ octets::varint_parse_len(stream.buf_bytes(1).unwrap()[0]);
+ stream.set_next_varint_len(frame_ty_len).unwrap();
+
+ let frame_ty = stream.get_varint().unwrap();
+ assert_eq!(stream.set_frame_type(frame_ty), Err(Error::UnexpectedFrame));
+ }
+
+ #[test]
+ fn inbound_request_stream_no_data() {
+ let mut stream = Stream::new(0, false);
+
+ assert_eq!(stream.ty, Some(Type::Request));
+ assert_eq!(*stream.state(), State::FrameTypeLen);
+
+ assert_eq!(
+ stream.set_stream_type(Type::Request),
+ Err(Error::InternalError)
+ );
+
+ assert_eq!(stream.more(), false);
+ assert_eq!(stream.get_varint(), Err(Error::Done));
+ assert_eq!(stream.set_frame_payload_len(100), Err(Error::InternalError));
+ assert_eq!(stream.get_frame(), None);
+ assert_eq!(stream.parse_frame(), Err(Error::InternalError));
+
+ assert_eq!(stream.set_frame_type(1), Err(Error::InternalError));
+ }
+
+ #[test]
+ fn inbound_request_stream() {
+ let mut stream = Stream::new(0, false);
+
+ let mut d = [42; 128];
+ let mut b = octets::Octets::with_slice(&mut d);
+
+ let header_block = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
+ let payload = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
+ let hdrs = frame::Frame::Headers { header_block };
+ let data = frame::Frame::Data { payload };
+
+ hdrs.to_bytes(&mut b).unwrap();
+ data.to_bytes(&mut b).unwrap();
+ let off = b.off();
+
+ stream.push(&mut d[..off]).unwrap();
+
+ // parse the HEADERS frame
+ assert_eq!(stream.more(), true);
+ let frame_ty_len =
+ octets::varint_parse_len(stream.buf_bytes(1).unwrap()[0]);
+ assert_eq!(frame_ty_len, 1);
+ stream.set_next_varint_len(frame_ty_len).unwrap();
+ assert_eq!(*stream.state(), State::FrameType);
+
+ let frame_ty = stream.get_varint().unwrap();
+ assert_eq!(frame_ty, frame::HEADERS_FRAME_TYPE_ID);
+
+ stream.set_frame_type(frame_ty).unwrap();
+ assert_eq!(*stream.state(), State::FramePayloadLenLen);
+
+ let frame_payload_len_len =
+ octets::varint_parse_len(stream.buf_bytes(1).unwrap()[0]);
+ assert_eq!(frame_payload_len_len, 1);
+ stream.set_next_varint_len(frame_payload_len_len).unwrap();
+ assert_eq!(*stream.state(), State::FramePayloadLen);
+
+ let frame_payload_len = stream.get_varint().unwrap();
+ assert_eq!(frame_payload_len, 12);
+ stream.set_frame_payload_len(frame_payload_len).unwrap();
+ assert_eq!(*stream.state(), State::FramePayload);
+
+ assert_eq!(stream.parse_frame(), Ok(()));
+ assert_eq!(*stream.state(), State::FrameTypeLen);
+
+ assert_eq!(stream.get_frame(), Some(hdrs));
+
+ // parse the DATA frame
+ assert_eq!(stream.more(), true);
+ let frame_ty_len =
+ octets::varint_parse_len(stream.buf_bytes(1).unwrap()[0]);
+ assert_eq!(frame_ty_len, 1);
+
+ stream.set_next_varint_len(frame_ty_len).unwrap();
+ assert_eq!(*stream.state(), State::FrameType);
+
+ let frame_ty = stream.get_varint().unwrap();
+ assert_eq!(frame_ty, frame::DATA_FRAME_TYPE_ID);
+
+ stream.set_frame_type(frame_ty).unwrap();
+ assert_eq!(*stream.state(), State::FramePayloadLenLen);
+
+ let frame_payload_len_len =
+ octets::varint_parse_len(stream.buf_bytes(1).unwrap()[0]);
+ assert_eq!(frame_payload_len_len, 1);
+ stream.set_next_varint_len(frame_ty_len).unwrap();
+ assert_eq!(*stream.state(), State::FramePayloadLen);
+
+ let frame_payload_len = stream.get_varint().unwrap();
+ assert_eq!(frame_payload_len, 12);
+ stream.set_frame_payload_len(frame_payload_len).unwrap();
+ assert_eq!(*stream.state(), State::FramePayload);
+
+ assert_eq!(stream.parse_frame(), Ok(()));
+ assert_eq!(*stream.state(), State::FrameTypeLen);
+
+ assert_eq!(stream.get_frame(), Some(data));
+
+ assert_eq!(stream.more(), false);
+ }
+
+ #[test]
+ fn inbound_push_stream() {
+ let mut stream = Stream::new(2, false);
+
+ let mut d = [42; 128];
+ let mut b = octets::Octets::with_slice(&mut d);
+
+ let header_block = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
+ let payload = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
+ let hdrs = frame::Frame::Headers { header_block };
+ let data = frame::Frame::Data { payload };
+
+ b.put_varint(HTTP3_PUSH_STREAM_TYPE_ID).unwrap();
+ b.put_varint(1).unwrap();
+ hdrs.to_bytes(&mut b).unwrap();
+ data.to_bytes(&mut b).unwrap();
+ let off = b.off();
+
+ stream.push(&mut d[..off]).unwrap();
+
+ // parse stream type
+ let stream_ty_len =
+ octets::varint_parse_len(stream.buf_bytes(1).unwrap()[0]);
+ stream.set_next_varint_len(stream_ty_len).unwrap();
+ let stream_ty = stream.get_varint().unwrap();
+ assert_eq!(stream_ty, HTTP3_PUSH_STREAM_TYPE_ID);
+ stream
+ .set_stream_type(Type::deserialize(stream_ty).unwrap())
+ .unwrap();
+ assert_eq!(*stream.state(), State::PushIdLen);
+
+ // parse push ID
+ let push_id_len =
+ octets::varint_parse_len(stream.buf_bytes(1).unwrap()[0]);
+ stream.set_next_varint_len(push_id_len).unwrap();
+ assert_eq!(*stream.state(), State::PushId);
+ let push_id = stream.get_varint().unwrap();
+ assert_eq!(push_id, 1);
+
+ // parse the HEADERS frame
+ let frame_ty_len =
+ octets::varint_parse_len(stream.buf_bytes(1).unwrap()[0]);
+ assert_eq!(frame_ty_len, 1);
+ stream.set_next_varint_len(frame_ty_len).unwrap();
+ assert_eq!(*stream.state(), State::FrameType);
+
+ let frame_ty = stream.get_varint().unwrap();
+ assert_eq!(frame_ty, frame::HEADERS_FRAME_TYPE_ID);
+
+ stream.set_frame_type(frame_ty).unwrap();
+ assert_eq!(*stream.state(), State::FramePayloadLenLen);
+
+ let frame_payload_len_len =
+ octets::varint_parse_len(stream.buf_bytes(1).unwrap()[0]);
+ assert_eq!(frame_payload_len_len, 1);
+ stream.set_next_varint_len(frame_payload_len_len).unwrap();
+ assert_eq!(*stream.state(), State::FramePayloadLen);
+
+ let frame_payload_len = stream.get_varint().unwrap();
+ assert_eq!(frame_payload_len, 12);
+ stream.set_frame_payload_len(frame_payload_len).unwrap();
+ assert_eq!(*stream.state(), State::FramePayload);
+
+ assert_eq!(stream.parse_frame(), Ok(()));
+ assert_eq!(*stream.state(), State::FrameTypeLen);
+
+ assert_eq!(stream.get_frame(), Some(hdrs));
+
+ // parse the DATA frame
+ assert_eq!(stream.more(), true);
+ let frame_ty_len =
+ octets::varint_parse_len(stream.buf_bytes(1).unwrap()[0]);
+ assert_eq!(frame_ty_len, 1);
+
+ stream.set_next_varint_len(frame_ty_len).unwrap();
+ assert_eq!(*stream.state(), State::FrameType);
+
+ let frame_ty = stream.get_varint().unwrap();
+ assert_eq!(frame_ty, frame::DATA_FRAME_TYPE_ID);
+
+ stream.set_frame_type(frame_ty).unwrap();
+ assert_eq!(*stream.state(), State::FramePayloadLenLen);
+
+ let frame_payload_len_len =
+ octets::varint_parse_len(stream.buf_bytes(1).unwrap()[0]);
+ assert_eq!(frame_payload_len_len, 1);
+ stream.set_next_varint_len(frame_ty_len).unwrap();
+ assert_eq!(*stream.state(), State::FramePayloadLen);
+
+ let frame_payload_len = stream.get_varint().unwrap();
+ assert_eq!(frame_payload_len, 12);
+ stream.set_frame_payload_len(frame_payload_len).unwrap();
+ assert_eq!(*stream.state(), State::FramePayload);
+
+ assert_eq!(stream.parse_frame(), Ok(()));
+ assert_eq!(*stream.state(), State::FrameTypeLen);
+
+ assert_eq!(stream.get_frame(), Some(data));
+
+ assert_eq!(stream.more(), false);
+ }
+}
diff --git a/src/rand.rs b/src/rand.rs
index 891722e..5290939 100644
--- a/src/rand.rs
+++ b/src/rand.rs
@@ -39,6 +39,14 @@
buf[0]
}
+pub fn rand_u64() -> u64 {
+ let mut buf = [0; 8];
+
+ rand_bytes(&mut buf);
+
+ u64::from_ne_bytes(buf)
+}
+
extern {
fn RAND_bytes(buf: *mut u8, len: libc::size_t) -> libc::c_int;
}