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;
 }