quiche crate v0.6.0

Bug: 155855709
Test: n/a
Change-Id: I9ac5a0d80b6506e6bf5f9412ad9ecb71e917eced
diff --git a/src/build.rs b/src/build.rs
new file mode 100644
index 0000000..98774aa
--- /dev/null
+++ b/src/build.rs
@@ -0,0 +1,228 @@
+// Additional parameters for Android build of BoringSSL.
+//
+// Android NDK < 18 with GCC.
+const CMAKE_PARAMS_ANDROID_NDK_OLD_GCC: &[(&str, &[(&str, &str)])] = &[
+    ("aarch64", &[(
+        "ANDROID_TOOLCHAIN_NAME",
+        "aarch64-linux-android-4.9",
+    )]),
+    ("arm", &[(
+        "ANDROID_TOOLCHAIN_NAME",
+        "arm-linux-androideabi-4.9",
+    )]),
+    ("x86", &[(
+        "ANDROID_TOOLCHAIN_NAME",
+        "x86-linux-android-4.9",
+    )]),
+    ("x86_64", &[(
+        "ANDROID_TOOLCHAIN_NAME",
+        "x86_64-linux-android-4.9",
+    )]),
+];
+
+// Android NDK >= 19.
+const CMAKE_PARAMS_ANDROID_NDK: &[(&str, &[(&str, &str)])] = &[
+    ("aarch64", &[("ANDROID_ABI", "arm64-v8a")]),
+    ("arm", &[("ANDROID_ABI", "armeabi-v7a")]),
+    ("x86", &[("ANDROID_ABI", "x86")]),
+    ("x86_64", &[("ANDROID_ABI", "x86_64")]),
+];
+
+const CMAKE_PARAMS_IOS: &[(&str, &[(&str, &str)])] = &[
+    ("aarch64", &[
+        ("CMAKE_OSX_ARCHITECTURES", "arm64"),
+        ("CMAKE_OSX_SYSROOT", "iphoneos"),
+    ]),
+    ("x86_64", &[
+        ("CMAKE_OSX_ARCHITECTURES", "x86_64"),
+        ("CMAKE_OSX_SYSROOT", "iphonesimulator"),
+    ]),
+];
+
+/// Returns the platform-specific output path for lib.
+///
+/// MSVC generator on Windows place static libs in a target sub-folder,
+/// so adjust library location based on platform and build target.
+/// See issue: https://github.com/alexcrichton/cmake-rs/issues/18
+fn get_boringssl_platform_output_path() -> String {
+    if cfg!(windows) {
+        // Code under this branch should match the logic in cmake-rs
+        let debug_env_var =
+            std::env::var("DEBUG").expect("DEBUG variable not defined in env");
+
+        let deb_info = match &debug_env_var[..] {
+            "false" => false,
+            "true" => true,
+            unknown => panic!("Unknown DEBUG={} env var.", unknown),
+        };
+
+        let opt_env_var = std::env::var("OPT_LEVEL")
+            .expect("OPT_LEVEL variable not defined in env");
+
+        let subdir = match &opt_env_var[..] {
+            "0" => "Debug",
+            "1" | "2" | "3" =>
+                if deb_info {
+                    "RelWithDebInfo"
+                } else {
+                    "Release"
+                },
+            "s" | "z" => "MinSizeRel",
+            unknown => panic!("Unknown OPT_LEVEL={} env var.", unknown),
+        };
+
+        subdir.to_string()
+    } else {
+        "".to_string()
+    }
+}
+
+/// Returns a new cmake::Config for building BoringSSL.
+///
+/// It will add platform-specific parameters if needed.
+fn get_boringssl_cmake_config() -> cmake::Config {
+    let arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap();
+    let os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
+    let pwd = std::env::current_dir().unwrap();
+
+    let mut boringssl_cmake = cmake::Config::new("deps/boringssl");
+
+    // Add platform-specific parameters.
+    match os.as_ref() {
+        "android" => {
+            let cmake_params_android = if cfg!(feature = "ndk-old-gcc") {
+                CMAKE_PARAMS_ANDROID_NDK_OLD_GCC
+            } else {
+                CMAKE_PARAMS_ANDROID_NDK
+            };
+
+            // We need ANDROID_NDK_HOME to be set properly.
+            let android_ndk_home = std::env::var("ANDROID_NDK_HOME")
+                .expect("Please set ANDROID_NDK_HOME for Android build");
+            let android_ndk_home = std::path::Path::new(&android_ndk_home);
+            for (android_arch, params) in cmake_params_android {
+                if *android_arch == arch {
+                    for (name, value) in *params {
+                        eprintln!("android arch={} add {}={}", arch, name, value);
+                        boringssl_cmake.define(name, value);
+                    }
+                }
+            }
+            let toolchain_file =
+                android_ndk_home.join("build/cmake/android.toolchain.cmake");
+            let toolchain_file = toolchain_file.to_str().unwrap();
+            eprintln!("android toolchain={}", toolchain_file);
+            boringssl_cmake.define("CMAKE_TOOLCHAIN_FILE", toolchain_file);
+
+            // 21 is the minimum level tested. You can give higher value.
+            boringssl_cmake.define("ANDROID_NATIVE_API_LEVEL", "21");
+            boringssl_cmake.define("ANDROID_STL", "c++_shared");
+
+            boringssl_cmake
+        },
+
+        "ios" => {
+            for (ios_arch, params) in CMAKE_PARAMS_IOS {
+                if *ios_arch == arch {
+                    for (name, value) in *params {
+                        eprintln!("ios arch={} add {}={}", arch, name, value);
+                        boringssl_cmake.define(name, value);
+                    }
+                }
+            }
+
+            // Bitcode is always on.
+            let bitcode_cflag = "-fembed-bitcode";
+
+            // Hack for Xcode 10.1.
+            let target_cflag = if arch == "x86_64" {
+                "-target x86_64-apple-ios-simulator"
+            } else {
+                ""
+            };
+
+            let cflag = format!("{} {}", bitcode_cflag, target_cflag);
+
+            boringssl_cmake.define("CMAKE_ASM_FLAGS", &cflag);
+            boringssl_cmake.cflag(&cflag);
+
+            boringssl_cmake
+        },
+
+        _ => {
+            // Configure BoringSSL for building on 32-bit non-windows platforms.
+            if arch == "x86" && os != "windows" {
+                boringssl_cmake.define(
+                    "CMAKE_TOOLCHAIN_FILE",
+                    pwd.join("deps/boringssl/src/util/32-bit-toolchain.cmake")
+                        .as_os_str(),
+                );
+            }
+
+            boringssl_cmake
+        },
+    }
+}
+
+fn write_pkg_config() {
+    use std::io::prelude::*;
+
+    let profile = std::env::var("PROFILE").unwrap();
+    let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
+    let target_dir = format!("{}/target/{}", manifest_dir, profile);
+
+    let out_path = std::path::Path::new(&target_dir).join("quiche.pc");
+    let mut out_file = std::fs::File::create(&out_path).unwrap();
+
+    let include_dir = format!("{}/include", manifest_dir);
+    let version = std::env::var("CARGO_PKG_VERSION").unwrap();
+
+    let output = format!(
+        "# quiche
+
+includedir={}
+libdir={}
+
+Name: quiche
+Description: quiche library
+URL: https://github.com/cloudflare/quiche
+Version: {}
+Libs: -Wl,-rpath,${{libdir}} -L${{libdir}} -lquiche
+Cflags: -I${{includedir}}
+",
+        include_dir, target_dir, version
+    );
+
+    out_file.write_all(output.as_bytes()).unwrap();
+}
+
+fn main() {
+    if cfg!(feature = "boringssl-vendored") {
+        let bssl_dir = std::env::var("QUICHE_BSSL_PATH").unwrap_or_else(|_| {
+            let mut cfg = get_boringssl_cmake_config();
+
+            if cfg!(feature = "fuzzing") {
+                cfg.cxxflag("-DBORINGSSL_UNSAFE_DETERMINISTIC_MODE")
+                    .cxxflag("-DBORINGSSL_UNSAFE_FUZZER_MODE");
+            }
+
+            cfg.build_target("bssl").build().display().to_string()
+        });
+
+        let build_path = get_boringssl_platform_output_path();
+        let build_dir = format!("{}/build/{}", bssl_dir, build_path);
+        println!("cargo:rustc-link-search=native={}", build_dir);
+
+        println!("cargo:rustc-link-lib=static=crypto");
+        println!("cargo:rustc-link-lib=static=ssl");
+    }
+
+    // MacOS: Allow cdylib to link with undefined symbols
+    if cfg!(target_os = "macos") {
+        println!("cargo:rustc-cdylib-link-arg=-Wl,-undefined,dynamic_lookup");
+    }
+
+    if cfg!(feature = "pkg-config-meta") {
+        write_pkg_config();
+    }
+}
diff --git a/src/crypto.rs b/src/crypto.rs
new file mode 100644
index 0000000..b45f4a5
--- /dev/null
+++ b/src/crypto.rs
@@ -0,0 +1,656 @@
+// Copyright (C) 2018-2019, Cloudflare, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright notice,
+//       this list of conditions and the following disclaimer.
+//
+//     * Redistributions in binary form must reproduce the above copyright
+//       notice, this list of conditions and the following disclaimer in the
+//       documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+use ring::aead;
+use ring::hkdf;
+
+use crate::Error;
+use crate::Result;
+
+use crate::packet;
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum Level {
+    Initial   = 0,
+    ZeroRTT   = 1,
+    Handshake = 2,
+    OneRTT    = 3,
+}
+
+impl Level {
+    pub fn from_epoch(e: packet::Epoch) -> Level {
+        match e {
+            packet::EPOCH_INITIAL => Level::Initial,
+
+            packet::EPOCH_HANDSHAKE => Level::Handshake,
+
+            packet::EPOCH_APPLICATION => Level::OneRTT,
+
+            _ => unreachable!(),
+        }
+    }
+}
+
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum Algorithm {
+    #[allow(non_camel_case_types)]
+    AES128_GCM,
+
+    #[allow(non_camel_case_types)]
+    AES256_GCM,
+
+    #[allow(non_camel_case_types)]
+    ChaCha20_Poly1305,
+}
+
+impl Algorithm {
+    fn get_ring_aead(self) -> &'static aead::Algorithm {
+        match self {
+            Algorithm::AES128_GCM => &aead::AES_128_GCM,
+            Algorithm::AES256_GCM => &aead::AES_256_GCM,
+            Algorithm::ChaCha20_Poly1305 => &aead::CHACHA20_POLY1305,
+        }
+    }
+
+    fn get_ring_hp(self) -> &'static aead::quic::Algorithm {
+        match self {
+            Algorithm::AES128_GCM => &aead::quic::AES_128,
+            Algorithm::AES256_GCM => &aead::quic::AES_256,
+            Algorithm::ChaCha20_Poly1305 => &aead::quic::CHACHA20,
+        }
+    }
+
+    fn get_ring_digest(self) -> hkdf::Algorithm {
+        match self {
+            Algorithm::AES128_GCM => hkdf::HKDF_SHA256,
+            Algorithm::AES256_GCM => hkdf::HKDF_SHA384,
+            Algorithm::ChaCha20_Poly1305 => hkdf::HKDF_SHA256,
+        }
+    }
+
+    pub fn key_len(self) -> usize {
+        self.get_ring_aead().key_len()
+    }
+
+    pub fn tag_len(self) -> usize {
+        if cfg!(feature = "fuzzing") {
+            return 0;
+        }
+
+        self.get_ring_aead().tag_len()
+    }
+
+    pub fn nonce_len(self) -> usize {
+        self.get_ring_aead().nonce_len()
+    }
+}
+
+pub struct Open {
+    alg: Algorithm,
+
+    hp_key: aead::quic::HeaderProtectionKey,
+
+    key: aead::LessSafeKey,
+
+    nonce: Vec<u8>,
+}
+
+impl Open {
+    pub fn new(
+        alg: Algorithm, key: &[u8], iv: &[u8], hp_key: &[u8],
+    ) -> Result<Open> {
+        Ok(Open {
+            hp_key: aead::quic::HeaderProtectionKey::new(
+                alg.get_ring_hp(),
+                hp_key,
+            )
+            .map_err(|_| Error::CryptoFail)?,
+
+            key: aead::LessSafeKey::new(
+                aead::UnboundKey::new(alg.get_ring_aead(), key)
+                    .map_err(|_| Error::CryptoFail)?,
+            ),
+
+            nonce: Vec::from(iv),
+
+            alg,
+        })
+    }
+
+    pub fn from_secret(aead: Algorithm, secret: &[u8]) -> Result<Open> {
+        let key_len = aead.key_len();
+        let nonce_len = aead.nonce_len();
+
+        let mut key = vec![0; key_len];
+        let mut iv = vec![0; nonce_len];
+        let mut pn_key = vec![0; key_len];
+
+        derive_pkt_key(aead, &secret, &mut key)?;
+        derive_pkt_iv(aead, &secret, &mut iv)?;
+        derive_hdr_key(aead, &secret, &mut pn_key)?;
+
+        Open::new(aead, &key, &iv, &pn_key)
+    }
+
+    pub fn open_with_u64_counter(
+        &self, counter: u64, ad: &[u8], buf: &mut [u8],
+    ) -> Result<usize> {
+        if cfg!(feature = "fuzzing") {
+            return Ok(buf.len());
+        }
+
+        let nonce = make_nonce(&self.nonce, counter);
+
+        let ad = aead::Aad::from(ad);
+
+        let plain = self
+            .key
+            .open_in_place(nonce, ad, buf)
+            .map_err(|_| Error::CryptoFail)?;
+
+        Ok(plain.len())
+    }
+
+    pub fn new_mask(&self, sample: &[u8]) -> Result<[u8; 5]> {
+        if cfg!(feature = "fuzzing") {
+            return Ok(<[u8; 5]>::default());
+        }
+
+        let mask = self
+            .hp_key
+            .new_mask(sample)
+            .map_err(|_| Error::CryptoFail)?;
+
+        Ok(mask)
+    }
+
+    pub fn alg(&self) -> Algorithm {
+        self.alg
+    }
+}
+
+pub struct Seal {
+    alg: Algorithm,
+
+    hp_key: aead::quic::HeaderProtectionKey,
+
+    key: aead::LessSafeKey,
+
+    nonce: Vec<u8>,
+}
+
+impl Seal {
+    pub fn new(
+        alg: Algorithm, key: &[u8], iv: &[u8], hp_key: &[u8],
+    ) -> Result<Seal> {
+        Ok(Seal {
+            hp_key: aead::quic::HeaderProtectionKey::new(
+                alg.get_ring_hp(),
+                hp_key,
+            )
+            .map_err(|_| Error::CryptoFail)?,
+
+            key: aead::LessSafeKey::new(
+                aead::UnboundKey::new(alg.get_ring_aead(), key)
+                    .map_err(|_| Error::CryptoFail)?,
+            ),
+
+            nonce: Vec::from(iv),
+
+            alg,
+        })
+    }
+
+    pub fn from_secret(aead: Algorithm, secret: &[u8]) -> Result<Seal> {
+        let key_len = aead.key_len();
+        let nonce_len = aead.nonce_len();
+
+        let mut key = vec![0; key_len];
+        let mut iv = vec![0; nonce_len];
+        let mut pn_key = vec![0; key_len];
+
+        derive_pkt_key(aead, &secret, &mut key)?;
+        derive_pkt_iv(aead, &secret, &mut iv)?;
+        derive_hdr_key(aead, &secret, &mut pn_key)?;
+
+        Seal::new(aead, &key, &iv, &pn_key)
+    }
+
+    pub fn seal_with_u64_counter(
+        &self, counter: u64, ad: &[u8], buf: &mut [u8],
+    ) -> Result<()> {
+        if cfg!(feature = "fuzzing") {
+            return Ok(());
+        }
+
+        let nonce = make_nonce(&self.nonce, counter);
+
+        let ad = aead::Aad::from(ad);
+
+        let tag_len = self.alg().tag_len();
+
+        let in_out_len =
+            buf.len().checked_sub(tag_len).ok_or(Error::CryptoFail)?;
+
+        let (in_out, tag_out) = buf.split_at_mut(in_out_len);
+
+        let tag = self
+            .key
+            .seal_in_place_separate_tag(nonce, ad, in_out)
+            .map_err(|_| Error::CryptoFail)?;
+
+        // Append the AEAD tag to the end of the sealed buffer.
+        tag_out.copy_from_slice(tag.as_ref());
+
+        Ok(())
+    }
+
+    pub fn new_mask(&self, sample: &[u8]) -> Result<[u8; 5]> {
+        if cfg!(feature = "fuzzing") {
+            return Ok(<[u8; 5]>::default());
+        }
+
+        let mask = self
+            .hp_key
+            .new_mask(sample)
+            .map_err(|_| Error::CryptoFail)?;
+
+        Ok(mask)
+    }
+
+    pub fn alg(&self) -> Algorithm {
+        self.alg
+    }
+}
+
+pub fn derive_initial_key_material(
+    cid: &[u8], version: u32, is_server: bool,
+) -> Result<(Open, Seal)> {
+    let mut secret = [0; 32];
+
+    let aead = Algorithm::AES128_GCM;
+
+    let key_len = aead.key_len();
+    let nonce_len = aead.nonce_len();
+
+    let initial_secret = derive_initial_secret(&cid, version)?;
+
+    // Client.
+    let mut client_key = vec![0; key_len];
+    let mut client_iv = vec![0; nonce_len];
+    let mut client_hp_key = vec![0; key_len];
+
+    derive_client_initial_secret(&initial_secret, &mut secret)?;
+    derive_pkt_key(aead, &secret, &mut client_key)?;
+    derive_pkt_iv(aead, &secret, &mut client_iv)?;
+    derive_hdr_key(aead, &secret, &mut client_hp_key)?;
+
+    // Server.
+    let mut server_key = vec![0; key_len];
+    let mut server_iv = vec![0; nonce_len];
+    let mut server_hp_key = vec![0; key_len];
+
+    derive_server_initial_secret(&initial_secret, &mut secret)?;
+    derive_pkt_key(aead, &secret, &mut server_key)?;
+    derive_pkt_iv(aead, &secret, &mut server_iv)?;
+    derive_hdr_key(aead, &secret, &mut server_hp_key)?;
+
+    let (open, seal) = if is_server {
+        (
+            Open::new(aead, &client_key, &client_iv, &client_hp_key)?,
+            Seal::new(aead, &server_key, &server_iv, &server_hp_key)?,
+        )
+    } else {
+        (
+            Open::new(aead, &server_key, &server_iv, &server_hp_key)?,
+            Seal::new(aead, &client_key, &client_iv, &client_hp_key)?,
+        )
+    };
+
+    Ok((open, seal))
+}
+
+fn derive_initial_secret(secret: &[u8], version: u32) -> Result<hkdf::Prk> {
+    const INITIAL_SALT: [u8; 20] = [
+        0xaf, 0xbf, 0xec, 0x28, 0x99, 0x93, 0xd2, 0x4c, 0x9e, 0x97, 0x86, 0xf1,
+        0x9c, 0x61, 0x11, 0xe0, 0x43, 0x90, 0xa8, 0x99,
+    ];
+
+    const INITIAL_SALT_OLD: [u8; 20] = [
+        0xc3, 0xee, 0xf7, 0x12, 0xc7, 0x2e, 0xbb, 0x5a, 0x11, 0xa7, 0xd2, 0x43,
+        0x2b, 0xb4, 0x63, 0x65, 0xbe, 0xf9, 0xf5, 0x02,
+    ];
+
+    let salt = match version {
+        crate::PROTOCOL_VERSION_DRAFT27 | crate::PROTOCOL_VERSION_DRAFT28 =>
+            &INITIAL_SALT_OLD,
+
+        _ => &INITIAL_SALT,
+    };
+
+    let salt = hkdf::Salt::new(hkdf::HKDF_SHA256, salt);
+    Ok(salt.extract(secret))
+}
+
+fn derive_client_initial_secret(prk: &hkdf::Prk, out: &mut [u8]) -> Result<()> {
+    const LABEL: &[u8] = b"client in";
+    hkdf_expand_label(prk, LABEL, out)
+}
+
+fn derive_server_initial_secret(prk: &hkdf::Prk, out: &mut [u8]) -> Result<()> {
+    const LABEL: &[u8] = b"server in";
+    hkdf_expand_label(prk, LABEL, out)
+}
+
+pub fn derive_hdr_key(
+    aead: Algorithm, secret: &[u8], out: &mut [u8],
+) -> Result<()> {
+    const LABEL: &[u8] = b"quic hp";
+
+    let key_len = aead.key_len();
+
+    if key_len > out.len() {
+        return Err(Error::CryptoFail);
+    }
+
+    let secret = hkdf::Prk::new_less_safe(aead.get_ring_digest(), secret);
+    hkdf_expand_label(&secret, LABEL, &mut out[..key_len])
+}
+
+pub fn derive_pkt_key(
+    aead: Algorithm, secret: &[u8], out: &mut [u8],
+) -> Result<()> {
+    const LABEL: &[u8] = b"quic key";
+
+    let key_len = aead.key_len();
+
+    if key_len > out.len() {
+        return Err(Error::CryptoFail);
+    }
+
+    let secret = hkdf::Prk::new_less_safe(aead.get_ring_digest(), secret);
+    hkdf_expand_label(&secret, LABEL, &mut out[..key_len])
+}
+
+pub fn derive_pkt_iv(
+    aead: Algorithm, secret: &[u8], out: &mut [u8],
+) -> Result<()> {
+    const LABEL: &[u8] = b"quic iv";
+
+    let nonce_len = aead.nonce_len();
+
+    if nonce_len > out.len() {
+        return Err(Error::CryptoFail);
+    }
+
+    let secret = hkdf::Prk::new_less_safe(aead.get_ring_digest(), secret);
+    hkdf_expand_label(&secret, LABEL, &mut out[..nonce_len])
+}
+
+fn hkdf_expand_label(
+    prk: &hkdf::Prk, label: &[u8], out: &mut [u8],
+) -> Result<()> {
+    const LABEL_PREFIX: &[u8] = b"tls13 ";
+
+    let out_len = (out.len() as u16).to_be_bytes();
+    let label_len = (LABEL_PREFIX.len() + label.len()) as u8;
+
+    let info = [&out_len, &[label_len][..], LABEL_PREFIX, label, &[0][..]];
+
+    prk.expand(&info, ArbitraryOutputLen(out.len()))
+        .map_err(|_| Error::CryptoFail)?
+        .fill(out)
+        .map_err(|_| Error::CryptoFail)?;
+
+    Ok(())
+}
+
+fn make_nonce(iv: &[u8], counter: u64) -> aead::Nonce {
+    let mut nonce = [0; aead::NONCE_LEN];
+    nonce.copy_from_slice(&iv);
+
+    // XOR the last bytes of the IV with the counter. This is equivalent to
+    // left-padding the counter with zero bytes.
+    for (a, b) in nonce[4..].iter_mut().zip(counter.to_be_bytes().iter()) {
+        *a ^= b;
+    }
+
+    aead::Nonce::assume_unique_for_key(nonce)
+}
+
+// The ring HKDF expand() API does not accept an arbitrary output length, so we
+// need to hide the `usize` length as part of a type that implements the trait
+// `ring::hkdf::KeyType` in order to trick ring into accepting it.
+struct ArbitraryOutputLen(usize);
+
+impl hkdf::KeyType for ArbitraryOutputLen {
+    fn len(&self) -> usize {
+        self.0
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn derive_initial_secrets() {
+        let dcid = [0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08];
+
+        let mut secret = [0; 32];
+        let mut pkt_key = [0; 16];
+        let mut pkt_iv = [0; 12];
+        let mut hdr_key = [0; 16];
+
+        let aead = Algorithm::AES128_GCM;
+
+        let initial_secret =
+            derive_initial_secret(&dcid, crate::PROTOCOL_VERSION).unwrap();
+
+        // Client.
+        assert!(
+            derive_client_initial_secret(&initial_secret, &mut secret).is_ok()
+        );
+        let expected_client_initial_secret = [
+            0x00, 0x88, 0x11, 0x92, 0x88, 0xf1, 0xd8, 0x66, 0x73, 0x3c, 0xee,
+            0xed, 0x15, 0xff, 0x9d, 0x50, 0x90, 0x2c, 0xf8, 0x29, 0x52, 0xee,
+            0xe2, 0x7e, 0x9d, 0x4d, 0x49, 0x18, 0xea, 0x37, 0x1d, 0x87,
+        ];
+        assert_eq!(&secret, &expected_client_initial_secret);
+
+        assert!(derive_pkt_key(aead, &secret, &mut pkt_key).is_ok());
+        let expected_client_pkt_key = [
+            0x17, 0x52, 0x57, 0xa3, 0x1e, 0xb0, 0x9d, 0xea, 0x93, 0x66, 0xd8,
+            0xbb, 0x79, 0xad, 0x80, 0xba,
+        ];
+        assert_eq!(&pkt_key, &expected_client_pkt_key);
+
+        assert!(derive_pkt_iv(aead, &secret, &mut pkt_iv).is_ok());
+        let expected_client_pkt_iv = [
+            0x6b, 0x26, 0x11, 0x4b, 0x9c, 0xba, 0x2b, 0x63, 0xa9, 0xe8, 0xdd,
+            0x4f,
+        ];
+        assert_eq!(&pkt_iv, &expected_client_pkt_iv);
+
+        assert!(derive_hdr_key(aead, &secret, &mut hdr_key).is_ok());
+        let expected_client_hdr_key = [
+            0x9d, 0xdd, 0x12, 0xc9, 0x94, 0xc0, 0x69, 0x8b, 0x89, 0x37, 0x4a,
+            0x9c, 0x07, 0x7a, 0x30, 0x77,
+        ];
+        assert_eq!(&hdr_key, &expected_client_hdr_key);
+
+        // Server.
+        assert!(
+            derive_server_initial_secret(&initial_secret, &mut secret).is_ok()
+        );
+        let expected_server_initial_secret = [
+            0x00, 0x6f, 0x88, 0x13, 0x59, 0x24, 0x4d, 0xd9, 0xad, 0x1a, 0xcf,
+            0x85, 0xf5, 0x95, 0xba, 0xd6, 0x7c, 0x13, 0xf9, 0xf5, 0x58, 0x6f,
+            0x5e, 0x64, 0xe1, 0xac, 0xae, 0x1d, 0x9e, 0xa8, 0xf6, 0x16,
+        ];
+        assert_eq!(&secret, &expected_server_initial_secret);
+
+        assert!(derive_pkt_key(aead, &secret, &mut pkt_key).is_ok());
+        let expected_server_pkt_key = [
+            0x14, 0x9d, 0x0b, 0x16, 0x62, 0xab, 0x87, 0x1f, 0xbe, 0x63, 0xc4,
+            0x9b, 0x5e, 0x65, 0x5a, 0x5d,
+        ];
+        assert_eq!(&pkt_key, &expected_server_pkt_key);
+
+        assert!(derive_pkt_iv(aead, &secret, &mut pkt_iv).is_ok());
+        let expected_server_pkt_iv = [
+            0xba, 0xb2, 0xb1, 0x2a, 0x4c, 0x76, 0x01, 0x6a, 0xce, 0x47, 0x85,
+            0x6d,
+        ];
+        assert_eq!(&pkt_iv, &expected_server_pkt_iv);
+
+        assert!(derive_hdr_key(aead, &secret, &mut hdr_key).is_ok());
+        let expected_server_hdr_key = [
+            0xc0, 0xc4, 0x99, 0xa6, 0x5a, 0x60, 0x02, 0x4a, 0x18, 0xa2, 0x50,
+            0x97, 0x4e, 0xa0, 0x1d, 0xfa,
+        ];
+        assert_eq!(&hdr_key, &expected_server_hdr_key);
+    }
+
+    #[test]
+    fn derive_initial_secrets_old() {
+        let dcid = [0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08];
+
+        let mut secret = [0; 32];
+        let mut pkt_key = [0; 16];
+        let mut pkt_iv = [0; 12];
+        let mut hdr_key = [0; 16];
+
+        let aead = Algorithm::AES128_GCM;
+
+        let initial_secret =
+            derive_initial_secret(&dcid, crate::PROTOCOL_VERSION_DRAFT28)
+                .unwrap();
+
+        // Client.
+        assert!(
+            derive_client_initial_secret(&initial_secret, &mut secret).is_ok()
+        );
+        let expected_client_initial_secret = [
+            0xfd, 0xa3, 0x95, 0x3a, 0xec, 0xc0, 0x40, 0xe4, 0x8b, 0x34, 0xe2,
+            0x7e, 0xf8, 0x7d, 0xe3, 0xa6, 0x09, 0x8e, 0xcf, 0x0e, 0x38, 0xb7,
+            0xe0, 0x32, 0xc5, 0xc5, 0x7b, 0xcb, 0xd5, 0x97, 0x5b, 0x84,
+        ];
+        assert_eq!(&secret, &expected_client_initial_secret);
+
+        assert!(derive_pkt_key(aead, &secret, &mut pkt_key).is_ok());
+        let expected_client_pkt_key = [
+            0xaf, 0x7f, 0xd7, 0xef, 0xeb, 0xd2, 0x18, 0x78, 0xff, 0x66, 0x81,
+            0x12, 0x48, 0x98, 0x36, 0x94,
+        ];
+        assert_eq!(&pkt_key, &expected_client_pkt_key);
+
+        assert!(derive_pkt_iv(aead, &secret, &mut pkt_iv).is_ok());
+        let expected_client_pkt_iv = [
+            0x86, 0x81, 0x35, 0x94, 0x10, 0xa7, 0x0b, 0xb9, 0xc9, 0x2f, 0x04,
+            0x20,
+        ];
+        assert_eq!(&pkt_iv, &expected_client_pkt_iv);
+
+        assert!(derive_hdr_key(aead, &secret, &mut hdr_key).is_ok());
+        let expected_client_hdr_key = [
+            0xa9, 0x80, 0xb8, 0xb4, 0xfb, 0x7d, 0x9f, 0xbc, 0x13, 0xe8, 0x14,
+            0xc2, 0x31, 0x64, 0x25, 0x3d,
+        ];
+        assert_eq!(&hdr_key, &expected_client_hdr_key);
+
+        // Server.
+        assert!(
+            derive_server_initial_secret(&initial_secret, &mut secret).is_ok()
+        );
+        let expected_server_initial_secret = [
+            0x55, 0x43, 0x66, 0xb8, 0x19, 0x12, 0xff, 0x90, 0xbe, 0x41, 0xf1,
+            0x7e, 0x80, 0x22, 0x21, 0x30, 0x90, 0xab, 0x17, 0xd8, 0x14, 0x91,
+            0x79, 0xbc, 0xad, 0xf2, 0x22, 0xf2, 0x9f, 0xf2, 0xdd, 0xd5,
+        ];
+        assert_eq!(&secret, &expected_server_initial_secret);
+
+        assert!(derive_pkt_key(aead, &secret, &mut pkt_key).is_ok());
+        let expected_server_pkt_key = [
+            0x5d, 0x51, 0xda, 0x9e, 0xe8, 0x97, 0xa2, 0x1b, 0x26, 0x59, 0xcc,
+            0xc7, 0xe5, 0xbf, 0xa5, 0x77,
+        ];
+        assert_eq!(&pkt_key, &expected_server_pkt_key);
+
+        assert!(derive_pkt_iv(aead, &secret, &mut pkt_iv).is_ok());
+        let expected_server_pkt_iv = [
+            0x5e, 0x5a, 0xe6, 0x51, 0xfd, 0x1e, 0x84, 0x95, 0xaf, 0x13, 0x50,
+            0x8b,
+        ];
+        assert_eq!(&pkt_iv, &expected_server_pkt_iv);
+
+        assert!(derive_hdr_key(aead, &secret, &mut hdr_key).is_ok());
+        let expected_server_hdr_key = [
+            0xa8, 0xed, 0x82, 0xe6, 0x66, 0x4f, 0x86, 0x5a, 0xed, 0xf6, 0x10,
+            0x69, 0x43, 0xf9, 0x5f, 0xb8,
+        ];
+        assert_eq!(&hdr_key, &expected_server_hdr_key);
+    }
+
+    #[test]
+    fn derive_chacha20_secrets() {
+        let secret = [
+            0x9a, 0xc3, 0x12, 0xa7, 0xf8, 0x77, 0x46, 0x8e, 0xbe, 0x69, 0x42,
+            0x27, 0x48, 0xad, 0x00, 0xa1, 0x54, 0x43, 0xf1, 0x82, 0x03, 0xa0,
+            0x7d, 0x60, 0x60, 0xf6, 0x88, 0xf3, 0x0f, 0x21, 0x63, 0x2b,
+        ];
+
+        let aead = Algorithm::ChaCha20_Poly1305;
+
+        let mut pkt_key = [0; 32];
+        let mut pkt_iv = [0; 12];
+        let mut hdr_key = [0; 32];
+
+        assert!(derive_pkt_key(aead, &secret, &mut pkt_key).is_ok());
+        let expected_pkt_key = [
+            0xc6, 0xd9, 0x8f, 0xf3, 0x44, 0x1c, 0x3f, 0xe1, 0xb2, 0x18, 0x20,
+            0x94, 0xf6, 0x9c, 0xaa, 0x2e, 0xd4, 0xb7, 0x16, 0xb6, 0x54, 0x88,
+            0x96, 0x0a, 0x7a, 0x98, 0x49, 0x79, 0xfb, 0x23, 0xe1, 0xc8,
+        ];
+        assert_eq!(&pkt_key, &expected_pkt_key);
+
+        assert!(derive_pkt_iv(aead, &secret, &mut pkt_iv).is_ok());
+        let expected_pkt_iv = [
+            0xe0, 0x45, 0x9b, 0x34, 0x74, 0xbd, 0xd0, 0xe4, 0x4a, 0x41, 0xc1,
+            0x44,
+        ];
+        assert_eq!(&pkt_iv, &expected_pkt_iv);
+
+        assert!(derive_hdr_key(aead, &secret, &mut hdr_key).is_ok());
+        let expected_hdr_key = [
+            0x25, 0xa2, 0x82, 0xb9, 0xe8, 0x2f, 0x06, 0xf2, 0x1f, 0x48, 0x89,
+            0x17, 0xa4, 0xfc, 0x8f, 0x1b, 0x73, 0x57, 0x36, 0x85, 0x60, 0x85,
+            0x97, 0xd0, 0xef, 0xcb, 0x07, 0x6b, 0x0a, 0xb7, 0xa7, 0xa4,
+        ];
+        assert_eq!(&hdr_key, &expected_hdr_key);
+    }
+}
diff --git a/src/dgram.rs b/src/dgram.rs
new file mode 100644
index 0000000..755d95f
--- /dev/null
+++ b/src/dgram.rs
@@ -0,0 +1,105 @@
+// Copyright (C) 2020, Cloudflare, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright notice,
+//       this list of conditions and the following disclaimer.
+//
+//     * Redistributions in binary form must reproduce the above copyright
+//       notice, this list of conditions and the following disclaimer in the
+//       documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+use crate::Error;
+use crate::Result;
+
+use std::collections::VecDeque;
+
+/// Keeps track of DATAGRAM frames.
+#[derive(Default)]
+pub struct DatagramQueue {
+    queue: VecDeque<Vec<u8>>,
+    queue_max_len: usize,
+    queue_bytes_size: usize,
+}
+
+impl DatagramQueue {
+    pub fn new(queue_max_len: usize) -> Self {
+        DatagramQueue {
+            queue: VecDeque::with_capacity(queue_max_len),
+            queue_bytes_size: 0,
+            queue_max_len,
+        }
+    }
+
+    pub fn push(&mut self, data: &[u8]) -> Result<()> {
+        if self.is_full() {
+            return Err(Error::Done);
+        }
+
+        self.queue.push_back(data.to_vec());
+        self.queue_bytes_size += data.len();
+        Ok(())
+    }
+
+    pub fn peek_front_len(&self) -> Option<usize> {
+        self.queue.front().map(|d| d.len())
+    }
+
+    pub fn peek_front_bytes(&self, buf: &mut [u8], len: usize) -> Result<usize> {
+        match self.queue.front() {
+            Some(d) => {
+                let len = std::cmp::min(len, d.len());
+                if buf.len() < len {
+                    return Err(Error::BufferTooShort);
+                }
+
+                buf[..len].copy_from_slice(&d[..len]);
+                Ok(len)
+            },
+
+            None => Err(Error::Done),
+        }
+    }
+
+    pub fn pop(&mut self) -> Option<Vec<u8>> {
+        if let Some(d) = self.queue.pop_front() {
+            self.queue_bytes_size = self.queue_bytes_size.saturating_sub(d.len());
+            return Some(d);
+        }
+
+        None
+    }
+
+    pub fn has_pending(&self) -> bool {
+        !self.queue.is_empty()
+    }
+
+    pub fn purge<F: Fn(&[u8]) -> bool>(&mut self, f: F) {
+        self.queue.retain(|d| !f(d));
+        self.queue_bytes_size =
+            self.queue.iter().fold(0, |total, d| total + d.len());
+    }
+
+    pub fn is_full(&self) -> bool {
+        self.queue.len() == self.queue_max_len
+    }
+
+    pub fn byte_size(&self) -> usize {
+        self.queue_bytes_size
+    }
+}
diff --git a/src/ffi.rs b/src/ffi.rs
new file mode 100644
index 0000000..13d98cd
--- /dev/null
+++ b/src/ffi.rs
@@ -0,0 +1,804 @@
+// Copyright (C) 2018-2019, Cloudflare, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright notice,
+//       this list of conditions and the following disclaimer.
+//
+//     * Redistributions in binary form must reproduce the above copyright
+//       notice, this list of conditions and the following disclaimer in the
+//       documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// 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::ffi;
+use std::ptr;
+use std::slice;
+use std::sync::atomic;
+
+#[cfg(unix)]
+use std::os::unix::io::FromRawFd;
+
+use libc::c_char;
+use libc::c_int;
+use libc::c_void;
+use libc::size_t;
+use libc::ssize_t;
+
+use crate::*;
+
+#[no_mangle]
+pub extern fn quiche_version() -> *const u8 {
+    static VERSION: &str = concat!(env!("CARGO_PKG_VERSION"), "\0");
+    VERSION.as_ptr()
+}
+
+struct Logger {
+    cb: extern fn(line: *const u8, argp: *mut c_void),
+    argp: std::sync::atomic::AtomicPtr<c_void>,
+}
+
+impl log::Log for Logger {
+    fn enabled(&self, _metadata: &log::Metadata) -> bool {
+        true
+    }
+
+    fn log(&self, record: &log::Record) {
+        let line = format!("{}: {}\0", record.target(), record.args());
+        (self.cb)(line.as_ptr(), self.argp.load(atomic::Ordering::Relaxed));
+    }
+
+    fn flush(&self) {}
+}
+
+#[no_mangle]
+pub extern fn quiche_enable_debug_logging(
+    cb: extern fn(line: *const u8, argp: *mut c_void), argp: *mut c_void,
+) -> c_int {
+    let argp = atomic::AtomicPtr::new(argp);
+    let logger = Box::new(Logger { cb, argp });
+
+    if log::set_boxed_logger(logger).is_err() {
+        return -1;
+    }
+
+    log::set_max_level(log::LevelFilter::Trace);
+
+    0
+}
+
+#[no_mangle]
+pub extern fn quiche_config_new(version: u32) -> *mut Config {
+    match Config::new(version) {
+        Ok(c) => Box::into_raw(Box::new(c)),
+
+        Err(_) => ptr::null_mut(),
+    }
+}
+
+#[no_mangle]
+pub extern fn quiche_config_load_cert_chain_from_pem_file(
+    config: &mut Config, path: *const c_char,
+) -> c_int {
+    let path = unsafe { ffi::CStr::from_ptr(path).to_str().unwrap() };
+
+    match config.load_cert_chain_from_pem_file(path) {
+        Ok(_) => 0,
+
+        Err(e) => e.to_c() as c_int,
+    }
+}
+
+#[no_mangle]
+pub extern fn quiche_config_load_priv_key_from_pem_file(
+    config: &mut Config, path: *const c_char,
+) -> c_int {
+    let path = unsafe { ffi::CStr::from_ptr(path).to_str().unwrap() };
+
+    match config.load_priv_key_from_pem_file(path) {
+        Ok(_) => 0,
+
+        Err(e) => e.to_c() as c_int,
+    }
+}
+
+#[no_mangle]
+pub extern fn quiche_config_verify_peer(config: &mut Config, v: bool) {
+    config.verify_peer(v);
+}
+
+#[no_mangle]
+pub extern fn quiche_config_grease(config: &mut Config, v: bool) {
+    config.grease(v);
+}
+
+#[no_mangle]
+pub extern fn quiche_config_log_keys(config: &mut Config) {
+    config.log_keys();
+}
+
+#[no_mangle]
+pub extern fn quiche_config_enable_early_data(config: &mut Config) {
+    config.enable_early_data();
+}
+
+#[no_mangle]
+pub extern fn quiche_config_set_application_protos(
+    config: &mut Config, protos: *const u8, protos_len: size_t,
+) -> c_int {
+    let protos = unsafe { slice::from_raw_parts(protos, protos_len) };
+
+    match config.set_application_protos(protos) {
+        Ok(_) => 0,
+
+        Err(e) => e.to_c() as c_int,
+    }
+}
+
+#[no_mangle]
+pub extern fn quiche_config_set_max_idle_timeout(config: &mut Config, v: u64) {
+    config.set_max_idle_timeout(v);
+}
+
+#[no_mangle]
+pub extern fn quiche_config_set_max_udp_payload_size(
+    config: &mut Config, v: u64,
+) {
+    config.set_max_udp_payload_size(v);
+}
+
+#[no_mangle]
+pub extern fn quiche_config_set_initial_max_data(config: &mut Config, v: u64) {
+    config.set_initial_max_data(v);
+}
+
+#[no_mangle]
+pub extern fn quiche_config_set_initial_max_stream_data_bidi_local(
+    config: &mut Config, v: u64,
+) {
+    config.set_initial_max_stream_data_bidi_local(v);
+}
+
+#[no_mangle]
+pub extern fn quiche_config_set_initial_max_stream_data_bidi_remote(
+    config: &mut Config, v: u64,
+) {
+    config.set_initial_max_stream_data_bidi_remote(v);
+}
+
+#[no_mangle]
+pub extern fn quiche_config_set_initial_max_stream_data_uni(
+    config: &mut Config, v: u64,
+) {
+    config.set_initial_max_stream_data_uni(v);
+}
+
+#[no_mangle]
+pub extern fn quiche_config_set_initial_max_streams_bidi(
+    config: &mut Config, v: u64,
+) {
+    config.set_initial_max_streams_bidi(v);
+}
+
+#[no_mangle]
+pub extern fn quiche_config_set_initial_max_streams_uni(
+    config: &mut Config, v: u64,
+) {
+    config.set_initial_max_streams_uni(v);
+}
+
+#[no_mangle]
+pub extern fn quiche_config_set_ack_delay_exponent(config: &mut Config, v: u64) {
+    config.set_ack_delay_exponent(v);
+}
+
+#[no_mangle]
+pub extern fn quiche_config_set_max_ack_delay(config: &mut Config, v: u64) {
+    config.set_max_ack_delay(v);
+}
+
+#[no_mangle]
+pub extern fn quiche_config_set_disable_active_migration(
+    config: &mut Config, v: bool,
+) {
+    config.set_disable_active_migration(v);
+}
+
+#[no_mangle]
+pub extern fn quiche_config_set_cc_algorithm_name(
+    config: &mut Config, name: *const c_char,
+) -> c_int {
+    let name = unsafe { ffi::CStr::from_ptr(name).to_str().unwrap() };
+    match config.set_cc_algorithm_name(name) {
+        Ok(_) => 0,
+
+        Err(e) => e.to_c() as c_int,
+    }
+}
+
+#[no_mangle]
+pub extern fn quiche_config_set_cc_algorithm(
+    config: &mut Config, algo: CongestionControlAlgorithm,
+) {
+    config.set_cc_algorithm(algo);
+}
+
+#[no_mangle]
+pub extern fn quiche_config_enable_hystart(config: &mut Config, v: bool) {
+    config.enable_hystart(v);
+}
+
+#[no_mangle]
+pub extern fn quiche_config_enable_dgram(
+    config: &mut Config, enabled: bool, recv_queue_len: size_t,
+    send_queue_len: size_t,
+) {
+    config.enable_dgram(enabled, recv_queue_len, send_queue_len);
+}
+
+#[no_mangle]
+pub extern fn quiche_config_free(config: *mut Config) {
+    unsafe { Box::from_raw(config) };
+}
+
+#[no_mangle]
+pub extern fn quiche_header_info(
+    buf: *mut u8, buf_len: size_t, dcil: size_t, version: *mut u32, ty: *mut u8,
+    scid: *mut u8, scid_len: *mut size_t, dcid: *mut u8, dcid_len: *mut size_t,
+    token: *mut u8, token_len: *mut size_t,
+) -> c_int {
+    let buf = unsafe { slice::from_raw_parts_mut(buf, buf_len) };
+    let hdr = match Header::from_slice(buf, dcil) {
+        Ok(v) => v,
+
+        Err(e) => return e.to_c() as c_int,
+    };
+
+    unsafe {
+        *version = hdr.version;
+
+        *ty = match hdr.ty {
+            Type::Initial => 1,
+            Type::Retry => 2,
+            Type::Handshake => 3,
+            Type::ZeroRTT => 4,
+            Type::Short => 5,
+            Type::VersionNegotiation => 6,
+        };
+
+        if *scid_len < hdr.scid.len() {
+            return -1;
+        }
+
+        let scid = slice::from_raw_parts_mut(scid, *scid_len);
+        let scid = &mut scid[..hdr.scid.len()];
+        scid.copy_from_slice(&hdr.scid);
+
+        *scid_len = hdr.scid.len();
+
+        if *dcid_len < hdr.dcid.len() {
+            return -1;
+        }
+
+        let dcid = slice::from_raw_parts_mut(dcid, *dcid_len);
+        let dcid = &mut dcid[..hdr.dcid.len()];
+        dcid.copy_from_slice(&hdr.dcid);
+
+        *dcid_len = hdr.dcid.len();
+
+        match hdr.token {
+            Some(tok) => {
+                if *token_len < tok.len() {
+                    return -1;
+                }
+
+                let token = slice::from_raw_parts_mut(token, *token_len);
+                let token = &mut token[..tok.len()];
+                token.copy_from_slice(&tok);
+
+                *token_len = tok.len();
+            },
+
+            None => *token_len = 0,
+        }
+    }
+
+    0
+}
+
+#[no_mangle]
+pub extern fn quiche_accept(
+    scid: *const u8, scid_len: size_t, odcid: *const u8, odcid_len: size_t,
+    config: &mut Config,
+) -> *mut Connection {
+    let scid = unsafe { slice::from_raw_parts(scid, scid_len) };
+
+    let odcid = if !odcid.is_null() || odcid_len == 0 {
+        Some(unsafe { slice::from_raw_parts(odcid, odcid_len) })
+    } else {
+        None
+    };
+
+    match accept(scid, odcid, config) {
+        Ok(c) => Box::into_raw(Pin::into_inner(c)),
+
+        Err(_) => ptr::null_mut(),
+    }
+}
+
+#[no_mangle]
+pub extern fn quiche_connect(
+    server_name: *const c_char, scid: *const u8, scid_len: size_t,
+    config: &mut Config,
+) -> *mut Connection {
+    let server_name = if server_name.is_null() {
+        None
+    } else {
+        Some(unsafe { ffi::CStr::from_ptr(server_name).to_str().unwrap() })
+    };
+
+    let scid = unsafe { slice::from_raw_parts(scid, scid_len) };
+
+    match connect(server_name, scid, config) {
+        Ok(c) => Box::into_raw(Pin::into_inner(c)),
+
+        Err(_) => ptr::null_mut(),
+    }
+}
+
+#[no_mangle]
+pub extern fn quiche_negotiate_version(
+    scid: *const u8, scid_len: size_t, dcid: *const u8, dcid_len: size_t,
+    out: *mut u8, out_len: size_t,
+) -> ssize_t {
+    let scid = unsafe { slice::from_raw_parts(scid, scid_len) };
+    let dcid = unsafe { slice::from_raw_parts(dcid, dcid_len) };
+    let out = unsafe { slice::from_raw_parts_mut(out, out_len) };
+
+    match negotiate_version(scid, dcid, out) {
+        Ok(v) => v as ssize_t,
+
+        Err(e) => e.to_c(),
+    }
+}
+
+#[no_mangle]
+pub extern fn quiche_version_is_supported(version: u32) -> bool {
+    version_is_supported(version)
+}
+
+#[no_mangle]
+pub extern fn quiche_retry(
+    scid: *const u8, scid_len: size_t, dcid: *const u8, dcid_len: size_t,
+    new_scid: *const u8, new_scid_len: size_t, token: *const u8,
+    token_len: size_t, version: u32, out: *mut u8, out_len: size_t,
+) -> ssize_t {
+    let scid = unsafe { slice::from_raw_parts(scid, scid_len) };
+    let dcid = unsafe { slice::from_raw_parts(dcid, dcid_len) };
+    let new_scid = unsafe { slice::from_raw_parts(new_scid, new_scid_len) };
+    let token = unsafe { slice::from_raw_parts(token, token_len) };
+    let out = unsafe { slice::from_raw_parts_mut(out, out_len) };
+
+    match retry(scid, dcid, new_scid, token, version, out) {
+        Ok(v) => v as ssize_t,
+
+        Err(e) => e.to_c(),
+    }
+}
+
+#[no_mangle]
+pub extern fn quiche_conn_new_with_tls(
+    scid: *const u8, scid_len: size_t, odcid: *const u8, odcid_len: size_t,
+    config: &mut Config, ssl: *mut c_void, is_server: bool,
+) -> *mut Connection {
+    let scid = unsafe { slice::from_raw_parts(scid, scid_len) };
+
+    let odcid = if !odcid.is_null() || odcid_len == 0 {
+        Some(unsafe { slice::from_raw_parts(odcid, odcid_len) })
+    } else {
+        None
+    };
+
+    let tls = unsafe { tls::Handshake::from_ptr(ssl) };
+
+    match Connection::with_tls(scid, odcid, config, tls, is_server) {
+        Ok(c) => Box::into_raw(Pin::into_inner(c)),
+
+        Err(_) => ptr::null_mut(),
+    }
+}
+
+#[no_mangle]
+pub extern fn quiche_conn_set_keylog_path(
+    conn: &mut Connection, path: *const c_char,
+) -> bool {
+    let filename = unsafe { ffi::CStr::from_ptr(path).to_str().unwrap() };
+
+    let file = std::fs::OpenOptions::new()
+        .create(true)
+        .append(true)
+        .open(filename);
+
+    let writer = match file {
+        Ok(f) => std::io::BufWriter::new(f),
+
+        Err(_) => return false,
+    };
+
+    conn.set_keylog(Box::new(writer));
+
+    true
+}
+
+#[no_mangle]
+#[cfg(unix)]
+pub extern fn quiche_conn_set_keylog_fd(conn: &mut Connection, fd: c_int) {
+    let f = unsafe { std::fs::File::from_raw_fd(fd) };
+    let writer = std::io::BufWriter::new(f);
+
+    conn.set_keylog(Box::new(writer));
+}
+
+#[no_mangle]
+#[cfg(feature = "qlog")]
+pub extern fn quiche_conn_set_qlog_path(
+    conn: &mut Connection, path: *const c_char, log_title: *const c_char,
+    log_desc: *const c_char,
+) -> bool {
+    let filename = unsafe { ffi::CStr::from_ptr(path).to_str().unwrap() };
+
+    let file = std::fs::OpenOptions::new()
+        .write(true)
+        .create_new(true)
+        .open(filename);
+
+    let writer = match file {
+        Ok(f) => std::io::BufWriter::new(f),
+
+        Err(_) => return false,
+    };
+
+    let title = unsafe { ffi::CStr::from_ptr(log_title).to_str().unwrap() };
+    let description = unsafe { ffi::CStr::from_ptr(log_desc).to_str().unwrap() };
+
+    conn.set_qlog(
+        Box::new(writer),
+        title.to_string(),
+        format!("{} id={}", description, conn.trace_id),
+    );
+
+    true
+}
+
+#[no_mangle]
+#[cfg(all(unix, feature = "qlog"))]
+pub extern fn quiche_conn_set_qlog_fd(
+    conn: &mut Connection, fd: c_int, log_title: *const c_char,
+    log_desc: *const c_char,
+) {
+    let f = unsafe { std::fs::File::from_raw_fd(fd) };
+    let writer = std::io::BufWriter::new(f);
+
+    let title = unsafe { ffi::CStr::from_ptr(log_title).to_str().unwrap() };
+    let description = unsafe { ffi::CStr::from_ptr(log_desc).to_str().unwrap() };
+
+    conn.set_qlog(
+        Box::new(writer),
+        title.to_string(),
+        format!("{} id={}", description, conn.trace_id),
+    );
+}
+
+#[no_mangle]
+pub extern fn quiche_conn_recv(
+    conn: &mut Connection, buf: *mut u8, buf_len: size_t,
+) -> ssize_t {
+    if buf_len > <ssize_t>::max_value() as usize {
+        panic!("The provided buffer is too large");
+    }
+
+    let buf = unsafe { slice::from_raw_parts_mut(buf, buf_len) };
+
+    match conn.recv(buf) {
+        Ok(v) => v as ssize_t,
+
+        Err(e) => e.to_c(),
+    }
+}
+
+#[no_mangle]
+pub extern fn quiche_conn_send(
+    conn: &mut Connection, out: *mut u8, out_len: size_t,
+) -> ssize_t {
+    if out_len > <ssize_t>::max_value() as usize {
+        panic!("The provided buffer is too large");
+    }
+
+    let out = unsafe { slice::from_raw_parts_mut(out, out_len) };
+
+    match conn.send(out) {
+        Ok(v) => v as ssize_t,
+
+        Err(e) => e.to_c(),
+    }
+}
+
+#[no_mangle]
+pub extern fn quiche_conn_stream_recv(
+    conn: &mut Connection, stream_id: u64, out: *mut u8, out_len: size_t,
+    fin: &mut bool,
+) -> ssize_t {
+    if out_len > <ssize_t>::max_value() as usize {
+        panic!("The provided buffer is too large");
+    }
+
+    let out = unsafe { slice::from_raw_parts_mut(out, out_len) };
+
+    let (out_len, out_fin) = match conn.stream_recv(stream_id, out) {
+        Ok(v) => v,
+
+        Err(e) => return e.to_c(),
+    };
+
+    *fin = out_fin;
+
+    out_len as ssize_t
+}
+
+#[no_mangle]
+pub extern fn quiche_conn_stream_send(
+    conn: &mut Connection, stream_id: u64, buf: *const u8, buf_len: size_t,
+    fin: bool,
+) -> ssize_t {
+    if buf_len > <ssize_t>::max_value() as usize {
+        panic!("The provided buffer is too large");
+    }
+
+    let buf = unsafe { slice::from_raw_parts(buf, buf_len) };
+
+    match conn.stream_send(stream_id, buf, fin) {
+        Ok(v) => v as ssize_t,
+
+        Err(e) => e.to_c(),
+    }
+}
+
+#[no_mangle]
+pub extern fn quiche_conn_stream_shutdown(
+    conn: &mut Connection, stream_id: u64, direction: Shutdown, err: u64,
+) -> c_int {
+    match conn.stream_shutdown(stream_id, direction, err) {
+        Ok(_) => 0,
+
+        Err(e) => e.to_c() as c_int,
+    }
+}
+
+#[no_mangle]
+pub extern fn quiche_conn_stream_capacity(
+    conn: &mut Connection, stream_id: u64,
+) -> ssize_t {
+    match conn.stream_capacity(stream_id) {
+        Ok(v) => v as ssize_t,
+
+        Err(e) => e.to_c(),
+    }
+}
+
+#[no_mangle]
+pub extern fn quiche_conn_stream_finished(
+    conn: &mut Connection, stream_id: u64,
+) -> bool {
+    conn.stream_finished(stream_id)
+}
+
+#[no_mangle]
+pub extern fn quiche_conn_readable(conn: &Connection) -> *mut StreamIter {
+    Box::into_raw(Box::new(conn.readable()))
+}
+
+#[no_mangle]
+pub extern fn quiche_conn_writable(conn: &Connection) -> *mut StreamIter {
+    Box::into_raw(Box::new(conn.writable()))
+}
+
+struct AppData(*mut c_void);
+unsafe impl Send for AppData {}
+
+#[no_mangle]
+pub extern fn quiche_conn_stream_init_application_data(
+    conn: &mut Connection, stream_id: u64, data: *mut c_void,
+) -> c_int {
+    match conn.stream_init_application_data(stream_id, AppData(data)) {
+        Ok(_) => 0,
+
+        Err(e) => e.to_c() as c_int,
+    }
+}
+
+#[no_mangle]
+pub extern fn quiche_conn_stream_application_data(
+    conn: &mut Connection, stream_id: u64,
+) -> *mut c_void {
+    match conn.stream_application_data(stream_id) {
+        Some(v) => v.downcast_mut::<AppData>().unwrap().0,
+
+        None => ptr::null_mut(),
+    }
+}
+
+#[no_mangle]
+pub extern fn quiche_conn_close(
+    conn: &mut Connection, app: bool, err: u64, reason: *const u8,
+    reason_len: size_t,
+) -> c_int {
+    let reason = unsafe { slice::from_raw_parts(reason, reason_len) };
+
+    match conn.close(app, err, reason) {
+        Ok(_) => 0,
+
+        Err(e) => e.to_c() as c_int,
+    }
+}
+
+#[no_mangle]
+pub extern fn quiche_conn_timeout_as_nanos(conn: &mut Connection) -> u64 {
+    match conn.timeout() {
+        Some(timeout) => timeout.as_nanos() as u64,
+
+        None => std::u64::MAX,
+    }
+}
+
+#[no_mangle]
+pub extern fn quiche_conn_timeout_as_millis(conn: &mut Connection) -> u64 {
+    match conn.timeout() {
+        Some(timeout) => timeout.as_millis() as u64,
+
+        None => std::u64::MAX,
+    }
+}
+
+#[no_mangle]
+pub extern fn quiche_conn_on_timeout(conn: &mut Connection) {
+    conn.on_timeout()
+}
+
+#[no_mangle]
+pub extern fn quiche_conn_application_proto(
+    conn: &mut Connection, out: &mut *const u8, out_len: &mut size_t,
+) {
+    let proto = conn.application_proto();
+
+    *out = proto.as_ptr();
+    *out_len = proto.len();
+}
+
+#[no_mangle]
+pub extern fn quiche_conn_is_established(conn: &mut Connection) -> bool {
+    conn.is_established()
+}
+
+#[no_mangle]
+pub extern fn quiche_conn_is_in_early_data(conn: &mut Connection) -> bool {
+    conn.is_in_early_data()
+}
+
+#[no_mangle]
+pub extern fn quiche_conn_is_closed(conn: &mut Connection) -> bool {
+    conn.is_closed()
+}
+
+#[no_mangle]
+pub extern fn quiche_stream_iter_next(
+    iter: &mut StreamIter, stream_id: *mut u64,
+) -> bool {
+    if let Some(v) = iter.next() {
+        unsafe { *stream_id = v };
+        return true;
+    }
+
+    false
+}
+
+#[no_mangle]
+pub extern fn quiche_stream_iter_free(iter: *mut StreamIter) {
+    unsafe { Box::from_raw(iter) };
+}
+
+#[repr(C)]
+pub struct Stats {
+    pub recv: usize,
+    pub sent: usize,
+    pub lost: usize,
+    pub rtt: u64,
+    pub cwnd: usize,
+    pub delivery_rate: u64,
+}
+
+#[no_mangle]
+pub extern fn quiche_conn_stats(conn: &Connection, out: &mut Stats) {
+    let stats = conn.stats();
+
+    out.recv = stats.recv;
+    out.sent = stats.sent;
+    out.lost = stats.lost;
+    out.rtt = stats.rtt.as_nanos() as u64;
+    out.cwnd = stats.cwnd;
+    out.delivery_rate = stats.delivery_rate;
+}
+
+#[no_mangle]
+pub extern fn quiche_conn_dgram_max_writable_len(conn: &Connection) -> ssize_t {
+    match conn.dgram_max_writable_len() {
+        None => Error::Done.to_c(),
+
+        Some(v) => v as ssize_t,
+    }
+}
+
+#[no_mangle]
+pub extern fn quiche_conn_dgram_send(
+    conn: &mut Connection, buf: *const u8, buf_len: size_t,
+) -> ssize_t {
+    if buf_len > <ssize_t>::max_value() as usize {
+        panic!("The provided buffer is too large");
+    }
+
+    let buf = unsafe { slice::from_raw_parts(buf, buf_len) };
+
+    match conn.dgram_send(buf) {
+        Ok(_) => buf_len as ssize_t,
+
+        Err(e) => e.to_c(),
+    }
+}
+
+#[no_mangle]
+pub extern fn quiche_conn_dgram_recv(
+    conn: &mut Connection, out: *mut u8, out_len: size_t,
+) -> ssize_t {
+    if out_len > <ssize_t>::max_value() as usize {
+        panic!("The provided buffer is too large");
+    }
+
+    let out = unsafe { slice::from_raw_parts_mut(out, out_len) };
+
+    let out_len = match conn.dgram_recv(out) {
+        Ok(v) => v,
+
+        Err(e) => return e.to_c(),
+    };
+
+    out_len as ssize_t
+}
+
+#[no_mangle]
+pub extern fn quiche_conn_dgram_purge_outgoing(
+    conn: &mut Connection, f: extern fn(*const u8, size_t) -> bool,
+) {
+    conn.dgram_purge_outgoing(|d: &[u8]| -> bool {
+        let ptr: *const u8 = d.as_ptr();
+        let len: size_t = d.len();
+
+        f(ptr, len)
+    });
+}
+
+#[no_mangle]
+pub extern fn quiche_conn_free(conn: *mut Connection) {
+    unsafe { Box::from_raw(conn) };
+}
diff --git a/src/frame.rs b/src/frame.rs
new file mode 100644
index 0000000..b191a42
--- /dev/null
+++ b/src/frame.rs
@@ -0,0 +1,1824 @@
+// Copyright (C) 2018-2019, Cloudflare, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright notice,
+//       this list of conditions and the following disclaimer.
+//
+//     * Redistributions in binary form must reproduce the above copyright
+//       notice, this list of conditions and the following disclaimer in the
+//       documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+use crate::Error;
+use crate::Result;
+
+use crate::octets;
+use crate::packet;
+use crate::ranges;
+use crate::stream;
+
+pub const MAX_CRYPTO_OVERHEAD: usize = 8;
+pub const MAX_DGRAM_OVERHEAD: usize = 2;
+pub const MAX_STREAM_OVERHEAD: usize = 12;
+pub const MAX_STREAM_SIZE: u64 = 1 << 62;
+
+#[derive(Clone, PartialEq)]
+pub enum Frame {
+    Padding {
+        len: usize,
+    },
+
+    Ping,
+
+    ACK {
+        ack_delay: u64,
+        ranges: ranges::RangeSet,
+    },
+
+    ResetStream {
+        stream_id: u64,
+        error_code: u64,
+        final_size: u64,
+    },
+
+    StopSending {
+        stream_id: u64,
+        error_code: u64,
+    },
+
+    Crypto {
+        data: stream::RangeBuf,
+    },
+
+    NewToken {
+        token: Vec<u8>,
+    },
+
+    Stream {
+        stream_id: u64,
+        data: stream::RangeBuf,
+    },
+
+    MaxData {
+        max: u64,
+    },
+
+    MaxStreamData {
+        stream_id: u64,
+        max: u64,
+    },
+
+    MaxStreamsBidi {
+        max: u64,
+    },
+
+    MaxStreamsUni {
+        max: u64,
+    },
+
+    DataBlocked {
+        limit: u64,
+    },
+
+    StreamDataBlocked {
+        stream_id: u64,
+        limit: u64,
+    },
+
+    StreamsBlockedBidi {
+        limit: u64,
+    },
+
+    StreamsBlockedUni {
+        limit: u64,
+    },
+
+    NewConnectionId {
+        seq_num: u64,
+        retire_prior_to: u64,
+        conn_id: Vec<u8>,
+        reset_token: Vec<u8>,
+    },
+
+    RetireConnectionId {
+        seq_num: u64,
+    },
+
+    PathChallenge {
+        data: Vec<u8>,
+    },
+
+    PathResponse {
+        data: Vec<u8>,
+    },
+
+    ConnectionClose {
+        error_code: u64,
+        frame_type: u64,
+        reason: Vec<u8>,
+    },
+
+    ApplicationClose {
+        error_code: u64,
+        reason: Vec<u8>,
+    },
+
+    HandshakeDone,
+
+    Datagram {
+        data: Vec<u8>,
+    },
+}
+
+impl Frame {
+    pub fn from_bytes(
+        b: &mut octets::Octets, pkt: packet::Type,
+    ) -> Result<Frame> {
+        let frame_type = b.get_varint()?;
+
+        // println!("GOT FRAME {:x}", frame_type);
+
+        let frame = match frame_type {
+            0x00 => {
+                let mut len = 1;
+
+                while b.peek_u8() == Ok(0x00) {
+                    b.get_u8()?;
+
+                    len += 1;
+                }
+
+                Frame::Padding { len }
+            },
+
+            0x01 => Frame::Ping,
+
+            0x02 => parse_ack_frame(frame_type, b)?,
+
+            0x04 => Frame::ResetStream {
+                stream_id: b.get_varint()?,
+                error_code: b.get_varint()?,
+                final_size: b.get_varint()?,
+            },
+
+            0x05 => Frame::StopSending {
+                stream_id: b.get_varint()?,
+                error_code: b.get_varint()?,
+            },
+
+            0x06 => {
+                let offset = b.get_varint()?;
+                let data = b.get_bytes_with_varint_length()?;
+                let data = stream::RangeBuf::from(data.as_ref(), offset, false);
+
+                Frame::Crypto { data }
+            },
+
+            0x07 => Frame::NewToken {
+                token: b.get_bytes_with_varint_length()?.to_vec(),
+            },
+
+            0x08..=0x0f => parse_stream_frame(frame_type, b)?,
+
+            0x10 => Frame::MaxData {
+                max: b.get_varint()?,
+            },
+
+            0x11 => Frame::MaxStreamData {
+                stream_id: b.get_varint()?,
+                max: b.get_varint()?,
+            },
+
+            0x12 => Frame::MaxStreamsBidi {
+                max: b.get_varint()?,
+            },
+
+            0x13 => Frame::MaxStreamsUni {
+                max: b.get_varint()?,
+            },
+
+            0x14 => Frame::DataBlocked {
+                limit: b.get_varint()?,
+            },
+
+            0x15 => Frame::StreamDataBlocked {
+                stream_id: b.get_varint()?,
+                limit: b.get_varint()?,
+            },
+
+            0x16 => Frame::StreamsBlockedBidi {
+                limit: b.get_varint()?,
+            },
+
+            0x17 => Frame::StreamsBlockedUni {
+                limit: b.get_varint()?,
+            },
+
+            0x18 => Frame::NewConnectionId {
+                seq_num: b.get_varint()?,
+                retire_prior_to: b.get_varint()?,
+                conn_id: b.get_bytes_with_u8_length()?.to_vec(),
+                reset_token: b.get_bytes(16)?.to_vec(),
+            },
+
+            0x19 => Frame::RetireConnectionId {
+                seq_num: b.get_varint()?,
+            },
+
+            0x1a => Frame::PathChallenge {
+                data: b.get_bytes(8)?.to_vec(),
+            },
+
+            0x1b => Frame::PathResponse {
+                data: b.get_bytes(8)?.to_vec(),
+            },
+
+            0x1c => Frame::ConnectionClose {
+                error_code: b.get_varint()?,
+                frame_type: b.get_varint()?,
+                reason: b.get_bytes_with_varint_length()?.to_vec(),
+            },
+
+            0x1d => Frame::ApplicationClose {
+                error_code: b.get_varint()?,
+                reason: b.get_bytes_with_varint_length()?.to_vec(),
+            },
+
+            0x1e => Frame::HandshakeDone,
+
+            0x30 | 0x31 => parse_datagram_frame(frame_type, b)?,
+
+            _ => return Err(Error::InvalidFrame),
+        };
+
+        let allowed = match (pkt, &frame) {
+            // PADDING and PING are allowed on all packet types.
+            (_, Frame::Padding { .. }) | (_, Frame::Ping { .. }) => true,
+
+            // ACK, CRYPTO, HANDSHAKE_DONE, NEW_TOKEN, PATH_RESPONSE, and
+            // RETIRE_CONNECTION_ID can't be sent on 0-RTT packets.
+            (packet::Type::ZeroRTT, Frame::ACK { .. }) => false,
+            (packet::Type::ZeroRTT, Frame::Crypto { .. }) => false,
+            (packet::Type::ZeroRTT, Frame::HandshakeDone) => false,
+            (packet::Type::ZeroRTT, Frame::NewToken { .. }) => false,
+            (packet::Type::ZeroRTT, Frame::PathResponse { .. }) => false,
+            (packet::Type::ZeroRTT, Frame::RetireConnectionId { .. }) => false,
+            (packet::Type::ZeroRTT, Frame::ConnectionClose { .. }) => false,
+
+            // ACK, CRYPTO and CONNECTION_CLOSE can be sent on all other packet
+            // types.
+            (_, Frame::ACK { .. }) => true,
+            (_, Frame::Crypto { .. }) => true,
+            (_, Frame::ConnectionClose { .. }) => true,
+
+            // All frames are allowed on 0-RTT and 1-RTT packets.
+            (packet::Type::Short, _) => true,
+            (packet::Type::ZeroRTT, _) => true,
+
+            // All other cases are forbidden.
+            (..) => false,
+        };
+
+        if !allowed {
+            return Err(Error::InvalidPacket);
+        }
+
+        Ok(frame)
+    }
+
+    pub fn to_bytes(&self, b: &mut octets::OctetsMut) -> Result<usize> {
+        let before = b.cap();
+
+        match self {
+            Frame::Padding { len } => {
+                let mut left = *len;
+
+                while left > 0 {
+                    b.put_varint(0x00)?;
+
+                    left -= 1;
+                }
+            },
+
+            Frame::Ping => {
+                b.put_varint(0x01)?;
+            },
+
+            Frame::ACK { ack_delay, ranges } => {
+                b.put_varint(0x02)?;
+
+                let mut it = ranges.iter().rev();
+
+                let first = it.next().unwrap();
+                let ack_block = (first.end - 1) - first.start;
+
+                b.put_varint(first.end - 1)?;
+                b.put_varint(*ack_delay)?;
+                b.put_varint(it.len() as u64)?;
+                b.put_varint(ack_block)?;
+
+                let mut smallest_ack = first.start;
+
+                for block in it {
+                    let gap = smallest_ack - block.end - 1;
+                    let ack_block = (block.end - 1) - block.start;
+
+                    b.put_varint(gap)?;
+                    b.put_varint(ack_block)?;
+
+                    smallest_ack = block.start;
+                }
+            },
+
+            Frame::ResetStream {
+                stream_id,
+                error_code,
+                final_size,
+            } => {
+                b.put_varint(0x04)?;
+
+                b.put_varint(*stream_id)?;
+                b.put_varint(*error_code)?;
+                b.put_varint(*final_size)?;
+            },
+
+            Frame::StopSending {
+                stream_id,
+                error_code,
+            } => {
+                b.put_varint(0x05)?;
+
+                b.put_varint(*stream_id)?;
+                b.put_varint(*error_code)?;
+            },
+
+            Frame::Crypto { data } => {
+                b.put_varint(0x06)?;
+
+                b.put_varint(data.off() as u64)?;
+                b.put_varint(data.len() as u64)?;
+                b.put_bytes(&data)?;
+            },
+
+            Frame::NewToken { token } => {
+                b.put_varint(0x07)?;
+
+                b.put_varint(token.len() as u64)?;
+                b.put_bytes(&token)?;
+            },
+
+            Frame::Stream { stream_id, data } => {
+                let mut ty: u8 = 0x08;
+
+                // Always encode offset
+                ty |= 0x04;
+
+                // Always encode length
+                ty |= 0x02;
+
+                if data.fin() {
+                    ty |= 0x01;
+                }
+
+                b.put_varint(u64::from(ty))?;
+
+                b.put_varint(*stream_id)?;
+                b.put_varint(data.off() as u64)?;
+                b.put_varint(data.len() as u64)?;
+                b.put_bytes(data.as_ref())?;
+            },
+
+            Frame::MaxData { max } => {
+                b.put_varint(0x10)?;
+
+                b.put_varint(*max)?;
+            },
+
+            Frame::MaxStreamData { stream_id, max } => {
+                b.put_varint(0x11)?;
+
+                b.put_varint(*stream_id)?;
+                b.put_varint(*max)?;
+            },
+
+            Frame::MaxStreamsBidi { max } => {
+                b.put_varint(0x12)?;
+
+                b.put_varint(*max)?;
+            },
+
+            Frame::MaxStreamsUni { max } => {
+                b.put_varint(0x13)?;
+
+                b.put_varint(*max)?;
+            },
+
+            Frame::DataBlocked { limit } => {
+                b.put_varint(0x14)?;
+
+                b.put_varint(*limit)?;
+            },
+
+            Frame::StreamDataBlocked { stream_id, limit } => {
+                b.put_varint(0x15)?;
+
+                b.put_varint(*stream_id)?;
+                b.put_varint(*limit)?;
+            },
+
+            Frame::StreamsBlockedBidi { limit } => {
+                b.put_varint(0x16)?;
+
+                b.put_varint(*limit)?;
+            },
+
+            Frame::StreamsBlockedUni { limit } => {
+                b.put_varint(0x17)?;
+
+                b.put_varint(*limit)?;
+            },
+
+            Frame::NewConnectionId {
+                seq_num,
+                retire_prior_to,
+                conn_id,
+                reset_token,
+            } => {
+                b.put_varint(0x18)?;
+
+                b.put_varint(*seq_num)?;
+                b.put_varint(*retire_prior_to)?;
+                b.put_u8(conn_id.len() as u8)?;
+                b.put_bytes(conn_id.as_ref())?;
+                b.put_bytes(reset_token.as_ref())?;
+            },
+
+            Frame::RetireConnectionId { seq_num } => {
+                b.put_varint(0x19)?;
+
+                b.put_varint(*seq_num)?;
+            },
+
+            Frame::PathChallenge { data } => {
+                b.put_varint(0x1a)?;
+
+                b.put_bytes(data.as_ref())?;
+            },
+
+            Frame::PathResponse { data } => {
+                b.put_varint(0x1b)?;
+
+                b.put_bytes(data.as_ref())?;
+            },
+
+            Frame::ConnectionClose {
+                error_code,
+                frame_type,
+                reason,
+            } => {
+                b.put_varint(0x1c)?;
+
+                b.put_varint(*error_code)?;
+                b.put_varint(*frame_type)?;
+                b.put_varint(reason.len() as u64)?;
+                b.put_bytes(reason.as_ref())?;
+            },
+
+            Frame::ApplicationClose { error_code, reason } => {
+                b.put_varint(0x1d)?;
+
+                b.put_varint(*error_code)?;
+                b.put_varint(reason.len() as u64)?;
+                b.put_bytes(reason.as_ref())?;
+            },
+
+            Frame::HandshakeDone => {
+                b.put_varint(0x1e)?;
+            },
+
+            Frame::Datagram { data } => {
+                let mut ty: u8 = 0x30;
+
+                // Always encode length
+                ty |= 0x01;
+
+                b.put_varint(u64::from(ty))?;
+
+                b.put_varint(data.len() as u64)?;
+                b.put_bytes(data.as_ref())?;
+            },
+        }
+
+        Ok(before - b.cap())
+    }
+
+    pub fn wire_len(&self) -> usize {
+        match self {
+            Frame::Padding { len } => *len,
+
+            Frame::Ping => 1,
+
+            Frame::ACK { ack_delay, ranges } => {
+                let mut it = ranges.iter().rev();
+
+                let first = it.next().unwrap();
+                let ack_block = (first.end - 1) - first.start;
+
+                let mut len = 1 + // frame type
+                    octets::varint_len(first.end - 1) + // largest_ack
+                    octets::varint_len(*ack_delay) + // ack_delay
+                    octets::varint_len(it.len() as u64) + // block_count
+                    octets::varint_len(ack_block); // first_block
+
+                let mut smallest_ack = first.start;
+
+                for block in it {
+                    let gap = smallest_ack - block.end - 1;
+                    let ack_block = (block.end - 1) - block.start;
+
+                    len += octets::varint_len(gap) + // gap
+                           octets::varint_len(ack_block); // ack_block
+
+                    smallest_ack = block.start;
+                }
+
+                len
+            },
+
+            Frame::ResetStream {
+                stream_id,
+                error_code,
+                final_size,
+            } => {
+                1 + // frame type
+                octets::varint_len(*stream_id) + // stream_id
+                octets::varint_len(*error_code) + // error_code
+                octets::varint_len(*final_size) // final_size
+            },
+
+            Frame::StopSending {
+                stream_id,
+                error_code,
+            } => {
+                1 + // frame type
+                octets::varint_len(*stream_id) + // stream_id
+                octets::varint_len(*error_code) // error_code
+            },
+
+            Frame::Crypto { data } => {
+                1 + // frame type
+                octets::varint_len(data.off() as u64) + // offset
+                octets::varint_len(data.len() as u64) + // length
+                data.len() // data
+            },
+
+            Frame::NewToken { token } => {
+                1 + // frame type
+                octets::varint_len(token.len() as u64) + // token length
+                token.len() // token
+            },
+
+            Frame::Stream { stream_id, data } => {
+                1 + // frame type
+                octets::varint_len(*stream_id) + // stream_id
+                octets::varint_len(data.off() as u64) + // offset
+                octets::varint_len(data.len() as u64) + // length
+                data.len() // data
+            },
+
+            Frame::MaxData { max } => {
+                1 + // frame type
+                octets::varint_len(*max) // max
+            },
+
+            Frame::MaxStreamData { stream_id, max } => {
+                1 + // frame type
+                octets::varint_len(*stream_id) + // stream_id
+                octets::varint_len(*max) // max
+            },
+
+            Frame::MaxStreamsBidi { max } => {
+                1 + // frame type
+                octets::varint_len(*max) // max
+            },
+
+            Frame::MaxStreamsUni { max } => {
+                1 + // frame type
+                octets::varint_len(*max) // max
+            },
+
+            Frame::DataBlocked { limit } => {
+                1 + // frame type
+                octets::varint_len(*limit) // limit
+            },
+
+            Frame::StreamDataBlocked { stream_id, limit } => {
+                1 + // frame type
+                octets::varint_len(*stream_id) + // stream_id
+                octets::varint_len(*limit) // limit
+            },
+
+            Frame::StreamsBlockedBidi { limit } => {
+                1 + // frame type
+                octets::varint_len(*limit) // limit
+            },
+
+            Frame::StreamsBlockedUni { limit } => {
+                1 + // frame type
+                octets::varint_len(*limit) // limit
+            },
+
+            Frame::NewConnectionId {
+                seq_num,
+                retire_prior_to,
+                conn_id,
+                reset_token,
+            } => {
+                1 + // frame type
+                octets::varint_len(*seq_num) + // seq_num
+                octets::varint_len(*retire_prior_to) + // retire_prior_to
+                1 + // conn_id length
+                conn_id.len() + // conn_id
+                reset_token.len() // reset_token
+            },
+
+            Frame::RetireConnectionId { seq_num } => {
+                1 + // frame type
+                octets::varint_len(*seq_num) // seq_num
+            },
+
+            Frame::PathChallenge { .. } => {
+                1 + // frame type
+                8 // data
+            },
+
+            Frame::PathResponse { .. } => {
+                1 + // frame type
+                8 // data
+            },
+
+            Frame::ConnectionClose {
+                frame_type,
+                error_code,
+                reason,
+                ..
+            } => {
+                1 + // frame type
+                octets::varint_len(*error_code) + // error_code
+                octets::varint_len(*frame_type) + // frame_type
+                octets::varint_len(reason.len() as u64) + // reason_len
+                reason.len() // reason
+            },
+
+            Frame::ApplicationClose { reason, error_code } => {
+                1 + // frame type
+                octets::varint_len(*error_code) + // error_code
+                octets::varint_len(reason.len() as u64) + // reason_len
+                reason.len() // reason
+            },
+
+            Frame::HandshakeDone => {
+                1 // frame type
+            },
+
+            Frame::Datagram { data } => {
+                1 + // frame type
+                octets::varint_len(data.len() as u64) + // length
+                data.len() // data
+            },
+        }
+    }
+
+    pub fn ack_eliciting(&self) -> bool {
+        // Any other frame is ack-eliciting (note the `!`).
+        !matches!(self, Frame::Padding { .. } |
+            Frame::ACK { .. } |
+            Frame::ApplicationClose { .. } |
+            Frame::ConnectionClose { .. })
+    }
+
+    pub fn shrink_for_retransmission(&mut self) {
+        if let Frame::Datagram { data } = self {
+            *data = Vec::new();
+        }
+    }
+
+    #[cfg(feature = "qlog")]
+    pub fn to_qlog(&self) -> qlog::QuicFrame {
+        match self {
+            Frame::Padding { .. } => qlog::QuicFrame::padding(),
+
+            Frame::Ping { .. } => qlog::QuicFrame::ping(),
+
+            Frame::ACK { ack_delay, ranges } => {
+                let ack_ranges =
+                    ranges.iter().map(|r| (r.start, r.end - 1)).collect();
+                qlog::QuicFrame::ack(
+                    Some(ack_delay.to_string()),
+                    Some(ack_ranges),
+                    None,
+                    None,
+                    None,
+                )
+            },
+
+            Frame::ResetStream {
+                stream_id,
+                error_code,
+                final_size,
+            } => qlog::QuicFrame::reset_stream(
+                stream_id.to_string(),
+                *error_code,
+                final_size.to_string(),
+            ),
+
+            Frame::StopSending {
+                stream_id,
+                error_code,
+            } =>
+                qlog::QuicFrame::stop_sending(stream_id.to_string(), *error_code),
+
+            Frame::Crypto { data } => qlog::QuicFrame::crypto(
+                data.off().to_string(),
+                data.len().to_string(),
+            ),
+
+            Frame::NewToken { token } => qlog::QuicFrame::new_token(
+                token.len().to_string(),
+                "TODO: https://github.com/quiclog/internet-drafts/issues/36"
+                    .to_string(),
+            ),
+
+            Frame::Stream { stream_id, data } => qlog::QuicFrame::stream(
+                stream_id.to_string(),
+                data.off().to_string(),
+                data.len().to_string(),
+                data.fin(),
+                None,
+            ),
+
+            Frame::MaxData { max } => qlog::QuicFrame::max_data(max.to_string()),
+
+            Frame::MaxStreamData { stream_id, max } =>
+                qlog::QuicFrame::max_stream_data(
+                    stream_id.to_string(),
+                    max.to_string(),
+                ),
+
+            Frame::MaxStreamsBidi { max } => qlog::QuicFrame::max_streams(
+                qlog::StreamType::Bidirectional,
+                max.to_string(),
+            ),
+
+            Frame::MaxStreamsUni { max } => qlog::QuicFrame::max_streams(
+                qlog::StreamType::Unidirectional,
+                max.to_string(),
+            ),
+
+            Frame::DataBlocked { limit } =>
+                qlog::QuicFrame::data_blocked(limit.to_string()),
+
+            Frame::StreamDataBlocked { stream_id, limit } =>
+                qlog::QuicFrame::stream_data_blocked(
+                    stream_id.to_string(),
+                    limit.to_string(),
+                ),
+
+            Frame::StreamsBlockedBidi { limit } =>
+                qlog::QuicFrame::streams_blocked(
+                    qlog::StreamType::Bidirectional,
+                    limit.to_string(),
+                ),
+
+            Frame::StreamsBlockedUni { limit } =>
+                qlog::QuicFrame::streams_blocked(
+                    qlog::StreamType::Unidirectional,
+                    limit.to_string(),
+                ),
+
+            Frame::NewConnectionId {
+                seq_num,
+                retire_prior_to,
+                conn_id,
+                ..
+            } => qlog::QuicFrame::new_connection_id(
+                seq_num.to_string(),
+                retire_prior_to.to_string(),
+                conn_id.len() as u64,
+                "TODO: https://github.com/quiclog/internet-drafts/issues/36"
+                    .to_string(),
+                "TODO: https://github.com/quiclog/internet-drafts/issues/36"
+                    .to_string(),
+            ),
+
+            Frame::RetireConnectionId { seq_num } =>
+                qlog::QuicFrame::retire_connection_id(seq_num.to_string()),
+
+            Frame::PathChallenge { .. } => qlog::QuicFrame::path_challenge(Some(
+                "TODO: https://github.com/quiclog/internet-drafts/issues/36"
+                    .to_string(),
+            )),
+
+            Frame::PathResponse { .. } => qlog::QuicFrame::path_response(Some(
+                "TODO: https://github.com/quiclog/internet-drafts/issues/36"
+                    .to_string(),
+            )),
+
+            Frame::ConnectionClose {
+                error_code, reason, ..
+            } => qlog::QuicFrame::connection_close(
+                qlog::ErrorSpace::TransportError,
+                *error_code,
+                *error_code,
+                String::from_utf8(reason.clone()).unwrap(),
+                Some(
+                    "TODO: https://github.com/quiclog/internet-drafts/issues/36"
+                        .to_string(),
+                ),
+            ),
+
+            Frame::ApplicationClose { error_code, reason } =>
+                qlog::QuicFrame::connection_close(
+                    qlog::ErrorSpace::ApplicationError,
+                    *error_code,
+                    *error_code,
+                    String::from_utf8(reason.clone()).unwrap(),
+                    None, /* Application variant of the frame has no trigger
+                           * frame type */
+                ),
+
+            Frame::HandshakeDone => qlog::QuicFrame::handshake_done(),
+
+            Frame::Datagram { .. } => qlog::QuicFrame::unknown(0x30),
+        }
+    }
+}
+
+impl std::fmt::Debug for Frame {
+    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+        match self {
+            Frame::Padding { len } => {
+                write!(f, "PADDING len={}", len)?;
+            },
+
+            Frame::Ping => {
+                write!(f, "PING")?;
+            },
+
+            Frame::ACK { ack_delay, ranges } => {
+                write!(f, "ACK delay={} blocks={:?}", ack_delay, ranges)?;
+            },
+
+            Frame::ResetStream {
+                stream_id,
+                error_code,
+                final_size,
+            } => {
+                write!(
+                    f,
+                    "RESET_STREAM stream={} err={:x} size={}",
+                    stream_id, error_code, final_size
+                )?;
+            },
+
+            Frame::StopSending {
+                stream_id,
+                error_code,
+            } => {
+                write!(
+                    f,
+                    "STOP_SENDING stream={} err={:x}",
+                    stream_id, error_code
+                )?;
+            },
+
+            Frame::Crypto { data } => {
+                write!(f, "CRYPTO off={} len={}", data.off(), data.len())?;
+            },
+
+            Frame::NewToken { .. } => {
+                write!(f, "NEW_TOKEN (TODO)")?;
+            },
+
+            Frame::Stream { stream_id, data } => {
+                write!(
+                    f,
+                    "STREAM id={} off={} len={} fin={}",
+                    stream_id,
+                    data.off(),
+                    data.len(),
+                    data.fin()
+                )?;
+            },
+
+            Frame::MaxData { max } => {
+                write!(f, "MAX_DATA max={}", max)?;
+            },
+
+            Frame::MaxStreamData { stream_id, max } => {
+                write!(f, "MAX_STREAM_DATA stream={} max={}", stream_id, max)?;
+            },
+
+            Frame::MaxStreamsBidi { max } => {
+                write!(f, "MAX_STREAMS type=bidi max={}", max)?;
+            },
+
+            Frame::MaxStreamsUni { max } => {
+                write!(f, "MAX_STREAMS type=uni max={}", max)?;
+            },
+
+            Frame::DataBlocked { limit } => {
+                write!(f, "DATA_BLOCKED limit={}", limit)?;
+            },
+
+            Frame::StreamDataBlocked { stream_id, limit } => {
+                write!(
+                    f,
+                    "STREAM_DATA_BLOCKED stream={} limit={}",
+                    stream_id, limit
+                )?;
+            },
+
+            Frame::StreamsBlockedBidi { limit } => {
+                write!(f, "STREAMS_BLOCKED type=bidi limit={}", limit)?;
+            },
+
+            Frame::StreamsBlockedUni { limit } => {
+                write!(f, "STREAMS_BLOCKED type=uni limit={}", limit)?;
+            },
+
+            Frame::NewConnectionId { .. } => {
+                write!(f, "NEW_CONNECTION_ID (TODO)")?;
+            },
+
+            Frame::RetireConnectionId { .. } => {
+                write!(f, "RETIRE_CONNECTION_ID (TODO)")?;
+            },
+
+            Frame::PathChallenge { data } => {
+                write!(f, "PATH_CHALLENGE data={:02x?}", data)?;
+            },
+
+            Frame::PathResponse { data } => {
+                write!(f, "PATH_RESPONSE data={:02x?}", data)?;
+            },
+
+            Frame::ConnectionClose {
+                error_code,
+                frame_type,
+                reason,
+            } => {
+                write!(
+                    f,
+                    "CONNECTION_CLOSE err={:x} frame={:x} reason={:x?}",
+                    error_code, frame_type, reason
+                )?;
+            },
+
+            Frame::ApplicationClose { error_code, reason } => {
+                write!(
+                    f,
+                    "APPLICATION_CLOSE err={:x} reason={:x?}",
+                    error_code, reason
+                )?;
+            },
+
+            Frame::HandshakeDone => {
+                write!(f, "HANDSHAKE_DONE")?;
+            },
+
+            Frame::Datagram { data } => {
+                write!(f, "DATAGRAM len={}", data.len(),)?;
+            },
+        }
+
+        Ok(())
+    }
+}
+
+fn parse_ack_frame(_ty: u64, b: &mut octets::Octets) -> Result<Frame> {
+    let largest_ack = b.get_varint()?;
+    let ack_delay = b.get_varint()?;
+    let block_count = b.get_varint()?;
+    let ack_block = b.get_varint()?;
+
+    if largest_ack < ack_block {
+        return Err(Error::InvalidFrame);
+    }
+
+    let mut smallest_ack = largest_ack - ack_block;
+
+    let mut ranges = ranges::RangeSet::default();
+
+    #[allow(clippy::range_plus_one)]
+    ranges.insert(smallest_ack..largest_ack + 1);
+
+    for _i in 0..block_count {
+        let gap = b.get_varint()?;
+
+        if smallest_ack < 2 + gap {
+            return Err(Error::InvalidFrame);
+        }
+
+        let largest_ack = (smallest_ack - gap) - 2;
+        let ack_block = b.get_varint()?;
+
+        if largest_ack < ack_block {
+            return Err(Error::InvalidFrame);
+        }
+
+        smallest_ack = largest_ack - ack_block;
+
+        #[allow(clippy::range_plus_one)]
+        ranges.insert(smallest_ack..largest_ack + 1);
+    }
+
+    Ok(Frame::ACK { ack_delay, ranges })
+}
+
+fn parse_stream_frame(ty: u64, b: &mut octets::Octets) -> Result<Frame> {
+    let first = ty as u8;
+
+    let stream_id = b.get_varint()?;
+
+    let offset = if first & 0x04 != 0 {
+        b.get_varint()?
+    } else {
+        0
+    };
+
+    let len = if first & 0x02 != 0 {
+        b.get_varint()? as usize
+    } else {
+        b.cap()
+    };
+
+    if offset + len as u64 >= MAX_STREAM_SIZE {
+        return Err(Error::InvalidFrame);
+    }
+
+    let fin = first & 0x01 != 0;
+
+    let data = b.get_bytes(len)?;
+    let data = stream::RangeBuf::from(data.as_ref(), offset, fin);
+
+    Ok(Frame::Stream { stream_id, data })
+}
+
+fn parse_datagram_frame(ty: u64, b: &mut octets::Octets) -> Result<Frame> {
+    let first = ty as u8;
+
+    let len = if first & 0x01 != 0 {
+        b.get_varint()? as usize
+    } else {
+        b.cap()
+    };
+
+    let data = b.get_bytes(len)?;
+
+    Ok(Frame::Datagram {
+        data: Vec::from(data.buf()),
+    })
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn padding() {
+        let mut d = [42; 128];
+
+        let frame = Frame::Padding { len: 128 };
+
+        let wire_len = {
+            let mut b = octets::OctetsMut::with_slice(&mut d);
+            frame.to_bytes(&mut b).unwrap()
+        };
+
+        assert_eq!(wire_len, 128);
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame));
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_ok());
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_ok());
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_ok());
+    }
+
+    #[test]
+    fn ping() {
+        let mut d = [42; 128];
+
+        let frame = Frame::Ping;
+
+        let wire_len = {
+            let mut b = octets::OctetsMut::with_slice(&mut d);
+            frame.to_bytes(&mut b).unwrap()
+        };
+
+        assert_eq!(wire_len, 1);
+        assert_eq!(&d[..wire_len], [0x01 as u8]);
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame));
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_ok());
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_ok());
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_ok());
+    }
+
+    #[test]
+    fn ack() {
+        let mut d = [42; 128];
+
+        let mut ranges = ranges::RangeSet::default();
+        ranges.insert(4..7);
+        ranges.insert(9..12);
+        ranges.insert(15..19);
+        ranges.insert(3000..5000);
+
+        let frame = Frame::ACK {
+            ack_delay: 874_656_534,
+            ranges,
+        };
+
+        let wire_len = {
+            let mut b = octets::OctetsMut::with_slice(&mut d);
+            frame.to_bytes(&mut b).unwrap()
+        };
+
+        assert_eq!(wire_len, 17);
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame));
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_ok());
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_err());
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_ok());
+    }
+
+    #[test]
+    fn reset_stream() {
+        let mut d = [42; 128];
+
+        let frame = Frame::ResetStream {
+            stream_id: 123_213,
+            error_code: 21_123_767,
+            final_size: 21_123_767,
+        };
+
+        let wire_len = {
+            let mut b = octets::OctetsMut::with_slice(&mut d);
+            frame.to_bytes(&mut b).unwrap()
+        };
+
+        assert_eq!(wire_len, 13);
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame));
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_ok());
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err());
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err());
+    }
+
+    #[test]
+    fn stop_sending() {
+        let mut d = [42; 128];
+
+        let frame = Frame::StopSending {
+            stream_id: 123_213,
+            error_code: 15_352,
+        };
+
+        let wire_len = {
+            let mut b = octets::OctetsMut::with_slice(&mut d);
+            frame.to_bytes(&mut b).unwrap()
+        };
+
+        assert_eq!(wire_len, 7);
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame));
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_ok());
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err());
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err());
+    }
+
+    #[test]
+    fn crypto() {
+        let mut d = [42; 128];
+
+        let data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
+
+        let frame = Frame::Crypto {
+            data: stream::RangeBuf::from(&data, 1230976, false),
+        };
+
+        let wire_len = {
+            let mut b = octets::OctetsMut::with_slice(&mut d);
+            frame.to_bytes(&mut b).unwrap()
+        };
+
+        assert_eq!(wire_len, 18);
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame));
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_ok());
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_err());
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_ok());
+    }
+
+    #[test]
+    fn new_token() {
+        let mut d = [42; 128];
+
+        let frame = Frame::NewToken {
+            token: Vec::from("this is a token"),
+        };
+
+        let wire_len = {
+            let mut b = octets::OctetsMut::with_slice(&mut d);
+            frame.to_bytes(&mut b).unwrap()
+        };
+
+        assert_eq!(wire_len, 17);
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame));
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err());
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_err());
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err());
+    }
+
+    #[test]
+    fn stream() {
+        let mut d = [42; 128];
+
+        let data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
+
+        let frame = Frame::Stream {
+            stream_id: 32,
+            data: stream::RangeBuf::from(&data, 1230976, true),
+        };
+
+        let wire_len = {
+            let mut b = octets::OctetsMut::with_slice(&mut d);
+            frame.to_bytes(&mut b).unwrap()
+        };
+
+        assert_eq!(wire_len, 19);
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame));
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_ok());
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err());
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err());
+    }
+
+    #[test]
+    fn stream_too_big() {
+        let mut d = [42; 128];
+
+        let data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
+
+        let frame = Frame::Stream {
+            stream_id: 32,
+            data: stream::RangeBuf::from(&data, MAX_STREAM_SIZE - 11, true),
+        };
+
+        let wire_len = {
+            let mut b = octets::OctetsMut::with_slice(&mut d);
+            frame.to_bytes(&mut b).unwrap()
+        };
+
+        assert_eq!(wire_len, 23);
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert_eq!(
+            Frame::from_bytes(&mut b, packet::Type::Short),
+            Err(Error::InvalidFrame)
+        );
+    }
+
+    #[test]
+    fn max_data() {
+        let mut d = [42; 128];
+
+        let frame = Frame::MaxData { max: 128_318_273 };
+
+        let wire_len = {
+            let mut b = octets::OctetsMut::with_slice(&mut d);
+            frame.to_bytes(&mut b).unwrap()
+        };
+
+        assert_eq!(wire_len, 5);
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame));
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_ok());
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err());
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err());
+    }
+
+    #[test]
+    fn max_stream_data() {
+        let mut d = [42; 128];
+
+        let frame = Frame::MaxStreamData {
+            stream_id: 12_321,
+            max: 128_318_273,
+        };
+
+        let wire_len = {
+            let mut b = octets::OctetsMut::with_slice(&mut d);
+            frame.to_bytes(&mut b).unwrap()
+        };
+
+        assert_eq!(wire_len, 7);
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame));
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_ok());
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err());
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err());
+    }
+
+    #[test]
+    fn max_streams_bidi() {
+        let mut d = [42; 128];
+
+        let frame = Frame::MaxStreamsBidi { max: 128_318_273 };
+
+        let wire_len = {
+            let mut b = octets::OctetsMut::with_slice(&mut d);
+            frame.to_bytes(&mut b).unwrap()
+        };
+
+        assert_eq!(wire_len, 5);
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame));
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_ok());
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err());
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err());
+    }
+
+    #[test]
+    fn max_streams_uni() {
+        let mut d = [42; 128];
+
+        let frame = Frame::MaxStreamsUni { max: 128_318_273 };
+
+        let wire_len = {
+            let mut b = octets::OctetsMut::with_slice(&mut d);
+            frame.to_bytes(&mut b).unwrap()
+        };
+
+        assert_eq!(wire_len, 5);
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame));
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_ok());
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err());
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err());
+    }
+
+    #[test]
+    fn data_blocked() {
+        let mut d = [42; 128];
+
+        let frame = Frame::DataBlocked { limit: 128_318_273 };
+
+        let wire_len = {
+            let mut b = octets::OctetsMut::with_slice(&mut d);
+            frame.to_bytes(&mut b).unwrap()
+        };
+
+        assert_eq!(wire_len, 5);
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame));
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_ok());
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err());
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err());
+    }
+
+    #[test]
+    fn stream_data_blocked() {
+        let mut d = [42; 128];
+
+        let frame = Frame::StreamDataBlocked {
+            stream_id: 12_321,
+            limit: 128_318_273,
+        };
+
+        let wire_len = {
+            let mut b = octets::OctetsMut::with_slice(&mut d);
+            frame.to_bytes(&mut b).unwrap()
+        };
+
+        assert_eq!(wire_len, 7);
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame));
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_ok());
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err());
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err());
+    }
+
+    #[test]
+    fn streams_blocked_bidi() {
+        let mut d = [42; 128];
+
+        let frame = Frame::StreamsBlockedBidi { limit: 128_318_273 };
+
+        let wire_len = {
+            let mut b = octets::OctetsMut::with_slice(&mut d);
+            frame.to_bytes(&mut b).unwrap()
+        };
+
+        assert_eq!(wire_len, 5);
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame));
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_ok());
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err());
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err());
+    }
+
+    #[test]
+    fn streams_blocked_uni() {
+        let mut d = [42; 128];
+
+        let frame = Frame::StreamsBlockedUni { limit: 128_318_273 };
+
+        let wire_len = {
+            let mut b = octets::OctetsMut::with_slice(&mut d);
+            frame.to_bytes(&mut b).unwrap()
+        };
+
+        assert_eq!(wire_len, 5);
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame));
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_ok());
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err());
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err());
+    }
+
+    #[test]
+    fn new_connection_id() {
+        let mut d = [42; 128];
+
+        let frame = Frame::NewConnectionId {
+            seq_num: 123_213,
+            retire_prior_to: 122_211,
+            conn_id: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
+            reset_token: vec![0x42; 16],
+        };
+
+        let wire_len = {
+            let mut b = octets::OctetsMut::with_slice(&mut d);
+            frame.to_bytes(&mut b).unwrap()
+        };
+
+        assert_eq!(wire_len, 41);
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame));
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_ok());
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err());
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err());
+    }
+
+    #[test]
+    fn retire_connection_id() {
+        let mut d = [42; 128];
+
+        let frame = Frame::RetireConnectionId { seq_num: 123_213 };
+
+        let wire_len = {
+            let mut b = octets::OctetsMut::with_slice(&mut d);
+            frame.to_bytes(&mut b).unwrap()
+        };
+
+        assert_eq!(wire_len, 5);
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame));
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err());
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_err());
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err());
+    }
+
+    #[test]
+    fn path_challenge() {
+        let mut d = [42; 128];
+
+        let frame = Frame::PathChallenge {
+            data: vec![1, 2, 3, 4, 5, 6, 7, 8],
+        };
+
+        let wire_len = {
+            let mut b = octets::OctetsMut::with_slice(&mut d);
+            frame.to_bytes(&mut b).unwrap()
+        };
+
+        assert_eq!(wire_len, 9);
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame));
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_ok());
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err());
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err());
+    }
+
+    #[test]
+    fn path_response() {
+        let mut d = [42; 128];
+
+        let frame = Frame::PathResponse {
+            data: vec![1, 2, 3, 4, 5, 6, 7, 8],
+        };
+
+        let wire_len = {
+            let mut b = octets::OctetsMut::with_slice(&mut d);
+            frame.to_bytes(&mut b).unwrap()
+        };
+
+        assert_eq!(wire_len, 9);
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame));
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err());
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_err());
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err());
+    }
+
+    #[test]
+    fn connection_close() {
+        let mut d = [42; 128];
+
+        let frame = Frame::ConnectionClose {
+            error_code: 0xbeef,
+            frame_type: 523_423,
+            reason: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
+        };
+
+        let wire_len = {
+            let mut b = octets::OctetsMut::with_slice(&mut d);
+            frame.to_bytes(&mut b).unwrap()
+        };
+
+        assert_eq!(wire_len, 22);
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame));
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_ok());
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_err());
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_ok());
+    }
+
+    #[test]
+    fn application_close() {
+        let mut d = [42; 128];
+
+        let frame = Frame::ApplicationClose {
+            error_code: 0xbeef,
+            reason: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
+        };
+
+        let wire_len = {
+            let mut b = octets::OctetsMut::with_slice(&mut d);
+            frame.to_bytes(&mut b).unwrap()
+        };
+
+        assert_eq!(wire_len, 18);
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame));
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_ok());
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err());
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err());
+    }
+
+    #[test]
+    fn handshake_done() {
+        let mut d = [42; 128];
+
+        let frame = Frame::HandshakeDone;
+
+        let wire_len = {
+            let mut b = octets::OctetsMut::with_slice(&mut d);
+            frame.to_bytes(&mut b).unwrap()
+        };
+
+        assert_eq!(wire_len, 1);
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame));
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err());
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_err());
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err());
+    }
+
+    #[test]
+    fn datagram() {
+        let mut d = [42; 128];
+
+        let data = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
+
+        let mut frame = Frame::Datagram { data: data.clone() };
+
+        let wire_len = {
+            let mut b = octets::OctetsMut::with_slice(&mut d);
+            frame.to_bytes(&mut b).unwrap()
+        };
+
+        assert_eq!(wire_len, 14);
+
+        let mut b = octets::Octets::with_slice(&mut d);
+        assert_eq!(
+            Frame::from_bytes(&mut b, packet::Type::Short),
+            Ok(frame.clone())
+        );
+
+        let mut b = octets::Octets::with_slice(&mut d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err());
+
+        let mut b = octets::Octets::with_slice(&mut d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_ok());
+
+        let mut b = octets::Octets::with_slice(&mut d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err());
+
+        let frame_data = match &frame {
+            Frame::Datagram { data } => data.clone(),
+
+            _ => unreachable!(),
+        };
+
+        assert_eq!(frame_data, data);
+
+        frame.shrink_for_retransmission();
+
+        let frame_data = match &frame {
+            Frame::Datagram { data } => data.clone(),
+
+            _ => unreachable!(),
+        };
+
+        assert_eq!(frame_data.len(), 0);
+    }
+}
diff --git a/src/h3/ffi.rs b/src/h3/ffi.rs
new file mode 100644
index 0000000..2d278c7
--- /dev/null
+++ b/src/h3/ffi.rs
@@ -0,0 +1,335 @@
+// Copyright (C) 2019, Cloudflare, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright notice,
+//       this list of conditions and the following disclaimer.
+//
+//     * Redistributions in binary form must reproduce the above copyright
+//       notice, this list of conditions and the following disclaimer in the
+//       documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// 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::ffi;
+use std::ptr;
+use std::slice;
+use std::str;
+
+use libc::c_char;
+use libc::c_int;
+use libc::c_void;
+use libc::size_t;
+use libc::ssize_t;
+
+use crate::*;
+
+use crate::h3::NameValue;
+
+#[no_mangle]
+pub extern fn quiche_h3_config_new() -> *mut h3::Config {
+    match h3::Config::new() {
+        Ok(c) => Box::into_raw(Box::new(c)),
+
+        Err(_) => ptr::null_mut(),
+    }
+}
+
+#[no_mangle]
+pub extern fn quiche_h3_config_set_max_header_list_size(
+    config: &mut h3::Config, v: u64,
+) {
+    config.set_max_header_list_size(v);
+}
+
+#[no_mangle]
+pub extern fn quiche_h3_config_set_qpack_max_table_capacity(
+    config: &mut h3::Config, v: u64,
+) {
+    config.set_qpack_max_table_capacity(v);
+}
+
+#[no_mangle]
+pub extern fn quiche_h3_config_set_qpack_blocked_streams(
+    config: &mut h3::Config, v: u64,
+) {
+    config.set_qpack_blocked_streams(v);
+}
+
+#[no_mangle]
+pub extern fn quiche_h3_config_free(config: *mut h3::Config) {
+    unsafe { Box::from_raw(config) };
+}
+
+#[no_mangle]
+pub extern fn quiche_h3_conn_new_with_transport(
+    quic_conn: &mut Connection, config: &mut h3::Config,
+) -> *mut h3::Connection {
+    match h3::Connection::with_transport(quic_conn, config) {
+        Ok(c) => Box::into_raw(Box::new(c)),
+
+        Err(_) => ptr::null_mut(),
+    }
+}
+
+#[no_mangle]
+pub extern fn quiche_h3_conn_poll(
+    conn: &mut h3::Connection, quic_conn: &mut Connection,
+    ev: *mut *const h3::Event,
+) -> i64 {
+    match conn.poll(quic_conn) {
+        Ok((id, v)) => {
+            unsafe {
+                *ev = Box::into_raw(Box::new(v));
+            }
+
+            id as i64
+        },
+
+        Err(e) => e.to_c() as i64,
+    }
+}
+
+#[no_mangle]
+pub extern fn quiche_h3_event_type(ev: &h3::Event) -> u32 {
+    match ev {
+        h3::Event::Headers { .. } => 0,
+
+        h3::Event::Data { .. } => 1,
+
+        h3::Event::Finished { .. } => 2,
+
+        h3::Event::Datagram { .. } => 3,
+
+        h3::Event::GoAway { .. } => 4,
+    }
+}
+
+#[no_mangle]
+pub extern fn quiche_h3_event_for_each_header(
+    ev: &h3::Event,
+    cb: extern fn(
+        name: *const u8,
+        name_len: size_t,
+
+        value: *const u8,
+        value_len: size_t,
+
+        argp: *mut c_void,
+    ) -> c_int,
+    argp: *mut c_void,
+) -> c_int {
+    match ev {
+        h3::Event::Headers { list, .. } =>
+            for h in list {
+                let rc = cb(
+                    h.name().as_ptr(),
+                    h.name().len(),
+                    h.value().as_ptr(),
+                    h.value().len(),
+                    argp,
+                );
+
+                if rc != 0 {
+                    return rc;
+                }
+            },
+
+        _ => unreachable!(),
+    }
+
+    0
+}
+
+#[no_mangle]
+pub extern fn quiche_h3_event_headers_has_body(ev: &h3::Event) -> bool {
+    match ev {
+        h3::Event::Headers { has_body, .. } => *has_body,
+
+        _ => unreachable!(),
+    }
+}
+
+#[no_mangle]
+pub extern fn quiche_h3_event_free(ev: *mut h3::Event) {
+    unsafe { Box::from_raw(ev) };
+}
+
+#[repr(C)]
+pub struct Header {
+    name: *mut u8,
+    name_len: usize,
+
+    value: *mut u8,
+    value_len: usize,
+}
+
+#[no_mangle]
+pub extern fn quiche_h3_send_request(
+    conn: &mut h3::Connection, quic_conn: &mut Connection,
+    headers: *const Header, headers_len: size_t, fin: bool,
+) -> i64 {
+    let req_headers = headers_from_ptr(headers, headers_len);
+
+    match conn.send_request(quic_conn, &req_headers, fin) {
+        Ok(v) => v as i64,
+
+        Err(e) => e.to_c() as i64,
+    }
+}
+
+#[no_mangle]
+pub extern fn quiche_h3_send_response(
+    conn: &mut h3::Connection, quic_conn: &mut Connection, stream_id: u64,
+    headers: *const Header, headers_len: size_t, fin: bool,
+) -> c_int {
+    let resp_headers = headers_from_ptr(headers, headers_len);
+
+    match conn.send_response(quic_conn, stream_id, &resp_headers, fin) {
+        Ok(_) => 0,
+
+        Err(e) => e.to_c() as c_int,
+    }
+}
+
+#[no_mangle]
+pub extern fn quiche_h3_send_response_with_priority(
+    conn: &mut h3::Connection, quic_conn: &mut Connection, stream_id: u64,
+    headers: *const Header, headers_len: size_t, priority: *const c_char,
+    fin: bool,
+) -> c_int {
+    let resp_headers = headers_from_ptr(headers, headers_len);
+    let priority = unsafe { ffi::CStr::from_ptr(priority).to_str().unwrap() };
+
+    match conn.send_response_with_priority(
+        quic_conn,
+        stream_id,
+        &resp_headers,
+        &priority,
+        fin,
+    ) {
+        Ok(_) => 0,
+
+        Err(e) => e.to_c() as c_int,
+    }
+}
+
+#[no_mangle]
+pub extern fn quiche_h3_send_body(
+    conn: &mut h3::Connection, quic_conn: &mut Connection, stream_id: u64,
+    body: *const u8, body_len: size_t, fin: bool,
+) -> ssize_t {
+    if body_len > <ssize_t>::max_value() as usize {
+        panic!("The provided buffer is too large");
+    }
+
+    let body = unsafe { slice::from_raw_parts(body, body_len) };
+
+    match conn.send_body(quic_conn, stream_id, body, fin) {
+        Ok(v) => v as ssize_t,
+
+        Err(e) => e.to_c(),
+    }
+}
+
+#[no_mangle]
+pub extern fn quiche_h3_recv_body(
+    conn: &mut h3::Connection, quic_conn: &mut Connection, stream_id: u64,
+    out: *mut u8, out_len: size_t,
+) -> ssize_t {
+    if out_len > <ssize_t>::max_value() as usize {
+        panic!("The provided buffer is too large");
+    }
+
+    let out = unsafe { slice::from_raw_parts_mut(out, out_len) };
+
+    match conn.recv_body(quic_conn, stream_id, out) {
+        Ok(v) => v as ssize_t,
+
+        Err(e) => e.to_c(),
+    }
+}
+
+#[no_mangle]
+pub extern fn quiche_h3_send_dgram(
+    conn: &mut h3::Connection, quic_conn: &mut Connection, flow_id: u64,
+    data: *const u8, data_len: size_t,
+) -> c_int {
+    if data_len > <ssize_t>::max_value() as usize {
+        panic!("The provided buffer is too large");
+    }
+
+    let data = unsafe { slice::from_raw_parts(data, data_len) };
+
+    match conn.send_dgram(quic_conn, flow_id, data) {
+        Ok(_) => 0,
+
+        Err(e) => e.to_c() as c_int,
+    }
+}
+
+#[no_mangle]
+pub extern fn quiche_h3_recv_dgram(
+    conn: &mut h3::Connection, quic_conn: &mut Connection, flow_id: *mut u64,
+    flow_id_len: *mut usize, out: *mut u8, out_len: size_t,
+) -> ssize_t {
+    if out_len > <ssize_t>::max_value() as usize {
+        panic!("The provided buffer is too large");
+    }
+
+    let out = unsafe { slice::from_raw_parts_mut(out, out_len) };
+
+    match conn.recv_dgram(quic_conn, out) {
+        Ok((len, id, id_len)) => {
+            unsafe { *flow_id = id };
+            unsafe { *flow_id_len = id_len };
+            len as ssize_t
+        },
+
+        Err(e) => e.to_c(),
+    }
+}
+
+#[no_mangle]
+pub extern fn quiche_h3_conn_free(conn: *mut h3::Connection) {
+    unsafe { Box::from_raw(conn) };
+}
+
+fn headers_from_ptr<'a>(
+    ptr: *const Header, len: size_t,
+) -> Vec<h3::HeaderRef<'a>> {
+    let headers = unsafe { slice::from_raw_parts(ptr, len) };
+
+    let mut out = Vec::new();
+
+    for h in headers {
+        out.push({
+            let name = unsafe {
+                let slice = slice::from_raw_parts(h.name, h.name_len);
+                str::from_utf8_unchecked(slice)
+            };
+
+            let value = unsafe {
+                let slice = slice::from_raw_parts(h.value, h.value_len);
+                str::from_utf8_unchecked(slice)
+            };
+
+            h3::HeaderRef::new(name, value)
+        });
+    }
+
+    out
+}
diff --git a/src/h3/frame.rs b/src/h3/frame.rs
new file mode 100644
index 0000000..bee2b3b
--- /dev/null
+++ b/src/h3/frame.rs
@@ -0,0 +1,647 @@
+// Copyright (C) 2019, Cloudflare, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright notice,
+//       this list of conditions and the following disclaimer.
+//
+//     * Redistributions in binary form must reproduce the above copyright
+//       notice, this list of conditions and the following disclaimer in the
+//       documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+use super::Result;
+
+use crate::octets;
+
+pub const DATA_FRAME_TYPE_ID: u64 = 0x0;
+pub const HEADERS_FRAME_TYPE_ID: u64 = 0x1;
+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;
+
+const SETTINGS_QPACK_MAX_TABLE_CAPACITY: u64 = 0x1;
+const SETTINGS_MAX_HEADER_LIST_SIZE: u64 = 0x6;
+const SETTINGS_QPACK_BLOCKED_STREAMS: u64 = 0x7;
+
+#[derive(Clone, PartialEq)]
+pub enum Frame {
+    Data {
+        payload: Vec<u8>,
+    },
+
+    Headers {
+        header_block: Vec<u8>,
+    },
+
+    CancelPush {
+        push_id: u64,
+    },
+
+    Settings {
+        max_header_list_size: Option<u64>,
+        qpack_max_table_capacity: Option<u64>,
+        qpack_blocked_streams: Option<u64>,
+        grease: Option<(u64, u64)>,
+    },
+
+    PushPromise {
+        push_id: u64,
+        header_block: Vec<u8>,
+    },
+
+    GoAway {
+        id: u64,
+    },
+
+    MaxPushId {
+        push_id: u64,
+    },
+
+    Unknown,
+}
+
+impl Frame {
+    pub fn from_bytes(
+        frame_type: u64, payload_length: u64, bytes: &[u8],
+    ) -> Result<Frame> {
+        let mut b = octets::Octets::with_slice(bytes);
+
+        // TODO: handling of 0-length frames
+        let frame = match frame_type {
+            DATA_FRAME_TYPE_ID => Frame::Data {
+                payload: b.get_bytes(payload_length as usize)?.to_vec(),
+            },
+
+            HEADERS_FRAME_TYPE_ID => Frame::Headers {
+                header_block: b.get_bytes(payload_length as usize)?.to_vec(),
+            },
+
+            CANCEL_PUSH_FRAME_TYPE_ID => Frame::CancelPush {
+                push_id: b.get_varint()?,
+            },
+
+            SETTINGS_FRAME_TYPE_ID =>
+                parse_settings_frame(&mut b, payload_length as usize)?,
+
+            PUSH_PROMISE_FRAME_TYPE_ID =>
+                parse_push_promise(payload_length, &mut b)?,
+
+            GOAWAY_FRAME_TYPE_ID => Frame::GoAway {
+                id: b.get_varint()?,
+            },
+
+            MAX_PUSH_FRAME_TYPE_ID => Frame::MaxPushId {
+                push_id: b.get_varint()?,
+            },
+
+            _ => Frame::Unknown,
+        };
+
+        Ok(frame)
+    }
+
+    pub fn to_bytes(&self, b: &mut octets::OctetsMut) -> Result<usize> {
+        let before = b.cap();
+
+        match self {
+            Frame::Data { payload } => {
+                b.put_varint(DATA_FRAME_TYPE_ID)?;
+                b.put_varint(payload.len() as u64)?;
+
+                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_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_varint(*push_id)?;
+            },
+
+            Frame::Settings {
+                max_header_list_size,
+                qpack_max_table_capacity,
+                qpack_blocked_streams,
+                grease,
+            } => {
+                let mut len = 0;
+
+                if let Some(val) = max_header_list_size {
+                    len += octets::varint_len(SETTINGS_MAX_HEADER_LIST_SIZE);
+                    len += octets::varint_len(*val);
+                }
+
+                if let Some(val) = qpack_max_table_capacity {
+                    len += octets::varint_len(SETTINGS_QPACK_MAX_TABLE_CAPACITY);
+                    len += octets::varint_len(*val);
+                }
+
+                if let Some(val) = qpack_blocked_streams {
+                    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)?;
+
+                if let Some(val) = max_header_list_size {
+                    b.put_varint(SETTINGS_MAX_HEADER_LIST_SIZE)?;
+                    b.put_varint(*val as u64)?;
+                }
+
+                if let Some(val) = qpack_max_table_capacity {
+                    b.put_varint(SETTINGS_QPACK_MAX_TABLE_CAPACITY)?;
+                    b.put_varint(*val as u64)?;
+                }
+
+                if let Some(val) = qpack_blocked_streams {
+                    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 {
+                push_id,
+                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_varint(*push_id)?;
+                b.put_bytes(header_block.as_ref())?;
+            },
+
+            Frame::GoAway { id } => {
+                b.put_varint(GOAWAY_FRAME_TYPE_ID)?;
+                b.put_varint(octets::varint_len(*id) as u64)?;
+
+                b.put_varint(*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_varint(*push_id)?;
+            },
+
+            Frame::Unknown => unreachable!(),
+        }
+
+        Ok(before - b.cap())
+    }
+}
+
+impl std::fmt::Debug for Frame {
+    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+        match self {
+            Frame::Data { payload } => {
+                write!(f, "DATA len={}", payload.len())?;
+            },
+
+            Frame::Headers { header_block } => {
+                write!(f, "HEADERS len={}", header_block.len())?;
+            },
+
+            Frame::CancelPush { push_id } => {
+                write!(f, "CANCEL_PUSH push_id={}", push_id)?;
+            },
+
+            Frame::Settings {
+                max_header_list_size,
+                qpack_max_table_capacity,
+                qpack_blocked_streams,
+                ..
+            } => {
+                write!(f, "SETTINGS max_headers={:?}, qpack_max_table={:?}, qpack_blocked={:?} ", max_header_list_size, qpack_max_table_capacity, qpack_blocked_streams)?;
+            },
+
+            Frame::PushPromise {
+                push_id,
+                header_block,
+            } => {
+                write!(
+                    f,
+                    "PUSH_PROMISE push_id={} len={}",
+                    push_id,
+                    header_block.len()
+                )?;
+            },
+
+            Frame::GoAway { id } => {
+                write!(f, "GOAWAY id={}", id)?;
+            },
+
+            Frame::MaxPushId { push_id } => {
+                write!(f, "MAX_PUSH_ID push_id={}", push_id)?;
+            },
+
+            Frame::Unknown => {
+                write!(f, "UNKNOWN")?;
+            },
+        }
+
+        Ok(())
+    }
+}
+
+fn parse_settings_frame(
+    b: &mut octets::Octets, settings_length: usize,
+) -> Result<Frame> {
+    let mut max_header_list_size = None;
+    let mut qpack_max_table_capacity = None;
+    let mut qpack_blocked_streams = None;
+
+    while b.off() < settings_length {
+        let setting_ty = b.get_varint()?;
+        let settings_val = b.get_varint()?;
+
+        match setting_ty {
+            SETTINGS_QPACK_MAX_TABLE_CAPACITY => {
+                qpack_max_table_capacity = Some(settings_val);
+            },
+
+            SETTINGS_MAX_HEADER_LIST_SIZE => {
+                max_header_list_size = Some(settings_val);
+            },
+
+            SETTINGS_QPACK_BLOCKED_STREAMS => {
+                qpack_blocked_streams = Some(settings_val);
+            },
+
+            // Unknown Settings parameters must be ignored.
+            _ => (),
+        }
+    }
+
+    Ok(Frame::Settings {
+        max_header_list_size,
+        qpack_max_table_capacity,
+        qpack_blocked_streams,
+        grease: None,
+    })
+}
+
+fn parse_push_promise(
+    payload_length: u64, b: &mut octets::Octets,
+) -> Result<Frame> {
+    let push_id = b.get_varint()?;
+    let header_block_length = payload_length - octets::varint_len(push_id) as u64;
+    let header_block = b.get_bytes(header_block_length as usize)?.to_vec();
+
+    Ok(Frame::PushPromise {
+        push_id,
+        header_block,
+    })
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn data() {
+        let mut d = [42; 128];
+
+        let payload = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
+        let frame_payload_len = payload.len();
+        let frame_header_len = 2;
+
+        let frame = Frame::Data { payload };
+
+        let wire_len = {
+            let mut b = octets::OctetsMut::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(
+                DATA_FRAME_TYPE_ID,
+                frame_payload_len as u64,
+                &d[frame_header_len..]
+            )
+            .unwrap(),
+            frame
+        );
+    }
+
+    #[test]
+    fn headers() {
+        let mut d = [42; 128];
+
+        let header_block = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
+        let frame_payload_len = header_block.len();
+        let frame_header_len = 2;
+
+        let frame = Frame::Headers { header_block };
+
+        let wire_len = {
+            let mut b = octets::OctetsMut::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(
+                HEADERS_FRAME_TYPE_ID,
+                frame_payload_len as u64,
+                &d[frame_header_len..]
+            )
+            .unwrap(),
+            frame
+        );
+    }
+
+    #[test]
+    fn cancel_push() {
+        let mut d = [42; 128];
+
+        let frame = Frame::CancelPush { push_id: 0 };
+
+        let frame_payload_len = 1;
+        let frame_header_len = 2;
+
+        let wire_len = {
+            let mut b = octets::OctetsMut::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(
+                CANCEL_PUSH_FRAME_TYPE_ID,
+                frame_payload_len as u64,
+                &d[frame_header_len..]
+            )
+            .unwrap(),
+            frame
+        );
+    }
+
+    #[test]
+    fn settings_all_no_grease() {
+        let mut d = [42; 128];
+
+        let frame = Frame::Settings {
+            max_header_list_size: Some(0),
+            qpack_max_table_capacity: Some(0),
+            qpack_blocked_streams: Some(0),
+            grease: None,
+        };
+
+        let frame_payload_len = 6;
+        let frame_header_len = 2;
+
+        let wire_len = {
+            let mut b = octets::OctetsMut::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,
+                &d[frame_header_len..]
+            )
+            .unwrap(),
+            frame
+        );
+    }
+
+    #[test]
+    fn settings_all_grease() {
+        let mut d = [42; 128];
+
+        let frame = Frame::Settings {
+            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 {
+            max_header_list_size: Some(0),
+            qpack_max_table_capacity: Some(0),
+            qpack_blocked_streams: Some(0),
+            grease: None,
+        };
+
+        let frame_payload_len = 8;
+        let frame_header_len = 2;
+
+        let wire_len = {
+            let mut b = octets::OctetsMut::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,
+                &d[frame_header_len..]
+            )
+            .unwrap(),
+            frame_parsed
+        );
+    }
+
+    #[test]
+    fn settings_h3_only() {
+        let mut d = [42; 128];
+
+        let frame = Frame::Settings {
+            max_header_list_size: Some(1024),
+            qpack_max_table_capacity: None,
+            qpack_blocked_streams: None,
+            grease: None,
+        };
+
+        let frame_payload_len = 3;
+        let frame_header_len = 2;
+
+        let wire_len = {
+            let mut b = octets::OctetsMut::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,
+                &d[frame_header_len..]
+            )
+            .unwrap(),
+            frame
+        );
+    }
+
+    #[test]
+    fn settings_qpack_only() {
+        let mut d = [42; 128];
+
+        let frame = Frame::Settings {
+            max_header_list_size: None,
+            qpack_max_table_capacity: Some(0),
+            qpack_blocked_streams: Some(0),
+            grease: None,
+        };
+
+        let frame_payload_len = 4;
+        let frame_header_len = 2;
+
+        let wire_len = {
+            let mut b = octets::OctetsMut::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,
+                &d[frame_header_len..]
+            )
+            .unwrap(),
+            frame
+        );
+    }
+
+    #[test]
+    fn push_promise() {
+        let mut d = [42; 128];
+
+        let header_block = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
+        let frame_payload_len = 1 + header_block.len();
+        let frame_header_len = 2;
+
+        let frame = Frame::PushPromise {
+            push_id: 0,
+            header_block,
+        };
+
+        let wire_len = {
+            let mut b = octets::OctetsMut::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(
+                PUSH_PROMISE_FRAME_TYPE_ID,
+                frame_payload_len as u64,
+                &d[frame_header_len..]
+            )
+            .unwrap(),
+            frame
+        );
+    }
+
+    #[test]
+    fn goaway() {
+        let mut d = [42; 128];
+
+        let frame = Frame::GoAway { id: 32 };
+
+        let frame_payload_len = 1;
+        let frame_header_len = 2;
+
+        let wire_len = {
+            let mut b = octets::OctetsMut::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(
+                GOAWAY_FRAME_TYPE_ID,
+                frame_payload_len as u64,
+                &d[frame_header_len..]
+            )
+            .unwrap(),
+            frame
+        );
+    }
+
+    #[test]
+    fn max_push_id() {
+        let mut d = [42; 128];
+
+        let frame = Frame::MaxPushId { push_id: 128 };
+
+        let frame_payload_len = 2;
+        let frame_header_len = 2;
+
+        let wire_len = {
+            let mut b = octets::OctetsMut::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(
+                MAX_PUSH_FRAME_TYPE_ID,
+                frame_payload_len as u64,
+                &d[frame_header_len..]
+            )
+            .unwrap(),
+            frame
+        );
+    }
+
+    #[test]
+    fn unknown_type() {
+        let d = [42; 12];
+
+        assert_eq!(Frame::from_bytes(255, 12345, &d[..]), Ok(Frame::Unknown));
+    }
+}
diff --git a/src/h3/mod.rs b/src/h3/mod.rs
new file mode 100644
index 0000000..6248688
--- /dev/null
+++ b/src/h3/mod.rs
@@ -0,0 +1,3168 @@
+// Copyright (C) 2019, Cloudflare, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright notice,
+//       this list of conditions and the following disclaimer.
+//
+//     * Redistributions in binary form must reproduce the above copyright
+//       notice, this list of conditions and the following disclaimer in the
+//       documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+//! HTTP/3 wire protocol and QPACK implementation.
+//!
+//! This module provides a high level API for sending and receiving HTTP/3
+//! requests and responses on top of the QUIC transport protocol.
+//!
+//! ## Connection setup
+//!
+//! HTTP/3 connections require a QUIC transport-layer connection, see
+//! [Connection setup] for a full description of the setup process.
+//!
+//! To use HTTP/3, the QUIC connection must be configured with a suitable
+//! Application Layer Protocol Negotiation (ALPN) Protocol ID:
+//!
+//! ```
+//! let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?;
+//! config.set_application_protos(quiche::h3::APPLICATION_PROTOCOL)?;
+//! # Ok::<(), quiche::Error>(())
+//! ```
+//!
+//! The QUIC handshake is driven by [sending] and [receiving] QUIC packets.
+//!
+//! Once the handshake has completed, the first step in establishing an HTTP/3
+//! connection is creating its configuration object:
+//!
+//! ```
+//! let h3_config = quiche::h3::Config::new()?;
+//! # Ok::<(), quiche::h3::Error>(())
+//! ```
+//!
+//! HTTP/3 client and server connections are both created using the
+//! [`with_transport()`] function, the role is inferred from the type of QUIC
+//! connection:
+//!
+//! ```no_run
+//! # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION).unwrap();
+//! # let scid = [0xba; 16];
+//! # let mut conn = quiche::connect(None, &scid, &mut config).unwrap();
+//! # let h3_config = quiche::h3::Config::new()?;
+//! let h3_conn = quiche::h3::Connection::with_transport(&mut conn, &h3_config)?;
+//! # Ok::<(), quiche::h3::Error>(())
+//! ```
+//!
+//! ## Sending a request
+//!
+//! An HTTP/3 client can send a request by using the connection's
+//! [`send_request()`] method to queue request headers; [sending] QUIC packets
+//! causes the requests to get sent to the peer:
+//!
+//! ```no_run
+//! # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION).unwrap();
+//! # let scid = [0xba; 16];
+//! # let mut conn = quiche::connect(None, &scid, &mut config).unwrap();
+//! # let h3_config = quiche::h3::Config::new()?;
+//! # let mut h3_conn = quiche::h3::Connection::with_transport(&mut conn, &h3_config)?;
+//! let req = vec![
+//!     quiche::h3::Header::new(":method", "GET"),
+//!     quiche::h3::Header::new(":scheme", "https"),
+//!     quiche::h3::Header::new(":authority", "quic.tech"),
+//!     quiche::h3::Header::new(":path", "/"),
+//!     quiche::h3::Header::new("user-agent", "quiche"),
+//! ];
+//!
+//! h3_conn.send_request(&mut conn, &req, true)?;
+//! # Ok::<(), quiche::h3::Error>(())
+//! ```
+//!
+//! An HTTP/3 client can send a request with additional body data by using
+//! the connection's [`send_body()`] method:
+//!
+//! ```no_run
+//! # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION).unwrap();
+//! # let scid = [0xba; 16];
+//! # let mut conn = quiche::connect(None, &scid, &mut config).unwrap();
+//! # let h3_config = quiche::h3::Config::new()?;
+//! # let mut h3_conn = quiche::h3::Connection::with_transport(&mut conn, &h3_config)?;
+//! let req = vec![
+//!     quiche::h3::Header::new(":method", "GET"),
+//!     quiche::h3::Header::new(":scheme", "https"),
+//!     quiche::h3::Header::new(":authority", "quic.tech"),
+//!     quiche::h3::Header::new(":path", "/"),
+//!     quiche::h3::Header::new("user-agent", "quiche"),
+//! ];
+//!
+//! let stream_id = h3_conn.send_request(&mut conn, &req, false)?;
+//! h3_conn.send_body(&mut conn, stream_id, b"Hello World!", true)?;
+//! # Ok::<(), quiche::h3::Error>(())
+//! ```
+//!
+//! ## Handling requests and responses
+//!
+//! After [receiving] QUIC packets, HTTP/3 data is processed using the
+//! connection's [`poll()`] method. On success, this returns an [`Event`] object
+//! and an ID corresponding to the stream where the `Event` originated.
+//!
+//! An HTTP/3 server uses [`poll()`] to read requests and responds to them using
+//! [`send_response()`] and [`send_body()`]:
+//!
+//! ```no_run
+//! use quiche::h3::NameValue;
+//!
+//! # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION).unwrap();
+//! # let scid = [0xba; 16];
+//! # let mut conn = quiche::accept(&scid, None, &mut config).unwrap();
+//! # let h3_config = quiche::h3::Config::new()?;
+//! # let mut h3_conn = quiche::h3::Connection::with_transport(&mut conn, &h3_config)?;
+//! loop {
+//!     match h3_conn.poll(&mut conn) {
+//!         Ok((stream_id, quiche::h3::Event::Headers{list, has_body})) => {
+//!             let mut headers = list.into_iter();
+//!
+//!             // Look for the request's method.
+//!             let method = headers.find(|h| h.name() == ":method").unwrap();
+//!
+//!             // Look for the request's path.
+//!             let path = headers.find(|h| h.name() == ":path").unwrap();
+//!
+//!             if method.value() == "GET" && path.value() == "/" {
+//!                 let resp = vec![
+//!                     quiche::h3::Header::new(":status", &200.to_string()),
+//!                     quiche::h3::Header::new("server", "quiche"),
+//!                 ];
+//!
+//!                 h3_conn.send_response(&mut conn, stream_id, &resp, false)?;
+//!                 h3_conn.send_body(&mut conn, stream_id, b"Hello World!", true)?;
+//!             }
+//!         },
+//!
+//!         Ok((stream_id, quiche::h3::Event::Data)) => {
+//!             // Request body data, handle it.
+//!             # return Ok(());
+//!         },
+//!
+//!         Ok((stream_id, quiche::h3::Event::Finished)) => {
+//!             // Peer terminated stream, handle it.
+//!         },
+//!
+//!         Ok((_flow_id, quiche::h3::Event::Datagram)) => (),
+//!
+//!         Ok((goaway_id, quiche::h3::Event::GoAway)) => {
+//!              // Peer signalled it is going away, handle it.
+//!         },
+//!
+//!         Err(quiche::h3::Error::Done) => {
+//!             // Done reading.
+//!             break;
+//!         },
+//!
+//!         Err(e) => {
+//!             // An error occurred, handle it.
+//!             break;
+//!         },
+//!     }
+//! }
+//! # Ok::<(), quiche::h3::Error>(())
+//! ```
+//!
+//! An HTTP/3 client uses [`poll()`] to read responses:
+//!
+//! ```no_run
+//! use quiche::h3::NameValue;
+//!
+//! # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION).unwrap();
+//! # let scid = [0xba; 16];
+//! # let mut conn = quiche::connect(None, &scid, &mut config).unwrap();
+//! # let h3_config = quiche::h3::Config::new()?;
+//! # let mut h3_conn = quiche::h3::Connection::with_transport(&mut conn, &h3_config)?;
+//! loop {
+//!     match h3_conn.poll(&mut conn) {
+//!         Ok((stream_id, quiche::h3::Event::Headers{list, has_body})) => {
+//!             let status = list.iter().find(|h| h.name() == ":status").unwrap();
+//!             println!("Received {} response on stream {}",
+//!                      status.value(), stream_id);
+//!         },
+//!
+//!         Ok((stream_id, quiche::h3::Event::Data)) => {
+//!             let mut body = vec![0; 4096];
+//!
+//!             if let Ok(read) =
+//!                 h3_conn.recv_body(&mut conn, stream_id, &mut body)
+//!             {
+//!                 println!("Received {} bytes of payload on stream {}",
+//!                          read, stream_id);
+//!             }
+//!         },
+//!
+//!         Ok((stream_id, quiche::h3::Event::Finished)) => {
+//!             // Peer terminated stream, handle it.
+//!         },
+//!
+//!         Ok((_flow_id, quiche::h3::Event::Datagram)) => (),
+//!
+//!         Ok((goaway_id, quiche::h3::Event::GoAway)) => {
+//!              // Peer signalled it is going away, handle it.
+//!         },
+//!
+//!         Err(quiche::h3::Error::Done) => {
+//!             // Done reading.
+//!             break;
+//!         },
+//!
+//!         Err(e) => {
+//!             // An error occurred, handle it.
+//!             break;
+//!         },
+//!     }
+//! }
+//! # Ok::<(), quiche::h3::Error>(())
+//! ```
+//!
+//! ## Detecting end of request or response
+//!
+//! A single HTTP/3 request or response may consist of several HEADERS and DATA
+//! frames; it is finished when the QUIC stream is closed. Calling [`poll()`]
+//! repeatedly will generate an [`Event`] for each of these. The application may
+//! use these event to do additional HTTP semantic validation.
+//!
+//! ## HTTP/3 protocol errors
+//!
+//! Quiche is responsible for managing the HTTP/3 connection, ensuring it is in
+//! a correct state and validating all messages received by a peer. This mainly
+//! takes place in the [`poll()`] method. If an HTTP/3 error occurs, quiche will
+//! close the connection and send an appropriate CONNECTION_CLOSE frame to the
+//! peer. An [`Error`] is returned to the application so that it can perform any
+//! required tidy up such as closing sockets.
+//!
+//! [`application_proto()`]: ../struct.Connection.html#method.application_proto
+//! [`stream_finished()`]: ../struct.Connection.html#method.stream_finished
+//! [Connection setup]: ../index.html#connection-setup
+//! [sending]: ../index.html#generating-outgoing-packets
+//! [receiving]: ../index.html#handling-incoming-packets
+//! [`with_transport()`]: struct.Connection.html#method.with_transport
+//! [`poll()`]: struct.Connection.html#method.poll
+//! [`Event`]: enum.Event.html
+//! [`Error`]: enum.Error.html
+//! [`send_request()`]: struct.Connection.html#method.send_response
+//! [`send_response()`]: struct.Connection.html#method.send_response
+//! [`send_body()`]: struct.Connection.html#method.send_body
+
+use std::collections::HashMap;
+use std::collections::VecDeque;
+
+use crate::octets;
+
+/// List of ALPN tokens of supported HTTP/3 versions.
+///
+/// This can be passed directly to the [`Config::set_application_protos()`]
+/// method when implementing HTTP/3 applications.
+///
+/// [`Config::set_application_protos()`]:
+/// ../struct.Config.html#method.set_application_protos
+pub const APPLICATION_PROTOCOL: &[u8] = b"\x05h3-29\x05h3-28\x05h3-27";
+
+// The offset used when converting HTTP/3 urgency to quiche urgency.
+const PRIORITY_URGENCY_OFFSET: u8 = 124;
+
+/// A specialized [`Result`] type for quiche HTTP/3 operations.
+///
+/// This type is used throughout quiche's HTTP/3 public API for any operation
+/// that can produce an error.
+///
+/// [`Result`]: https://doc.rust-lang.org/std/result/enum.Result.html
+pub type Result<T> = std::result::Result<T, Error>;
+
+/// An HTTP/3 error.
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum Error {
+    /// There is no error or no work to do
+    Done,
+
+    /// The provided buffer is too short.
+    BufferTooShort,
+
+    /// Internal error in the HTTP/3 stack.
+    InternalError,
+
+    /// Endpoint detected that the peer is exhibiting behavior that causes.
+    /// excessive load.
+    ExcessiveLoad,
+
+    /// Stream ID or Push ID greater that current maximum was
+    /// used incorrectly, such as exceeding a limit, reducing a limit,
+    /// or being reused.
+    IdError,
+
+    /// The endpoint detected that its peer created a stream that it will not
+    /// accept.
+    StreamCreationError,
+
+    /// A required critical stream was closed.
+    ClosedCriticalStream,
+
+    /// No SETTINGS frame at beginning of control stream.
+    MissingSettings,
+
+    /// A frame was received which is not permitted in the current state.
+    FrameUnexpected,
+
+    /// Frame violated layout or size rules.
+    FrameError,
+
+    /// QPACK Header block decompression failure.
+    QpackDecompressionFailed,
+
+    /// Error originated from the transport layer.
+    TransportError(crate::Error),
+
+    /// The underlying QUIC stream (or connection) doesn't have enough capacity
+    /// for the operation to complete. The application should retry later on.
+    StreamBlocked,
+}
+
+impl Error {
+    fn to_wire(self) -> u64 {
+        match self {
+            Error::Done => 0x100,
+            Error::InternalError => 0x102,
+            Error::StreamCreationError => 0x103,
+            Error::ClosedCriticalStream => 0x104,
+            Error::FrameUnexpected => 0x105,
+            Error::FrameError => 0x106,
+            Error::ExcessiveLoad => 0x107,
+            Error::IdError => 0x108,
+            Error::MissingSettings => 0x10A,
+            Error::QpackDecompressionFailed => 0x200,
+            Error::BufferTooShort => 0x999,
+            Error::TransportError { .. } => 0xFF,
+            Error::StreamBlocked => 0xFF,
+        }
+    }
+
+    fn to_c(self) -> libc::ssize_t {
+        match self {
+            Error::Done => -1,
+            Error::BufferTooShort => -2,
+            Error::InternalError => -3,
+            Error::ExcessiveLoad => -4,
+            Error::IdError => -5,
+            Error::StreamCreationError => -6,
+            Error::ClosedCriticalStream => -7,
+            Error::MissingSettings => -8,
+            Error::FrameUnexpected => -9,
+            Error::FrameError => -10,
+            Error::QpackDecompressionFailed => -11,
+            Error::TransportError { .. } => -12,
+            Error::StreamBlocked => -13,
+        }
+    }
+}
+
+impl std::fmt::Display for Error {
+    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+        write!(f, "{:?}", self)
+    }
+}
+
+impl std::error::Error for Error {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        None
+    }
+}
+
+impl std::convert::From<super::Error> for Error {
+    fn from(err: super::Error) -> Self {
+        match err {
+            super::Error::Done => Error::Done,
+
+            _ => Error::TransportError(err),
+        }
+    }
+}
+
+impl std::convert::From<octets::BufferTooShortError> for Error {
+    fn from(_err: octets::BufferTooShortError) -> Self {
+        Error::BufferTooShort
+    }
+}
+
+/// An HTTP/3 configuration.
+pub struct Config {
+    max_header_list_size: Option<u64>,
+    qpack_max_table_capacity: Option<u64>,
+    qpack_blocked_streams: Option<u64>,
+}
+
+impl Config {
+    /// Creates a new configuration object with default settings.
+    pub fn new() -> Result<Config> {
+        Ok(Config {
+            max_header_list_size: None,
+            qpack_max_table_capacity: None,
+            qpack_blocked_streams: None,
+        })
+    }
+
+    /// Sets the `SETTINGS_MAX_HEADER_LIST_SIZE` setting.
+    ///
+    /// By default no limit is enforced.
+    pub fn set_max_header_list_size(&mut self, v: u64) {
+        self.max_header_list_size = Some(v);
+    }
+
+    /// Sets the `SETTINGS_QPACK_MAX_TABLE_CAPACITY` setting.
+    ///
+    /// The default value is `0`.
+    pub fn set_qpack_max_table_capacity(&mut self, v: u64) {
+        self.qpack_max_table_capacity = Some(v);
+    }
+
+    /// Sets the `SETTINGS_QPACK_BLOCKED_STREAMS` setting.
+    ///
+    /// The default value is `0`.
+    pub fn set_qpack_blocked_streams(&mut self, v: u64) {
+        self.qpack_blocked_streams = Some(v);
+    }
+}
+
+/// A trait for types with associated string name and value.
+pub trait NameValue {
+    /// Returns the object's name.
+    fn name(&self) -> &str;
+
+    /// Returns the object's value.
+    fn value(&self) -> &str;
+}
+
+/// An owned name-value pair representing a raw HTTP header.
+#[derive(Clone, Debug, PartialEq)]
+pub struct Header(String, String);
+
+impl Header {
+    /// Creates a new header.
+    ///
+    /// Both `name` and `value` will be cloned.
+    pub fn new(name: &str, value: &str) -> Self {
+        Self(String::from(name), String::from(value))
+    }
+}
+
+impl NameValue for Header {
+    fn name(&self) -> &str {
+        &self.0
+    }
+
+    fn value(&self) -> &str {
+        &self.1
+    }
+}
+
+/// A non-owned name-value pair representing a raw HTTP header.
+#[derive(Clone, Debug, PartialEq)]
+pub struct HeaderRef<'a>(&'a str, &'a str);
+
+impl<'a> HeaderRef<'a> {
+    /// Creates a new header.
+    pub fn new(name: &'a str, value: &'a str) -> Self {
+        Self(name, value)
+    }
+}
+
+impl<'a> NameValue for HeaderRef<'a> {
+    fn name(&self) -> &str {
+        self.0
+    }
+
+    fn value(&self) -> &str {
+        self.1
+    }
+}
+
+/// An HTTP/3 connection event.
+#[derive(Clone, Debug, PartialEq)]
+pub enum Event {
+    /// Request/response headers were received.
+    Headers {
+        /// The list of received header fields. The application should validate
+        /// pseudo-headers and headers.
+        list: Vec<Header>,
+
+        /// Whether data will follow the headers on the stream.
+        has_body: bool,
+    },
+
+    /// Data was received.
+    ///
+    /// This indicates that the application can use the [`recv_body()`] method
+    /// to retrieve the data from the stream.
+    ///
+    /// This event will keep being reported until all the available data is
+    /// retrieved by the application.
+    ///
+    /// [`recv_body()`]: struct.Connection.html#method.recv_body
+    Data,
+
+    /// Stream was closed,
+    Finished,
+
+    /// DATAGRAM was received.
+    Datagram,
+
+    /// GOAWAY was received.
+    GoAway,
+}
+
+struct ConnectionSettings {
+    pub max_header_list_size: Option<u64>,
+    pub qpack_max_table_capacity: Option<u64>,
+    pub qpack_blocked_streams: Option<u64>,
+}
+
+struct QpackStreams {
+    pub encoder_stream_id: Option<u64>,
+    pub decoder_stream_id: Option<u64>,
+}
+
+/// An HTTP/3 connection.
+pub struct Connection {
+    is_server: bool,
+
+    next_request_stream_id: u64,
+    next_uni_stream_id: u64,
+
+    streams: HashMap<u64, stream::Stream>,
+
+    local_settings: ConnectionSettings,
+    peer_settings: ConnectionSettings,
+
+    control_stream_id: Option<u64>,
+    peer_control_stream_id: Option<u64>,
+
+    qpack_encoder: qpack::Encoder,
+    qpack_decoder: qpack::Decoder,
+
+    local_qpack_streams: QpackStreams,
+    peer_qpack_streams: QpackStreams,
+
+    max_push_id: u64,
+
+    finished_streams: VecDeque<u64>,
+
+    frames_greased: bool,
+
+    local_goaway_id: Option<u64>,
+    peer_goaway_id: Option<u64>,
+}
+
+impl Connection {
+    fn new(config: &Config, is_server: bool) -> Result<Connection> {
+        let initial_uni_stream_id = if is_server { 0x3 } else { 0x2 };
+
+        Ok(Connection {
+            is_server,
+
+            next_request_stream_id: 0,
+
+            next_uni_stream_id: initial_uni_stream_id,
+
+            streams: HashMap::new(),
+
+            local_settings: ConnectionSettings {
+                max_header_list_size: config.max_header_list_size,
+                qpack_max_table_capacity: config.qpack_max_table_capacity,
+                qpack_blocked_streams: config.qpack_blocked_streams,
+            },
+
+            peer_settings: ConnectionSettings {
+                max_header_list_size: None,
+                qpack_max_table_capacity: None,
+                qpack_blocked_streams: None,
+            },
+
+            control_stream_id: None,
+            peer_control_stream_id: None,
+
+            qpack_encoder: qpack::Encoder::new(),
+            qpack_decoder: qpack::Decoder::new(),
+
+            local_qpack_streams: QpackStreams {
+                encoder_stream_id: None,
+                decoder_stream_id: None,
+            },
+
+            peer_qpack_streams: QpackStreams {
+                encoder_stream_id: None,
+                decoder_stream_id: None,
+            },
+
+            max_push_id: 0,
+
+            finished_streams: VecDeque::new(),
+
+            frames_greased: false,
+
+            local_goaway_id: None,
+            peer_goaway_id: None,
+        })
+    }
+
+    /// Creates a new HTTP/3 connection using the provided QUIC connection.
+    ///
+    /// This will also initiate the HTTP/3 handshake with the peer by opening
+    /// all control streams (including QPACK) and sending the local settings.
+    pub fn with_transport(
+        conn: &mut super::Connection, config: &Config,
+    ) -> Result<Connection> {
+        let mut http3_conn = Connection::new(config, conn.is_server)?;
+
+        http3_conn.send_settings(conn)?;
+
+        // Try opening QPACK streams, but ignore errors if it fails since we
+        // don't need them right now.
+        http3_conn.open_qpack_encoder_stream(conn).ok();
+        http3_conn.open_qpack_decoder_stream(conn).ok();
+
+        if conn.grease {
+            // Try opening a GREASE stream, but ignore errors since it's not
+            // critical.
+            http3_conn.open_grease_stream(conn).ok();
+        }
+
+        Ok(http3_conn)
+    }
+
+    /// Sends an HTTP/3 request.
+    ///
+    /// The request is encoded from the provided list of headers without a
+    /// body, and sent on a newly allocated stream. To include a body,
+    /// set `fin` as `false` and subsequently call [`send_body()`] with the
+    /// same `conn` and the `stream_id` returned from this method.
+    ///
+    /// On success the newly allocated stream ID is returned.
+    ///
+    /// The [`StreamBlocked`] error is returned when the underlying QUIC stream
+    /// doesn't have enough capacity for the operation to complete. When this
+    /// happens the application should retry the operation once the stream is
+    /// reported as writable again.
+    ///
+    /// [`send_body()`]: struct.Connection.html#method.send_body
+    /// [`StreamBlocked`]: enum.Error.html#variant.StreamBlocked
+    pub fn send_request<T: NameValue>(
+        &mut self, conn: &mut super::Connection, headers: &[T], fin: bool,
+    ) -> Result<u64> {
+        // If we received a GOAWAY from the peer, MUST NOT initiate new
+        // requests.
+        if self.peer_goaway_id.is_some() {
+            return Err(Error::FrameUnexpected);
+        }
+
+        let stream_id = self.next_request_stream_id;
+
+        self.streams
+            .insert(stream_id, stream::Stream::new(stream_id, true));
+
+        // The underlying QUIC stream does not exist yet, so calls to e.g.
+        // stream_capacity() will fail. By writing a 0-length buffer, we force
+        // the creation of the QUIC stream state, without actually writing
+        // anything.
+        conn.stream_send(stream_id, b"", false)?;
+
+        self.send_headers(conn, stream_id, headers, fin)?;
+
+        // To avoid skipping stream IDs, we only calculate the next available
+        // stream ID when a request has been successfully buffered.
+        self.next_request_stream_id = self
+            .next_request_stream_id
+            .checked_add(4)
+            .ok_or(Error::IdError)?;
+
+        Ok(stream_id)
+    }
+
+    /// Sends an HTTP/3 response on the specified stream with default priority.
+    ///
+    /// This method sends the provided `headers` without a body. To include a
+    /// body, set `fin` as `false` and subsequently call [`send_body()`] with
+    /// the same `conn` and `stream_id`.
+    ///
+    /// The [`StreamBlocked`] error is returned when the underlying QUIC stream
+    /// doesn't have enough capacity for the operation to complete. When this
+    /// happens the application should retry the operation once the stream is
+    /// reported as writable again.
+    ///
+    /// [`send_body()`]: struct.Connection.html#method.send_body
+    /// [`StreamBlocked`]: enum.Error.html#variant.StreamBlocked
+    pub fn send_response<T: NameValue>(
+        &mut self, conn: &mut super::Connection, stream_id: u64, headers: &[T],
+        fin: bool,
+    ) -> Result<()> {
+        let priority = "u=3";
+
+        self.send_response_with_priority(
+            conn, stream_id, headers, priority, fin,
+        )?;
+
+        Ok(())
+    }
+
+    /// Sends an HTTP/3 response on the specified stream with specified
+    /// priority.
+    ///
+    /// The [`StreamBlocked`] error is returned when the underlying QUIC stream
+    /// doesn't have enough capacity for the operation to complete. When this
+    /// happens the application should retry the operation once the stream is
+    /// reported as writable again.
+    ///
+    /// [`StreamBlocked`]: enum.Error.html#variant.StreamBlocked
+    pub fn send_response_with_priority<T: NameValue>(
+        &mut self, conn: &mut super::Connection, stream_id: u64, headers: &[T],
+        priority: &str, fin: bool,
+    ) -> Result<()> {
+        if !self.streams.contains_key(&stream_id) {
+            return Err(Error::FrameUnexpected);
+        }
+
+        let mut urgency = 3;
+        let mut incremental = false;
+
+        for param in priority.split(',') {
+            if param.trim() == "i" {
+                incremental = true;
+                continue;
+            }
+
+            if param.trim().starts_with("u=") {
+                // u is an sh-integer (an i64) but it has a constrained range of
+                // 0-7. So detect anything outside that range and clamp it to
+                // the lowest urgency in order to avoid it interfering with
+                // valid items.
+                //
+                // TODO: this also detects when u is not an sh-integer and
+                // clamps it in the same way. A real structured header parser
+                // would actually fail to parse.
+                let mut u =
+                    i64::from_str_radix(param.rsplit('=').next().unwrap(), 10)
+                        .unwrap_or(7);
+
+                if u < 0 || u > 7 {
+                    u = 7;
+                }
+
+                // The HTTP/3 urgency needs to be shifted into the quiche
+                // urgency range.
+                urgency = (u as u8).saturating_add(PRIORITY_URGENCY_OFFSET);
+            }
+        }
+
+        conn.stream_priority(stream_id, urgency, incremental)?;
+
+        self.send_headers(conn, stream_id, headers, fin)?;
+
+        Ok(())
+    }
+
+    fn encode_header_block<T: NameValue>(
+        &mut self, headers: &[T],
+    ) -> Result<Vec<u8>> {
+        let headers_len = headers
+            .iter()
+            .fold(0, |acc, h| acc + h.value().len() + h.name().len() + 32);
+
+        let mut header_block = vec![0; headers_len];
+        let len = self
+            .qpack_encoder
+            .encode(&headers, &mut header_block)
+            .map_err(|_| Error::InternalError)?;
+
+        header_block.truncate(len);
+
+        Ok(header_block)
+    }
+
+    fn send_headers<T: NameValue>(
+        &mut self, conn: &mut super::Connection, stream_id: u64, headers: &[T],
+        fin: bool,
+    ) -> Result<()> {
+        let mut d = [42; 10];
+        let mut b = octets::OctetsMut::with_slice(&mut d);
+
+        if !self.frames_greased && conn.grease {
+            self.send_grease_frames(conn, stream_id)?;
+            self.frames_greased = true;
+        }
+
+        let stream_cap = conn.stream_capacity(stream_id)?;
+
+        let header_block = self.encode_header_block(headers)?;
+
+        let overhead = octets::varint_len(frame::HEADERS_FRAME_TYPE_ID) +
+            octets::varint_len(header_block.len() as u64);
+
+        if stream_cap < overhead + header_block.len() {
+            return Err(Error::StreamBlocked);
+        }
+
+        trace!(
+            "{} tx frm HEADERS stream={} len={} fin={}",
+            conn.trace_id(),
+            stream_id,
+            header_block.len(),
+            fin
+        );
+
+        b.put_varint(frame::HEADERS_FRAME_TYPE_ID)?;
+        b.put_varint(header_block.len() as u64)?;
+        let off = b.off();
+        conn.stream_send(stream_id, &d[..off], false)?;
+
+        // Sending header block separately avoids unnecessary copy.
+        conn.stream_send(stream_id, &header_block, fin)?;
+
+        if let Some(s) = self.streams.get_mut(&stream_id) {
+            s.initialize_local();
+        }
+
+        if fin && conn.stream_finished(stream_id) {
+            self.streams.remove(&stream_id);
+        }
+
+        Ok(())
+    }
+
+    /// Sends an HTTP/3 body chunk on the given stream.
+    ///
+    /// On success the number of bytes written is returned, or [`Done`] if no
+    /// bytes could be written (e.g. because the stream is blocked).
+    ///
+    /// Note that the number of written bytes returned can be lower than the
+    /// length of the input buffer when the underlying QUIC stream doesn't have
+    /// enough capacity for the operation to complete.
+    ///
+    /// When a partial write happens (including when [`Done`] is returned) the
+    /// application should retry the operation once the stream is reported as
+    /// writable again.
+    ///
+    /// [`Done`]: enum.Error.html#variant.Done
+    pub fn send_body(
+        &mut self, conn: &mut super::Connection, stream_id: u64, body: &[u8],
+        fin: bool,
+    ) -> Result<usize> {
+        let mut d = [42; 10];
+        let mut b = octets::OctetsMut::with_slice(&mut d);
+
+        // Validate that it is sane to send data on the stream.
+        if stream_id % 4 != 0 {
+            return Err(Error::FrameUnexpected);
+        }
+
+        match self.streams.get(&stream_id) {
+            Some(s) =>
+                if !s.local_initialized() {
+                    return Err(Error::FrameUnexpected);
+                },
+
+            None => {
+                return Err(Error::FrameUnexpected);
+            },
+        };
+
+        let overhead = octets::varint_len(frame::DATA_FRAME_TYPE_ID) +
+            octets::varint_len(body.len() as u64);
+
+        let stream_cap = conn.stream_capacity(stream_id)?;
+
+        // Make sure there is enough capacity to send the frame header and at
+        // least one byte of frame payload (this to avoid sending 0-length DATA
+        // frames).
+        if stream_cap <= overhead {
+            return Err(Error::Done);
+        }
+
+        // Cap the frame payload length to the stream's capacity.
+        let body_len = std::cmp::min(body.len(), stream_cap - overhead);
+
+        // If we can't send the entire body, set the fin flag to false so the
+        // application can try again later.
+        let fin = if body_len != body.len() { false } else { fin };
+
+        trace!(
+            "{} tx frm DATA stream={} len={} fin={}",
+            conn.trace_id(),
+            stream_id,
+            body_len,
+            fin
+        );
+
+        b.put_varint(frame::DATA_FRAME_TYPE_ID)?;
+        b.put_varint(body_len as u64)?;
+        let off = b.off();
+        conn.stream_send(stream_id, &d[..off], false)?;
+
+        // Return how many bytes were written, excluding the frame header.
+        // Sending body separately avoids unnecessary copy.
+        let written = conn.stream_send(stream_id, &body[..body_len], fin)?;
+
+        if fin && written == body.len() && conn.stream_finished(stream_id) {
+            self.streams.remove(&stream_id);
+        }
+
+        Ok(written)
+    }
+
+    /// Sends an HTTP/3 DATAGRAM with the specified flow ID.
+    pub fn send_dgram(
+        &mut self, conn: &mut super::Connection, flow_id: u64, buf: &[u8],
+    ) -> Result<()> {
+        let len = octets::varint_len(flow_id) + buf.len();
+        let mut d = vec![0; len as usize];
+        let mut b = octets::OctetsMut::with_slice(&mut d);
+
+        b.put_varint(flow_id)?;
+        b.put_bytes(buf)?;
+
+        conn.dgram_send(&d)?;
+
+        Ok(())
+    }
+
+    /// Reads a DATAGRAM into the provided buffer.
+    ///
+    /// Applications should call this method whenever the [`poll()`] method
+    /// returns a [`Datagram`] event.
+    ///
+    /// On success the DATAGRAM data is returned, with length and Flow ID and
+    /// length of the Flow ID.
+    ///
+    /// [`Done`] is returned if there is no data to read.
+    ///
+    /// [`BufferTooShort`] is returned if the provided buffer is too small for
+    /// the data.
+    ///
+    /// [`poll()`]: struct.Connection.html#method.poll
+    /// [`Datagram`]: enum.Event.html#variant.Datagram
+    /// [`Done`]: enum.Error.html#variant.Done
+    /// [`BufferTooShort`]: enum.Error.html#variant.BufferTooShort
+    pub fn recv_dgram(
+        &mut self, conn: &mut super::Connection, buf: &mut [u8],
+    ) -> Result<(usize, u64, usize)> {
+        let len = conn.dgram_recv(buf)?;
+        let mut b = octets::Octets::with_slice(buf);
+        let flow_id = b.get_varint()?;
+        Ok((len, flow_id, b.off()))
+    }
+
+    /// Returns the maximum HTTP/3 DATAGRAM payload that can be sent.
+    pub fn dgram_max_writable_len(
+        &self, conn: &super::Connection, flow_id: u64,
+    ) -> Option<usize> {
+        let flow_id_len = octets::varint_len(flow_id);
+        match conn.dgram_max_writable_len() {
+            None => None,
+            Some(len) => len.checked_sub(flow_id_len),
+        }
+    }
+
+    /// Reads request or response body data into the provided buffer.
+    ///
+    /// Applications should call this method whenever the [`poll()`] method
+    /// returns a [`Data`] event.
+    ///
+    /// On success the amount of bytes read is returned, or [`Done`] if there
+    /// is no data to read.
+    ///
+    /// [`poll()`]: struct.Connection.html#method.poll
+    /// [`Data`]: enum.Event.html#variant.Data
+    /// [`Done`]: enum.Error.html#variant.Done
+    pub fn recv_body(
+        &mut self, conn: &mut super::Connection, stream_id: u64, out: &mut [u8],
+    ) -> Result<usize> {
+        let stream = self.streams.get_mut(&stream_id).ok_or(Error::Done)?;
+
+        if stream.state() != stream::State::Data {
+            return Err(Error::Done);
+        }
+
+        let read = stream.try_consume_data(conn, out)?;
+
+        // While body is being received, the stream is marked as finished only
+        // when all data is read by the application.
+        if conn.stream_finished(stream_id) {
+            self.finished_streams.push_back(stream_id);
+        }
+
+        Ok(read)
+    }
+
+    /// Processes HTTP/3 data received from the peer.
+    ///
+    /// On success it returns an [`Event`] and an ID.
+    ///
+    /// The events [`Headers`], [`Data`] and [`Finished`] return a stream ID,
+    /// which is used in methods [`recv_body()`], [`send_response()`] or
+    /// [`send_body()`].
+    ///
+    /// The event [`Datagram`] returns a flow ID.
+    ///
+    /// The event [`GoAway`] returns an ID that depends on the connection role.
+    /// A client receives the largest processed stream ID. A server receives the
+    /// the largest permitted push ID.
+    ///
+    /// If an error occurs while processing data, the connection is closed with
+    /// the appropriate error code, using the transport's [`close()`] method.
+    ///
+    /// [`Event`]: enum.Event.html
+    /// [`Headers`]: enum.Event.html#variant.Headers
+    /// [`Data`]: enum.Event.html#variant.Data
+    /// [`Finished`]: enum.Event.html#variant.Finished
+    /// [`Datagram`]: enum.Event.html#variant.Datagram
+    /// [`GoAway`]: enum.Event.html#variant.GoAWay
+    /// [`recv_body()`]: struct.Connection.html#method.recv_body
+    /// [`send_response()`]: struct.Connection.html#method.send_response
+    /// [`send_body()`]: struct.Connection.html#method.send_body
+    /// [`recv_dgram()`]: struct.Connection.html#method.recv_dgram
+    /// [`close()`]: ../struct.Connection.html#method.close
+    pub fn poll(&mut self, conn: &mut super::Connection) -> Result<(u64, Event)> {
+        // When connection close is initiated by the local application (e.g. due
+        // to a protocol error), the connection itself might be in a broken
+        // state, so return early.
+        if conn.error.is_some() || conn.app_error.is_some() {
+            return Err(Error::Done);
+        }
+
+        // Process control streams first.
+        if let Some(stream_id) = self.peer_control_stream_id {
+            match self.process_control_stream(conn, stream_id) {
+                Ok(ev) => return Ok(ev),
+
+                Err(Error::Done) => (),
+
+                Err(e) => return Err(e),
+            };
+        }
+
+        if let Some(stream_id) = self.peer_qpack_streams.encoder_stream_id {
+            match self.process_control_stream(conn, stream_id) {
+                Ok(ev) => return Ok(ev),
+
+                Err(Error::Done) => (),
+
+                Err(e) => return Err(e),
+            };
+        }
+
+        if let Some(stream_id) = self.peer_qpack_streams.decoder_stream_id {
+            match self.process_control_stream(conn, stream_id) {
+                Ok(ev) => return Ok(ev),
+
+                Err(Error::Done) => (),
+
+                Err(e) => return Err(e),
+            };
+        }
+
+        // Process finished streams list.
+        if let Some(finished) = self.finished_streams.pop_front() {
+            return Ok((finished, Event::Finished));
+        }
+
+        // Process DATAGRAMs
+        let mut d = [0; 8];
+
+        match conn.dgram_recv_peek(&mut d, 8) {
+            Ok(_) => {
+                let mut b = octets::Octets::with_slice(&d);
+                let flow_id = b.get_varint()?;
+                return Ok((flow_id, Event::Datagram));
+            },
+
+            Err(crate::Error::Done) => (),
+
+            Err(e) => return Err(Error::TransportError(e)),
+        };
+
+        // Process HTTP/3 data from readable streams.
+        for s in conn.readable() {
+            trace!("{} stream id {} is readable", conn.trace_id(), s);
+
+            let ev = match self.process_readable_stream(conn, s) {
+                Ok(v) => Some(v),
+
+                Err(Error::Done) => None,
+
+                Err(e) => return Err(e),
+            };
+
+            if conn.stream_finished(s) {
+                self.finished_streams.push_back(s);
+            }
+
+            // TODO: check if stream is completed so it can be freed
+
+            if let Some(ev) = ev {
+                return Ok(ev);
+            }
+        }
+
+        Err(Error::Done)
+    }
+
+    /// Sends a GOAWAY frame to initiate graceful connection closure.
+    ///
+    /// When quiche is used in the server role, the `id` parameter is the stream
+    /// ID of the highest processed request. This can be any valid ID between 0
+    /// and 2^62-4. However, the ID cannot be increased. Failure to satisfy
+    /// these conditions will return an error.
+    ///
+    /// This method does not close the QUIC connection. Applications are
+    /// required to call [`close()`] themselves.
+    ///
+    /// [`close()`]: ../struct.Connection.html#method.close
+    pub fn send_goaway(
+        &mut self, conn: &mut super::Connection, id: u64,
+    ) -> Result<()> {
+        if !self.is_server {
+            // TODO: server push
+            return Ok(());
+        }
+
+        if self.is_server && id % 4 != 0 {
+            return Err(Error::IdError);
+        }
+
+        if let Some(sent_id) = self.local_goaway_id {
+            if id > sent_id {
+                return Err(Error::IdError);
+            }
+        }
+
+        if let Some(stream_id) = self.control_stream_id {
+            let mut d = [42; 10];
+            let mut b = octets::OctetsMut::with_slice(&mut d);
+
+            let frame = frame::Frame::GoAway { id };
+
+            let wire_len = frame.to_bytes(&mut b)?;
+            let stream_cap = conn.stream_capacity(stream_id)?;
+
+            if stream_cap < wire_len {
+                return Err(Error::StreamBlocked);
+            }
+
+            trace!("{} tx frm {:?}", conn.trace_id(), frame);
+
+            let off = b.off();
+            conn.stream_send(stream_id, &d[..off], false)?;
+
+            self.local_goaway_id = Some(id);
+        }
+
+        Ok(())
+    }
+
+    fn open_uni_stream(
+        &mut self, conn: &mut super::Connection, ty: u64,
+    ) -> Result<u64> {
+        let stream_id = self.next_uni_stream_id;
+
+        let mut d = [0; 8];
+        let mut b = octets::OctetsMut::with_slice(&mut d);
+
+        match ty {
+            // Control and QPACK streams are the most important to schedule.
+            stream::HTTP3_CONTROL_STREAM_TYPE_ID |
+            stream::QPACK_ENCODER_STREAM_TYPE_ID |
+            stream::QPACK_DECODER_STREAM_TYPE_ID => {
+                conn.stream_priority(stream_id, 0, true)?;
+            },
+
+            // TODO: Server push
+            stream::HTTP3_PUSH_STREAM_TYPE_ID => (),
+
+            // Anything else is a GREASE stream, so make it the least important.
+            _ => {
+                conn.stream_priority(stream_id, 255, true)?;
+            },
+        }
+
+        conn.stream_send(stream_id, b.put_varint(ty)?, false)?;
+
+        // To avoid skipping stream IDs, we only calculate the next available
+        // stream ID when data has been successfully buffered.
+        self.next_uni_stream_id = self
+            .next_uni_stream_id
+            .checked_add(4)
+            .ok_or(Error::IdError)?;
+
+        Ok(stream_id)
+    }
+
+    fn open_qpack_encoder_stream(
+        &mut self, conn: &mut super::Connection,
+    ) -> Result<()> {
+        self.local_qpack_streams.encoder_stream_id = Some(
+            self.open_uni_stream(conn, stream::QPACK_ENCODER_STREAM_TYPE_ID)?,
+        );
+
+        Ok(())
+    }
+
+    fn open_qpack_decoder_stream(
+        &mut self, conn: &mut super::Connection,
+    ) -> Result<()> {
+        self.local_qpack_streams.decoder_stream_id = Some(
+            self.open_uni_stream(conn, stream::QPACK_DECODER_STREAM_TYPE_ID)?,
+        );
+
+        Ok(())
+    }
+
+    /// Send GREASE frames on the provided stream ID.
+    fn send_grease_frames(
+        &mut self, conn: &mut super::Connection, stream_id: u64,
+    ) -> Result<()> {
+        let mut d = [0; 8];
+
+        let stream_cap = conn.stream_capacity(stream_id)?;
+
+        let grease_frame1 = grease_value();
+        let grease_frame2 = grease_value();
+        let grease_payload = b"GREASE is the word";
+
+        let overhead = octets::varint_len(grease_frame1) + // frame type
+            1 + // payload len
+            octets::varint_len(grease_frame2) + // frame type
+            1 + // payload len
+            grease_payload.len(); // payload
+
+        // Don't send GREASE if there is not enough capacity for it. Greasing
+        // will _not_ be attempted again later on.
+        if stream_cap < overhead {
+            return Ok(());
+        }
+
+        trace!("{} tx frm GREASE stream={}", conn.trace_id(), stream_id);
+
+        // Empty GREASE frame.
+        let mut b = octets::OctetsMut::with_slice(&mut d);
+        conn.stream_send(stream_id, b.put_varint(grease_frame1)?, false)?;
+
+        let mut b = octets::OctetsMut::with_slice(&mut d);
+        conn.stream_send(stream_id, b.put_varint(0)?, false)?;
+
+        // GREASE frame with payload.
+        let mut b = octets::OctetsMut::with_slice(&mut d);
+        conn.stream_send(stream_id, b.put_varint(grease_frame2)?, false)?;
+
+        let mut b = octets::OctetsMut::with_slice(&mut d);
+        conn.stream_send(stream_id, b.put_varint(18)?, false)?;
+
+        conn.stream_send(stream_id, grease_payload, false)?;
+
+        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<()> {
+        match self.open_uni_stream(conn, grease_value()) {
+            Ok(stream_id) => {
+                trace!("{} open GREASE stream {}", conn.trace_id(), stream_id);
+
+                conn.stream_send(stream_id, b"GREASE is the word", false)?;
+            },
+
+            Err(Error::IdError) => {
+                trace!("{} GREASE stream blocked", conn.trace_id(),);
+
+                return Ok(());
+            },
+
+            Err(e) => return Err(e),
+        };
+
+        Ok(())
+    }
+
+    /// Sends SETTINGS frame based on HTTP/3 configuration.
+    fn send_settings(&mut self, conn: &mut super::Connection) -> Result<()> {
+        self.control_stream_id = Some(
+            self.open_uni_stream(conn, stream::HTTP3_CONTROL_STREAM_TYPE_ID)?,
+        );
+
+        let grease = if conn.grease {
+            Some((grease_value(), grease_value()))
+        } else {
+            None
+        };
+
+        let frame = frame::Frame::Settings {
+            max_header_list_size: self.local_settings.max_header_list_size,
+            qpack_max_table_capacity: self
+                .local_settings
+                .qpack_max_table_capacity,
+            qpack_blocked_streams: self.local_settings.qpack_blocked_streams,
+            grease,
+        };
+
+        let mut d = [42; 128];
+        let mut b = octets::OctetsMut::with_slice(&mut d);
+
+        frame.to_bytes(&mut b)?;
+
+        let off = b.off();
+
+        if let Some(id) = self.control_stream_id {
+            conn.stream_send(id, &d[..off], false)?;
+        }
+
+        Ok(())
+    }
+
+    fn process_control_stream(
+        &mut self, conn: &mut super::Connection, stream_id: u64,
+    ) -> Result<(u64, Event)> {
+        if conn.stream_finished(stream_id) {
+            conn.close(
+                true,
+                Error::ClosedCriticalStream.to_wire(),
+                b"Critical stream closed.",
+            )?;
+
+            return Err(Error::ClosedCriticalStream);
+        }
+
+        match self.process_readable_stream(conn, stream_id) {
+            Ok(ev) => return Ok(ev),
+
+            Err(Error::Done) => (),
+
+            Err(e) => return Err(e),
+        };
+
+        if conn.stream_finished(stream_id) {
+            conn.close(
+                true,
+                Error::ClosedCriticalStream.to_wire(),
+                b"Critical stream closed.",
+            )?;
+
+            return Err(Error::ClosedCriticalStream);
+        }
+
+        Err(Error::Done)
+    }
+
+    fn process_readable_stream(
+        &mut self, conn: &mut super::Connection, stream_id: u64,
+    ) -> Result<(u64, Event)> {
+        self.streams
+            .entry(stream_id)
+            .or_insert_with(|| stream::Stream::new(stream_id, false));
+
+        // We need to get a fresh reference to the stream for each
+        // iteration, to avoid borrowing `self` for the entire duration
+        // of the loop, because we'll need to borrow it again in the
+        // `State::FramePayload` case below.
+        while let Some(stream) = self.streams.get_mut(&stream_id) {
+            match stream.state() {
+                stream::State::StreamType => {
+                    stream.try_fill_buffer(conn)?;
+
+                    let varint = match stream.try_consume_varint() {
+                        Ok(v) => v,
+
+                        Err(_) => continue,
+                    };
+
+                    let ty = stream::Type::deserialize(varint)?;
+
+                    if let Err(e) = stream.set_ty(ty) {
+                        conn.close(true, e.to_wire(), b"")?;
+                        return Err(e);
+                    }
+
+                    match &ty {
+                        stream::Type::Control => {
+                            // Only one control stream allowed.
+                            if self.peer_control_stream_id.is_some() {
+                                conn.close(
+                                    true,
+                                    Error::StreamCreationError.to_wire(),
+                                    b"Received multiple control streams",
+                                )?;
+
+                                return Err(Error::StreamCreationError);
+                            }
+
+                            trace!(
+                                "{} open peer's control stream {}",
+                                conn.trace_id(),
+                                stream_id
+                            );
+
+                            self.peer_control_stream_id = Some(stream_id);
+                        },
+
+                        stream::Type::Push => {
+                            // Only clients can receive push stream.
+                            if self.is_server {
+                                conn.close(
+                                    true,
+                                    Error::StreamCreationError.to_wire(),
+                                    b"Server received push stream.",
+                                )?;
+
+                                return Err(Error::StreamCreationError);
+                            }
+                        },
+
+                        stream::Type::QpackEncoder => {
+                            // Only one qpack encoder stream allowed.
+                            if self.peer_qpack_streams.encoder_stream_id.is_some()
+                            {
+                                conn.close(
+                                    true,
+                                    Error::StreamCreationError.to_wire(),
+                                    b"Received multiple QPACK encoder streams",
+                                )?;
+
+                                return Err(Error::StreamCreationError);
+                            }
+
+                            self.peer_qpack_streams.encoder_stream_id =
+                                Some(stream_id);
+                        },
+
+                        stream::Type::QpackDecoder => {
+                            // Only one qpack decoder allowed.
+                            if self.peer_qpack_streams.decoder_stream_id.is_some()
+                            {
+                                conn.close(
+                                    true,
+                                    Error::StreamCreationError.to_wire(),
+                                    b"Received multiple QPACK decoder streams",
+                                )?;
+
+                                return Err(Error::StreamCreationError);
+                            }
+
+                            self.peer_qpack_streams.decoder_stream_id =
+                                Some(stream_id);
+                        },
+
+                        stream::Type::Unknown => {
+                            // Unknown stream types are ignored.
+                            // TODO: we MAY send STOP_SENDING
+                        },
+
+                        stream::Type::Request => unreachable!(),
+                    }
+                },
+
+                stream::State::PushId => {
+                    stream.try_fill_buffer(conn)?;
+
+                    let varint = match stream.try_consume_varint() {
+                        Ok(v) => v,
+
+                        Err(_) => continue,
+                    };
+
+                    if let Err(e) = stream.set_push_id(varint) {
+                        conn.close(true, e.to_wire(), b"")?;
+                        return Err(e);
+                    }
+                },
+
+                stream::State::FrameType => {
+                    stream.try_fill_buffer(conn)?;
+
+                    let varint = match stream.try_consume_varint() {
+                        Ok(v) => v,
+
+                        Err(_) => continue,
+                    };
+
+                    match stream.set_frame_type(varint) {
+                        Err(Error::FrameUnexpected) => {
+                            let msg = format!("Unexpected frame type {}", varint);
+
+                            conn.close(
+                                true,
+                                Error::FrameUnexpected.to_wire(),
+                                msg.as_bytes(),
+                            )?;
+
+                            return Err(Error::FrameUnexpected);
+                        },
+
+                        Err(e) => {
+                            conn.close(
+                                true,
+                                e.to_wire(),
+                                b"Error handling frame.",
+                            )?;
+
+                            return Err(e);
+                        },
+
+                        _ => (),
+                    }
+                },
+
+                stream::State::FramePayloadLen => {
+                    stream.try_fill_buffer(conn)?;
+
+                    let varint = match stream.try_consume_varint() {
+                        Ok(v) => v,
+
+                        Err(_) => continue,
+                    };
+
+                    if let Err(e) = stream.set_frame_payload_len(varint) {
+                        conn.close(true, e.to_wire(), b"")?;
+                        return Err(e);
+                    }
+                },
+
+                stream::State::FramePayload => {
+                    stream.try_fill_buffer(conn)?;
+
+                    let frame = match stream.try_consume_frame() {
+                        Ok(frame) => frame,
+
+                        Err(Error::Done) => return Err(Error::Done),
+
+                        Err(e) => {
+                            conn.close(
+                                true,
+                                e.to_wire(),
+                                b"Error handling frame.",
+                            )?;
+
+                            return Err(e);
+                        },
+                    };
+
+                    match self.process_frame(conn, stream_id, frame) {
+                        Ok(ev) => return Ok(ev),
+
+                        Err(Error::Done) => (),
+
+                        Err(e) => return Err(e),
+                    };
+                },
+
+                stream::State::Data => {
+                    return Ok((stream_id, Event::Data));
+                },
+
+                stream::State::QpackInstruction => {
+                    let mut d = [0; 4096];
+
+                    // Read data from the stream and discard immediately.
+                    loop {
+                        conn.stream_recv(stream_id, &mut d)?;
+                    }
+                },
+
+                stream::State::Drain => {
+                    // Discard incoming data on the stream.
+                    conn.stream_shutdown(stream_id, crate::Shutdown::Read, 0)?;
+
+                    break;
+                },
+            }
+        }
+
+        Err(Error::Done)
+    }
+
+    fn process_frame(
+        &mut self, conn: &mut super::Connection, stream_id: u64,
+        frame: frame::Frame,
+    ) -> Result<(u64, Event)> {
+        trace!(
+            "{} rx frm {:?} stream={}",
+            conn.trace_id(),
+            frame,
+            stream_id
+        );
+
+        match frame {
+            frame::Frame::Settings {
+                max_header_list_size,
+                qpack_max_table_capacity,
+                qpack_blocked_streams,
+                ..
+            } => {
+                self.peer_settings = ConnectionSettings {
+                    max_header_list_size,
+                    qpack_max_table_capacity,
+                    qpack_blocked_streams,
+                };
+            },
+
+            frame::Frame::Headers { header_block } => {
+                if Some(stream_id) == self.peer_control_stream_id {
+                    conn.close(
+                        true,
+                        Error::FrameUnexpected.to_wire(),
+                        b"HEADERS received on control stream",
+                    )?;
+
+                    return Err(Error::FrameUnexpected);
+                }
+
+                // Use "infinite" as default value for max_header_list_size if
+                // it is not configured by the application.
+                let max_size = self
+                    .local_settings
+                    .max_header_list_size
+                    .unwrap_or(std::u64::MAX);
+
+                let headers = self
+                    .qpack_decoder
+                    .decode(&header_block[..], max_size)
+                    .map_err(|e| match e {
+                        qpack::Error::HeaderListTooLarge => Error::ExcessiveLoad,
+
+                        _ => Error::QpackDecompressionFailed,
+                    })?;
+
+                let has_body = !conn.stream_finished(stream_id);
+
+                return Ok((stream_id, Event::Headers {
+                    list: headers,
+                    has_body,
+                }));
+            },
+
+            frame::Frame::Data { .. } => {
+                if Some(stream_id) == self.peer_control_stream_id {
+                    conn.close(
+                        true,
+                        Error::FrameUnexpected.to_wire(),
+                        b"DATA received on control stream",
+                    )?;
+
+                    return Err(Error::FrameUnexpected);
+                }
+
+                // Do nothing. The Data event is returned separately.
+            },
+
+            frame::Frame::GoAway { id } => {
+                if Some(stream_id) != self.peer_control_stream_id {
+                    conn.close(
+                        true,
+                        Error::FrameUnexpected.to_wire(),
+                        b"GOAWAY received on non-control stream",
+                    )?;
+
+                    return Err(Error::FrameUnexpected);
+                }
+
+                if !self.is_server && id % 4 != 0 {
+                    conn.close(
+                        true,
+                        Error::FrameUnexpected.to_wire(),
+                        b"GOAWAY received with ID of non-request stream",
+                    )?;
+
+                    return Err(Error::IdError);
+                }
+
+                if let Some(received_id) = self.peer_goaway_id {
+                    if id > received_id {
+                        conn.close(
+                            true,
+                            Error::IdError.to_wire(),
+                            b"GOAWAY received with ID larger than previously received",
+                        )?;
+
+                        return Err(Error::IdError);
+                    }
+                }
+
+                self.peer_goaway_id = Some(id);
+
+                return Ok((id, Event::GoAway));
+            },
+
+            frame::Frame::MaxPushId { push_id } => {
+                if Some(stream_id) != self.peer_control_stream_id {
+                    conn.close(
+                        true,
+                        Error::FrameUnexpected.to_wire(),
+                        b"MAX_PUSH_ID received on non-control stream",
+                    )?;
+
+                    return Err(Error::FrameUnexpected);
+                }
+
+                if !self.is_server {
+                    conn.close(
+                        true,
+                        Error::FrameUnexpected.to_wire(),
+                        b"MAX_PUSH_ID received by client",
+                    )?;
+
+                    return Err(Error::FrameUnexpected);
+                }
+
+                if push_id < self.max_push_id {
+                    conn.close(
+                        true,
+                        Error::IdError.to_wire(),
+                        b"MAX_PUSH_ID reduced limit",
+                    )?;
+
+                    return Err(Error::IdError);
+                }
+
+                self.max_push_id = push_id;
+            },
+
+            frame::Frame::PushPromise { .. } => {
+                if self.is_server {
+                    conn.close(
+                        true,
+                        Error::FrameUnexpected.to_wire(),
+                        b"PUSH_PROMISE received by server",
+                    )?;
+
+                    return Err(Error::FrameUnexpected);
+                }
+
+                if stream_id % 4 != 0 {
+                    conn.close(
+                        true,
+                        Error::FrameUnexpected.to_wire(),
+                        b"PUSH_PROMISE received on non-request stream",
+                    )?;
+
+                    return Err(Error::FrameUnexpected);
+                }
+
+                // TODO: implement more checks and PUSH_PROMISE event
+            },
+
+            frame::Frame::CancelPush { .. } => {
+                if Some(stream_id) != self.peer_control_stream_id {
+                    conn.close(
+                        true,
+                        Error::FrameUnexpected.to_wire(),
+                        b"CANCEL_PUSH received on non-control stream",
+                    )?;
+
+                    return Err(Error::FrameUnexpected);
+                }
+
+                // TODO: implement CANCEL_PUSH frame
+            },
+
+            frame::Frame::Unknown => (),
+        }
+
+        Err(Error::Done)
+    }
+}
+
+/// Generates an HTTP/3 GREASE variable length integer.
+fn grease_value() -> u64 {
+    let n = super::rand::rand_u64_uniform(148_764_065_110_560_899);
+    31 * n + 33
+}
+
+#[doc(hidden)]
+pub mod testing {
+    use super::*;
+
+    use crate::testing;
+
+    /// Session is an HTTP/3 test helper structure. It holds a client, server
+    /// and pipe that allows them to communicate.
+    ///
+    /// `default()` creates a session with some sensible default
+    /// configuration. `with_configs()` allows for providing a specific
+    /// configuration.
+    ///
+    /// `handshake()` performs all the steps needed to establish an HTTP/3
+    /// connection.
+    ///
+    /// Some utility functions are provided that make it less verbose to send
+    /// request, responses and individual headers. The full quiche API remains
+    /// available for any test that need to do unconventional things (such as
+    /// bad behaviour that triggers errors).
+    pub struct Session {
+        pub pipe: testing::Pipe,
+        pub client: Connection,
+        pub server: Connection,
+
+        buf: [u8; 65535],
+    }
+
+    impl Session {
+        pub fn default() -> Result<Session> {
+            let mut config = crate::Config::new(crate::PROTOCOL_VERSION)?;
+            config.load_cert_chain_from_pem_file("examples/cert.crt")?;
+            config.load_priv_key_from_pem_file("examples/cert.key")?;
+            config.set_application_protos(b"\x02h3")?;
+            config.set_initial_max_data(1500);
+            config.set_initial_max_stream_data_bidi_local(150);
+            config.set_initial_max_stream_data_bidi_remote(150);
+            config.set_initial_max_stream_data_uni(150);
+            config.set_initial_max_streams_bidi(5);
+            config.set_initial_max_streams_uni(5);
+            config.verify_peer(false);
+
+            let h3_config = Config::new()?;
+            Session::with_configs(&mut config, &h3_config)
+        }
+
+        pub fn with_configs(
+            config: &mut crate::Config, h3_config: &Config,
+        ) -> Result<Session> {
+            Ok(Session {
+                pipe: testing::Pipe::with_config(config)?,
+                client: Connection::new(&h3_config, false)?,
+                server: Connection::new(&h3_config, true)?,
+                buf: [0; 65535],
+            })
+        }
+
+        /// Do the HTTP/3 handshake so both ends are in sane initial state.
+        pub fn handshake(&mut self) -> Result<()> {
+            self.pipe.handshake(&mut self.buf)?;
+
+            // Client streams.
+            self.client.send_settings(&mut self.pipe.client)?;
+            self.pipe.advance(&mut self.buf).ok();
+
+            self.client
+                .open_qpack_encoder_stream(&mut self.pipe.client)?;
+            self.pipe.advance(&mut self.buf).ok();
+
+            self.client
+                .open_qpack_decoder_stream(&mut self.pipe.client)?;
+            self.pipe.advance(&mut self.buf).ok();
+
+            if self.pipe.client.grease {
+                self.client.open_grease_stream(&mut self.pipe.client)?;
+            }
+
+            self.pipe.advance(&mut self.buf).ok();
+
+            // Server streams.
+            self.server.send_settings(&mut self.pipe.server)?;
+            self.pipe.advance(&mut self.buf).ok();
+
+            self.server
+                .open_qpack_encoder_stream(&mut self.pipe.server)?;
+            self.pipe.advance(&mut self.buf).ok();
+
+            self.server
+                .open_qpack_decoder_stream(&mut self.pipe.server)?;
+            self.pipe.advance(&mut self.buf).ok();
+
+            if self.pipe.server.grease {
+                self.server.open_grease_stream(&mut self.pipe.server)?;
+            }
+
+            self.advance().ok();
+
+            while self.client.poll(&mut self.pipe.client).is_ok() {
+                // Do nothing.
+            }
+
+            while self.server.poll(&mut self.pipe.server).is_ok() {
+                // Do nothing.
+            }
+
+            Ok(())
+        }
+
+        /// Advances the session pipe over the buffer.
+        pub fn advance(&mut self) -> crate::Result<()> {
+            self.pipe.advance(&mut self.buf)
+        }
+
+        /// Polls the client for events.
+        pub fn poll_client(&mut self) -> Result<(u64, Event)> {
+            self.client.poll(&mut self.pipe.client)
+        }
+
+        /// Polls the server for events.
+        pub fn poll_server(&mut self) -> Result<(u64, Event)> {
+            self.server.poll(&mut self.pipe.server)
+        }
+
+        /// Sends a request from client with default headers.
+        ///
+        /// On success it returns the newly allocated stream and the headers.
+        pub fn send_request(&mut self, fin: bool) -> Result<(u64, Vec<Header>)> {
+            let req = vec![
+                Header::new(":method", "GET"),
+                Header::new(":scheme", "https"),
+                Header::new(":authority", "quic.tech"),
+                Header::new(":path", "/test"),
+                Header::new("user-agent", "quiche-test"),
+            ];
+
+            let stream =
+                self.client.send_request(&mut self.pipe.client, &req, fin)?;
+
+            self.advance().ok();
+
+            Ok((stream, req))
+        }
+
+        /// Sends a response from server with default headers.
+        ///
+        /// On success it returns the headers.
+        pub fn send_response(
+            &mut self, stream: u64, fin: bool,
+        ) -> Result<Vec<Header>> {
+            let resp = vec![
+                Header::new(":status", "200"),
+                Header::new("server", "quiche-test"),
+            ];
+
+            self.server.send_response(
+                &mut self.pipe.server,
+                stream,
+                &resp,
+                fin,
+            )?;
+
+            self.advance().ok();
+
+            Ok(resp)
+        }
+
+        /// Sends some default payload from client.
+        ///
+        /// On success it returns the payload.
+        pub fn send_body_client(
+            &mut self, stream: u64, fin: bool,
+        ) -> Result<Vec<u8>> {
+            let bytes = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
+
+            self.client
+                .send_body(&mut self.pipe.client, stream, &bytes, fin)?;
+
+            self.advance().ok();
+
+            Ok(bytes)
+        }
+
+        /// Fetches DATA payload from the server.
+        ///
+        /// On success it returns the number of bytes received.
+        pub fn recv_body_client(
+            &mut self, stream: u64, buf: &mut [u8],
+        ) -> Result<usize> {
+            self.client.recv_body(&mut self.pipe.client, stream, buf)
+        }
+
+        /// Sends some default payload from server.
+        ///
+        /// On success it returns the payload.
+        pub fn send_body_server(
+            &mut self, stream: u64, fin: bool,
+        ) -> Result<Vec<u8>> {
+            let bytes = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
+
+            self.server
+                .send_body(&mut self.pipe.server, stream, &bytes, fin)?;
+
+            self.advance().ok();
+
+            Ok(bytes)
+        }
+
+        /// Fetches DATA payload from the client.
+        ///
+        /// On success it returns the number of bytes received.
+        pub fn recv_body_server(
+            &mut self, stream: u64, buf: &mut [u8],
+        ) -> Result<usize> {
+            self.server.recv_body(&mut self.pipe.server, stream, buf)
+        }
+
+        /// Sends a single HTTP/3 frame from the client.
+        pub fn send_frame_client(
+            &mut self, frame: frame::Frame, stream_id: u64, fin: bool,
+        ) -> Result<()> {
+            let mut d = [42; 65535];
+
+            let mut b = octets::OctetsMut::with_slice(&mut d);
+
+            frame.to_bytes(&mut b)?;
+
+            let off = b.off();
+            self.pipe.client.stream_send(stream_id, &d[..off], fin)?;
+
+            self.advance().ok();
+
+            Ok(())
+        }
+
+        /// Sends a single HTTP/3 frame from the server.
+        pub fn send_frame_server(
+            &mut self, frame: frame::Frame, stream_id: u64, fin: bool,
+        ) -> Result<()> {
+            let mut d = [42; 65535];
+
+            let mut b = octets::OctetsMut::with_slice(&mut d);
+
+            frame.to_bytes(&mut b)?;
+
+            let off = b.off();
+            self.pipe.server.stream_send(stream_id, &d[..off], fin)?;
+
+            self.advance().ok();
+
+            Ok(())
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    use super::testing::*;
+
+    #[test]
+    /// Make sure that random GREASE values is within the specified limit.
+    fn grease_value_in_varint_limit() {
+        assert!(grease_value() < 2u64.pow(62) - 1);
+    }
+
+    #[test]
+    /// Send a request with no body, get a response with no body.
+    fn request_no_body_response_no_body() {
+        let mut s = Session::default().unwrap();
+        s.handshake().unwrap();
+
+        let (stream, req) = s.send_request(true).unwrap();
+
+        assert_eq!(stream, 0);
+
+        let ev_headers = Event::Headers {
+            list: req,
+            has_body: false,
+        };
+
+        assert_eq!(s.poll_server(), Ok((stream, ev_headers)));
+        assert_eq!(s.poll_server(), Ok((stream, Event::Finished)));
+
+        let resp = s.send_response(stream, true).unwrap();
+
+        let ev_headers = Event::Headers {
+            list: resp,
+            has_body: false,
+        };
+
+        assert_eq!(s.poll_client(), Ok((stream, ev_headers)));
+        assert_eq!(s.poll_client(), Ok((stream, Event::Finished)));
+        assert_eq!(s.poll_client(), Err(Error::Done));
+    }
+
+    #[test]
+    /// Send a request with no body, get a response with one DATA frame.
+    fn request_no_body_response_one_chunk() {
+        let mut s = Session::default().unwrap();
+        s.handshake().unwrap();
+
+        let (stream, req) = s.send_request(true).unwrap();
+        assert_eq!(stream, 0);
+
+        let ev_headers = Event::Headers {
+            list: req,
+            has_body: false,
+        };
+
+        assert_eq!(s.poll_server(), Ok((stream, ev_headers)));
+
+        assert_eq!(s.poll_server(), Ok((stream, Event::Finished)));
+
+        let resp = s.send_response(stream, false).unwrap();
+
+        let body = s.send_body_server(stream, true).unwrap();
+
+        let mut recv_buf = vec![0; body.len()];
+
+        let ev_headers = Event::Headers {
+            list: resp,
+            has_body: true,
+        };
+
+        assert_eq!(s.poll_client(), Ok((stream, ev_headers)));
+
+        assert_eq!(s.poll_client(), Ok((stream, Event::Data)));
+        assert_eq!(s.recv_body_client(stream, &mut recv_buf), Ok(body.len()));
+
+        assert_eq!(s.poll_client(), Ok((stream, Event::Finished)));
+        assert_eq!(s.poll_client(), Err(Error::Done));
+    }
+
+    #[test]
+    /// Send a request with no body, get a response with multiple DATA frames.
+    fn request_no_body_response_many_chunks() {
+        let mut s = Session::default().unwrap();
+        s.handshake().unwrap();
+
+        let (stream, req) = s.send_request(true).unwrap();
+
+        let ev_headers = Event::Headers {
+            list: req,
+            has_body: false,
+        };
+
+        assert_eq!(s.poll_server(), Ok((stream, ev_headers)));
+        assert_eq!(s.poll_server(), Ok((stream, Event::Finished)));
+
+        let total_data_frames = 4;
+
+        let resp = s.send_response(stream, false).unwrap();
+
+        for _ in 0..total_data_frames - 1 {
+            s.send_body_server(stream, false).unwrap();
+        }
+
+        let body = s.send_body_server(stream, true).unwrap();
+
+        let mut recv_buf = vec![0; body.len()];
+
+        let ev_headers = Event::Headers {
+            list: resp,
+            has_body: true,
+        };
+
+        assert_eq!(s.poll_client(), Ok((stream, ev_headers)));
+
+        for _ in 0..total_data_frames {
+            assert_eq!(s.poll_client(), Ok((stream, Event::Data)));
+            assert_eq!(s.recv_body_client(stream, &mut recv_buf), Ok(body.len()));
+        }
+
+        assert_eq!(s.poll_client(), Ok((stream, Event::Finished)));
+        assert_eq!(s.poll_client(), Err(Error::Done));
+    }
+
+    #[test]
+    /// Send a request with one DATA frame, get a response with no body.
+    fn request_one_chunk_response_no_body() {
+        let mut s = Session::default().unwrap();
+        s.handshake().unwrap();
+
+        let (stream, req) = s.send_request(false).unwrap();
+
+        let body = s.send_body_client(stream, true).unwrap();
+
+        let mut recv_buf = vec![0; body.len()];
+
+        let ev_headers = Event::Headers {
+            list: req,
+            has_body: true,
+        };
+
+        assert_eq!(s.poll_server(), Ok((stream, ev_headers)));
+
+        assert_eq!(s.poll_server(), Ok((stream, Event::Data)));
+        assert_eq!(s.recv_body_server(stream, &mut recv_buf), Ok(body.len()));
+
+        assert_eq!(s.poll_server(), Ok((stream, Event::Finished)));
+
+        let resp = s.send_response(stream, true).unwrap();
+
+        let ev_headers = Event::Headers {
+            list: resp,
+            has_body: false,
+        };
+
+        assert_eq!(s.poll_client(), Ok((stream, ev_headers)));
+        assert_eq!(s.poll_client(), Ok((stream, Event::Finished)));
+    }
+
+    #[test]
+    /// Send a request with multiple DATA frames, get a response with no body.
+    fn request_many_chunks_response_no_body() {
+        let mut s = Session::default().unwrap();
+        s.handshake().unwrap();
+
+        let (stream, req) = s.send_request(false).unwrap();
+
+        let total_data_frames = 4;
+
+        for _ in 0..total_data_frames - 1 {
+            s.send_body_client(stream, false).unwrap();
+        }
+
+        let body = s.send_body_client(stream, true).unwrap();
+
+        let mut recv_buf = vec![0; body.len()];
+
+        let ev_headers = Event::Headers {
+            list: req,
+            has_body: true,
+        };
+
+        assert_eq!(s.poll_server(), Ok((stream, ev_headers)));
+
+        for _ in 0..total_data_frames {
+            assert_eq!(s.poll_server(), Ok((stream, Event::Data)));
+            assert_eq!(s.recv_body_server(stream, &mut recv_buf), Ok(body.len()));
+        }
+
+        assert_eq!(s.poll_server(), Ok((stream, Event::Finished)));
+
+        let resp = s.send_response(stream, true).unwrap();
+
+        let ev_headers = Event::Headers {
+            list: resp,
+            has_body: false,
+        };
+
+        assert_eq!(s.poll_client(), Ok((stream, ev_headers)));
+        assert_eq!(s.poll_client(), Ok((stream, Event::Finished)));
+    }
+
+    #[test]
+    /// Send a request with multiple DATA frames, get a response with one DATA
+    /// frame.
+    fn many_requests_many_chunks_response_one_chunk() {
+        let mut s = Session::default().unwrap();
+        s.handshake().unwrap();
+
+        let mut reqs = Vec::new();
+
+        let (stream1, req1) = s.send_request(false).unwrap();
+        assert_eq!(stream1, 0);
+        reqs.push(req1);
+
+        let (stream2, req2) = s.send_request(false).unwrap();
+        assert_eq!(stream2, 4);
+        reqs.push(req2);
+
+        let (stream3, req3) = s.send_request(false).unwrap();
+        assert_eq!(stream3, 8);
+        reqs.push(req3);
+
+        let body = s.send_body_client(stream1, false).unwrap();
+        s.send_body_client(stream2, false).unwrap();
+        s.send_body_client(stream3, false).unwrap();
+
+        let mut recv_buf = vec![0; body.len()];
+
+        // Reverse order of writes.
+
+        s.send_body_client(stream3, true).unwrap();
+        s.send_body_client(stream2, true).unwrap();
+        s.send_body_client(stream1, true).unwrap();
+
+        for _ in 0..reqs.len() {
+            let (stream, ev) = s.poll_server().unwrap();
+            let ev_headers = Event::Headers {
+                list: reqs[(stream / 4) as usize].clone(),
+                has_body: true,
+            };
+            assert_eq!(ev, ev_headers);
+            assert_eq!(s.poll_server(), Ok((stream, Event::Data)));
+            assert_eq!(s.recv_body_server(stream, &mut recv_buf), Ok(body.len()));
+            assert_eq!(s.poll_server(), Ok((stream, Event::Data)));
+            assert_eq!(s.recv_body_server(stream, &mut recv_buf), Ok(body.len()));
+            assert_eq!(s.poll_server(), Ok((stream, Event::Finished)));
+        }
+
+        assert_eq!(s.poll_server(), Err(Error::Done));
+
+        let mut resps = Vec::new();
+
+        let resp1 = s.send_response(stream1, true).unwrap();
+        resps.push(resp1);
+
+        let resp2 = s.send_response(stream2, true).unwrap();
+        resps.push(resp2);
+
+        let resp3 = s.send_response(stream3, true).unwrap();
+        resps.push(resp3);
+
+        for _ in 0..resps.len() {
+            let (stream, ev) = s.poll_client().unwrap();
+            let ev_headers = Event::Headers {
+                list: resps[(stream / 4) as usize].clone(),
+                has_body: false,
+            };
+            assert_eq!(ev, ev_headers);
+            assert_eq!(s.poll_client(), Ok((stream, Event::Finished)));
+        }
+
+        assert_eq!(s.poll_client(), Err(Error::Done));
+    }
+
+    #[test]
+    /// Try to send DATA frames before HEADERS.
+    fn body_response_before_headers() {
+        let mut s = Session::default().unwrap();
+        s.handshake().unwrap();
+
+        let (stream, req) = s.send_request(true).unwrap();
+        assert_eq!(stream, 0);
+
+        let ev_headers = Event::Headers {
+            list: req,
+            has_body: false,
+        };
+
+        assert_eq!(s.poll_server(), Ok((stream, ev_headers)));
+
+        assert_eq!(s.poll_server(), Ok((stream, Event::Finished)));
+
+        assert_eq!(
+            s.send_body_server(stream, true),
+            Err(Error::FrameUnexpected)
+        );
+
+        assert_eq!(s.poll_client(), Err(Error::Done));
+    }
+
+    #[test]
+    /// Try to send DATA frames on wrong streams, ensure the API returns an
+    /// error before anything hits the transport layer.
+    fn send_body_invalid_client_stream() {
+        let mut s = Session::default().unwrap();
+        s.handshake().unwrap();
+
+        assert_eq!(s.send_body_client(0, true), Err(Error::FrameUnexpected));
+
+        assert_eq!(
+            s.send_body_client(s.client.control_stream_id.unwrap(), true),
+            Err(Error::FrameUnexpected)
+        );
+
+        assert_eq!(
+            s.send_body_client(
+                s.client.local_qpack_streams.encoder_stream_id.unwrap(),
+                true
+            ),
+            Err(Error::FrameUnexpected)
+        );
+
+        assert_eq!(
+            s.send_body_client(
+                s.client.local_qpack_streams.decoder_stream_id.unwrap(),
+                true
+            ),
+            Err(Error::FrameUnexpected)
+        );
+
+        assert_eq!(
+            s.send_body_client(s.client.peer_control_stream_id.unwrap(), true),
+            Err(Error::FrameUnexpected)
+        );
+
+        assert_eq!(
+            s.send_body_client(
+                s.client.peer_qpack_streams.encoder_stream_id.unwrap(),
+                true
+            ),
+            Err(Error::FrameUnexpected)
+        );
+
+        assert_eq!(
+            s.send_body_client(
+                s.client.peer_qpack_streams.decoder_stream_id.unwrap(),
+                true
+            ),
+            Err(Error::FrameUnexpected)
+        );
+    }
+
+    #[test]
+    /// Try to send DATA frames on wrong streams, ensure the API returns an
+    /// error before anything hits the transport layer.
+    fn send_body_invalid_server_stream() {
+        let mut s = Session::default().unwrap();
+        s.handshake().unwrap();
+
+        assert_eq!(s.send_body_server(0, true), Err(Error::FrameUnexpected));
+
+        assert_eq!(
+            s.send_body_server(s.server.control_stream_id.unwrap(), true),
+            Err(Error::FrameUnexpected)
+        );
+
+        assert_eq!(
+            s.send_body_server(
+                s.server.local_qpack_streams.encoder_stream_id.unwrap(),
+                true
+            ),
+            Err(Error::FrameUnexpected)
+        );
+
+        assert_eq!(
+            s.send_body_server(
+                s.server.local_qpack_streams.decoder_stream_id.unwrap(),
+                true
+            ),
+            Err(Error::FrameUnexpected)
+        );
+
+        assert_eq!(
+            s.send_body_server(s.server.peer_control_stream_id.unwrap(), true),
+            Err(Error::FrameUnexpected)
+        );
+
+        assert_eq!(
+            s.send_body_server(
+                s.server.peer_qpack_streams.encoder_stream_id.unwrap(),
+                true
+            ),
+            Err(Error::FrameUnexpected)
+        );
+
+        assert_eq!(
+            s.send_body_server(
+                s.server.peer_qpack_streams.decoder_stream_id.unwrap(),
+                true
+            ),
+            Err(Error::FrameUnexpected)
+        );
+    }
+
+    #[test]
+    /// Send a MAX_PUSH_ID frame from the client on a valid stream.
+    fn max_push_id_from_client_good() {
+        let mut s = Session::default().unwrap();
+        s.handshake().unwrap();
+
+        s.send_frame_client(
+            frame::Frame::MaxPushId { push_id: 1 },
+            s.client.control_stream_id.unwrap(),
+            false,
+        )
+        .unwrap();
+
+        assert_eq!(s.poll_server(), Err(Error::Done));
+    }
+
+    #[test]
+    /// Send a MAX_PUSH_ID frame from the client on an invalid stream.
+    fn max_push_id_from_client_bad_stream() {
+        let mut s = Session::default().unwrap();
+        s.handshake().unwrap();
+
+        let (stream, req) = s.send_request(false).unwrap();
+
+        s.send_frame_client(
+            frame::Frame::MaxPushId { push_id: 2 },
+            stream,
+            false,
+        )
+        .unwrap();
+
+        let ev_headers = Event::Headers {
+            list: req,
+            has_body: true,
+        };
+
+        assert_eq!(s.poll_server(), Ok((stream, ev_headers)));
+        assert_eq!(s.poll_server(), Err(Error::FrameUnexpected));
+    }
+
+    #[test]
+    /// Send a sequence of MAX_PUSH_ID frames from the client that attempt to
+    /// reduce the limit.
+    fn max_push_id_from_client_limit_reduction() {
+        let mut s = Session::default().unwrap();
+        s.handshake().unwrap();
+
+        s.send_frame_client(
+            frame::Frame::MaxPushId { push_id: 2 },
+            s.client.control_stream_id.unwrap(),
+            false,
+        )
+        .unwrap();
+
+        s.send_frame_client(
+            frame::Frame::MaxPushId { push_id: 1 },
+            s.client.control_stream_id.unwrap(),
+            false,
+        )
+        .unwrap();
+
+        assert_eq!(s.poll_server(), Err(Error::IdError));
+    }
+
+    #[test]
+    /// Send a MAX_PUSH_ID frame from the server, which is forbidden.
+    fn max_push_id_from_server() {
+        let mut s = Session::default().unwrap();
+        s.handshake().unwrap();
+
+        s.send_frame_server(
+            frame::Frame::MaxPushId { push_id: 1 },
+            s.server.control_stream_id.unwrap(),
+            false,
+        )
+        .unwrap();
+
+        assert_eq!(s.poll_client(), Err(Error::FrameUnexpected));
+    }
+
+    #[test]
+    /// Send a PUSH_PROMISE frame from the client, which is forbidden.
+    fn push_promise_from_client() {
+        let mut s = Session::default().unwrap();
+        s.handshake().unwrap();
+
+        let (stream, req) = s.send_request(false).unwrap();
+
+        let header_block = s.client.encode_header_block(&req).unwrap();
+
+        s.send_frame_client(
+            frame::Frame::PushPromise {
+                push_id: 1,
+                header_block,
+            },
+            stream,
+            false,
+        )
+        .unwrap();
+
+        let ev_headers = Event::Headers {
+            list: req,
+            has_body: true,
+        };
+
+        assert_eq!(s.poll_server(), Ok((stream, ev_headers)));
+        assert_eq!(s.poll_server(), Err(Error::FrameUnexpected));
+    }
+
+    #[test]
+    /// Send a CANCEL_PUSH frame from the client.
+    fn cancel_push_from_client() {
+        let mut s = Session::default().unwrap();
+        s.handshake().unwrap();
+
+        s.send_frame_client(
+            frame::Frame::CancelPush { push_id: 1 },
+            s.client.control_stream_id.unwrap(),
+            false,
+        )
+        .unwrap();
+
+        assert_eq!(s.poll_server(), Err(Error::Done));
+    }
+
+    #[test]
+    /// Send a CANCEL_PUSH frame from the client on an invalid stream.
+    fn cancel_push_from_client_bad_stream() {
+        let mut s = Session::default().unwrap();
+        s.handshake().unwrap();
+
+        let (stream, req) = s.send_request(false).unwrap();
+
+        s.send_frame_client(
+            frame::Frame::CancelPush { push_id: 2 },
+            stream,
+            false,
+        )
+        .unwrap();
+
+        let ev_headers = Event::Headers {
+            list: req,
+            has_body: true,
+        };
+
+        assert_eq!(s.poll_server(), Ok((stream, ev_headers)));
+        assert_eq!(s.poll_server(), Err(Error::FrameUnexpected));
+    }
+
+    #[test]
+    /// Send a CANCEL_PUSH frame from the client.
+    fn cancel_push_from_server() {
+        let mut s = Session::default().unwrap();
+        s.handshake().unwrap();
+
+        s.send_frame_server(
+            frame::Frame::CancelPush { push_id: 1 },
+            s.server.control_stream_id.unwrap(),
+            false,
+        )
+        .unwrap();
+
+        assert_eq!(s.poll_client(), Err(Error::Done));
+    }
+
+    #[test]
+    /// Send a GOAWAY frame from the client.
+    fn goaway_from_client_good() {
+        let mut s = Session::default().unwrap();
+        s.handshake().unwrap();
+
+        s.client.send_goaway(&mut s.pipe.client, 1).unwrap();
+
+        s.advance().ok();
+
+        // TODO: server push
+        assert_eq!(s.poll_server(), Err(Error::Done));
+    }
+
+    #[test]
+    /// Send a GOAWAY frame from the server.
+    fn goaway_from_server_good() {
+        let mut s = Session::default().unwrap();
+        s.handshake().unwrap();
+
+        s.server.send_goaway(&mut s.pipe.server, 4000).unwrap();
+
+        s.advance().ok();
+
+        assert_eq!(s.poll_client(), Ok((4000, Event::GoAway)));
+    }
+
+    #[test]
+    /// A client MUST NOT send a request after it receives GOAWAY.
+    fn client_request_after_goaway() {
+        let mut s = Session::default().unwrap();
+        s.handshake().unwrap();
+
+        s.server.send_goaway(&mut s.pipe.server, 4000).unwrap();
+
+        s.advance().ok();
+
+        assert_eq!(s.poll_client(), Ok((4000, Event::GoAway)));
+
+        assert_eq!(s.send_request(true), Err(Error::FrameUnexpected));
+    }
+
+    #[test]
+    /// Send a GOAWAY frame from the server, using an invalid goaway ID.
+    fn goaway_from_server_invalid_id() {
+        let mut s = Session::default().unwrap();
+        s.handshake().unwrap();
+
+        s.send_frame_server(
+            frame::Frame::GoAway { id: 1 },
+            s.server.control_stream_id.unwrap(),
+            false,
+        )
+        .unwrap();
+
+        assert_eq!(s.poll_client(), Err(Error::IdError));
+    }
+
+    #[test]
+    /// Send multiple GOAWAY frames from the server, that increase the goaway
+    /// ID.
+    fn goaway_from_server_increase_id() {
+        let mut s = Session::default().unwrap();
+        s.handshake().unwrap();
+
+        s.send_frame_server(
+            frame::Frame::GoAway { id: 0 },
+            s.server.control_stream_id.unwrap(),
+            false,
+        )
+        .unwrap();
+
+        s.send_frame_server(
+            frame::Frame::GoAway { id: 4 },
+            s.server.control_stream_id.unwrap(),
+            false,
+        )
+        .unwrap();
+
+        assert_eq!(s.poll_client(), Ok((0, Event::GoAway)));
+
+        assert_eq!(s.poll_client(), Err(Error::IdError));
+    }
+
+    #[test]
+    /// Ensure quiche allocates streams for client and server roles as expected.
+    fn uni_stream_local_counting() {
+        let config = Config::new().unwrap();
+
+        let h3_cln = Connection::new(&config, false).unwrap();
+        assert_eq!(h3_cln.next_uni_stream_id, 2);
+
+        let h3_srv = Connection::new(&config, true).unwrap();
+        assert_eq!(h3_srv.next_uni_stream_id, 3);
+    }
+
+    #[test]
+    /// Client opens multiple control streams, which is forbidden.
+    fn open_multiple_control_streams() {
+        let mut s = Session::default().unwrap();
+        s.handshake().unwrap();
+
+        let stream_id = s.client.next_uni_stream_id;
+
+        let mut d = [42; 8];
+        let mut b = octets::OctetsMut::with_slice(&mut d);
+
+        s.pipe
+            .client
+            .stream_send(
+                stream_id,
+                b.put_varint(stream::HTTP3_CONTROL_STREAM_TYPE_ID).unwrap(),
+                false,
+            )
+            .unwrap();
+
+        s.advance().ok();
+
+        assert_eq!(s.poll_server(), Err(Error::StreamCreationError));
+    }
+
+    #[test]
+    /// Client closes the control stream, which is forbidden.
+    fn close_control_stream() {
+        let mut s = Session::default().unwrap();
+        s.handshake().unwrap();
+
+        let mut control_stream_closed = false;
+
+        s.send_frame_client(
+            frame::Frame::MaxPushId { push_id: 1 },
+            s.client.control_stream_id.unwrap(),
+            true,
+        )
+        .unwrap();
+
+        loop {
+            match s.server.poll(&mut s.pipe.server) {
+                Ok(_) => (),
+
+                Err(Error::Done) => {
+                    break;
+                },
+
+                Err(Error::ClosedCriticalStream) => {
+                    control_stream_closed = true;
+                    break;
+                },
+
+                Err(_) => (),
+            }
+        }
+
+        assert!(control_stream_closed);
+    }
+
+    #[test]
+    /// Client closes QPACK stream, which is forbidden.
+    fn close_qpack_stream() {
+        let mut s = Session::default().unwrap();
+        s.handshake().unwrap();
+
+        let mut qpack_stream_closed = false;
+
+        let stream_id = s.client.local_qpack_streams.encoder_stream_id.unwrap();
+        let d = [0; 1];
+
+        s.pipe.client.stream_send(stream_id, &d, false).unwrap();
+        s.pipe.client.stream_send(stream_id, &d, true).unwrap();
+
+        s.advance().ok();
+
+        loop {
+            match s.server.poll(&mut s.pipe.server) {
+                Ok(_) => (),
+
+                Err(Error::Done) => {
+                    break;
+                },
+
+                Err(Error::ClosedCriticalStream) => {
+                    qpack_stream_closed = true;
+                    break;
+                },
+
+                Err(_) => (),
+            }
+        }
+
+        assert!(qpack_stream_closed);
+    }
+
+    #[test]
+    /// Client sends QPACK data.
+    fn qpack_data() {
+        // TODO: QPACK instructions are ignored until dynamic table support is
+        // added so we just test that the data is safely ignored.
+        let mut s = Session::default().unwrap();
+        s.handshake().unwrap();
+
+        let e_stream_id = s.client.local_qpack_streams.encoder_stream_id.unwrap();
+        let d_stream_id = s.client.local_qpack_streams.decoder_stream_id.unwrap();
+        let d = [0; 20];
+
+        s.pipe.client.stream_send(e_stream_id, &d, false).unwrap();
+        s.advance().ok();
+
+        s.pipe.client.stream_send(d_stream_id, &d, false).unwrap();
+        s.advance().ok();
+
+        loop {
+            match s.server.poll(&mut s.pipe.server) {
+                Ok(_) => (),
+
+                Err(Error::Done) => {
+                    break;
+                },
+
+                Err(_) => {
+                    panic!();
+                },
+            }
+        }
+    }
+
+    #[test]
+    /// Tests limits for the stream state buffer maximum size.
+    fn max_state_buf_size() {
+        // DATA frames don't consume the state buffer, so can be of any size.
+        let mut s = Session::default().unwrap();
+        s.handshake().unwrap();
+
+        let mut d = [42; 128];
+        let mut b = octets::OctetsMut::with_slice(&mut d);
+
+        let frame_type = b.put_varint(frame::DATA_FRAME_TYPE_ID).unwrap();
+        s.pipe.client.stream_send(0, frame_type, false).unwrap();
+
+        let frame_len = b.put_varint(1 << 24).unwrap();
+        s.pipe.client.stream_send(0, frame_len, false).unwrap();
+
+        s.pipe.client.stream_send(0, &d, false).unwrap();
+
+        s.advance().ok();
+
+        assert_eq!(s.server.poll(&mut s.pipe.server), Ok((0, Event::Data)));
+
+        // GREASE frames consume the state buffer, so need to be limited.
+        let mut s = Session::default().unwrap();
+        s.handshake().unwrap();
+
+        let mut d = [42; 128];
+        let mut b = octets::OctetsMut::with_slice(&mut d);
+
+        let frame_type = b.put_varint(148_764_065_110_560_899).unwrap();
+        s.pipe.client.stream_send(0, frame_type, false).unwrap();
+
+        let frame_len = b.put_varint(1 << 24).unwrap();
+        s.pipe.client.stream_send(0, frame_len, false).unwrap();
+
+        s.pipe.client.stream_send(0, &d, false).unwrap();
+
+        s.advance().ok();
+
+        assert_eq!(s.server.poll(&mut s.pipe.server), Err(Error::InternalError));
+    }
+
+    #[test]
+    /// Tests that DATA frames are properly truncated depending on the request
+    /// stream's outgoing flow control capacity.
+    fn stream_backpressure() {
+        let bytes = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
+
+        let mut s = Session::default().unwrap();
+        s.handshake().unwrap();
+
+        let (stream, req) = s.send_request(false).unwrap();
+
+        let total_data_frames = 6;
+
+        for _ in 0..total_data_frames {
+            assert_eq!(
+                s.client
+                    .send_body(&mut s.pipe.client, stream, &bytes, false),
+                Ok(bytes.len())
+            );
+
+            s.advance().ok();
+        }
+
+        assert_eq!(
+            s.client.send_body(&mut s.pipe.client, stream, &bytes, true),
+            Ok(bytes.len() - 2)
+        );
+
+        s.advance().ok();
+
+        let mut recv_buf = vec![0; bytes.len()];
+
+        let ev_headers = Event::Headers {
+            list: req,
+            has_body: true,
+        };
+
+        assert_eq!(s.poll_server(), Ok((stream, ev_headers)));
+
+        for _ in 0..total_data_frames {
+            assert_eq!(s.poll_server(), Ok((stream, Event::Data)));
+            assert_eq!(
+                s.recv_body_server(stream, &mut recv_buf),
+                Ok(bytes.len())
+            );
+        }
+
+        assert_eq!(s.poll_server(), Ok((stream, Event::Data)));
+        assert_eq!(
+            s.recv_body_server(stream, &mut recv_buf),
+            Ok(bytes.len() - 2)
+        );
+
+        // Fin flag from last send_body() call was not sent as the buffer was
+        // only partially written.
+        assert_eq!(s.poll_server(), Err(Error::Done));
+    }
+
+    #[test]
+    /// Tests that the max header list size setting is enforced.
+    fn request_max_header_size_limit() {
+        let mut config = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
+        config
+            .load_cert_chain_from_pem_file("examples/cert.crt")
+            .unwrap();
+        config
+            .load_priv_key_from_pem_file("examples/cert.key")
+            .unwrap();
+        config.set_application_protos(b"\x02h3").unwrap();
+        config.set_initial_max_data(1500);
+        config.set_initial_max_stream_data_bidi_local(150);
+        config.set_initial_max_stream_data_bidi_remote(150);
+        config.set_initial_max_stream_data_uni(150);
+        config.set_initial_max_streams_bidi(5);
+        config.set_initial_max_streams_uni(5);
+        config.verify_peer(false);
+
+        let mut h3_config = Config::new().unwrap();
+        h3_config.set_max_header_list_size(65);
+
+        let mut s = Session::with_configs(&mut config, &mut h3_config).unwrap();
+
+        s.handshake().unwrap();
+
+        let req = vec![
+            Header::new(":method", "GET"),
+            Header::new(":scheme", "https"),
+            Header::new(":authority", "quic.tech"),
+            Header::new(":path", "/test"),
+            Header::new("aaaaaaa", "aaaaaaaa"),
+        ];
+
+        let stream = s
+            .client
+            .send_request(&mut s.pipe.client, &req, true)
+            .unwrap();
+
+        s.advance().ok();
+
+        assert_eq!(stream, 0);
+
+        assert_eq!(s.poll_server(), Err(Error::ExcessiveLoad));
+    }
+
+    #[test]
+    /// Tests that Error::TransportError contains a transport error.
+    fn transport_error() {
+        let mut s = Session::default().unwrap();
+        s.handshake().unwrap();
+
+        let req = vec![
+            Header::new(":method", "GET"),
+            Header::new(":scheme", "https"),
+            Header::new(":authority", "quic.tech"),
+            Header::new(":path", "/test"),
+            Header::new("user-agent", "quiche-test"),
+        ];
+
+        // We need to open all streams in the same flight, so we can't use the
+        // Session::send_request() method because it also calls advance(),
+        // otherwise the server would send a MAX_STREAMS frame and the client
+        // wouldn't hit the streams limit.
+        assert_eq!(s.client.send_request(&mut s.pipe.client, &req, true), Ok(0));
+        assert_eq!(s.client.send_request(&mut s.pipe.client, &req, true), Ok(4));
+        assert_eq!(s.client.send_request(&mut s.pipe.client, &req, true), Ok(8));
+        assert_eq!(
+            s.client.send_request(&mut s.pipe.client, &req, true),
+            Ok(12)
+        );
+        assert_eq!(
+            s.client.send_request(&mut s.pipe.client, &req, true),
+            Ok(16)
+        );
+
+        assert_eq!(
+            s.client.send_request(&mut s.pipe.client, &req, true),
+            Err(Error::TransportError(crate::Error::StreamLimit))
+        );
+    }
+
+    #[test]
+    /// Tests that calling poll() after an error occured does nothing.
+    fn poll_after_error() {
+        // DATA frames don't consume the state buffer, so can be of any size.
+        let mut s = Session::default().unwrap();
+        s.handshake().unwrap();
+
+        let mut d = [42; 128];
+        let mut b = octets::OctetsMut::with_slice(&mut d);
+
+        let frame_type = b.put_varint(frame::DATA_FRAME_TYPE_ID).unwrap();
+        s.pipe.client.stream_send(0, frame_type, false).unwrap();
+
+        let frame_len = b.put_varint(1 << 24).unwrap();
+        s.pipe.client.stream_send(0, frame_len, false).unwrap();
+
+        s.pipe.client.stream_send(0, &d, false).unwrap();
+
+        s.advance().ok();
+
+        assert_eq!(s.server.poll(&mut s.pipe.server), Ok((0, Event::Data)));
+
+        // GREASE frames consume the state buffer, so need to be limited.
+        let mut s = Session::default().unwrap();
+        s.handshake().unwrap();
+
+        let mut d = [42; 128];
+        let mut b = octets::OctetsMut::with_slice(&mut d);
+
+        let frame_type = b.put_varint(148_764_065_110_560_899).unwrap();
+        s.pipe.client.stream_send(0, frame_type, false).unwrap();
+
+        let frame_len = b.put_varint(1 << 24).unwrap();
+        s.pipe.client.stream_send(0, frame_len, false).unwrap();
+
+        s.pipe.client.stream_send(0, &d, false).unwrap();
+
+        s.advance().ok();
+
+        assert_eq!(s.server.poll(&mut s.pipe.server), Err(Error::InternalError));
+
+        // Try to call poll() again after an error occurred.
+        assert_eq!(s.server.poll(&mut s.pipe.server), Err(Error::Done));
+    }
+
+    #[test]
+    /// Tests that we limit sending HEADERS based on the stream capacity.
+    fn headers_blocked() {
+        let mut config = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
+        config
+            .load_cert_chain_from_pem_file("examples/cert.crt")
+            .unwrap();
+        config
+            .load_priv_key_from_pem_file("examples/cert.key")
+            .unwrap();
+        config.set_application_protos(b"\x02h3").unwrap();
+        config.set_initial_max_data(70);
+        config.set_initial_max_stream_data_bidi_local(150);
+        config.set_initial_max_stream_data_bidi_remote(150);
+        config.set_initial_max_stream_data_uni(150);
+        config.set_initial_max_streams_bidi(100);
+        config.set_initial_max_streams_uni(5);
+        config.verify_peer(false);
+
+        let mut h3_config = Config::new().unwrap();
+
+        let mut s = Session::with_configs(&mut config, &mut h3_config).unwrap();
+
+        s.handshake().unwrap();
+
+        let req = vec![
+            Header::new(":method", "GET"),
+            Header::new(":scheme", "https"),
+            Header::new(":authority", "quic.tech"),
+            Header::new(":path", "/test"),
+        ];
+
+        assert_eq!(s.client.send_request(&mut s.pipe.client, &req, true), Ok(0));
+
+        assert_eq!(
+            s.client.send_request(&mut s.pipe.client, &req, true),
+            Err(Error::StreamBlocked)
+        );
+
+        s.advance().ok();
+
+        // Once the server gives flow control credits back, we can send the
+        // request.
+        assert_eq!(s.client.send_request(&mut s.pipe.client, &req, true), Ok(4));
+    }
+
+    #[test]
+    /// Tests that blocked 0-length DATA writes are reported correctly.
+    fn zero_length_data_blocked() {
+        let mut config = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
+        config
+            .load_cert_chain_from_pem_file("examples/cert.crt")
+            .unwrap();
+        config
+            .load_priv_key_from_pem_file("examples/cert.key")
+            .unwrap();
+        config.set_application_protos(b"\x02h3").unwrap();
+        config.set_initial_max_data(70);
+        config.set_initial_max_stream_data_bidi_local(150);
+        config.set_initial_max_stream_data_bidi_remote(150);
+        config.set_initial_max_stream_data_uni(150);
+        config.set_initial_max_streams_bidi(100);
+        config.set_initial_max_streams_uni(5);
+        config.verify_peer(false);
+
+        let mut h3_config = Config::new().unwrap();
+
+        let mut s = Session::with_configs(&mut config, &mut h3_config).unwrap();
+
+        s.handshake().unwrap();
+
+        let req = vec![
+            Header::new(":method", "GET"),
+            Header::new(":scheme", "https"),
+            Header::new(":authority", "quic.tech"),
+            Header::new(":path", "/test"),
+        ];
+
+        assert_eq!(
+            s.client.send_request(&mut s.pipe.client, &req, false),
+            Ok(0)
+        );
+
+        assert_eq!(
+            s.client.send_body(&mut s.pipe.client, 0, b"", true),
+            Err(Error::Done)
+        );
+
+        s.advance().ok();
+
+        // Once the server gives flow control credits back, we can send the body.
+        assert_eq!(s.client.send_body(&mut s.pipe.client, 0, b"", true), Ok(0));
+    }
+}
+
+mod ffi;
+mod frame;
+#[doc(hidden)]
+pub mod qpack;
+mod stream;
diff --git a/src/h3/qpack/decoder.rs b/src/h3/qpack/decoder.rs
new file mode 100644
index 0000000..240ca69
--- /dev/null
+++ b/src/h3/qpack/decoder.rs
@@ -0,0 +1,305 @@
+// Copyright (C) 2019, Cloudflare, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright notice,
+//       this list of conditions and the following disclaimer.
+//
+//     * Redistributions in binary form must reproduce the above copyright
+//       notice, this list of conditions and the following disclaimer in the
+//       documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+use crate::octets;
+
+use super::Error;
+use super::Result;
+
+use crate::h3::Header;
+
+use super::INDEXED;
+use super::INDEXED_WITH_POST_BASE;
+use super::LITERAL;
+use super::LITERAL_WITH_NAME_REF;
+
+#[derive(Clone, Copy, Debug, PartialEq)]
+enum Representation {
+    Indexed,
+    IndexedWithPostBase,
+    Literal,
+    LiteralWithNameRef,
+    LiteralWithPostBase,
+}
+
+impl Representation {
+    pub fn from_byte(b: u8) -> Representation {
+        if b & INDEXED == INDEXED {
+            return Representation::Indexed;
+        }
+
+        if b & LITERAL_WITH_NAME_REF == LITERAL_WITH_NAME_REF {
+            return Representation::LiteralWithNameRef;
+        }
+
+        if b & LITERAL == LITERAL {
+            return Representation::Literal;
+        }
+
+        if b & INDEXED_WITH_POST_BASE == INDEXED_WITH_POST_BASE {
+            return Representation::IndexedWithPostBase;
+        }
+
+        Representation::LiteralWithPostBase
+    }
+}
+
+/// A QPACK decoder.
+pub struct Decoder {}
+
+impl Default for Decoder {
+    fn default() -> Decoder {
+        Decoder {}
+    }
+}
+
+impl Decoder {
+    /// Creates a new QPACK decoder.
+    pub fn new() -> Decoder {
+        Decoder::default()
+    }
+
+    /// Processes control instructions from the encoder.
+    pub fn control(&mut self, _buf: &mut [u8]) -> Result<()> {
+        // TODO: process control instructions
+        Ok(())
+    }
+
+    /// Decodes a QPACK header block into a list of headers.
+    pub fn decode(&mut self, buf: &[u8], max_size: u64) -> Result<Vec<Header>> {
+        let mut b = octets::Octets::with_slice(buf);
+
+        let mut out = Vec::new();
+
+        let mut left = max_size;
+
+        let req_insert_count = decode_int(&mut b, 8)?;
+        let base = decode_int(&mut b, 7)?;
+
+        trace!("Header count={} base={}", req_insert_count, base);
+
+        while b.cap() > 0 {
+            let first = b.peek_u8()?;
+
+            match Representation::from_byte(first) {
+                Representation::Indexed => {
+                    const STATIC: u8 = 0x40;
+
+                    let s = first & STATIC == STATIC;
+                    let index = decode_int(&mut b, 6)?;
+
+                    trace!("Indexed index={} static={}", index, s);
+
+                    if !s {
+                        // TODO: implement dynamic table
+                        return Err(Error::InvalidHeaderValue);
+                    }
+
+                    let (name, value) = lookup_static(index)?;
+
+                    left = left
+                        .checked_sub((name.len() + value.len()) as u64)
+                        .ok_or(Error::HeaderListTooLarge)?;
+
+                    let hdr = Header::new(name, value);
+                    out.push(hdr);
+                },
+
+                Representation::IndexedWithPostBase => {
+                    let index = decode_int(&mut b, 4)?;
+
+                    trace!("Indexed With Post Base index={}", index);
+
+                    // TODO: implement dynamic table
+                    return Err(Error::InvalidHeaderValue);
+                },
+
+                Representation::Literal => {
+                    let name_huff = b.as_ref()[0] & 0x08 == 0x08;
+                    let name_len = decode_int(&mut b, 3)? as usize;
+
+                    let mut name = b.get_bytes(name_len)?;
+
+                    let name = if name_huff {
+                        super::huffman::decode(&mut name)?
+                    } else {
+                        name.to_vec()
+                    };
+
+                    let name = String::from_utf8(name)
+                        .map_err(|_| Error::InvalidHeaderValue)?;
+
+                    let value = decode_str(&mut b)?;
+
+                    trace!(
+                        "Literal Without Name Reference name={:?} value={:?}",
+                        name,
+                        value,
+                    );
+
+                    left = left
+                        .checked_sub((name.len() + value.len()) as u64)
+                        .ok_or(Error::HeaderListTooLarge)?;
+
+                    // Instead of calling Header::new(), create Header directly
+                    // from `name` and `value`, which are already String.
+                    let hdr = Header(name, value);
+                    out.push(hdr);
+                },
+
+                Representation::LiteralWithNameRef => {
+                    const STATIC: u8 = 0x10;
+
+                    let s = first & STATIC == STATIC;
+                    let name_idx = decode_int(&mut b, 4)?;
+                    let value = decode_str(&mut b)?;
+
+                    trace!(
+                        "Literal name_idx={} static={} value={:?}",
+                        name_idx,
+                        s,
+                        value
+                    );
+
+                    if !s {
+                        // TODO: implement dynamic table
+                        return Err(Error::InvalidHeaderValue);
+                    }
+
+                    let (name, _) = lookup_static(name_idx)?;
+
+                    left = left
+                        .checked_sub((name.len() + value.len()) as u64)
+                        .ok_or(Error::HeaderListTooLarge)?;
+
+                    // Instead of calling Header::new(), create Header directly
+                    // from `value`, which is already String, but clone `name`
+                    // as it is just a reference.
+                    let hdr = Header(name.to_string(), value);
+                    out.push(hdr);
+                },
+
+                Representation::LiteralWithPostBase => {
+                    trace!("Literal With Post Base");
+
+                    // TODO: implement dynamic table
+                    return Err(Error::InvalidHeaderValue);
+                },
+            }
+        }
+
+        Ok(out)
+    }
+}
+
+fn lookup_static(idx: u64) -> Result<(&'static str, &'static str)> {
+    if idx >= super::static_table::STATIC_TABLE.len() as u64 {
+        return Err(Error::InvalidStaticTableIndex);
+    }
+
+    Ok(super::static_table::STATIC_TABLE[idx as usize])
+}
+
+fn decode_int(b: &mut octets::Octets, prefix: usize) -> Result<u64> {
+    let mask = 2u64.pow(prefix as u32) - 1;
+
+    let mut val = u64::from(b.get_u8()?);
+    val &= mask;
+
+    if val < mask {
+        return Ok(val);
+    }
+
+    let mut shift = 0;
+
+    while b.cap() > 0 {
+        let byte = b.get_u8()?;
+
+        let inc = u64::from(byte & 0x7f)
+            .checked_shl(shift)
+            .ok_or(Error::BufferTooShort)?;
+
+        val = val.checked_add(inc).ok_or(Error::BufferTooShort)?;
+
+        shift += 7;
+
+        if byte & 0x80 == 0 {
+            return Ok(val);
+        }
+    }
+
+    Err(Error::BufferTooShort)
+}
+
+fn decode_str<'a>(b: &'a mut octets::Octets) -> Result<String> {
+    let first = b.peek_u8()?;
+
+    let huff = first & 0x80 == 0x80;
+
+    let len = decode_int(b, 7)? as usize;
+
+    let mut val = b.get_bytes(len)?;
+
+    let val = if huff {
+        super::huffman::decode(&mut val)?
+    } else {
+        val.to_vec()
+    };
+
+    let val = String::from_utf8(val).map_err(|_| Error::InvalidHeaderValue)?;
+    Ok(val)
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    use crate::octets;
+
+    #[test]
+    fn decode_int1() {
+        let mut encoded = [0b01010, 0x02];
+        let mut b = octets::Octets::with_slice(&mut encoded);
+
+        assert_eq!(decode_int(&mut b, 5), Ok(10));
+    }
+
+    #[test]
+    fn decode_int2() {
+        let mut encoded = [0b11111, 0b10011010, 0b00001010];
+        let mut b = octets::Octets::with_slice(&mut encoded);
+
+        assert_eq!(decode_int(&mut b, 5), Ok(1337));
+    }
+
+    #[test]
+    fn decode_int3() {
+        let mut encoded = [0b101010];
+        let mut b = octets::Octets::with_slice(&mut encoded);
+
+        assert_eq!(decode_int(&mut b, 8), Ok(42));
+    }
+}
diff --git a/src/h3/qpack/encoder.rs b/src/h3/qpack/encoder.rs
new file mode 100644
index 0000000..1307df3
--- /dev/null
+++ b/src/h3/qpack/encoder.rs
@@ -0,0 +1,202 @@
+// Copyright (C) 2019, Cloudflare, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright notice,
+//       this list of conditions and the following disclaimer.
+//
+//     * Redistributions in binary form must reproduce the above copyright
+//       notice, this list of conditions and the following disclaimer in the
+//       documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+use super::Result;
+
+use crate::octets;
+
+use crate::h3::NameValue;
+
+use super::INDEXED;
+use super::LITERAL;
+use super::LITERAL_WITH_NAME_REF;
+
+/// A QPACK encoder.
+pub struct Encoder {}
+
+impl Default for Encoder {
+    fn default() -> Encoder {
+        Encoder {}
+    }
+}
+
+impl Encoder {
+    /// Creates a new QPACK encoder.
+    pub fn new() -> Encoder {
+        Encoder::default()
+    }
+
+    /// Encodes a list of headers into a QPACK header block.
+    pub fn encode<T: NameValue>(
+        &mut self, headers: &[T], out: &mut [u8],
+    ) -> Result<usize> {
+        let mut b = octets::OctetsMut::with_slice(out);
+
+        // Request Insert Count.
+        encode_int(0, 0, 8, &mut b)?;
+
+        // Base.
+        encode_int(0, 0, 7, &mut b)?;
+
+        for h in headers {
+            match lookup_static(h) {
+                Some((idx, true)) => {
+                    const STATIC: u8 = 0x40;
+
+                    // Encode as statically indexed.
+                    encode_int(idx, INDEXED | STATIC, 6, &mut b)?;
+                },
+
+                Some((idx, false)) => {
+                    const STATIC: u8 = 0x10;
+
+                    // Encode value as literal with static name reference.
+                    encode_int(idx, LITERAL_WITH_NAME_REF | STATIC, 4, &mut b)?;
+                    encode_str(h.value(), 7, &mut b)?;
+                },
+
+                None => {
+                    // Encode as fully literal.
+                    let name_len = super::huffman::encode_output_length(
+                        h.name().as_bytes(),
+                        true,
+                    )?;
+
+                    encode_int(name_len as u64, LITERAL | 0x08, 3, &mut b)?;
+
+                    super::huffman::encode(h.name().as_bytes(), &mut b, true)?;
+
+                    encode_str(h.value(), 7, &mut b)?;
+                },
+            };
+        }
+
+        Ok(b.off())
+    }
+}
+
+fn lookup_static<T: NameValue>(h: &T) -> Option<(u64, bool)> {
+    let mut name_match = None;
+
+    for (i, e) in super::static_table::STATIC_TABLE.iter().enumerate() {
+        // Match header name first.
+        if h.name().len() == e.0.len() && h.name().eq_ignore_ascii_case(e.0) {
+            // No header value to match, return early.
+            if e.1.is_empty() {
+                return Some((i as u64, false));
+            }
+
+            // Match header value.
+            if h.value().len() == e.1.len() && h.value() == e.1 {
+                return Some((i as u64, true));
+            }
+
+            // Remember name-only match for later, but keep searching.
+            name_match = Some((i as u64, false));
+        }
+    }
+
+    name_match
+}
+
+fn encode_int(
+    mut v: u64, first: u8, prefix: usize, b: &mut octets::OctetsMut,
+) -> Result<()> {
+    let mask = 2u64.pow(prefix as u32) - 1;
+
+    // Encode I on N bits.
+    if v < mask {
+        b.put_u8(first | v as u8)?;
+        return Ok(());
+    }
+
+    // Encode (2^N - 1) on N bits.
+    b.put_u8(first | mask as u8)?;
+
+    v -= mask;
+
+    while v >= 128 {
+        // Encode (I % 128 + 128) on 8 bits.
+        b.put_u8((v % 128 + 128) as u8)?;
+
+        v >>= 7;
+    }
+
+    // Encode I on 8 bits.
+    b.put_u8(v as u8)?;
+
+    Ok(())
+}
+
+fn encode_str(v: &str, prefix: usize, b: &mut octets::OctetsMut) -> Result<()> {
+    let len = super::huffman::encode_output_length(v.as_bytes(), false)?;
+
+    encode_int(len as u64, 0x80, prefix, b)?;
+
+    super::huffman::encode(v.as_bytes(), b, false)?;
+
+    Ok(())
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    use crate::octets;
+
+    #[test]
+    fn encode_int1() {
+        let expected = [0b01010];
+        let mut encoded = [0; 1];
+        let mut b = octets::OctetsMut::with_slice(&mut encoded);
+
+        assert!(encode_int(10, 0, 5, &mut b).is_ok());
+
+        assert_eq!(expected, encoded);
+    }
+
+    #[test]
+    fn encode_int2() {
+        let expected = [0b11111, 0b10011010, 0b00001010];
+        let mut encoded = [0; 3];
+        let mut b = octets::OctetsMut::with_slice(&mut encoded);
+
+        assert!(encode_int(1337, 0, 5, &mut b).is_ok());
+
+        assert_eq!(expected, encoded);
+    }
+
+    #[test]
+    fn encode_int3() {
+        let expected = [0b101010];
+        let mut encoded = [0; 1];
+        let mut b = octets::OctetsMut::with_slice(&mut encoded);
+
+        assert!(encode_int(42, 0, 8, &mut b).is_ok());
+
+        assert_eq!(expected, encoded);
+    }
+}
diff --git a/src/h3/qpack/huffman/mod.rs b/src/h3/qpack/huffman/mod.rs
new file mode 100644
index 0000000..3a6ff06
--- /dev/null
+++ b/src/h3/qpack/huffman/mod.rs
@@ -0,0 +1,153 @@
+// Copyright (C) 2019, Cloudflare, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright notice,
+//       this list of conditions and the following disclaimer.
+//
+//     * Redistributions in binary form must reproduce the above copyright
+//       notice, this list of conditions and the following disclaimer in the
+//       documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+use crate::octets;
+
+use super::Error;
+use super::Result;
+
+use self::table::DECODE_TABLE;
+use self::table::ENCODE_TABLE;
+
+pub fn decode(b: &mut octets::Octets) -> Result<Vec<u8>> {
+    // Max compression ratio is >= 0.5
+    let mut out = Vec::with_capacity(b.len() << 1);
+
+    let mut decoder = Decoder::new();
+
+    while b.cap() > 0 {
+        let byte = b.get_u8()?;
+
+        if let Some(b) = decoder.decode4(byte >> 4)? {
+            out.push(b);
+        }
+
+        if let Some(b) = decoder.decode4(byte & 0xf)? {
+            out.push(b);
+        }
+    }
+
+    if !decoder.is_final() {
+        return Err(Error::InvalidHuffmanEncoding);
+    }
+
+    Ok(out)
+}
+
+pub fn encode(src: &[u8], out: &mut octets::OctetsMut, low: bool) -> Result<()> {
+    let mut bits: u64 = 0;
+    let mut bits_left = 40;
+
+    for &b in src {
+        let b = if low { b.to_ascii_lowercase() } else { b };
+
+        let (nbits, code) = ENCODE_TABLE[b as usize];
+
+        bits |= code << (bits_left - nbits);
+        bits_left -= nbits;
+
+        while bits_left <= 32 {
+            out.put_u8((bits >> 32) as u8)?;
+
+            bits <<= 8;
+            bits_left += 8;
+        }
+    }
+
+    if bits_left != 40 {
+        // This writes the EOS token
+        bits |= (1 << bits_left) - 1;
+
+        out.put_u8((bits >> 32) as u8)?;
+    }
+
+    Ok(())
+}
+
+pub fn encode_output_length(src: &[u8], low: bool) -> Result<usize> {
+    let mut bits: usize = 0;
+
+    for &b in src {
+        let b = if low { b.to_ascii_lowercase() } else { b };
+
+        let (nbits, _) = ENCODE_TABLE[b as usize];
+        bits += nbits;
+    }
+
+    let mut len = bits / 8;
+
+    if bits & 7 != 0 {
+        len += 1;
+    }
+
+    Ok(len)
+}
+
+struct Decoder {
+    state: usize,
+    maybe_eos: bool,
+}
+
+impl Decoder {
+    fn new() -> Decoder {
+        Decoder {
+            state: 0,
+            maybe_eos: false,
+        }
+    }
+
+    // Decodes 4 bits
+    fn decode4(&mut self, input: u8) -> Result<Option<u8>> {
+        const MAYBE_EOS: u8 = 1;
+        const DECODED: u8 = 2;
+        const ERROR: u8 = 4;
+
+        // (next-state, byte, flags)
+        let (next, byte, flags) = DECODE_TABLE[self.state][input as usize];
+
+        if flags & ERROR == ERROR {
+            // Data followed the EOS marker
+            return Err(Error::InvalidHuffmanEncoding);
+        }
+
+        let ret = if flags & DECODED == DECODED {
+            Some(byte)
+        } else {
+            None
+        };
+
+        self.state = next;
+        self.maybe_eos = flags & MAYBE_EOS == MAYBE_EOS;
+
+        Ok(ret)
+    }
+
+    fn is_final(&self) -> bool {
+        self.state == 0 || self.maybe_eos
+    }
+}
+
+mod table;
diff --git a/src/h3/qpack/huffman/table.rs b/src/h3/qpack/huffman/table.rs
new file mode 100644
index 0000000..7162aff
--- /dev/null
+++ b/src/h3/qpack/huffman/table.rs
@@ -0,0 +1,5130 @@
+#[allow(clippy::unreadable_literal)]
+
+// (num-bits, bits)
+pub const ENCODE_TABLE: [(usize, u64); 257] = [
+    (13, 0x1ff8),
+    (23, 0x7fffd8),
+    (28, 0xfffffe2),
+    (28, 0xfffffe3),
+    (28, 0xfffffe4),
+    (28, 0xfffffe5),
+    (28, 0xfffffe6),
+    (28, 0xfffffe7),
+    (28, 0xfffffe8),
+    (24, 0xffffea),
+    (30, 0x3ffffffc),
+    (28, 0xfffffe9),
+    (28, 0xfffffea),
+    (30, 0x3ffffffd),
+    (28, 0xfffffeb),
+    (28, 0xfffffec),
+    (28, 0xfffffed),
+    (28, 0xfffffee),
+    (28, 0xfffffef),
+    (28, 0xffffff0),
+    (28, 0xffffff1),
+    (28, 0xffffff2),
+    (30, 0x3ffffffe),
+    (28, 0xffffff3),
+    (28, 0xffffff4),
+    (28, 0xffffff5),
+    (28, 0xffffff6),
+    (28, 0xffffff7),
+    (28, 0xffffff8),
+    (28, 0xffffff9),
+    (28, 0xffffffa),
+    (28, 0xffffffb),
+    (6, 0x14),
+    (10, 0x3f8),
+    (10, 0x3f9),
+    (12, 0xffa),
+    (13, 0x1ff9),
+    (6, 0x15),
+    (8, 0xf8),
+    (11, 0x7fa),
+    (10, 0x3fa),
+    (10, 0x3fb),
+    (8, 0xf9),
+    (11, 0x7fb),
+    (8, 0xfa),
+    (6, 0x16),
+    (6, 0x17),
+    (6, 0x18),
+    (5, 0x0),
+    (5, 0x1),
+    (5, 0x2),
+    (6, 0x19),
+    (6, 0x1a),
+    (6, 0x1b),
+    (6, 0x1c),
+    (6, 0x1d),
+    (6, 0x1e),
+    (6, 0x1f),
+    (7, 0x5c),
+    (8, 0xfb),
+    (15, 0x7ffc),
+    (6, 0x20),
+    (12, 0xffb),
+    (10, 0x3fc),
+    (13, 0x1ffa),
+    (6, 0x21),
+    (7, 0x5d),
+    (7, 0x5e),
+    (7, 0x5f),
+    (7, 0x60),
+    (7, 0x61),
+    (7, 0x62),
+    (7, 0x63),
+    (7, 0x64),
+    (7, 0x65),
+    (7, 0x66),
+    (7, 0x67),
+    (7, 0x68),
+    (7, 0x69),
+    (7, 0x6a),
+    (7, 0x6b),
+    (7, 0x6c),
+    (7, 0x6d),
+    (7, 0x6e),
+    (7, 0x6f),
+    (7, 0x70),
+    (7, 0x71),
+    (7, 0x72),
+    (8, 0xfc),
+    (7, 0x73),
+    (8, 0xfd),
+    (13, 0x1ffb),
+    (19, 0x7fff0),
+    (13, 0x1ffc),
+    (14, 0x3ffc),
+    (6, 0x22),
+    (15, 0x7ffd),
+    (5, 0x3),
+    (6, 0x23),
+    (5, 0x4),
+    (6, 0x24),
+    (5, 0x5),
+    (6, 0x25),
+    (6, 0x26),
+    (6, 0x27),
+    (5, 0x6),
+    (7, 0x74),
+    (7, 0x75),
+    (6, 0x28),
+    (6, 0x29),
+    (6, 0x2a),
+    (5, 0x7),
+    (6, 0x2b),
+    (7, 0x76),
+    (6, 0x2c),
+    (5, 0x8),
+    (5, 0x9),
+    (6, 0x2d),
+    (7, 0x77),
+    (7, 0x78),
+    (7, 0x79),
+    (7, 0x7a),
+    (7, 0x7b),
+    (15, 0x7ffe),
+    (11, 0x7fc),
+    (14, 0x3ffd),
+    (13, 0x1ffd),
+    (28, 0xffffffc),
+    (20, 0xfffe6),
+    (22, 0x3fffd2),
+    (20, 0xfffe7),
+    (20, 0xfffe8),
+    (22, 0x3fffd3),
+    (22, 0x3fffd4),
+    (22, 0x3fffd5),
+    (23, 0x7fffd9),
+    (22, 0x3fffd6),
+    (23, 0x7fffda),
+    (23, 0x7fffdb),
+    (23, 0x7fffdc),
+    (23, 0x7fffdd),
+    (23, 0x7fffde),
+    (24, 0xffffeb),
+    (23, 0x7fffdf),
+    (24, 0xffffec),
+    (24, 0xffffed),
+    (22, 0x3fffd7),
+    (23, 0x7fffe0),
+    (24, 0xffffee),
+    (23, 0x7fffe1),
+    (23, 0x7fffe2),
+    (23, 0x7fffe3),
+    (23, 0x7fffe4),
+    (21, 0x1fffdc),
+    (22, 0x3fffd8),
+    (23, 0x7fffe5),
+    (22, 0x3fffd9),
+    (23, 0x7fffe6),
+    (23, 0x7fffe7),
+    (24, 0xffffef),
+    (22, 0x3fffda),
+    (21, 0x1fffdd),
+    (20, 0xfffe9),
+    (22, 0x3fffdb),
+    (22, 0x3fffdc),
+    (23, 0x7fffe8),
+    (23, 0x7fffe9),
+    (21, 0x1fffde),
+    (23, 0x7fffea),
+    (22, 0x3fffdd),
+    (22, 0x3fffde),
+    (24, 0xfffff0),
+    (21, 0x1fffdf),
+    (22, 0x3fffdf),
+    (23, 0x7fffeb),
+    (23, 0x7fffec),
+    (21, 0x1fffe0),
+    (21, 0x1fffe1),
+    (22, 0x3fffe0),
+    (21, 0x1fffe2),
+    (23, 0x7fffed),
+    (22, 0x3fffe1),
+    (23, 0x7fffee),
+    (23, 0x7fffef),
+    (20, 0xfffea),
+    (22, 0x3fffe2),
+    (22, 0x3fffe3),
+    (22, 0x3fffe4),
+    (23, 0x7ffff0),
+    (22, 0x3fffe5),
+    (22, 0x3fffe6),
+    (23, 0x7ffff1),
+    (26, 0x3ffffe0),
+    (26, 0x3ffffe1),
+    (20, 0xfffeb),
+    (19, 0x7fff1),
+    (22, 0x3fffe7),
+    (23, 0x7ffff2),
+    (22, 0x3fffe8),
+    (25, 0x1ffffec),
+    (26, 0x3ffffe2),
+    (26, 0x3ffffe3),
+    (26, 0x3ffffe4),
+    (27, 0x7ffffde),
+    (27, 0x7ffffdf),
+    (26, 0x3ffffe5),
+    (24, 0xfffff1),
+    (25, 0x1ffffed),
+    (19, 0x7fff2),
+    (21, 0x1fffe3),
+    (26, 0x3ffffe6),
+    (27, 0x7ffffe0),
+    (27, 0x7ffffe1),
+    (26, 0x3ffffe7),
+    (27, 0x7ffffe2),
+    (24, 0xfffff2),
+    (21, 0x1fffe4),
+    (21, 0x1fffe5),
+    (26, 0x3ffffe8),
+    (26, 0x3ffffe9),
+    (28, 0xffffffd),
+    (27, 0x7ffffe3),
+    (27, 0x7ffffe4),
+    (27, 0x7ffffe5),
+    (20, 0xfffec),
+    (24, 0xfffff3),
+    (20, 0xfffed),
+    (21, 0x1fffe6),
+    (22, 0x3fffe9),
+    (21, 0x1fffe7),
+    (21, 0x1fffe8),
+    (23, 0x7ffff3),
+    (22, 0x3fffea),
+    (22, 0x3fffeb),
+    (25, 0x1ffffee),
+    (25, 0x1ffffef),
+    (24, 0xfffff4),
+    (24, 0xfffff5),
+    (26, 0x3ffffea),
+    (23, 0x7ffff4),
+    (26, 0x3ffffeb),
+    (27, 0x7ffffe6),
+    (26, 0x3ffffec),
+    (26, 0x3ffffed),
+    (27, 0x7ffffe7),
+    (27, 0x7ffffe8),
+    (27, 0x7ffffe9),
+    (27, 0x7ffffea),
+    (27, 0x7ffffeb),
+    (28, 0xffffffe),
+    (27, 0x7ffffec),
+    (27, 0x7ffffed),
+    (27, 0x7ffffee),
+    (27, 0x7ffffef),
+    (27, 0x7fffff0),
+    (26, 0x3ffffee),
+    (30, 0x3fffffff),
+];
+
+// (next-state, byte, flags)
+pub const DECODE_TABLE: [[(usize, u8, u8); 16]; 256] = [
+    // 0
+    [
+        (4, 0, 0x00),
+        (5, 0, 0x00),
+        (7, 0, 0x00),
+        (8, 0, 0x00),
+        (11, 0, 0x00),
+        (12, 0, 0x00),
+        (16, 0, 0x00),
+        (19, 0, 0x00),
+        (25, 0, 0x00),
+        (28, 0, 0x00),
+        (32, 0, 0x00),
+        (35, 0, 0x00),
+        (42, 0, 0x00),
+        (49, 0, 0x00),
+        (57, 0, 0x00),
+        (64, 0, 0x01),
+    ],
+    // 1
+    [
+        (0, 48, 0x02),
+        (0, 49, 0x02),
+        (0, 50, 0x02),
+        (0, 97, 0x02),
+        (0, 99, 0x02),
+        (0, 101, 0x02),
+        (0, 105, 0x02),
+        (0, 111, 0x02),
+        (0, 115, 0x02),
+        (0, 116, 0x02),
+        (13, 0, 0x00),
+        (14, 0, 0x00),
+        (17, 0, 0x00),
+        (18, 0, 0x00),
+        (20, 0, 0x00),
+        (21, 0, 0x00),
+    ],
+    // 2
+    [
+        (1, 48, 0x02),
+        (22, 48, 0x03),
+        (1, 49, 0x02),
+        (22, 49, 0x03),
+        (1, 50, 0x02),
+        (22, 50, 0x03),
+        (1, 97, 0x02),
+        (22, 97, 0x03),
+        (1, 99, 0x02),
+        (22, 99, 0x03),
+        (1, 101, 0x02),
+        (22, 101, 0x03),
+        (1, 105, 0x02),
+        (22, 105, 0x03),
+        (1, 111, 0x02),
+        (22, 111, 0x03),
+    ],
+    // 3
+    [
+        (2, 48, 0x02),
+        (9, 48, 0x02),
+        (23, 48, 0x02),
+        (40, 48, 0x03),
+        (2, 49, 0x02),
+        (9, 49, 0x02),
+        (23, 49, 0x02),
+        (40, 49, 0x03),
+        (2, 50, 0x02),
+        (9, 50, 0x02),
+        (23, 50, 0x02),
+        (40, 50, 0x03),
+        (2, 97, 0x02),
+        (9, 97, 0x02),
+        (23, 97, 0x02),
+        (40, 97, 0x03),
+    ],
+    // 4
+    [
+        (3, 48, 0x02),
+        (6, 48, 0x02),
+        (10, 48, 0x02),
+        (15, 48, 0x02),
+        (24, 48, 0x02),
+        (31, 48, 0x02),
+        (41, 48, 0x02),
+        (56, 48, 0x03),
+        (3, 49, 0x02),
+        (6, 49, 0x02),
+        (10, 49, 0x02),
+        (15, 49, 0x02),
+        (24, 49, 0x02),
+        (31, 49, 0x02),
+        (41, 49, 0x02),
+        (56, 49, 0x03),
+    ],
+    // 5
+    [
+        (3, 50, 0x02),
+        (6, 50, 0x02),
+        (10, 50, 0x02),
+        (15, 50, 0x02),
+        (24, 50, 0x02),
+        (31, 50, 0x02),
+        (41, 50, 0x02),
+        (56, 50, 0x03),
+        (3, 97, 0x02),
+        (6, 97, 0x02),
+        (10, 97, 0x02),
+        (15, 97, 0x02),
+        (24, 97, 0x02),
+        (31, 97, 0x02),
+        (41, 97, 0x02),
+        (56, 97, 0x03),
+    ],
+    // 6
+    [
+        (2, 99, 0x02),
+        (9, 99, 0x02),
+        (23, 99, 0x02),
+        (40, 99, 0x03),
+        (2, 101, 0x02),
+        (9, 101, 0x02),
+        (23, 101, 0x02),
+        (40, 101, 0x03),
+        (2, 105, 0x02),
+        (9, 105, 0x02),
+        (23, 105, 0x02),
+        (40, 105, 0x03),
+        (2, 111, 0x02),
+        (9, 111, 0x02),
+        (23, 111, 0x02),
+        (40, 111, 0x03),
+    ],
+    // 7
+    [
+        (3, 99, 0x02),
+        (6, 99, 0x02),
+        (10, 99, 0x02),
+        (15, 99, 0x02),
+        (24, 99, 0x02),
+        (31, 99, 0x02),
+        (41, 99, 0x02),
+        (56, 99, 0x03),
+        (3, 101, 0x02),
+        (6, 101, 0x02),
+        (10, 101, 0x02),
+        (15, 101, 0x02),
+        (24, 101, 0x02),
+        (31, 101, 0x02),
+        (41, 101, 0x02),
+        (56, 101, 0x03),
+    ],
+    // 8
+    [
+        (3, 105, 0x02),
+        (6, 105, 0x02),
+        (10, 105, 0x02),
+        (15, 105, 0x02),
+        (24, 105, 0x02),
+        (31, 105, 0x02),
+        (41, 105, 0x02),
+        (56, 105, 0x03),
+        (3, 111, 0x02),
+        (6, 111, 0x02),
+        (10, 111, 0x02),
+        (15, 111, 0x02),
+        (24, 111, 0x02),
+        (31, 111, 0x02),
+        (41, 111, 0x02),
+        (56, 111, 0x03),
+    ],
+    // 9
+    [
+        (1, 115, 0x02),
+        (22, 115, 0x03),
+        (1, 116, 0x02),
+        (22, 116, 0x03),
+        (0, 32, 0x02),
+        (0, 37, 0x02),
+        (0, 45, 0x02),
+        (0, 46, 0x02),
+        (0, 47, 0x02),
+        (0, 51, 0x02),
+        (0, 52, 0x02),
+        (0, 53, 0x02),
+        (0, 54, 0x02),
+        (0, 55, 0x02),
+        (0, 56, 0x02),
+        (0, 57, 0x02),
+    ],
+    // 10
+    [
+        (2, 115, 0x02),
+        (9, 115, 0x02),
+        (23, 115, 0x02),
+        (40, 115, 0x03),
+        (2, 116, 0x02),
+        (9, 116, 0x02),
+        (23, 116, 0x02),
+        (40, 116, 0x03),
+        (1, 32, 0x02),
+        (22, 32, 0x03),
+        (1, 37, 0x02),
+        (22, 37, 0x03),
+        (1, 45, 0x02),
+        (22, 45, 0x03),
+        (1, 46, 0x02),
+        (22, 46, 0x03),
+    ],
+    // 11
+    [
+        (3, 115, 0x02),
+        (6, 115, 0x02),
+        (10, 115, 0x02),
+        (15, 115, 0x02),
+        (24, 115, 0x02),
+        (31, 115, 0x02),
+        (41, 115, 0x02),
+        (56, 115, 0x03),
+        (3, 116, 0x02),
+        (6, 116, 0x02),
+        (10, 116, 0x02),
+        (15, 116, 0x02),
+        (24, 116, 0x02),
+        (31, 116, 0x02),
+        (41, 116, 0x02),
+        (56, 116, 0x03),
+    ],
+    // 12
+    [
+        (2, 32, 0x02),
+        (9, 32, 0x02),
+        (23, 32, 0x02),
+        (40, 32, 0x03),
+        (2, 37, 0x02),
+        (9, 37, 0x02),
+        (23, 37, 0x02),
+        (40, 37, 0x03),
+        (2, 45, 0x02),
+        (9, 45, 0x02),
+        (23, 45, 0x02),
+        (40, 45, 0x03),
+        (2, 46, 0x02),
+        (9, 46, 0x02),
+        (23, 46, 0x02),
+        (40, 46, 0x03),
+    ],
+    // 13
+    [
+        (3, 32, 0x02),
+        (6, 32, 0x02),
+        (10, 32, 0x02),
+        (15, 32, 0x02),
+        (24, 32, 0x02),
+        (31, 32, 0x02),
+        (41, 32, 0x02),
+        (56, 32, 0x03),
+        (3, 37, 0x02),
+        (6, 37, 0x02),
+        (10, 37, 0x02),
+        (15, 37, 0x02),
+        (24, 37, 0x02),
+        (31, 37, 0x02),
+        (41, 37, 0x02),
+        (56, 37, 0x03),
+    ],
+    // 14
+    [
+        (3, 45, 0x02),
+        (6, 45, 0x02),
+        (10, 45, 0x02),
+        (15, 45, 0x02),
+        (24, 45, 0x02),
+        (31, 45, 0x02),
+        (41, 45, 0x02),
+        (56, 45, 0x03),
+        (3, 46, 0x02),
+        (6, 46, 0x02),
+        (10, 46, 0x02),
+        (15, 46, 0x02),
+        (24, 46, 0x02),
+        (31, 46, 0x02),
+        (41, 46, 0x02),
+        (56, 46, 0x03),
+    ],
+    // 15
+    [
+        (1, 47, 0x02),
+        (22, 47, 0x03),
+        (1, 51, 0x02),
+        (22, 51, 0x03),
+        (1, 52, 0x02),
+        (22, 52, 0x03),
+        (1, 53, 0x02),
+        (22, 53, 0x03),
+        (1, 54, 0x02),
+        (22, 54, 0x03),
+        (1, 55, 0x02),
+        (22, 55, 0x03),
+        (1, 56, 0x02),
+        (22, 56, 0x03),
+        (1, 57, 0x02),
+        (22, 57, 0x03),
+    ],
+    // 16
+    [
+        (2, 47, 0x02),
+        (9, 47, 0x02),
+        (23, 47, 0x02),
+        (40, 47, 0x03),
+        (2, 51, 0x02),
+        (9, 51, 0x02),
+        (23, 51, 0x02),
+        (40, 51, 0x03),
+        (2, 52, 0x02),
+        (9, 52, 0x02),
+        (23, 52, 0x02),
+        (40, 52, 0x03),
+        (2, 53, 0x02),
+        (9, 53, 0x02),
+        (23, 53, 0x02),
+        (40, 53, 0x03),
+    ],
+    // 17
+    [
+        (3, 47, 0x02),
+        (6, 47, 0x02),
+        (10, 47, 0x02),
+        (15, 47, 0x02),
+        (24, 47, 0x02),
+        (31, 47, 0x02),
+        (41, 47, 0x02),
+        (56, 47, 0x03),
+        (3, 51, 0x02),
+        (6, 51, 0x02),
+        (10, 51, 0x02),
+        (15, 51, 0x02),
+        (24, 51, 0x02),
+        (31, 51, 0x02),
+        (41, 51, 0x02),
+        (56, 51, 0x03),
+    ],
+    // 18
+    [
+        (3, 52, 0x02),
+        (6, 52, 0x02),
+        (10, 52, 0x02),
+        (15, 52, 0x02),
+        (24, 52, 0x02),
+        (31, 52, 0x02),
+        (41, 52, 0x02),
+        (56, 52, 0x03),
+        (3, 53, 0x02),
+        (6, 53, 0x02),
+        (10, 53, 0x02),
+        (15, 53, 0x02),
+        (24, 53, 0x02),
+        (31, 53, 0x02),
+        (41, 53, 0x02),
+        (56, 53, 0x03),
+    ],
+    // 19
+    [
+        (2, 54, 0x02),
+        (9, 54, 0x02),
+        (23, 54, 0x02),
+        (40, 54, 0x03),
+        (2, 55, 0x02),
+        (9, 55, 0x02),
+        (23, 55, 0x02),
+        (40, 55, 0x03),
+        (2, 56, 0x02),
+        (9, 56, 0x02),
+        (23, 56, 0x02),
+        (40, 56, 0x03),
+        (2, 57, 0x02),
+        (9, 57, 0x02),
+        (23, 57, 0x02),
+        (40, 57, 0x03),
+    ],
+    // 20
+    [
+        (3, 54, 0x02),
+        (6, 54, 0x02),
+        (10, 54, 0x02),
+        (15, 54, 0x02),
+        (24, 54, 0x02),
+        (31, 54, 0x02),
+        (41, 54, 0x02),
+        (56, 54, 0x03),
+        (3, 55, 0x02),
+        (6, 55, 0x02),
+        (10, 55, 0x02),
+        (15, 55, 0x02),
+        (24, 55, 0x02),
+        (31, 55, 0x02),
+        (41, 55, 0x02),
+        (56, 55, 0x03),
+    ],
+    // 21
+    [
+        (3, 56, 0x02),
+        (6, 56, 0x02),
+        (10, 56, 0x02),
+        (15, 56, 0x02),
+        (24, 56, 0x02),
+        (31, 56, 0x02),
+        (41, 56, 0x02),
+        (56, 56, 0x03),
+        (3, 57, 0x02),
+        (6, 57, 0x02),
+        (10, 57, 0x02),
+        (15, 57, 0x02),
+        (24, 57, 0x02),
+        (31, 57, 0x02),
+        (41, 57, 0x02),
+        (56, 57, 0x03),
+    ],
+    // 22
+    [
+        (26, 0, 0x00),
+        (27, 0, 0x00),
+        (29, 0, 0x00),
+        (30, 0, 0x00),
+        (33, 0, 0x00),
+        (34, 0, 0x00),
+        (36, 0, 0x00),
+        (37, 0, 0x00),
+        (43, 0, 0x00),
+        (46, 0, 0x00),
+        (50, 0, 0x00),
+        (53, 0, 0x00),
+        (58, 0, 0x00),
+        (61, 0, 0x00),
+        (65, 0, 0x00),
+        (68, 0, 0x01),
+    ],
+    // 23
+    [
+        (0, 61, 0x02),
+        (0, 65, 0x02),
+        (0, 95, 0x02),
+        (0, 98, 0x02),
+        (0, 100, 0x02),
+        (0, 102, 0x02),
+        (0, 103, 0x02),
+        (0, 104, 0x02),
+        (0, 108, 0x02),
+        (0, 109, 0x02),
+        (0, 110, 0x02),
+        (0, 112, 0x02),
+        (0, 114, 0x02),
+        (0, 117, 0x02),
+        (38, 0, 0x00),
+        (39, 0, 0x00),
+    ],
+    // 24
+    [
+        (1, 61, 0x02),
+        (22, 61, 0x03),
+        (1, 65, 0x02),
+        (22, 65, 0x03),
+        (1, 95, 0x02),
+        (22, 95, 0x03),
+        (1, 98, 0x02),
+        (22, 98, 0x03),
+        (1, 100, 0x02),
+        (22, 100, 0x03),
+        (1, 102, 0x02),
+        (22, 102, 0x03),
+        (1, 103, 0x02),
+        (22, 103, 0x03),
+        (1, 104, 0x02),
+        (22, 104, 0x03),
+    ],
+    // 25
+    [
+        (2, 61, 0x02),
+        (9, 61, 0x02),
+        (23, 61, 0x02),
+        (40, 61, 0x03),
+        (2, 65, 0x02),
+        (9, 65, 0x02),
+        (23, 65, 0x02),
+        (40, 65, 0x03),
+        (2, 95, 0x02),
+        (9, 95, 0x02),
+        (23, 95, 0x02),
+        (40, 95, 0x03),
+        (2, 98, 0x02),
+        (9, 98, 0x02),
+        (23, 98, 0x02),
+        (40, 98, 0x03),
+    ],
+    // 26
+    [
+        (3, 61, 0x02),
+        (6, 61, 0x02),
+        (10, 61, 0x02),
+        (15, 61, 0x02),
+        (24, 61, 0x02),
+        (31, 61, 0x02),
+        (41, 61, 0x02),
+        (56, 61, 0x03),
+        (3, 65, 0x02),
+        (6, 65, 0x02),
+        (10, 65, 0x02),
+        (15, 65, 0x02),
+        (24, 65, 0x02),
+        (31, 65, 0x02),
+        (41, 65, 0x02),
+        (56, 65, 0x03),
+    ],
+    // 27
+    [
+        (3, 95, 0x02),
+        (6, 95, 0x02),
+        (10, 95, 0x02),
+        (15, 95, 0x02),
+        (24, 95, 0x02),
+        (31, 95, 0x02),
+        (41, 95, 0x02),
+        (56, 95, 0x03),
+        (3, 98, 0x02),
+        (6, 98, 0x02),
+        (10, 98, 0x02),
+        (15, 98, 0x02),
+        (24, 98, 0x02),
+        (31, 98, 0x02),
+        (41, 98, 0x02),
+        (56, 98, 0x03),
+    ],
+    // 28
+    [
+        (2, 100, 0x02),
+        (9, 100, 0x02),
+        (23, 100, 0x02),
+        (40, 100, 0x03),
+        (2, 102, 0x02),
+        (9, 102, 0x02),
+        (23, 102, 0x02),
+        (40, 102, 0x03),
+        (2, 103, 0x02),
+        (9, 103, 0x02),
+        (23, 103, 0x02),
+        (40, 103, 0x03),
+        (2, 104, 0x02),
+        (9, 104, 0x02),
+        (23, 104, 0x02),
+        (40, 104, 0x03),
+    ],
+    // 29
+    [
+        (3, 100, 0x02),
+        (6, 100, 0x02),
+        (10, 100, 0x02),
+        (15, 100, 0x02),
+        (24, 100, 0x02),
+        (31, 100, 0x02),
+        (41, 100, 0x02),
+        (56, 100, 0x03),
+        (3, 102, 0x02),
+        (6, 102, 0x02),
+        (10, 102, 0x02),
+        (15, 102, 0x02),
+        (24, 102, 0x02),
+        (31, 102, 0x02),
+        (41, 102, 0x02),
+        (56, 102, 0x03),
+    ],
+    // 30
+    [
+        (3, 103, 0x02),
+        (6, 103, 0x02),
+        (10, 103, 0x02),
+        (15, 103, 0x02),
+        (24, 103, 0x02),
+        (31, 103, 0x02),
+        (41, 103, 0x02),
+        (56, 103, 0x03),
+        (3, 104, 0x02),
+        (6, 104, 0x02),
+        (10, 104, 0x02),
+        (15, 104, 0x02),
+        (24, 104, 0x02),
+        (31, 104, 0x02),
+        (41, 104, 0x02),
+        (56, 104, 0x03),
+    ],
+    // 31
+    [
+        (1, 108, 0x02),
+        (22, 108, 0x03),
+        (1, 109, 0x02),
+        (22, 109, 0x03),
+        (1, 110, 0x02),
+        (22, 110, 0x03),
+        (1, 112, 0x02),
+        (22, 112, 0x03),
+        (1, 114, 0x02),
+        (22, 114, 0x03),
+        (1, 117, 0x02),
+        (22, 117, 0x03),
+        (0, 58, 0x02),
+        (0, 66, 0x02),
+        (0, 67, 0x02),
+        (0, 68, 0x02),
+    ],
+    // 32
+    [
+        (2, 108, 0x02),
+        (9, 108, 0x02),
+        (23, 108, 0x02),
+        (40, 108, 0x03),
+        (2, 109, 0x02),
+        (9, 109, 0x02),
+        (23, 109, 0x02),
+        (40, 109, 0x03),
+        (2, 110, 0x02),
+        (9, 110, 0x02),
+        (23, 110, 0x02),
+        (40, 110, 0x03),
+        (2, 112, 0x02),
+        (9, 112, 0x02),
+        (23, 112, 0x02),
+        (40, 112, 0x03),
+    ],
+    // 33
+    [
+        (3, 108, 0x02),
+        (6, 108, 0x02),
+        (10, 108, 0x02),
+        (15, 108, 0x02),
+        (24, 108, 0x02),
+        (31, 108, 0x02),
+        (41, 108, 0x02),
+        (56, 108, 0x03),
+        (3, 109, 0x02),
+        (6, 109, 0x02),
+        (10, 109, 0x02),
+        (15, 109, 0x02),
+        (24, 109, 0x02),
+        (31, 109, 0x02),
+        (41, 109, 0x02),
+        (56, 109, 0x03),
+    ],
+    // 34
+    [
+        (3, 110, 0x02),
+        (6, 110, 0x02),
+        (10, 110, 0x02),
+        (15, 110, 0x02),
+        (24, 110, 0x02),
+        (31, 110, 0x02),
+        (41, 110, 0x02),
+        (56, 110, 0x03),
+        (3, 112, 0x02),
+        (6, 112, 0x02),
+        (10, 112, 0x02),
+        (15, 112, 0x02),
+        (24, 112, 0x02),
+        (31, 112, 0x02),
+        (41, 112, 0x02),
+        (56, 112, 0x03),
+    ],
+    // 35
+    [
+        (2, 114, 0x02),
+        (9, 114, 0x02),
+        (23, 114, 0x02),
+        (40, 114, 0x03),
+        (2, 117, 0x02),
+        (9, 117, 0x02),
+        (23, 117, 0x02),
+        (40, 117, 0x03),
+        (1, 58, 0x02),
+        (22, 58, 0x03),
+        (1, 66, 0x02),
+        (22, 66, 0x03),
+        (1, 67, 0x02),
+        (22, 67, 0x03),
+        (1, 68, 0x02),
+        (22, 68, 0x03),
+    ],
+    // 36
+    [
+        (3, 114, 0x02),
+        (6, 114, 0x02),
+        (10, 114, 0x02),
+        (15, 114, 0x02),
+        (24, 114, 0x02),
+        (31, 114, 0x02),
+        (41, 114, 0x02),
+        (56, 114, 0x03),
+        (3, 117, 0x02),
+        (6, 117, 0x02),
+        (10, 117, 0x02),
+        (15, 117, 0x02),
+        (24, 117, 0x02),
+        (31, 117, 0x02),
+        (41, 117, 0x02),
+        (56, 117, 0x03),
+    ],
+    // 37
+    [
+        (2, 58, 0x02),
+        (9, 58, 0x02),
+        (23, 58, 0x02),
+        (40, 58, 0x03),
+        (2, 66, 0x02),
+        (9, 66, 0x02),
+        (23, 66, 0x02),
+        (40, 66, 0x03),
+        (2, 67, 0x02),
+        (9, 67, 0x02),
+        (23, 67, 0x02),
+        (40, 67, 0x03),
+        (2, 68, 0x02),
+        (9, 68, 0x02),
+        (23, 68, 0x02),
+        (40, 68, 0x03),
+    ],
+    // 38
+    [
+        (3, 58, 0x02),
+        (6, 58, 0x02),
+        (10, 58, 0x02),
+        (15, 58, 0x02),
+        (24, 58, 0x02),
+        (31, 58, 0x02),
+        (41, 58, 0x02),
+        (56, 58, 0x03),
+        (3, 66, 0x02),
+        (6, 66, 0x02),
+        (10, 66, 0x02),
+        (15, 66, 0x02),
+        (24, 66, 0x02),
+        (31, 66, 0x02),
+        (41, 66, 0x02),
+        (56, 66, 0x03),
+    ],
+    // 39
+    [
+        (3, 67, 0x02),
+        (6, 67, 0x02),
+        (10, 67, 0x02),
+        (15, 67, 0x02),
+        (24, 67, 0x02),
+        (31, 67, 0x02),
+        (41, 67, 0x02),
+        (56, 67, 0x03),
+        (3, 68, 0x02),
+        (6, 68, 0x02),
+        (10, 68, 0x02),
+        (15, 68, 0x02),
+        (24, 68, 0x02),
+        (31, 68, 0x02),
+        (41, 68, 0x02),
+        (56, 68, 0x03),
+    ],
+    // 40
+    [
+        (44, 0, 0x00),
+        (45, 0, 0x00),
+        (47, 0, 0x00),
+        (48, 0, 0x00),
+        (51, 0, 0x00),
+        (52, 0, 0x00),
+        (54, 0, 0x00),
+        (55, 0, 0x00),
+        (59, 0, 0x00),
+        (60, 0, 0x00),
+        (62, 0, 0x00),
+        (63, 0, 0x00),
+        (66, 0, 0x00),
+        (67, 0, 0x00),
+        (69, 0, 0x00),
+        (72, 0, 0x01),
+    ],
+    // 41
+    [
+        (0, 69, 0x02),
+        (0, 70, 0x02),
+        (0, 71, 0x02),
+        (0, 72, 0x02),
+        (0, 73, 0x02),
+        (0, 74, 0x02),
+        (0, 75, 0x02),
+        (0, 76, 0x02),
+        (0, 77, 0x02),
+        (0, 78, 0x02),
+        (0, 79, 0x02),
+        (0, 80, 0x02),
+        (0, 81, 0x02),
+        (0, 82, 0x02),
+        (0, 83, 0x02),
+        (0, 84, 0x02),
+    ],
+    // 42
+    [
+        (1, 69, 0x02),
+        (22, 69, 0x03),
+        (1, 70, 0x02),
+        (22, 70, 0x03),
+        (1, 71, 0x02),
+        (22, 71, 0x03),
+        (1, 72, 0x02),
+        (22, 72, 0x03),
+        (1, 73, 0x02),
+        (22, 73, 0x03),
+        (1, 74, 0x02),
+        (22, 74, 0x03),
+        (1, 75, 0x02),
+        (22, 75, 0x03),
+        (1, 76, 0x02),
+        (22, 76, 0x03),
+    ],
+    // 43
+    [
+        (2, 69, 0x02),
+        (9, 69, 0x02),
+        (23, 69, 0x02),
+        (40, 69, 0x03),
+        (2, 70, 0x02),
+        (9, 70, 0x02),
+        (23, 70, 0x02),
+        (40, 70, 0x03),
+        (2, 71, 0x02),
+        (9, 71, 0x02),
+        (23, 71, 0x02),
+        (40, 71, 0x03),
+        (2, 72, 0x02),
+        (9, 72, 0x02),
+        (23, 72, 0x02),
+        (40, 72, 0x03),
+    ],
+    // 44
+    [
+        (3, 69, 0x02),
+        (6, 69, 0x02),
+        (10, 69, 0x02),
+        (15, 69, 0x02),
+        (24, 69, 0x02),
+        (31, 69, 0x02),
+        (41, 69, 0x02),
+        (56, 69, 0x03),
+        (3, 70, 0x02),
+        (6, 70, 0x02),
+        (10, 70, 0x02),
+        (15, 70, 0x02),
+        (24, 70, 0x02),
+        (31, 70, 0x02),
+        (41, 70, 0x02),
+        (56, 70, 0x03),
+    ],
+    // 45
+    [
+        (3, 71, 0x02),
+        (6, 71, 0x02),
+        (10, 71, 0x02),
+        (15, 71, 0x02),
+        (24, 71, 0x02),
+        (31, 71, 0x02),
+        (41, 71, 0x02),
+        (56, 71, 0x03),
+        (3, 72, 0x02),
+        (6, 72, 0x02),
+        (10, 72, 0x02),
+        (15, 72, 0x02),
+        (24, 72, 0x02),
+        (31, 72, 0x02),
+        (41, 72, 0x02),
+        (56, 72, 0x03),
+    ],
+    // 46
+    [
+        (2, 73, 0x02),
+        (9, 73, 0x02),
+        (23, 73, 0x02),
+        (40, 73, 0x03),
+        (2, 74, 0x02),
+        (9, 74, 0x02),
+        (23, 74, 0x02),
+        (40, 74, 0x03),
+        (2, 75, 0x02),
+        (9, 75, 0x02),
+        (23, 75, 0x02),
+        (40, 75, 0x03),
+        (2, 76, 0x02),
+        (9, 76, 0x02),
+        (23, 76, 0x02),
+        (40, 76, 0x03),
+    ],
+    // 47
+    [
+        (3, 73, 0x02),
+        (6, 73, 0x02),
+        (10, 73, 0x02),
+        (15, 73, 0x02),
+        (24, 73, 0x02),
+        (31, 73, 0x02),
+        (41, 73, 0x02),
+        (56, 73, 0x03),
+        (3, 74, 0x02),
+        (6, 74, 0x02),
+        (10, 74, 0x02),
+        (15, 74, 0x02),
+        (24, 74, 0x02),
+        (31, 74, 0x02),
+        (41, 74, 0x02),
+        (56, 74, 0x03),
+    ],
+    // 48
+    [
+        (3, 75, 0x02),
+        (6, 75, 0x02),
+        (10, 75, 0x02),
+        (15, 75, 0x02),
+        (24, 75, 0x02),
+        (31, 75, 0x02),
+        (41, 75, 0x02),
+        (56, 75, 0x03),
+        (3, 76, 0x02),
+        (6, 76, 0x02),
+        (10, 76, 0x02),
+        (15, 76, 0x02),
+        (24, 76, 0x02),
+        (31, 76, 0x02),
+        (41, 76, 0x02),
+        (56, 76, 0x03),
+    ],
+    // 49
+    [
+        (1, 77, 0x02),
+        (22, 77, 0x03),
+        (1, 78, 0x02),
+        (22, 78, 0x03),
+        (1, 79, 0x02),
+        (22, 79, 0x03),
+        (1, 80, 0x02),
+        (22, 80, 0x03),
+        (1, 81, 0x02),
+        (22, 81, 0x03),
+        (1, 82, 0x02),
+        (22, 82, 0x03),
+        (1, 83, 0x02),
+        (22, 83, 0x03),
+        (1, 84, 0x02),
+        (22, 84, 0x03),
+    ],
+    // 50
+    [
+        (2, 77, 0x02),
+        (9, 77, 0x02),
+        (23, 77, 0x02),
+        (40, 77, 0x03),
+        (2, 78, 0x02),
+        (9, 78, 0x02),
+        (23, 78, 0x02),
+        (40, 78, 0x03),
+        (2, 79, 0x02),
+        (9, 79, 0x02),
+        (23, 79, 0x02),
+        (40, 79, 0x03),
+        (2, 80, 0x02),
+        (9, 80, 0x02),
+        (23, 80, 0x02),
+        (40, 80, 0x03),
+    ],
+    // 51
+    [
+        (3, 77, 0x02),
+        (6, 77, 0x02),
+        (10, 77, 0x02),
+        (15, 77, 0x02),
+        (24, 77, 0x02),
+        (31, 77, 0x02),
+        (41, 77, 0x02),
+        (56, 77, 0x03),
+        (3, 78, 0x02),
+        (6, 78, 0x02),
+        (10, 78, 0x02),
+        (15, 78, 0x02),
+        (24, 78, 0x02),
+        (31, 78, 0x02),
+        (41, 78, 0x02),
+        (56, 78, 0x03),
+    ],
+    // 52
+    [
+        (3, 79, 0x02),
+        (6, 79, 0x02),
+        (10, 79, 0x02),
+        (15, 79, 0x02),
+        (24, 79, 0x02),
+        (31, 79, 0x02),
+        (41, 79, 0x02),
+        (56, 79, 0x03),
+        (3, 80, 0x02),
+        (6, 80, 0x02),
+        (10, 80, 0x02),
+        (15, 80, 0x02),
+        (24, 80, 0x02),
+        (31, 80, 0x02),
+        (41, 80, 0x02),
+        (56, 80, 0x03),
+    ],
+    // 53
+    [
+        (2, 81, 0x02),
+        (9, 81, 0x02),
+        (23, 81, 0x02),
+        (40, 81, 0x03),
+        (2, 82, 0x02),
+        (9, 82, 0x02),
+        (23, 82, 0x02),
+        (40, 82, 0x03),
+        (2, 83, 0x02),
+        (9, 83, 0x02),
+        (23, 83, 0x02),
+        (40, 83, 0x03),
+        (2, 84, 0x02),
+        (9, 84, 0x02),
+        (23, 84, 0x02),
+        (40, 84, 0x03),
+    ],
+    // 54
+    [
+        (3, 81, 0x02),
+        (6, 81, 0x02),
+        (10, 81, 0x02),
+        (15, 81, 0x02),
+        (24, 81, 0x02),
+        (31, 81, 0x02),
+        (41, 81, 0x02),
+        (56, 81, 0x03),
+        (3, 82, 0x02),
+        (6, 82, 0x02),
+        (10, 82, 0x02),
+        (15, 82, 0x02),
+        (24, 82, 0x02),
+        (31, 82, 0x02),
+        (41, 82, 0x02),
+        (56, 82, 0x03),
+    ],
+    // 55
+    [
+        (3, 83, 0x02),
+        (6, 83, 0x02),
+        (10, 83, 0x02),
+        (15, 83, 0x02),
+        (24, 83, 0x02),
+        (31, 83, 0x02),
+        (41, 83, 0x02),
+        (56, 83, 0x03),
+        (3, 84, 0x02),
+        (6, 84, 0x02),
+        (10, 84, 0x02),
+        (15, 84, 0x02),
+        (24, 84, 0x02),
+        (31, 84, 0x02),
+        (41, 84, 0x02),
+        (56, 84, 0x03),
+    ],
+    // 56
+    [
+        (0, 85, 0x02),
+        (0, 86, 0x02),
+        (0, 87, 0x02),
+        (0, 89, 0x02),
+        (0, 106, 0x02),
+        (0, 107, 0x02),
+        (0, 113, 0x02),
+        (0, 118, 0x02),
+        (0, 119, 0x02),
+        (0, 120, 0x02),
+        (0, 121, 0x02),
+        (0, 122, 0x02),
+        (70, 0, 0x00),
+        (71, 0, 0x00),
+        (73, 0, 0x00),
+        (74, 0, 0x01),
+    ],
+    // 57
+    [
+        (1, 85, 0x02),
+        (22, 85, 0x03),
+        (1, 86, 0x02),
+        (22, 86, 0x03),
+        (1, 87, 0x02),
+        (22, 87, 0x03),
+        (1, 89, 0x02),
+        (22, 89, 0x03),
+        (1, 106, 0x02),
+        (22, 106, 0x03),
+        (1, 107, 0x02),
+        (22, 107, 0x03),
+        (1, 113, 0x02),
+        (22, 113, 0x03),
+        (1, 118, 0x02),
+        (22, 118, 0x03),
+    ],
+    // 58
+    [
+        (2, 85, 0x02),
+        (9, 85, 0x02),
+        (23, 85, 0x02),
+        (40, 85, 0x03),
+        (2, 86, 0x02),
+        (9, 86, 0x02),
+        (23, 86, 0x02),
+        (40, 86, 0x03),
+        (2, 87, 0x02),
+        (9, 87, 0x02),
+        (23, 87, 0x02),
+        (40, 87, 0x03),
+        (2, 89, 0x02),
+        (9, 89, 0x02),
+        (23, 89, 0x02),
+        (40, 89, 0x03),
+    ],
+    // 59
+    [
+        (3, 85, 0x02),
+        (6, 85, 0x02),
+        (10, 85, 0x02),
+        (15, 85, 0x02),
+        (24, 85, 0x02),
+        (31, 85, 0x02),
+        (41, 85, 0x02),
+        (56, 85, 0x03),
+        (3, 86, 0x02),
+        (6, 86, 0x02),
+        (10, 86, 0x02),
+        (15, 86, 0x02),
+        (24, 86, 0x02),
+        (31, 86, 0x02),
+        (41, 86, 0x02),
+        (56, 86, 0x03),
+    ],
+    // 60
+    [
+        (3, 87, 0x02),
+        (6, 87, 0x02),
+        (10, 87, 0x02),
+        (15, 87, 0x02),
+        (24, 87, 0x02),
+        (31, 87, 0x02),
+        (41, 87, 0x02),
+        (56, 87, 0x03),
+        (3, 89, 0x02),
+        (6, 89, 0x02),
+        (10, 89, 0x02),
+        (15, 89, 0x02),
+        (24, 89, 0x02),
+        (31, 89, 0x02),
+        (41, 89, 0x02),
+        (56, 89, 0x03),
+    ],
+    // 61
+    [
+        (2, 106, 0x02),
+        (9, 106, 0x02),
+        (23, 106, 0x02),
+        (40, 106, 0x03),
+        (2, 107, 0x02),
+        (9, 107, 0x02),
+        (23, 107, 0x02),
+        (40, 107, 0x03),
+        (2, 113, 0x02),
+        (9, 113, 0x02),
+        (23, 113, 0x02),
+        (40, 113, 0x03),
+        (2, 118, 0x02),
+        (9, 118, 0x02),
+        (23, 118, 0x02),
+        (40, 118, 0x03),
+    ],
+    // 62
+    [
+        (3, 106, 0x02),
+        (6, 106, 0x02),
+        (10, 106, 0x02),
+        (15, 106, 0x02),
+        (24, 106, 0x02),
+        (31, 106, 0x02),
+        (41, 106, 0x02),
+        (56, 106, 0x03),
+        (3, 107, 0x02),
+        (6, 107, 0x02),
+        (10, 107, 0x02),
+        (15, 107, 0x02),
+        (24, 107, 0x02),
+        (31, 107, 0x02),
+        (41, 107, 0x02),
+        (56, 107, 0x03),
+    ],
+    // 63
+    [
+        (3, 113, 0x02),
+        (6, 113, 0x02),
+        (10, 113, 0x02),
+        (15, 113, 0x02),
+        (24, 113, 0x02),
+        (31, 113, 0x02),
+        (41, 113, 0x02),
+        (56, 113, 0x03),
+        (3, 118, 0x02),
+        (6, 118, 0x02),
+        (10, 118, 0x02),
+        (15, 118, 0x02),
+        (24, 118, 0x02),
+        (31, 118, 0x02),
+        (41, 118, 0x02),
+        (56, 118, 0x03),
+    ],
+    // 64
+    [
+        (1, 119, 0x02),
+        (22, 119, 0x03),
+        (1, 120, 0x02),
+        (22, 120, 0x03),
+        (1, 121, 0x02),
+        (22, 121, 0x03),
+        (1, 122, 0x02),
+        (22, 122, 0x03),
+        (0, 38, 0x02),
+        (0, 42, 0x02),
+        (0, 44, 0x02),
+        (0, 59, 0x02),
+        (0, 88, 0x02),
+        (0, 90, 0x02),
+        (75, 0, 0x00),
+        (78, 0, 0x00),
+    ],
+    // 65
+    [
+        (2, 119, 0x02),
+        (9, 119, 0x02),
+        (23, 119, 0x02),
+        (40, 119, 0x03),
+        (2, 120, 0x02),
+        (9, 120, 0x02),
+        (23, 120, 0x02),
+        (40, 120, 0x03),
+        (2, 121, 0x02),
+        (9, 121, 0x02),
+        (23, 121, 0x02),
+        (40, 121, 0x03),
+        (2, 122, 0x02),
+        (9, 122, 0x02),
+        (23, 122, 0x02),
+        (40, 122, 0x03),
+    ],
+    // 66
+    [
+        (3, 119, 0x02),
+        (6, 119, 0x02),
+        (10, 119, 0x02),
+        (15, 119, 0x02),
+        (24, 119, 0x02),
+        (31, 119, 0x02),
+        (41, 119, 0x02),
+        (56, 119, 0x03),
+        (3, 120, 0x02),
+        (6, 120, 0x02),
+        (10, 120, 0x02),
+        (15, 120, 0x02),
+        (24, 120, 0x02),
+        (31, 120, 0x02),
+        (41, 120, 0x02),
+        (56, 120, 0x03),
+    ],
+    // 67
+    [
+        (3, 121, 0x02),
+        (6, 121, 0x02),
+        (10, 121, 0x02),
+        (15, 121, 0x02),
+        (24, 121, 0x02),
+        (31, 121, 0x02),
+        (41, 121, 0x02),
+        (56, 121, 0x03),
+        (3, 122, 0x02),
+        (6, 122, 0x02),
+        (10, 122, 0x02),
+        (15, 122, 0x02),
+        (24, 122, 0x02),
+        (31, 122, 0x02),
+        (41, 122, 0x02),
+        (56, 122, 0x03),
+    ],
+    // 68
+    [
+        (1, 38, 0x02),
+        (22, 38, 0x03),
+        (1, 42, 0x02),
+        (22, 42, 0x03),
+        (1, 44, 0x02),
+        (22, 44, 0x03),
+        (1, 59, 0x02),
+        (22, 59, 0x03),
+        (1, 88, 0x02),
+        (22, 88, 0x03),
+        (1, 90, 0x02),
+        (22, 90, 0x03),
+        (76, 0, 0x00),
+        (77, 0, 0x00),
+        (79, 0, 0x00),
+        (81, 0, 0x00),
+    ],
+    // 69
+    [
+        (2, 38, 0x02),
+        (9, 38, 0x02),
+        (23, 38, 0x02),
+        (40, 38, 0x03),
+        (2, 42, 0x02),
+        (9, 42, 0x02),
+        (23, 42, 0x02),
+        (40, 42, 0x03),
+        (2, 44, 0x02),
+        (9, 44, 0x02),
+        (23, 44, 0x02),
+        (40, 44, 0x03),
+        (2, 59, 0x02),
+        (9, 59, 0x02),
+        (23, 59, 0x02),
+        (40, 59, 0x03),
+    ],
+    // 70
+    [
+        (3, 38, 0x02),
+        (6, 38, 0x02),
+        (10, 38, 0x02),
+        (15, 38, 0x02),
+        (24, 38, 0x02),
+        (31, 38, 0x02),
+        (41, 38, 0x02),
+        (56, 38, 0x03),
+        (3, 42, 0x02),
+        (6, 42, 0x02),
+        (10, 42, 0x02),
+        (15, 42, 0x02),
+        (24, 42, 0x02),
+        (31, 42, 0x02),
+        (41, 42, 0x02),
+        (56, 42, 0x03),
+    ],
+    // 71
+    [
+        (3, 44, 0x02),
+        (6, 44, 0x02),
+        (10, 44, 0x02),
+        (15, 44, 0x02),
+        (24, 44, 0x02),
+        (31, 44, 0x02),
+        (41, 44, 0x02),
+        (56, 44, 0x03),
+        (3, 59, 0x02),
+        (6, 59, 0x02),
+        (10, 59, 0x02),
+        (15, 59, 0x02),
+        (24, 59, 0x02),
+        (31, 59, 0x02),
+        (41, 59, 0x02),
+        (56, 59, 0x03),
+    ],
+    // 72
+    [
+        (2, 88, 0x02),
+        (9, 88, 0x02),
+        (23, 88, 0x02),
+        (40, 88, 0x03),
+        (2, 90, 0x02),
+        (9, 90, 0x02),
+        (23, 90, 0x02),
+        (40, 90, 0x03),
+        (0, 33, 0x02),
+        (0, 34, 0x02),
+        (0, 40, 0x02),
+        (0, 41, 0x02),
+        (0, 63, 0x02),
+        (80, 0, 0x00),
+        (82, 0, 0x00),
+        (84, 0, 0x00),
+    ],
+    // 73
+    [
+        (3, 88, 0x02),
+        (6, 88, 0x02),
+        (10, 88, 0x02),
+        (15, 88, 0x02),
+        (24, 88, 0x02),
+        (31, 88, 0x02),
+        (41, 88, 0x02),
+        (56, 88, 0x03),
+        (3, 90, 0x02),
+        (6, 90, 0x02),
+        (10, 90, 0x02),
+        (15, 90, 0x02),
+        (24, 90, 0x02),
+        (31, 90, 0x02),
+        (41, 90, 0x02),
+        (56, 90, 0x03),
+    ],
+    // 74
+    [
+        (1, 33, 0x02),
+        (22, 33, 0x03),
+        (1, 34, 0x02),
+        (22, 34, 0x03),
+        (1, 40, 0x02),
+        (22, 40, 0x03),
+        (1, 41, 0x02),
+        (22, 41, 0x03),
+        (1, 63, 0x02),
+        (22, 63, 0x03),
+        (0, 39, 0x02),
+        (0, 43, 0x02),
+        (0, 124, 0x02),
+        (83, 0, 0x00),
+        (85, 0, 0x00),
+        (88, 0, 0x00),
+    ],
+    // 75
+    [
+        (2, 33, 0x02),
+        (9, 33, 0x02),
+        (23, 33, 0x02),
+        (40, 33, 0x03),
+        (2, 34, 0x02),
+        (9, 34, 0x02),
+        (23, 34, 0x02),
+        (40, 34, 0x03),
+        (2, 40, 0x02),
+        (9, 40, 0x02),
+        (23, 40, 0x02),
+        (40, 40, 0x03),
+        (2, 41, 0x02),
+        (9, 41, 0x02),
+        (23, 41, 0x02),
+        (40, 41, 0x03),
+    ],
+    // 76
+    [
+        (3, 33, 0x02),
+        (6, 33, 0x02),
+        (10, 33, 0x02),
+        (15, 33, 0x02),
+        (24, 33, 0x02),
+        (31, 33, 0x02),
+        (41, 33, 0x02),
+        (56, 33, 0x03),
+        (3, 34, 0x02),
+        (6, 34, 0x02),
+        (10, 34, 0x02),
+        (15, 34, 0x02),
+        (24, 34, 0x02),
+        (31, 34, 0x02),
+        (41, 34, 0x02),
+        (56, 34, 0x03),
+    ],
+    // 77
+    [
+        (3, 40, 0x02),
+        (6, 40, 0x02),
+        (10, 40, 0x02),
+        (15, 40, 0x02),
+        (24, 40, 0x02),
+        (31, 40, 0x02),
+        (41, 40, 0x02),
+        (56, 40, 0x03),
+        (3, 41, 0x02),
+        (6, 41, 0x02),
+        (10, 41, 0x02),
+        (15, 41, 0x02),
+        (24, 41, 0x02),
+        (31, 41, 0x02),
+        (41, 41, 0x02),
+        (56, 41, 0x03),
+    ],
+    // 78
+    [
+        (2, 63, 0x02),
+        (9, 63, 0x02),
+        (23, 63, 0x02),
+        (40, 63, 0x03),
+        (1, 39, 0x02),
+        (22, 39, 0x03),
+        (1, 43, 0x02),
+        (22, 43, 0x03),
+        (1, 124, 0x02),
+        (22, 124, 0x03),
+        (0, 35, 0x02),
+        (0, 62, 0x02),
+        (86, 0, 0x00),
+        (87, 0, 0x00),
+        (89, 0, 0x00),
+        (90, 0, 0x00),
+    ],
+    // 79
+    [
+        (3, 63, 0x02),
+        (6, 63, 0x02),
+        (10, 63, 0x02),
+        (15, 63, 0x02),
+        (24, 63, 0x02),
+        (31, 63, 0x02),
+        (41, 63, 0x02),
+        (56, 63, 0x03),
+        (2, 39, 0x02),
+        (9, 39, 0x02),
+        (23, 39, 0x02),
+        (40, 39, 0x03),
+        (2, 43, 0x02),
+        (9, 43, 0x02),
+        (23, 43, 0x02),
+        (40, 43, 0x03),
+    ],
+    // 80
+    [
+        (3, 39, 0x02),
+        (6, 39, 0x02),
+        (10, 39, 0x02),
+        (15, 39, 0x02),
+        (24, 39, 0x02),
+        (31, 39, 0x02),
+        (41, 39, 0x02),
+        (56, 39, 0x03),
+        (3, 43, 0x02),
+        (6, 43, 0x02),
+        (10, 43, 0x02),
+        (15, 43, 0x02),
+        (24, 43, 0x02),
+        (31, 43, 0x02),
+        (41, 43, 0x02),
+        (56, 43, 0x03),
+    ],
+    // 81
+    [
+        (2, 124, 0x02),
+        (9, 124, 0x02),
+        (23, 124, 0x02),
+        (40, 124, 0x03),
+        (1, 35, 0x02),
+        (22, 35, 0x03),
+        (1, 62, 0x02),
+        (22, 62, 0x03),
+        (0, 0, 0x02),
+        (0, 36, 0x02),
+        (0, 64, 0x02),
+        (0, 91, 0x02),
+        (0, 93, 0x02),
+        (0, 126, 0x02),
+        (91, 0, 0x00),
+        (92, 0, 0x00),
+    ],
+    // 82
+    [
+        (3, 124, 0x02),
+        (6, 124, 0x02),
+        (10, 124, 0x02),
+        (15, 124, 0x02),
+        (24, 124, 0x02),
+        (31, 124, 0x02),
+        (41, 124, 0x02),
+        (56, 124, 0x03),
+        (2, 35, 0x02),
+        (9, 35, 0x02),
+        (23, 35, 0x02),
+        (40, 35, 0x03),
+        (2, 62, 0x02),
+        (9, 62, 0x02),
+        (23, 62, 0x02),
+        (40, 62, 0x03),
+    ],
+    // 83
+    [
+        (3, 35, 0x02),
+        (6, 35, 0x02),
+        (10, 35, 0x02),
+        (15, 35, 0x02),
+        (24, 35, 0x02),
+        (31, 35, 0x02),
+        (41, 35, 0x02),
+        (56, 35, 0x03),
+        (3, 62, 0x02),
+        (6, 62, 0x02),
+        (10, 62, 0x02),
+        (15, 62, 0x02),
+        (24, 62, 0x02),
+        (31, 62, 0x02),
+        (41, 62, 0x02),
+        (56, 62, 0x03),
+    ],
+    // 84
+    [
+        (1, 0, 0x02),
+        (22, 0, 0x03),
+        (1, 36, 0x02),
+        (22, 36, 0x03),
+        (1, 64, 0x02),
+        (22, 64, 0x03),
+        (1, 91, 0x02),
+        (22, 91, 0x03),
+        (1, 93, 0x02),
+        (22, 93, 0x03),
+        (1, 126, 0x02),
+        (22, 126, 0x03),
+        (0, 94, 0x02),
+        (0, 125, 0x02),
+        (93, 0, 0x00),
+        (94, 0, 0x00),
+    ],
+    // 85
+    [
+        (2, 0, 0x02),
+        (9, 0, 0x02),
+        (23, 0, 0x02),
+        (40, 0, 0x03),
+        (2, 36, 0x02),
+        (9, 36, 0x02),
+        (23, 36, 0x02),
+        (40, 36, 0x03),
+        (2, 64, 0x02),
+        (9, 64, 0x02),
+        (23, 64, 0x02),
+        (40, 64, 0x03),
+        (2, 91, 0x02),
+        (9, 91, 0x02),
+        (23, 91, 0x02),
+        (40, 91, 0x03),
+    ],
+    // 86
+    [
+        (3, 0, 0x02),
+        (6, 0, 0x02),
+        (10, 0, 0x02),
+        (15, 0, 0x02),
+        (24, 0, 0x02),
+        (31, 0, 0x02),
+        (41, 0, 0x02),
+        (56, 0, 0x03),
+        (3, 36, 0x02),
+        (6, 36, 0x02),
+        (10, 36, 0x02),
+        (15, 36, 0x02),
+        (24, 36, 0x02),
+        (31, 36, 0x02),
+        (41, 36, 0x02),
+        (56, 36, 0x03),
+    ],
+    // 87
+    [
+        (3, 64, 0x02),
+        (6, 64, 0x02),
+        (10, 64, 0x02),
+        (15, 64, 0x02),
+        (24, 64, 0x02),
+        (31, 64, 0x02),
+        (41, 64, 0x02),
+        (56, 64, 0x03),
+        (3, 91, 0x02),
+        (6, 91, 0x02),
+        (10, 91, 0x02),
+        (15, 91, 0x02),
+        (24, 91, 0x02),
+        (31, 91, 0x02),
+        (41, 91, 0x02),
+        (56, 91, 0x03),
+    ],
+    // 88
+    [
+        (2, 93, 0x02),
+        (9, 93, 0x02),
+        (23, 93, 0x02),
+        (40, 93, 0x03),
+        (2, 126, 0x02),
+        (9, 126, 0x02),
+        (23, 126, 0x02),
+        (40, 126, 0x03),
+        (1, 94, 0x02),
+        (22, 94, 0x03),
+        (1, 125, 0x02),
+        (22, 125, 0x03),
+        (0, 60, 0x02),
+        (0, 96, 0x02),
+        (0, 123, 0x02),
+        (95, 0, 0x00),
+    ],
+    // 89
+    [
+        (3, 93, 0x02),
+        (6, 93, 0x02),
+        (10, 93, 0x02),
+        (15, 93, 0x02),
+        (24, 93, 0x02),
+        (31, 93, 0x02),
+        (41, 93, 0x02),
+        (56, 93, 0x03),
+        (3, 126, 0x02),
+        (6, 126, 0x02),
+        (10, 126, 0x02),
+        (15, 126, 0x02),
+        (24, 126, 0x02),
+        (31, 126, 0x02),
+        (41, 126, 0x02),
+        (56, 126, 0x03),
+    ],
+    // 90
+    [
+        (2, 94, 0x02),
+        (9, 94, 0x02),
+        (23, 94, 0x02),
+        (40, 94, 0x03),
+        (2, 125, 0x02),
+        (9, 125, 0x02),
+        (23, 125, 0x02),
+        (40, 125, 0x03),
+        (1, 60, 0x02),
+        (22, 60, 0x03),
+        (1, 96, 0x02),
+        (22, 96, 0x03),
+        (1, 123, 0x02),
+        (22, 123, 0x03),
+        (96, 0, 0x00),
+        (110, 0, 0x00),
+    ],
+    // 91
+    [
+        (3, 94, 0x02),
+        (6, 94, 0x02),
+        (10, 94, 0x02),
+        (15, 94, 0x02),
+        (24, 94, 0x02),
+        (31, 94, 0x02),
+        (41, 94, 0x02),
+        (56, 94, 0x03),
+        (3, 125, 0x02),
+        (6, 125, 0x02),
+        (10, 125, 0x02),
+        (15, 125, 0x02),
+        (24, 125, 0x02),
+        (31, 125, 0x02),
+        (41, 125, 0x02),
+        (56, 125, 0x03),
+    ],
+    // 92
+    [
+        (2, 60, 0x02),
+        (9, 60, 0x02),
+        (23, 60, 0x02),
+        (40, 60, 0x03),
+        (2, 96, 0x02),
+        (9, 96, 0x02),
+        (23, 96, 0x02),
+        (40, 96, 0x03),
+        (2, 123, 0x02),
+        (9, 123, 0x02),
+        (23, 123, 0x02),
+        (40, 123, 0x03),
+        (97, 0, 0x00),
+        (101, 0, 0x00),
+        (111, 0, 0x00),
+        (133, 0, 0x00),
+    ],
+    // 93
+    [
+        (3, 60, 0x02),
+        (6, 60, 0x02),
+        (10, 60, 0x02),
+        (15, 60, 0x02),
+        (24, 60, 0x02),
+        (31, 60, 0x02),
+        (41, 60, 0x02),
+        (56, 60, 0x03),
+        (3, 96, 0x02),
+        (6, 96, 0x02),
+        (10, 96, 0x02),
+        (15, 96, 0x02),
+        (24, 96, 0x02),
+        (31, 96, 0x02),
+        (41, 96, 0x02),
+        (56, 96, 0x03),
+    ],
+    // 94
+    [
+        (3, 123, 0x02),
+        (6, 123, 0x02),
+        (10, 123, 0x02),
+        (15, 123, 0x02),
+        (24, 123, 0x02),
+        (31, 123, 0x02),
+        (41, 123, 0x02),
+        (56, 123, 0x03),
+        (98, 0, 0x00),
+        (99, 0, 0x00),
+        (102, 0, 0x00),
+        (105, 0, 0x00),
+        (112, 0, 0x00),
+        (119, 0, 0x00),
+        (134, 0, 0x00),
+        (153, 0, 0x00),
+    ],
+    // 95
+    [
+        (0, 92, 0x02),
+        (0, 195, 0x02),
+        (0, 208, 0x02),
+        (100, 0, 0x00),
+        (103, 0, 0x00),
+        (104, 0, 0x00),
+        (106, 0, 0x00),
+        (107, 0, 0x00),
+        (113, 0, 0x00),
+        (116, 0, 0x00),
+        (120, 0, 0x00),
+        (126, 0, 0x00),
+        (135, 0, 0x00),
+        (142, 0, 0x00),
+        (154, 0, 0x00),
+        (169, 0, 0x00),
+    ],
+    // 96
+    [
+        (1, 92, 0x02),
+        (22, 92, 0x03),
+        (1, 195, 0x02),
+        (22, 195, 0x03),
+        (1, 208, 0x02),
+        (22, 208, 0x03),
+        (0, 128, 0x02),
+        (0, 130, 0x02),
+        (0, 131, 0x02),
+        (0, 162, 0x02),
+        (0, 184, 0x02),
+        (0, 194, 0x02),
+        (0, 224, 0x02),
+        (0, 226, 0x02),
+        (108, 0, 0x00),
+        (109, 0, 0x00),
+    ],
+    // 97
+    [
+        (2, 92, 0x02),
+        (9, 92, 0x02),
+        (23, 92, 0x02),
+        (40, 92, 0x03),
+        (2, 195, 0x02),
+        (9, 195, 0x02),
+        (23, 195, 0x02),
+        (40, 195, 0x03),
+        (2, 208, 0x02),
+        (9, 208, 0x02),
+        (23, 208, 0x02),
+        (40, 208, 0x03),
+        (1, 128, 0x02),
+        (22, 128, 0x03),
+        (1, 130, 0x02),
+        (22, 130, 0x03),
+    ],
+    // 98
+    [
+        (3, 92, 0x02),
+        (6, 92, 0x02),
+        (10, 92, 0x02),
+        (15, 92, 0x02),
+        (24, 92, 0x02),
+        (31, 92, 0x02),
+        (41, 92, 0x02),
+        (56, 92, 0x03),
+        (3, 195, 0x02),
+        (6, 195, 0x02),
+        (10, 195, 0x02),
+        (15, 195, 0x02),
+        (24, 195, 0x02),
+        (31, 195, 0x02),
+        (41, 195, 0x02),
+        (56, 195, 0x03),
+    ],
+    // 99
+    [
+        (3, 208, 0x02),
+        (6, 208, 0x02),
+        (10, 208, 0x02),
+        (15, 208, 0x02),
+        (24, 208, 0x02),
+        (31, 208, 0x02),
+        (41, 208, 0x02),
+        (56, 208, 0x03),
+        (2, 128, 0x02),
+        (9, 128, 0x02),
+        (23, 128, 0x02),
+        (40, 128, 0x03),
+        (2, 130, 0x02),
+        (9, 130, 0x02),
+        (23, 130, 0x02),
+        (40, 130, 0x03),
+    ],
+    // 100
+    [
+        (3, 128, 0x02),
+        (6, 128, 0x02),
+        (10, 128, 0x02),
+        (15, 128, 0x02),
+        (24, 128, 0x02),
+        (31, 128, 0x02),
+        (41, 128, 0x02),
+        (56, 128, 0x03),
+        (3, 130, 0x02),
+        (6, 130, 0x02),
+        (10, 130, 0x02),
+        (15, 130, 0x02),
+        (24, 130, 0x02),
+        (31, 130, 0x02),
+        (41, 130, 0x02),
+        (56, 130, 0x03),
+    ],
+    // 101
+    [
+        (1, 131, 0x02),
+        (22, 131, 0x03),
+        (1, 162, 0x02),
+        (22, 162, 0x03),
+        (1, 184, 0x02),
+        (22, 184, 0x03),
+        (1, 194, 0x02),
+        (22, 194, 0x03),
+        (1, 224, 0x02),
+        (22, 224, 0x03),
+        (1, 226, 0x02),
+        (22, 226, 0x03),
+        (0, 153, 0x02),
+        (0, 161, 0x02),
+        (0, 167, 0x02),
+        (0, 172, 0x02),
+    ],
+    // 102
+    [
+        (2, 131, 0x02),
+        (9, 131, 0x02),
+        (23, 131, 0x02),
+        (40, 131, 0x03),
+        (2, 162, 0x02),
+        (9, 162, 0x02),
+        (23, 162, 0x02),
+        (40, 162, 0x03),
+        (2, 184, 0x02),
+        (9, 184, 0x02),
+        (23, 184, 0x02),
+        (40, 184, 0x03),
+        (2, 194, 0x02),
+        (9, 194, 0x02),
+        (23, 194, 0x02),
+        (40, 194, 0x03),
+    ],
+    // 103
+    [
+        (3, 131, 0x02),
+        (6, 131, 0x02),
+        (10, 131, 0x02),
+        (15, 131, 0x02),
+        (24, 131, 0x02),
+        (31, 131, 0x02),
+        (41, 131, 0x02),
+        (56, 131, 0x03),
+        (3, 162, 0x02),
+        (6, 162, 0x02),
+        (10, 162, 0x02),
+        (15, 162, 0x02),
+        (24, 162, 0x02),
+        (31, 162, 0x02),
+        (41, 162, 0x02),
+        (56, 162, 0x03),
+    ],
+    // 104
+    [
+        (3, 184, 0x02),
+        (6, 184, 0x02),
+        (10, 184, 0x02),
+        (15, 184, 0x02),
+        (24, 184, 0x02),
+        (31, 184, 0x02),
+        (41, 184, 0x02),
+        (56, 184, 0x03),
+        (3, 194, 0x02),
+        (6, 194, 0x02),
+        (10, 194, 0x02),
+        (15, 194, 0x02),
+        (24, 194, 0x02),
+        (31, 194, 0x02),
+        (41, 194, 0x02),
+        (56, 194, 0x03),
+    ],
+    // 105
+    [
+        (2, 224, 0x02),
+        (9, 224, 0x02),
+        (23, 224, 0x02),
+        (40, 224, 0x03),
+        (2, 226, 0x02),
+        (9, 226, 0x02),
+        (23, 226, 0x02),
+        (40, 226, 0x03),
+        (1, 153, 0x02),
+        (22, 153, 0x03),
+        (1, 161, 0x02),
+        (22, 161, 0x03),
+        (1, 167, 0x02),
+        (22, 167, 0x03),
+        (1, 172, 0x02),
+        (22, 172, 0x03),
+    ],
+    // 106
+    [
+        (3, 224, 0x02),
+        (6, 224, 0x02),
+        (10, 224, 0x02),
+        (15, 224, 0x02),
+        (24, 224, 0x02),
+        (31, 224, 0x02),
+        (41, 224, 0x02),
+        (56, 224, 0x03),
+        (3, 226, 0x02),
+        (6, 226, 0x02),
+        (10, 226, 0x02),
+        (15, 226, 0x02),
+        (24, 226, 0x02),
+        (31, 226, 0x02),
+        (41, 226, 0x02),
+        (56, 226, 0x03),
+    ],
+    // 107
+    [
+        (2, 153, 0x02),
+        (9, 153, 0x02),
+        (23, 153, 0x02),
+        (40, 153, 0x03),
+        (2, 161, 0x02),
+        (9, 161, 0x02),
+        (23, 161, 0x02),
+        (40, 161, 0x03),
+        (2, 167, 0x02),
+        (9, 167, 0x02),
+        (23, 167, 0x02),
+        (40, 167, 0x03),
+        (2, 172, 0x02),
+        (9, 172, 0x02),
+        (23, 172, 0x02),
+        (40, 172, 0x03),
+    ],
+    // 108
+    [
+        (3, 153, 0x02),
+        (6, 153, 0x02),
+        (10, 153, 0x02),
+        (15, 153, 0x02),
+        (24, 153, 0x02),
+        (31, 153, 0x02),
+        (41, 153, 0x02),
+        (56, 153, 0x03),
+        (3, 161, 0x02),
+        (6, 161, 0x02),
+        (10, 161, 0x02),
+        (15, 161, 0x02),
+        (24, 161, 0x02),
+        (31, 161, 0x02),
+        (41, 161, 0x02),
+        (56, 161, 0x03),
+    ],
+    // 109
+    [
+        (3, 167, 0x02),
+        (6, 167, 0x02),
+        (10, 167, 0x02),
+        (15, 167, 0x02),
+        (24, 167, 0x02),
+        (31, 167, 0x02),
+        (41, 167, 0x02),
+        (56, 167, 0x03),
+        (3, 172, 0x02),
+        (6, 172, 0x02),
+        (10, 172, 0x02),
+        (15, 172, 0x02),
+        (24, 172, 0x02),
+        (31, 172, 0x02),
+        (41, 172, 0x02),
+        (56, 172, 0x03),
+    ],
+    // 110
+    [
+        (114, 0, 0x00),
+        (115, 0, 0x00),
+        (117, 0, 0x00),
+        (118, 0, 0x00),
+        (121, 0, 0x00),
+        (123, 0, 0x00),
+        (127, 0, 0x00),
+        (130, 0, 0x00),
+        (136, 0, 0x00),
+        (139, 0, 0x00),
+        (143, 0, 0x00),
+        (146, 0, 0x00),
+        (155, 0, 0x00),
+        (162, 0, 0x00),
+        (170, 0, 0x00),
+        (180, 0, 0x00),
+    ],
+    // 111
+    [
+        (0, 176, 0x02),
+        (0, 177, 0x02),
+        (0, 179, 0x02),
+        (0, 209, 0x02),
+        (0, 216, 0x02),
+        (0, 217, 0x02),
+        (0, 227, 0x02),
+        (0, 229, 0x02),
+        (0, 230, 0x02),
+        (122, 0, 0x00),
+        (124, 0, 0x00),
+        (125, 0, 0x00),
+        (128, 0, 0x00),
+        (129, 0, 0x00),
+        (131, 0, 0x00),
+        (132, 0, 0x00),
+    ],
+    // 112
+    [
+        (1, 176, 0x02),
+        (22, 176, 0x03),
+        (1, 177, 0x02),
+        (22, 177, 0x03),
+        (1, 179, 0x02),
+        (22, 179, 0x03),
+        (1, 209, 0x02),
+        (22, 209, 0x03),
+        (1, 216, 0x02),
+        (22, 216, 0x03),
+        (1, 217, 0x02),
+        (22, 217, 0x03),
+        (1, 227, 0x02),
+        (22, 227, 0x03),
+        (1, 229, 0x02),
+        (22, 229, 0x03),
+    ],
+    // 113
+    [
+        (2, 176, 0x02),
+        (9, 176, 0x02),
+        (23, 176, 0x02),
+        (40, 176, 0x03),
+        (2, 177, 0x02),
+        (9, 177, 0x02),
+        (23, 177, 0x02),
+        (40, 177, 0x03),
+        (2, 179, 0x02),
+        (9, 179, 0x02),
+        (23, 179, 0x02),
+        (40, 179, 0x03),
+        (2, 209, 0x02),
+        (9, 209, 0x02),
+        (23, 209, 0x02),
+        (40, 209, 0x03),
+    ],
+    // 114
+    [
+        (3, 176, 0x02),
+        (6, 176, 0x02),
+        (10, 176, 0x02),
+        (15, 176, 0x02),
+        (24, 176, 0x02),
+        (31, 176, 0x02),
+        (41, 176, 0x02),
+        (56, 176, 0x03),
+        (3, 177, 0x02),
+        (6, 177, 0x02),
+        (10, 177, 0x02),
+        (15, 177, 0x02),
+        (24, 177, 0x02),
+        (31, 177, 0x02),
+        (41, 177, 0x02),
+        (56, 177, 0x03),
+    ],
+    // 115
+    [
+        (3, 179, 0x02),
+        (6, 179, 0x02),
+        (10, 179, 0x02),
+        (15, 179, 0x02),
+        (24, 179, 0x02),
+        (31, 179, 0x02),
+        (41, 179, 0x02),
+        (56, 179, 0x03),
+        (3, 209, 0x02),
+        (6, 209, 0x02),
+        (10, 209, 0x02),
+        (15, 209, 0x02),
+        (24, 209, 0x02),
+        (31, 209, 0x02),
+        (41, 209, 0x02),
+        (56, 209, 0x03),
+    ],
+    // 116
+    [
+        (2, 216, 0x02),
+        (9, 216, 0x02),
+        (23, 216, 0x02),
+        (40, 216, 0x03),
+        (2, 217, 0x02),
+        (9, 217, 0x02),
+        (23, 217, 0x02),
+        (40, 217, 0x03),
+        (2, 227, 0x02),
+        (9, 227, 0x02),
+        (23, 227, 0x02),
+        (40, 227, 0x03),
+        (2, 229, 0x02),
+        (9, 229, 0x02),
+        (23, 229, 0x02),
+        (40, 229, 0x03),
+    ],
+    // 117
+    [
+        (3, 216, 0x02),
+        (6, 216, 0x02),
+        (10, 216, 0x02),
+        (15, 216, 0x02),
+        (24, 216, 0x02),
+        (31, 216, 0x02),
+        (41, 216, 0x02),
+        (56, 216, 0x03),
+        (3, 217, 0x02),
+        (6, 217, 0x02),
+        (10, 217, 0x02),
+        (15, 217, 0x02),
+        (24, 217, 0x02),
+        (31, 217, 0x02),
+        (41, 217, 0x02),
+        (56, 217, 0x03),
+    ],
+    // 118
+    [
+        (3, 227, 0x02),
+        (6, 227, 0x02),
+        (10, 227, 0x02),
+        (15, 227, 0x02),
+        (24, 227, 0x02),
+        (31, 227, 0x02),
+        (41, 227, 0x02),
+        (56, 227, 0x03),
+        (3, 229, 0x02),
+        (6, 229, 0x02),
+        (10, 229, 0x02),
+        (15, 229, 0x02),
+        (24, 229, 0x02),
+        (31, 229, 0x02),
+        (41, 229, 0x02),
+        (56, 229, 0x03),
+    ],
+    // 119
+    [
+        (1, 230, 0x02),
+        (22, 230, 0x03),
+        (0, 129, 0x02),
+        (0, 132, 0x02),
+        (0, 133, 0x02),
+        (0, 134, 0x02),
+        (0, 136, 0x02),
+        (0, 146, 0x02),
+        (0, 154, 0x02),
+        (0, 156, 0x02),
+        (0, 160, 0x02),
+        (0, 163, 0x02),
+        (0, 164, 0x02),
+        (0, 169, 0x02),
+        (0, 170, 0x02),
+        (0, 173, 0x02),
+    ],
+    // 120
+    [
+        (2, 230, 0x02),
+        (9, 230, 0x02),
+        (23, 230, 0x02),
+        (40, 230, 0x03),
+        (1, 129, 0x02),
+        (22, 129, 0x03),
+        (1, 132, 0x02),
+        (22, 132, 0x03),
+        (1, 133, 0x02),
+        (22, 133, 0x03),
+        (1, 134, 0x02),
+        (22, 134, 0x03),
+        (1, 136, 0x02),
+        (22, 136, 0x03),
+        (1, 146, 0x02),
+        (22, 146, 0x03),
+    ],
+    // 121
+    [
+        (3, 230, 0x02),
+        (6, 230, 0x02),
+        (10, 230, 0x02),
+        (15, 230, 0x02),
+        (24, 230, 0x02),
+        (31, 230, 0x02),
+        (41, 230, 0x02),
+        (56, 230, 0x03),
+        (2, 129, 0x02),
+        (9, 129, 0x02),
+        (23, 129, 0x02),
+        (40, 129, 0x03),
+        (2, 132, 0x02),
+        (9, 132, 0x02),
+        (23, 132, 0x02),
+        (40, 132, 0x03),
+    ],
+    // 122
+    [
+        (3, 129, 0x02),
+        (6, 129, 0x02),
+        (10, 129, 0x02),
+        (15, 129, 0x02),
+        (24, 129, 0x02),
+        (31, 129, 0x02),
+        (41, 129, 0x02),
+        (56, 129, 0x03),
+        (3, 132, 0x02),
+        (6, 132, 0x02),
+        (10, 132, 0x02),
+        (15, 132, 0x02),
+        (24, 132, 0x02),
+        (31, 132, 0x02),
+        (41, 132, 0x02),
+        (56, 132, 0x03),
+    ],
+    // 123
+    [
+        (2, 133, 0x02),
+        (9, 133, 0x02),
+        (23, 133, 0x02),
+        (40, 133, 0x03),
+        (2, 134, 0x02),
+        (9, 134, 0x02),
+        (23, 134, 0x02),
+        (40, 134, 0x03),
+        (2, 136, 0x02),
+        (9, 136, 0x02),
+        (23, 136, 0x02),
+        (40, 136, 0x03),
+        (2, 146, 0x02),
+        (9, 146, 0x02),
+        (23, 146, 0x02),
+        (40, 146, 0x03),
+    ],
+    // 124
+    [
+        (3, 133, 0x02),
+        (6, 133, 0x02),
+        (10, 133, 0x02),
+        (15, 133, 0x02),
+        (24, 133, 0x02),
+        (31, 133, 0x02),
+        (41, 133, 0x02),
+        (56, 133, 0x03),
+        (3, 134, 0x02),
+        (6, 134, 0x02),
+        (10, 134, 0x02),
+        (15, 134, 0x02),
+        (24, 134, 0x02),
+        (31, 134, 0x02),
+        (41, 134, 0x02),
+        (56, 134, 0x03),
+    ],
+    // 125
+    [
+        (3, 136, 0x02),
+        (6, 136, 0x02),
+        (10, 136, 0x02),
+        (15, 136, 0x02),
+        (24, 136, 0x02),
+        (31, 136, 0x02),
+        (41, 136, 0x02),
+        (56, 136, 0x03),
+        (3, 146, 0x02),
+        (6, 146, 0x02),
+        (10, 146, 0x02),
+        (15, 146, 0x02),
+        (24, 146, 0x02),
+        (31, 146, 0x02),
+        (41, 146, 0x02),
+        (56, 146, 0x03),
+    ],
+    // 126
+    [
+        (1, 154, 0x02),
+        (22, 154, 0x03),
+        (1, 156, 0x02),
+        (22, 156, 0x03),
+        (1, 160, 0x02),
+        (22, 160, 0x03),
+        (1, 163, 0x02),
+        (22, 163, 0x03),
+        (1, 164, 0x02),
+        (22, 164, 0x03),
+        (1, 169, 0x02),
+        (22, 169, 0x03),
+        (1, 170, 0x02),
+        (22, 170, 0x03),
+        (1, 173, 0x02),
+        (22, 173, 0x03),
+    ],
+    // 127
+    [
+        (2, 154, 0x02),
+        (9, 154, 0x02),
+        (23, 154, 0x02),
+        (40, 154, 0x03),
+        (2, 156, 0x02),
+        (9, 156, 0x02),
+        (23, 156, 0x02),
+        (40, 156, 0x03),
+        (2, 160, 0x02),
+        (9, 160, 0x02),
+        (23, 160, 0x02),
+        (40, 160, 0x03),
+        (2, 163, 0x02),
+        (9, 163, 0x02),
+        (23, 163, 0x02),
+        (40, 163, 0x03),
+    ],
+    // 128
+    [
+        (3, 154, 0x02),
+        (6, 154, 0x02),
+        (10, 154, 0x02),
+        (15, 154, 0x02),
+        (24, 154, 0x02),
+        (31, 154, 0x02),
+        (41, 154, 0x02),
+        (56, 154, 0x03),
+        (3, 156, 0x02),
+        (6, 156, 0x02),
+        (10, 156, 0x02),
+        (15, 156, 0x02),
+        (24, 156, 0x02),
+        (31, 156, 0x02),
+        (41, 156, 0x02),
+        (56, 156, 0x03),
+    ],
+    // 129
+    [
+        (3, 160, 0x02),
+        (6, 160, 0x02),
+        (10, 160, 0x02),
+        (15, 160, 0x02),
+        (24, 160, 0x02),
+        (31, 160, 0x02),
+        (41, 160, 0x02),
+        (56, 160, 0x03),
+        (3, 163, 0x02),
+        (6, 163, 0x02),
+        (10, 163, 0x02),
+        (15, 163, 0x02),
+        (24, 163, 0x02),
+        (31, 163, 0x02),
+        (41, 163, 0x02),
+        (56, 163, 0x03),
+    ],
+    // 130
+    [
+        (2, 164, 0x02),
+        (9, 164, 0x02),
+        (23, 164, 0x02),
+        (40, 164, 0x03),
+        (2, 169, 0x02),
+        (9, 169, 0x02),
+        (23, 169, 0x02),
+        (40, 169, 0x03),
+        (2, 170, 0x02),
+        (9, 170, 0x02),
+        (23, 170, 0x02),
+        (40, 170, 0x03),
+        (2, 173, 0x02),
+        (9, 173, 0x02),
+        (23, 173, 0x02),
+        (40, 173, 0x03),
+    ],
+    // 131
+    [
+        (3, 164, 0x02),
+        (6, 164, 0x02),
+        (10, 164, 0x02),
+        (15, 164, 0x02),
+        (24, 164, 0x02),
+        (31, 164, 0x02),
+        (41, 164, 0x02),
+        (56, 164, 0x03),
+        (3, 169, 0x02),
+        (6, 169, 0x02),
+        (10, 169, 0x02),
+        (15, 169, 0x02),
+        (24, 169, 0x02),
+        (31, 169, 0x02),
+        (41, 169, 0x02),
+        (56, 169, 0x03),
+    ],
+    // 132
+    [
+        (3, 170, 0x02),
+        (6, 170, 0x02),
+        (10, 170, 0x02),
+        (15, 170, 0x02),
+        (24, 170, 0x02),
+        (31, 170, 0x02),
+        (41, 170, 0x02),
+        (56, 170, 0x03),
+        (3, 173, 0x02),
+        (6, 173, 0x02),
+        (10, 173, 0x02),
+        (15, 173, 0x02),
+        (24, 173, 0x02),
+        (31, 173, 0x02),
+        (41, 173, 0x02),
+        (56, 173, 0x03),
+    ],
+    // 133
+    [
+        (137, 0, 0x00),
+        (138, 0, 0x00),
+        (140, 0, 0x00),
+        (141, 0, 0x00),
+        (144, 0, 0x00),
+        (145, 0, 0x00),
+        (147, 0, 0x00),
+        (150, 0, 0x00),
+        (156, 0, 0x00),
+        (159, 0, 0x00),
+        (163, 0, 0x00),
+        (166, 0, 0x00),
+        (171, 0, 0x00),
+        (174, 0, 0x00),
+        (181, 0, 0x00),
+        (190, 0, 0x00),
+    ],
+    // 134
+    [
+        (0, 178, 0x02),
+        (0, 181, 0x02),
+        (0, 185, 0x02),
+        (0, 186, 0x02),
+        (0, 187, 0x02),
+        (0, 189, 0x02),
+        (0, 190, 0x02),
+        (0, 196, 0x02),
+        (0, 198, 0x02),
+        (0, 228, 0x02),
+        (0, 232, 0x02),
+        (0, 233, 0x02),
+        (148, 0, 0x00),
+        (149, 0, 0x00),
+        (151, 0, 0x00),
+        (152, 0, 0x00),
+    ],
+    // 135
+    [
+        (1, 178, 0x02),
+        (22, 178, 0x03),
+        (1, 181, 0x02),
+        (22, 181, 0x03),
+        (1, 185, 0x02),
+        (22, 185, 0x03),
+        (1, 186, 0x02),
+        (22, 186, 0x03),
+        (1, 187, 0x02),
+        (22, 187, 0x03),
+        (1, 189, 0x02),
+        (22, 189, 0x03),
+        (1, 190, 0x02),
+        (22, 190, 0x03),
+        (1, 196, 0x02),
+        (22, 196, 0x03),
+    ],
+    // 136
+    [
+        (2, 178, 0x02),
+        (9, 178, 0x02),
+        (23, 178, 0x02),
+        (40, 178, 0x03),
+        (2, 181, 0x02),
+        (9, 181, 0x02),
+        (23, 181, 0x02),
+        (40, 181, 0x03),
+        (2, 185, 0x02),
+        (9, 185, 0x02),
+        (23, 185, 0x02),
+        (40, 185, 0x03),
+        (2, 186, 0x02),
+        (9, 186, 0x02),
+        (23, 186, 0x02),
+        (40, 186, 0x03),
+    ],
+    // 137
+    [
+        (3, 178, 0x02),
+        (6, 178, 0x02),
+        (10, 178, 0x02),
+        (15, 178, 0x02),
+        (24, 178, 0x02),
+        (31, 178, 0x02),
+        (41, 178, 0x02),
+        (56, 178, 0x03),
+        (3, 181, 0x02),
+        (6, 181, 0x02),
+        (10, 181, 0x02),
+        (15, 181, 0x02),
+        (24, 181, 0x02),
+        (31, 181, 0x02),
+        (41, 181, 0x02),
+        (56, 181, 0x03),
+    ],
+    // 138
+    [
+        (3, 185, 0x02),
+        (6, 185, 0x02),
+        (10, 185, 0x02),
+        (15, 185, 0x02),
+        (24, 185, 0x02),
+        (31, 185, 0x02),
+        (41, 185, 0x02),
+        (56, 185, 0x03),
+        (3, 186, 0x02),
+        (6, 186, 0x02),
+        (10, 186, 0x02),
+        (15, 186, 0x02),
+        (24, 186, 0x02),
+        (31, 186, 0x02),
+        (41, 186, 0x02),
+        (56, 186, 0x03),
+    ],
+    // 139
+    [
+        (2, 187, 0x02),
+        (9, 187, 0x02),
+        (23, 187, 0x02),
+        (40, 187, 0x03),
+        (2, 189, 0x02),
+        (9, 189, 0x02),
+        (23, 189, 0x02),
+        (40, 189, 0x03),
+        (2, 190, 0x02),
+        (9, 190, 0x02),
+        (23, 190, 0x02),
+        (40, 190, 0x03),
+        (2, 196, 0x02),
+        (9, 196, 0x02),
+        (23, 196, 0x02),
+        (40, 196, 0x03),
+    ],
+    // 140
+    [
+        (3, 187, 0x02),
+        (6, 187, 0x02),
+        (10, 187, 0x02),
+        (15, 187, 0x02),
+        (24, 187, 0x02),
+        (31, 187, 0x02),
+        (41, 187, 0x02),
+        (56, 187, 0x03),
+        (3, 189, 0x02),
+        (6, 189, 0x02),
+        (10, 189, 0x02),
+        (15, 189, 0x02),
+        (24, 189, 0x02),
+        (31, 189, 0x02),
+        (41, 189, 0x02),
+        (56, 189, 0x03),
+    ],
+    // 141
+    [
+        (3, 190, 0x02),
+        (6, 190, 0x02),
+        (10, 190, 0x02),
+        (15, 190, 0x02),
+        (24, 190, 0x02),
+        (31, 190, 0x02),
+        (41, 190, 0x02),
+        (56, 190, 0x03),
+        (3, 196, 0x02),
+        (6, 196, 0x02),
+        (10, 196, 0x02),
+        (15, 196, 0x02),
+        (24, 196, 0x02),
+        (31, 196, 0x02),
+        (41, 196, 0x02),
+        (56, 196, 0x03),
+    ],
+    // 142
+    [
+        (1, 198, 0x02),
+        (22, 198, 0x03),
+        (1, 228, 0x02),
+        (22, 228, 0x03),
+        (1, 232, 0x02),
+        (22, 232, 0x03),
+        (1, 233, 0x02),
+        (22, 233, 0x03),
+        (0, 1, 0x02),
+        (0, 135, 0x02),
+        (0, 137, 0x02),
+        (0, 138, 0x02),
+        (0, 139, 0x02),
+        (0, 140, 0x02),
+        (0, 141, 0x02),
+        (0, 143, 0x02),
+    ],
+    // 143
+    [
+        (2, 198, 0x02),
+        (9, 198, 0x02),
+        (23, 198, 0x02),
+        (40, 198, 0x03),
+        (2, 228, 0x02),
+        (9, 228, 0x02),
+        (23, 228, 0x02),
+        (40, 228, 0x03),
+        (2, 232, 0x02),
+        (9, 232, 0x02),
+        (23, 232, 0x02),
+        (40, 232, 0x03),
+        (2, 233, 0x02),
+        (9, 233, 0x02),
+        (23, 233, 0x02),
+        (40, 233, 0x03),
+    ],
+    // 144
+    [
+        (3, 198, 0x02),
+        (6, 198, 0x02),
+        (10, 198, 0x02),
+        (15, 198, 0x02),
+        (24, 198, 0x02),
+        (31, 198, 0x02),
+        (41, 198, 0x02),
+        (56, 198, 0x03),
+        (3, 228, 0x02),
+        (6, 228, 0x02),
+        (10, 228, 0x02),
+        (15, 228, 0x02),
+        (24, 228, 0x02),
+        (31, 228, 0x02),
+        (41, 228, 0x02),
+        (56, 228, 0x03),
+    ],
+    // 145
+    [
+        (3, 232, 0x02),
+        (6, 232, 0x02),
+        (10, 232, 0x02),
+        (15, 232, 0x02),
+        (24, 232, 0x02),
+        (31, 232, 0x02),
+        (41, 232, 0x02),
+        (56, 232, 0x03),
+        (3, 233, 0x02),
+        (6, 233, 0x02),
+        (10, 233, 0x02),
+        (15, 233, 0x02),
+        (24, 233, 0x02),
+        (31, 233, 0x02),
+        (41, 233, 0x02),
+        (56, 233, 0x03),
+    ],
+    // 146
+    [
+        (1, 1, 0x02),
+        (22, 1, 0x03),
+        (1, 135, 0x02),
+        (22, 135, 0x03),
+        (1, 137, 0x02),
+        (22, 137, 0x03),
+        (1, 138, 0x02),
+        (22, 138, 0x03),
+        (1, 139, 0x02),
+        (22, 139, 0x03),
+        (1, 140, 0x02),
+        (22, 140, 0x03),
+        (1, 141, 0x02),
+        (22, 141, 0x03),
+        (1, 143, 0x02),
+        (22, 143, 0x03),
+    ],
+    // 147
+    [
+        (2, 1, 0x02),
+        (9, 1, 0x02),
+        (23, 1, 0x02),
+        (40, 1, 0x03),
+        (2, 135, 0x02),
+        (9, 135, 0x02),
+        (23, 135, 0x02),
+        (40, 135, 0x03),
+        (2, 137, 0x02),
+        (9, 137, 0x02),
+        (23, 137, 0x02),
+        (40, 137, 0x03),
+        (2, 138, 0x02),
+        (9, 138, 0x02),
+        (23, 138, 0x02),
+        (40, 138, 0x03),
+    ],
+    // 148
+    [
+        (3, 1, 0x02),
+        (6, 1, 0x02),
+        (10, 1, 0x02),
+        (15, 1, 0x02),
+        (24, 1, 0x02),
+        (31, 1, 0x02),
+        (41, 1, 0x02),
+        (56, 1, 0x03),
+        (3, 135, 0x02),
+        (6, 135, 0x02),
+        (10, 135, 0x02),
+        (15, 135, 0x02),
+        (24, 135, 0x02),
+        (31, 135, 0x02),
+        (41, 135, 0x02),
+        (56, 135, 0x03),
+    ],
+    // 149
+    [
+        (3, 137, 0x02),
+        (6, 137, 0x02),
+        (10, 137, 0x02),
+        (15, 137, 0x02),
+        (24, 137, 0x02),
+        (31, 137, 0x02),
+        (41, 137, 0x02),
+        (56, 137, 0x03),
+        (3, 138, 0x02),
+        (6, 138, 0x02),
+        (10, 138, 0x02),
+        (15, 138, 0x02),
+        (24, 138, 0x02),
+        (31, 138, 0x02),
+        (41, 138, 0x02),
+        (56, 138, 0x03),
+    ],
+    // 150
+    [
+        (2, 139, 0x02),
+        (9, 139, 0x02),
+        (23, 139, 0x02),
+        (40, 139, 0x03),
+        (2, 140, 0x02),
+        (9, 140, 0x02),
+        (23, 140, 0x02),
+        (40, 140, 0x03),
+        (2, 141, 0x02),
+        (9, 141, 0x02),
+        (23, 141, 0x02),
+        (40, 141, 0x03),
+        (2, 143, 0x02),
+        (9, 143, 0x02),
+        (23, 143, 0x02),
+        (40, 143, 0x03),
+    ],
+    // 151
+    [
+        (3, 139, 0x02),
+        (6, 139, 0x02),
+        (10, 139, 0x02),
+        (15, 139, 0x02),
+        (24, 139, 0x02),
+        (31, 139, 0x02),
+        (41, 139, 0x02),
+        (56, 139, 0x03),
+        (3, 140, 0x02),
+        (6, 140, 0x02),
+        (10, 140, 0x02),
+        (15, 140, 0x02),
+        (24, 140, 0x02),
+        (31, 140, 0x02),
+        (41, 140, 0x02),
+        (56, 140, 0x03),
+    ],
+    // 152
+    [
+        (3, 141, 0x02),
+        (6, 141, 0x02),
+        (10, 141, 0x02),
+        (15, 141, 0x02),
+        (24, 141, 0x02),
+        (31, 141, 0x02),
+        (41, 141, 0x02),
+        (56, 141, 0x03),
+        (3, 143, 0x02),
+        (6, 143, 0x02),
+        (10, 143, 0x02),
+        (15, 143, 0x02),
+        (24, 143, 0x02),
+        (31, 143, 0x02),
+        (41, 143, 0x02),
+        (56, 143, 0x03),
+    ],
+    // 153
+    [
+        (157, 0, 0x00),
+        (158, 0, 0x00),
+        (160, 0, 0x00),
+        (161, 0, 0x00),
+        (164, 0, 0x00),
+        (165, 0, 0x00),
+        (167, 0, 0x00),
+        (168, 0, 0x00),
+        (172, 0, 0x00),
+        (173, 0, 0x00),
+        (175, 0, 0x00),
+        (177, 0, 0x00),
+        (182, 0, 0x00),
+        (185, 0, 0x00),
+        (191, 0, 0x00),
+        (207, 0, 0x00),
+    ],
+    // 154
+    [
+        (0, 147, 0x02),
+        (0, 149, 0x02),
+        (0, 150, 0x02),
+        (0, 151, 0x02),
+        (0, 152, 0x02),
+        (0, 155, 0x02),
+        (0, 157, 0x02),
+        (0, 158, 0x02),
+        (0, 165, 0x02),
+        (0, 166, 0x02),
+        (0, 168, 0x02),
+        (0, 174, 0x02),
+        (0, 175, 0x02),
+        (0, 180, 0x02),
+        (0, 182, 0x02),
+        (0, 183, 0x02),
+    ],
+    // 155
+    [
+        (1, 147, 0x02),
+        (22, 147, 0x03),
+        (1, 149, 0x02),
+        (22, 149, 0x03),
+        (1, 150, 0x02),
+        (22, 150, 0x03),
+        (1, 151, 0x02),
+        (22, 151, 0x03),
+        (1, 152, 0x02),
+        (22, 152, 0x03),
+        (1, 155, 0x02),
+        (22, 155, 0x03),
+        (1, 157, 0x02),
+        (22, 157, 0x03),
+        (1, 158, 0x02),
+        (22, 158, 0x03),
+    ],
+    // 156
+    [
+        (2, 147, 0x02),
+        (9, 147, 0x02),
+        (23, 147, 0x02),
+        (40, 147, 0x03),
+        (2, 149, 0x02),
+        (9, 149, 0x02),
+        (23, 149, 0x02),
+        (40, 149, 0x03),
+        (2, 150, 0x02),
+        (9, 150, 0x02),
+        (23, 150, 0x02),
+        (40, 150, 0x03),
+        (2, 151, 0x02),
+        (9, 151, 0x02),
+        (23, 151, 0x02),
+        (40, 151, 0x03),
+    ],
+    // 157
+    [
+        (3, 147, 0x02),
+        (6, 147, 0x02),
+        (10, 147, 0x02),
+        (15, 147, 0x02),
+        (24, 147, 0x02),
+        (31, 147, 0x02),
+        (41, 147, 0x02),
+        (56, 147, 0x03),
+        (3, 149, 0x02),
+        (6, 149, 0x02),
+        (10, 149, 0x02),
+        (15, 149, 0x02),
+        (24, 149, 0x02),
+        (31, 149, 0x02),
+        (41, 149, 0x02),
+        (56, 149, 0x03),
+    ],
+    // 158
+    [
+        (3, 150, 0x02),
+        (6, 150, 0x02),
+        (10, 150, 0x02),
+        (15, 150, 0x02),
+        (24, 150, 0x02),
+        (31, 150, 0x02),
+        (41, 150, 0x02),
+        (56, 150, 0x03),
+        (3, 151, 0x02),
+        (6, 151, 0x02),
+        (10, 151, 0x02),
+        (15, 151, 0x02),
+        (24, 151, 0x02),
+        (31, 151, 0x02),
+        (41, 151, 0x02),
+        (56, 151, 0x03),
+    ],
+    // 159
+    [
+        (2, 152, 0x02),
+        (9, 152, 0x02),
+        (23, 152, 0x02),
+        (40, 152, 0x03),
+        (2, 155, 0x02),
+        (9, 155, 0x02),
+        (23, 155, 0x02),
+        (40, 155, 0x03),
+        (2, 157, 0x02),
+        (9, 157, 0x02),
+        (23, 157, 0x02),
+        (40, 157, 0x03),
+        (2, 158, 0x02),
+        (9, 158, 0x02),
+        (23, 158, 0x02),
+        (40, 158, 0x03),
+    ],
+    // 160
+    [
+        (3, 152, 0x02),
+        (6, 152, 0x02),
+        (10, 152, 0x02),
+        (15, 152, 0x02),
+        (24, 152, 0x02),
+        (31, 152, 0x02),
+        (41, 152, 0x02),
+        (56, 152, 0x03),
+        (3, 155, 0x02),
+        (6, 155, 0x02),
+        (10, 155, 0x02),
+        (15, 155, 0x02),
+        (24, 155, 0x02),
+        (31, 155, 0x02),
+        (41, 155, 0x02),
+        (56, 155, 0x03),
+    ],
+    // 161
+    [
+        (3, 157, 0x02),
+        (6, 157, 0x02),
+        (10, 157, 0x02),
+        (15, 157, 0x02),
+        (24, 157, 0x02),
+        (31, 157, 0x02),
+        (41, 157, 0x02),
+        (56, 157, 0x03),
+        (3, 158, 0x02),
+        (6, 158, 0x02),
+        (10, 158, 0x02),
+        (15, 158, 0x02),
+        (24, 158, 0x02),
+        (31, 158, 0x02),
+        (41, 158, 0x02),
+        (56, 158, 0x03),
+    ],
+    // 162
+    [
+        (1, 165, 0x02),
+        (22, 165, 0x03),
+        (1, 166, 0x02),
+        (22, 166, 0x03),
+        (1, 168, 0x02),
+        (22, 168, 0x03),
+        (1, 174, 0x02),
+        (22, 174, 0x03),
+        (1, 175, 0x02),
+        (22, 175, 0x03),
+        (1, 180, 0x02),
+        (22, 180, 0x03),
+        (1, 182, 0x02),
+        (22, 182, 0x03),
+        (1, 183, 0x02),
+        (22, 183, 0x03),
+    ],
+    // 163
+    [
+        (2, 165, 0x02),
+        (9, 165, 0x02),
+        (23, 165, 0x02),
+        (40, 165, 0x03),
+        (2, 166, 0x02),
+        (9, 166, 0x02),
+        (23, 166, 0x02),
+        (40, 166, 0x03),
+        (2, 168, 0x02),
+        (9, 168, 0x02),
+        (23, 168, 0x02),
+        (40, 168, 0x03),
+        (2, 174, 0x02),
+        (9, 174, 0x02),
+        (23, 174, 0x02),
+        (40, 174, 0x03),
+    ],
+    // 164
+    [
+        (3, 165, 0x02),
+        (6, 165, 0x02),
+        (10, 165, 0x02),
+        (15, 165, 0x02),
+        (24, 165, 0x02),
+        (31, 165, 0x02),
+        (41, 165, 0x02),
+        (56, 165, 0x03),
+        (3, 166, 0x02),
+        (6, 166, 0x02),
+        (10, 166, 0x02),
+        (15, 166, 0x02),
+        (24, 166, 0x02),
+        (31, 166, 0x02),
+        (41, 166, 0x02),
+        (56, 166, 0x03),
+    ],
+    // 165
+    [
+        (3, 168, 0x02),
+        (6, 168, 0x02),
+        (10, 168, 0x02),
+        (15, 168, 0x02),
+        (24, 168, 0x02),
+        (31, 168, 0x02),
+        (41, 168, 0x02),
+        (56, 168, 0x03),
+        (3, 174, 0x02),
+        (6, 174, 0x02),
+        (10, 174, 0x02),
+        (15, 174, 0x02),
+        (24, 174, 0x02),
+        (31, 174, 0x02),
+        (41, 174, 0x02),
+        (56, 174, 0x03),
+    ],
+    // 166
+    [
+        (2, 175, 0x02),
+        (9, 175, 0x02),
+        (23, 175, 0x02),
+        (40, 175, 0x03),
+        (2, 180, 0x02),
+        (9, 180, 0x02),
+        (23, 180, 0x02),
+        (40, 180, 0x03),
+        (2, 182, 0x02),
+        (9, 182, 0x02),
+        (23, 182, 0x02),
+        (40, 182, 0x03),
+        (2, 183, 0x02),
+        (9, 183, 0x02),
+        (23, 183, 0x02),
+        (40, 183, 0x03),
+    ],
+    // 167
+    [
+        (3, 175, 0x02),
+        (6, 175, 0x02),
+        (10, 175, 0x02),
+        (15, 175, 0x02),
+        (24, 175, 0x02),
+        (31, 175, 0x02),
+        (41, 175, 0x02),
+        (56, 175, 0x03),
+        (3, 180, 0x02),
+        (6, 180, 0x02),
+        (10, 180, 0x02),
+        (15, 180, 0x02),
+        (24, 180, 0x02),
+        (31, 180, 0x02),
+        (41, 180, 0x02),
+        (56, 180, 0x03),
+    ],
+    // 168
+    [
+        (3, 182, 0x02),
+        (6, 182, 0x02),
+        (10, 182, 0x02),
+        (15, 182, 0x02),
+        (24, 182, 0x02),
+        (31, 182, 0x02),
+        (41, 182, 0x02),
+        (56, 182, 0x03),
+        (3, 183, 0x02),
+        (6, 183, 0x02),
+        (10, 183, 0x02),
+        (15, 183, 0x02),
+        (24, 183, 0x02),
+        (31, 183, 0x02),
+        (41, 183, 0x02),
+        (56, 183, 0x03),
+    ],
+    // 169
+    [
+        (0, 188, 0x02),
+        (0, 191, 0x02),
+        (0, 197, 0x02),
+        (0, 231, 0x02),
+        (0, 239, 0x02),
+        (176, 0, 0x00),
+        (178, 0, 0x00),
+        (179, 0, 0x00),
+        (183, 0, 0x00),
+        (184, 0, 0x00),
+        (186, 0, 0x00),
+        (187, 0, 0x00),
+        (192, 0, 0x00),
+        (199, 0, 0x00),
+        (208, 0, 0x00),
+        (223, 0, 0x00),
+    ],
+    // 170
+    [
+        (1, 188, 0x02),
+        (22, 188, 0x03),
+        (1, 191, 0x02),
+        (22, 191, 0x03),
+        (1, 197, 0x02),
+        (22, 197, 0x03),
+        (1, 231, 0x02),
+        (22, 231, 0x03),
+        (1, 239, 0x02),
+        (22, 239, 0x03),
+        (0, 9, 0x02),
+        (0, 142, 0x02),
+        (0, 144, 0x02),
+        (0, 145, 0x02),
+        (0, 148, 0x02),
+        (0, 159, 0x02),
+    ],
+    // 171
+    [
+        (2, 188, 0x02),
+        (9, 188, 0x02),
+        (23, 188, 0x02),
+        (40, 188, 0x03),
+        (2, 191, 0x02),
+        (9, 191, 0x02),
+        (23, 191, 0x02),
+        (40, 191, 0x03),
+        (2, 197, 0x02),
+        (9, 197, 0x02),
+        (23, 197, 0x02),
+        (40, 197, 0x03),
+        (2, 231, 0x02),
+        (9, 231, 0x02),
+        (23, 231, 0x02),
+        (40, 231, 0x03),
+    ],
+    // 172
+    [
+        (3, 188, 0x02),
+        (6, 188, 0x02),
+        (10, 188, 0x02),
+        (15, 188, 0x02),
+        (24, 188, 0x02),
+        (31, 188, 0x02),
+        (41, 188, 0x02),
+        (56, 188, 0x03),
+        (3, 191, 0x02),
+        (6, 191, 0x02),
+        (10, 191, 0x02),
+        (15, 191, 0x02),
+        (24, 191, 0x02),
+        (31, 191, 0x02),
+        (41, 191, 0x02),
+        (56, 191, 0x03),
+    ],
+    // 173
+    [
+        (3, 197, 0x02),
+        (6, 197, 0x02),
+        (10, 197, 0x02),
+        (15, 197, 0x02),
+        (24, 197, 0x02),
+        (31, 197, 0x02),
+        (41, 197, 0x02),
+        (56, 197, 0x03),
+        (3, 231, 0x02),
+        (6, 231, 0x02),
+        (10, 231, 0x02),
+        (15, 231, 0x02),
+        (24, 231, 0x02),
+        (31, 231, 0x02),
+        (41, 231, 0x02),
+        (56, 231, 0x03),
+    ],
+    // 174
+    [
+        (2, 239, 0x02),
+        (9, 239, 0x02),
+        (23, 239, 0x02),
+        (40, 239, 0x03),
+        (1, 9, 0x02),
+        (22, 9, 0x03),
+        (1, 142, 0x02),
+        (22, 142, 0x03),
+        (1, 144, 0x02),
+        (22, 144, 0x03),
+        (1, 145, 0x02),
+        (22, 145, 0x03),
+        (1, 148, 0x02),
+        (22, 148, 0x03),
+        (1, 159, 0x02),
+        (22, 159, 0x03),
+    ],
+    // 175
+    [
+        (3, 239, 0x02),
+        (6, 239, 0x02),
+        (10, 239, 0x02),
+        (15, 239, 0x02),
+        (24, 239, 0x02),
+        (31, 239, 0x02),
+        (41, 239, 0x02),
+        (56, 239, 0x03),
+        (2, 9, 0x02),
+        (9, 9, 0x02),
+        (23, 9, 0x02),
+        (40, 9, 0x03),
+        (2, 142, 0x02),
+        (9, 142, 0x02),
+        (23, 142, 0x02),
+        (40, 142, 0x03),
+    ],
+    // 176
+    [
+        (3, 9, 0x02),
+        (6, 9, 0x02),
+        (10, 9, 0x02),
+        (15, 9, 0x02),
+        (24, 9, 0x02),
+        (31, 9, 0x02),
+        (41, 9, 0x02),
+        (56, 9, 0x03),
+        (3, 142, 0x02),
+        (6, 142, 0x02),
+        (10, 142, 0x02),
+        (15, 142, 0x02),
+        (24, 142, 0x02),
+        (31, 142, 0x02),
+        (41, 142, 0x02),
+        (56, 142, 0x03),
+    ],
+    // 177
+    [
+        (2, 144, 0x02),
+        (9, 144, 0x02),
+        (23, 144, 0x02),
+        (40, 144, 0x03),
+        (2, 145, 0x02),
+        (9, 145, 0x02),
+        (23, 145, 0x02),
+        (40, 145, 0x03),
+        (2, 148, 0x02),
+        (9, 148, 0x02),
+        (23, 148, 0x02),
+        (40, 148, 0x03),
+        (2, 159, 0x02),
+        (9, 159, 0x02),
+        (23, 159, 0x02),
+        (40, 159, 0x03),
+    ],
+    // 178
+    [
+        (3, 144, 0x02),
+        (6, 144, 0x02),
+        (10, 144, 0x02),
+        (15, 144, 0x02),
+        (24, 144, 0x02),
+        (31, 144, 0x02),
+        (41, 144, 0x02),
+        (56, 144, 0x03),
+        (3, 145, 0x02),
+        (6, 145, 0x02),
+        (10, 145, 0x02),
+        (15, 145, 0x02),
+        (24, 145, 0x02),
+        (31, 145, 0x02),
+        (41, 145, 0x02),
+        (56, 145, 0x03),
+    ],
+    // 179
+    [
+        (3, 148, 0x02),
+        (6, 148, 0x02),
+        (10, 148, 0x02),
+        (15, 148, 0x02),
+        (24, 148, 0x02),
+        (31, 148, 0x02),
+        (41, 148, 0x02),
+        (56, 148, 0x03),
+        (3, 159, 0x02),
+        (6, 159, 0x02),
+        (10, 159, 0x02),
+        (15, 159, 0x02),
+        (24, 159, 0x02),
+        (31, 159, 0x02),
+        (41, 159, 0x02),
+        (56, 159, 0x03),
+    ],
+    // 180
+    [
+        (0, 171, 0x02),
+        (0, 206, 0x02),
+        (0, 215, 0x02),
+        (0, 225, 0x02),
+        (0, 236, 0x02),
+        (0, 237, 0x02),
+        (188, 0, 0x00),
+        (189, 0, 0x00),
+        (193, 0, 0x00),
+        (196, 0, 0x00),
+        (200, 0, 0x00),
+        (203, 0, 0x00),
+        (209, 0, 0x00),
+        (216, 0, 0x00),
+        (224, 0, 0x00),
+        (238, 0, 0x00),
+    ],
+    // 181
+    [
+        (1, 171, 0x02),
+        (22, 171, 0x03),
+        (1, 206, 0x02),
+        (22, 206, 0x03),
+        (1, 215, 0x02),
+        (22, 215, 0x03),
+        (1, 225, 0x02),
+        (22, 225, 0x03),
+        (1, 236, 0x02),
+        (22, 236, 0x03),
+        (1, 237, 0x02),
+        (22, 237, 0x03),
+        (0, 199, 0x02),
+        (0, 207, 0x02),
+        (0, 234, 0x02),
+        (0, 235, 0x02),
+    ],
+    // 182
+    [
+        (2, 171, 0x02),
+        (9, 171, 0x02),
+        (23, 171, 0x02),
+        (40, 171, 0x03),
+        (2, 206, 0x02),
+        (9, 206, 0x02),
+        (23, 206, 0x02),
+        (40, 206, 0x03),
+        (2, 215, 0x02),
+        (9, 215, 0x02),
+        (23, 215, 0x02),
+        (40, 215, 0x03),
+        (2, 225, 0x02),
+        (9, 225, 0x02),
+        (23, 225, 0x02),
+        (40, 225, 0x03),
+    ],
+    // 183
+    [
+        (3, 171, 0x02),
+        (6, 171, 0x02),
+        (10, 171, 0x02),
+        (15, 171, 0x02),
+        (24, 171, 0x02),
+        (31, 171, 0x02),
+        (41, 171, 0x02),
+        (56, 171, 0x03),
+        (3, 206, 0x02),
+        (6, 206, 0x02),
+        (10, 206, 0x02),
+        (15, 206, 0x02),
+        (24, 206, 0x02),
+        (31, 206, 0x02),
+        (41, 206, 0x02),
+        (56, 206, 0x03),
+    ],
+    // 184
+    [
+        (3, 215, 0x02),
+        (6, 215, 0x02),
+        (10, 215, 0x02),
+        (15, 215, 0x02),
+        (24, 215, 0x02),
+        (31, 215, 0x02),
+        (41, 215, 0x02),
+        (56, 215, 0x03),
+        (3, 225, 0x02),
+        (6, 225, 0x02),
+        (10, 225, 0x02),
+        (15, 225, 0x02),
+        (24, 225, 0x02),
+        (31, 225, 0x02),
+        (41, 225, 0x02),
+        (56, 225, 0x03),
+    ],
+    // 185
+    [
+        (2, 236, 0x02),
+        (9, 236, 0x02),
+        (23, 236, 0x02),
+        (40, 236, 0x03),
+        (2, 237, 0x02),
+        (9, 237, 0x02),
+        (23, 237, 0x02),
+        (40, 237, 0x03),
+        (1, 199, 0x02),
+        (22, 199, 0x03),
+        (1, 207, 0x02),
+        (22, 207, 0x03),
+        (1, 234, 0x02),
+        (22, 234, 0x03),
+        (1, 235, 0x02),
+        (22, 235, 0x03),
+    ],
+    // 186
+    [
+        (3, 236, 0x02),
+        (6, 236, 0x02),
+        (10, 236, 0x02),
+        (15, 236, 0x02),
+        (24, 236, 0x02),
+        (31, 236, 0x02),
+        (41, 236, 0x02),
+        (56, 236, 0x03),
+        (3, 237, 0x02),
+        (6, 237, 0x02),
+        (10, 237, 0x02),
+        (15, 237, 0x02),
+        (24, 237, 0x02),
+        (31, 237, 0x02),
+        (41, 237, 0x02),
+        (56, 237, 0x03),
+    ],
+    // 187
+    [
+        (2, 199, 0x02),
+        (9, 199, 0x02),
+        (23, 199, 0x02),
+        (40, 199, 0x03),
+        (2, 207, 0x02),
+        (9, 207, 0x02),
+        (23, 207, 0x02),
+        (40, 207, 0x03),
+        (2, 234, 0x02),
+        (9, 234, 0x02),
+        (23, 234, 0x02),
+        (40, 234, 0x03),
+        (2, 235, 0x02),
+        (9, 235, 0x02),
+        (23, 235, 0x02),
+        (40, 235, 0x03),
+    ],
+    // 188
+    [
+        (3, 199, 0x02),
+        (6, 199, 0x02),
+        (10, 199, 0x02),
+        (15, 199, 0x02),
+        (24, 199, 0x02),
+        (31, 199, 0x02),
+        (41, 199, 0x02),
+        (56, 199, 0x03),
+        (3, 207, 0x02),
+        (6, 207, 0x02),
+        (10, 207, 0x02),
+        (15, 207, 0x02),
+        (24, 207, 0x02),
+        (31, 207, 0x02),
+        (41, 207, 0x02),
+        (56, 207, 0x03),
+    ],
+    // 189
+    [
+        (3, 234, 0x02),
+        (6, 234, 0x02),
+        (10, 234, 0x02),
+        (15, 234, 0x02),
+        (24, 234, 0x02),
+        (31, 234, 0x02),
+        (41, 234, 0x02),
+        (56, 234, 0x03),
+        (3, 235, 0x02),
+        (6, 235, 0x02),
+        (10, 235, 0x02),
+        (15, 235, 0x02),
+        (24, 235, 0x02),
+        (31, 235, 0x02),
+        (41, 235, 0x02),
+        (56, 235, 0x03),
+    ],
+    // 190
+    [
+        (194, 0, 0x00),
+        (195, 0, 0x00),
+        (197, 0, 0x00),
+        (198, 0, 0x00),
+        (201, 0, 0x00),
+        (202, 0, 0x00),
+        (204, 0, 0x00),
+        (205, 0, 0x00),
+        (210, 0, 0x00),
+        (213, 0, 0x00),
+        (217, 0, 0x00),
+        (220, 0, 0x00),
+        (225, 0, 0x00),
+        (231, 0, 0x00),
+        (239, 0, 0x00),
+        (246, 0, 0x00),
+    ],
+    // 191
+    [
+        (0, 192, 0x02),
+        (0, 193, 0x02),
+        (0, 200, 0x02),
+        (0, 201, 0x02),
+        (0, 202, 0x02),
+        (0, 205, 0x02),
+        (0, 210, 0x02),
+        (0, 213, 0x02),
+        (0, 218, 0x02),
+        (0, 219, 0x02),
+        (0, 238, 0x02),
+        (0, 240, 0x02),
+        (0, 242, 0x02),
+        (0, 243, 0x02),
+        (0, 255, 0x02),
+        (206, 0, 0x00),
+    ],
+    // 192
+    [
+        (1, 192, 0x02),
+        (22, 192, 0x03),
+        (1, 193, 0x02),
+        (22, 193, 0x03),
+        (1, 200, 0x02),
+        (22, 200, 0x03),
+        (1, 201, 0x02),
+        (22, 201, 0x03),
+        (1, 202, 0x02),
+        (22, 202, 0x03),
+        (1, 205, 0x02),
+        (22, 205, 0x03),
+        (1, 210, 0x02),
+        (22, 210, 0x03),
+        (1, 213, 0x02),
+        (22, 213, 0x03),
+    ],
+    // 193
+    [
+        (2, 192, 0x02),
+        (9, 192, 0x02),
+        (23, 192, 0x02),
+        (40, 192, 0x03),
+        (2, 193, 0x02),
+        (9, 193, 0x02),
+        (23, 193, 0x02),
+        (40, 193, 0x03),
+        (2, 200, 0x02),
+        (9, 200, 0x02),
+        (23, 200, 0x02),
+        (40, 200, 0x03),
+        (2, 201, 0x02),
+        (9, 201, 0x02),
+        (23, 201, 0x02),
+        (40, 201, 0x03),
+    ],
+    // 194
+    [
+        (3, 192, 0x02),
+        (6, 192, 0x02),
+        (10, 192, 0x02),
+        (15, 192, 0x02),
+        (24, 192, 0x02),
+        (31, 192, 0x02),
+        (41, 192, 0x02),
+        (56, 192, 0x03),
+        (3, 193, 0x02),
+        (6, 193, 0x02),
+        (10, 193, 0x02),
+        (15, 193, 0x02),
+        (24, 193, 0x02),
+        (31, 193, 0x02),
+        (41, 193, 0x02),
+        (56, 193, 0x03),
+    ],
+    // 195
+    [
+        (3, 200, 0x02),
+        (6, 200, 0x02),
+        (10, 200, 0x02),
+        (15, 200, 0x02),
+        (24, 200, 0x02),
+        (31, 200, 0x02),
+        (41, 200, 0x02),
+        (56, 200, 0x03),
+        (3, 201, 0x02),
+        (6, 201, 0x02),
+        (10, 201, 0x02),
+        (15, 201, 0x02),
+        (24, 201, 0x02),
+        (31, 201, 0x02),
+        (41, 201, 0x02),
+        (56, 201, 0x03),
+    ],
+    // 196
+    [
+        (2, 202, 0x02),
+        (9, 202, 0x02),
+        (23, 202, 0x02),
+        (40, 202, 0x03),
+        (2, 205, 0x02),
+        (9, 205, 0x02),
+        (23, 205, 0x02),
+        (40, 205, 0x03),
+        (2, 210, 0x02),
+        (9, 210, 0x02),
+        (23, 210, 0x02),
+        (40, 210, 0x03),
+        (2, 213, 0x02),
+        (9, 213, 0x02),
+        (23, 213, 0x02),
+        (40, 213, 0x03),
+    ],
+    // 197
+    [
+        (3, 202, 0x02),
+        (6, 202, 0x02),
+        (10, 202, 0x02),
+        (15, 202, 0x02),
+        (24, 202, 0x02),
+        (31, 202, 0x02),
+        (41, 202, 0x02),
+        (56, 202, 0x03),
+        (3, 205, 0x02),
+        (6, 205, 0x02),
+        (10, 205, 0x02),
+        (15, 205, 0x02),
+        (24, 205, 0x02),
+        (31, 205, 0x02),
+        (41, 205, 0x02),
+        (56, 205, 0x03),
+    ],
+    // 198
+    [
+        (3, 210, 0x02),
+        (6, 210, 0x02),
+        (10, 210, 0x02),
+        (15, 210, 0x02),
+        (24, 210, 0x02),
+        (31, 210, 0x02),
+        (41, 210, 0x02),
+        (56, 210, 0x03),
+        (3, 213, 0x02),
+        (6, 213, 0x02),
+        (10, 213, 0x02),
+        (15, 213, 0x02),
+        (24, 213, 0x02),
+        (31, 213, 0x02),
+        (41, 213, 0x02),
+        (56, 213, 0x03),
+    ],
+    // 199
+    [
+        (1, 218, 0x02),
+        (22, 218, 0x03),
+        (1, 219, 0x02),
+        (22, 219, 0x03),
+        (1, 238, 0x02),
+        (22, 238, 0x03),
+        (1, 240, 0x02),
+        (22, 240, 0x03),
+        (1, 242, 0x02),
+        (22, 242, 0x03),
+        (1, 243, 0x02),
+        (22, 243, 0x03),
+        (1, 255, 0x02),
+        (22, 255, 0x03),
+        (0, 203, 0x02),
+        (0, 204, 0x02),
+    ],
+    // 200
+    [
+        (2, 218, 0x02),
+        (9, 218, 0x02),
+        (23, 218, 0x02),
+        (40, 218, 0x03),
+        (2, 219, 0x02),
+        (9, 219, 0x02),
+        (23, 219, 0x02),
+        (40, 219, 0x03),
+        (2, 238, 0x02),
+        (9, 238, 0x02),
+        (23, 238, 0x02),
+        (40, 238, 0x03),
+        (2, 240, 0x02),
+        (9, 240, 0x02),
+        (23, 240, 0x02),
+        (40, 240, 0x03),
+    ],
+    // 201
+    [
+        (3, 218, 0x02),
+        (6, 218, 0x02),
+        (10, 218, 0x02),
+        (15, 218, 0x02),
+        (24, 218, 0x02),
+        (31, 218, 0x02),
+        (41, 218, 0x02),
+        (56, 218, 0x03),
+        (3, 219, 0x02),
+        (6, 219, 0x02),
+        (10, 219, 0x02),
+        (15, 219, 0x02),
+        (24, 219, 0x02),
+        (31, 219, 0x02),
+        (41, 219, 0x02),
+        (56, 219, 0x03),
+    ],
+    // 202
+    [
+        (3, 238, 0x02),
+        (6, 238, 0x02),
+        (10, 238, 0x02),
+        (15, 238, 0x02),
+        (24, 238, 0x02),
+        (31, 238, 0x02),
+        (41, 238, 0x02),
+        (56, 238, 0x03),
+        (3, 240, 0x02),
+        (6, 240, 0x02),
+        (10, 240, 0x02),
+        (15, 240, 0x02),
+        (24, 240, 0x02),
+        (31, 240, 0x02),
+        (41, 240, 0x02),
+        (56, 240, 0x03),
+    ],
+    // 203
+    [
+        (2, 242, 0x02),
+        (9, 242, 0x02),
+        (23, 242, 0x02),
+        (40, 242, 0x03),
+        (2, 243, 0x02),
+        (9, 243, 0x02),
+        (23, 243, 0x02),
+        (40, 243, 0x03),
+        (2, 255, 0x02),
+        (9, 255, 0x02),
+        (23, 255, 0x02),
+        (40, 255, 0x03),
+        (1, 203, 0x02),
+        (22, 203, 0x03),
+        (1, 204, 0x02),
+        (22, 204, 0x03),
+    ],
+    // 204
+    [
+        (3, 242, 0x02),
+        (6, 242, 0x02),
+        (10, 242, 0x02),
+        (15, 242, 0x02),
+        (24, 242, 0x02),
+        (31, 242, 0x02),
+        (41, 242, 0x02),
+        (56, 242, 0x03),
+        (3, 243, 0x02),
+        (6, 243, 0x02),
+        (10, 243, 0x02),
+        (15, 243, 0x02),
+        (24, 243, 0x02),
+        (31, 243, 0x02),
+        (41, 243, 0x02),
+        (56, 243, 0x03),
+    ],
+    // 205
+    [
+        (3, 255, 0x02),
+        (6, 255, 0x02),
+        (10, 255, 0x02),
+        (15, 255, 0x02),
+        (24, 255, 0x02),
+        (31, 255, 0x02),
+        (41, 255, 0x02),
+        (56, 255, 0x03),
+        (2, 203, 0x02),
+        (9, 203, 0x02),
+        (23, 203, 0x02),
+        (40, 203, 0x03),
+        (2, 204, 0x02),
+        (9, 204, 0x02),
+        (23, 204, 0x02),
+        (40, 204, 0x03),
+    ],
+    // 206
+    [
+        (3, 203, 0x02),
+        (6, 203, 0x02),
+        (10, 203, 0x02),
+        (15, 203, 0x02),
+        (24, 203, 0x02),
+        (31, 203, 0x02),
+        (41, 203, 0x02),
+        (56, 203, 0x03),
+        (3, 204, 0x02),
+        (6, 204, 0x02),
+        (10, 204, 0x02),
+        (15, 204, 0x02),
+        (24, 204, 0x02),
+        (31, 204, 0x02),
+        (41, 204, 0x02),
+        (56, 204, 0x03),
+    ],
+    // 207
+    [
+        (211, 0, 0x00),
+        (212, 0, 0x00),
+        (214, 0, 0x00),
+        (215, 0, 0x00),
+        (218, 0, 0x00),
+        (219, 0, 0x00),
+        (221, 0, 0x00),
+        (222, 0, 0x00),
+        (226, 0, 0x00),
+        (228, 0, 0x00),
+        (232, 0, 0x00),
+        (235, 0, 0x00),
+        (240, 0, 0x00),
+        (243, 0, 0x00),
+        (247, 0, 0x00),
+        (250, 0, 0x00),
+    ],
+    // 208
+    [
+        (0, 211, 0x02),
+        (0, 212, 0x02),
+        (0, 214, 0x02),
+        (0, 221, 0x02),
+        (0, 222, 0x02),
+        (0, 223, 0x02),
+        (0, 241, 0x02),
+        (0, 244, 0x02),
+        (0, 245, 0x02),
+        (0, 246, 0x02),
+        (0, 247, 0x02),
+        (0, 248, 0x02),
+        (0, 250, 0x02),
+        (0, 251, 0x02),
+        (0, 252, 0x02),
+        (0, 253, 0x02),
+    ],
+    // 209
+    [
+        (1, 211, 0x02),
+        (22, 211, 0x03),
+        (1, 212, 0x02),
+        (22, 212, 0x03),
+        (1, 214, 0x02),
+        (22, 214, 0x03),
+        (1, 221, 0x02),
+        (22, 221, 0x03),
+        (1, 222, 0x02),
+        (22, 222, 0x03),
+        (1, 223, 0x02),
+        (22, 223, 0x03),
+        (1, 241, 0x02),
+        (22, 241, 0x03),
+        (1, 244, 0x02),
+        (22, 244, 0x03),
+    ],
+    // 210
+    [
+        (2, 211, 0x02),
+        (9, 211, 0x02),
+        (23, 211, 0x02),
+        (40, 211, 0x03),
+        (2, 212, 0x02),
+        (9, 212, 0x02),
+        (23, 212, 0x02),
+        (40, 212, 0x03),
+        (2, 214, 0x02),
+        (9, 214, 0x02),
+        (23, 214, 0x02),
+        (40, 214, 0x03),
+        (2, 221, 0x02),
+        (9, 221, 0x02),
+        (23, 221, 0x02),
+        (40, 221, 0x03),
+    ],
+    // 211
+    [
+        (3, 211, 0x02),
+        (6, 211, 0x02),
+        (10, 211, 0x02),
+        (15, 211, 0x02),
+        (24, 211, 0x02),
+        (31, 211, 0x02),
+        (41, 211, 0x02),
+        (56, 211, 0x03),
+        (3, 212, 0x02),
+        (6, 212, 0x02),
+        (10, 212, 0x02),
+        (15, 212, 0x02),
+        (24, 212, 0x02),
+        (31, 212, 0x02),
+        (41, 212, 0x02),
+        (56, 212, 0x03),
+    ],
+    // 212
+    [
+        (3, 214, 0x02),
+        (6, 214, 0x02),
+        (10, 214, 0x02),
+        (15, 214, 0x02),
+        (24, 214, 0x02),
+        (31, 214, 0x02),
+        (41, 214, 0x02),
+        (56, 214, 0x03),
+        (3, 221, 0x02),
+        (6, 221, 0x02),
+        (10, 221, 0x02),
+        (15, 221, 0x02),
+        (24, 221, 0x02),
+        (31, 221, 0x02),
+        (41, 221, 0x02),
+        (56, 221, 0x03),
+    ],
+    // 213
+    [
+        (2, 222, 0x02),
+        (9, 222, 0x02),
+        (23, 222, 0x02),
+        (40, 222, 0x03),
+        (2, 223, 0x02),
+        (9, 223, 0x02),
+        (23, 223, 0x02),
+        (40, 223, 0x03),
+        (2, 241, 0x02),
+        (9, 241, 0x02),
+        (23, 241, 0x02),
+        (40, 241, 0x03),
+        (2, 244, 0x02),
+        (9, 244, 0x02),
+        (23, 244, 0x02),
+        (40, 244, 0x03),
+    ],
+    // 214
+    [
+        (3, 222, 0x02),
+        (6, 222, 0x02),
+        (10, 222, 0x02),
+        (15, 222, 0x02),
+        (24, 222, 0x02),
+        (31, 222, 0x02),
+        (41, 222, 0x02),
+        (56, 222, 0x03),
+        (3, 223, 0x02),
+        (6, 223, 0x02),
+        (10, 223, 0x02),
+        (15, 223, 0x02),
+        (24, 223, 0x02),
+        (31, 223, 0x02),
+        (41, 223, 0x02),
+        (56, 223, 0x03),
+    ],
+    // 215
+    [
+        (3, 241, 0x02),
+        (6, 241, 0x02),
+        (10, 241, 0x02),
+        (15, 241, 0x02),
+        (24, 241, 0x02),
+        (31, 241, 0x02),
+        (41, 241, 0x02),
+        (56, 241, 0x03),
+        (3, 244, 0x02),
+        (6, 244, 0x02),
+        (10, 244, 0x02),
+        (15, 244, 0x02),
+        (24, 244, 0x02),
+        (31, 244, 0x02),
+        (41, 244, 0x02),
+        (56, 244, 0x03),
+    ],
+    // 216
+    [
+        (1, 245, 0x02),
+        (22, 245, 0x03),
+        (1, 246, 0x02),
+        (22, 246, 0x03),
+        (1, 247, 0x02),
+        (22, 247, 0x03),
+        (1, 248, 0x02),
+        (22, 248, 0x03),
+        (1, 250, 0x02),
+        (22, 250, 0x03),
+        (1, 251, 0x02),
+        (22, 251, 0x03),
+        (1, 252, 0x02),
+        (22, 252, 0x03),
+        (1, 253, 0x02),
+        (22, 253, 0x03),
+    ],
+    // 217
+    [
+        (2, 245, 0x02),
+        (9, 245, 0x02),
+        (23, 245, 0x02),
+        (40, 245, 0x03),
+        (2, 246, 0x02),
+        (9, 246, 0x02),
+        (23, 246, 0x02),
+        (40, 246, 0x03),
+        (2, 247, 0x02),
+        (9, 247, 0x02),
+        (23, 247, 0x02),
+        (40, 247, 0x03),
+        (2, 248, 0x02),
+        (9, 248, 0x02),
+        (23, 248, 0x02),
+        (40, 248, 0x03),
+    ],
+    // 218
+    [
+        (3, 245, 0x02),
+        (6, 245, 0x02),
+        (10, 245, 0x02),
+        (15, 245, 0x02),
+        (24, 245, 0x02),
+        (31, 245, 0x02),
+        (41, 245, 0x02),
+        (56, 245, 0x03),
+        (3, 246, 0x02),
+        (6, 246, 0x02),
+        (10, 246, 0x02),
+        (15, 246, 0x02),
+        (24, 246, 0x02),
+        (31, 246, 0x02),
+        (41, 246, 0x02),
+        (56, 246, 0x03),
+    ],
+    // 219
+    [
+        (3, 247, 0x02),
+        (6, 247, 0x02),
+        (10, 247, 0x02),
+        (15, 247, 0x02),
+        (24, 247, 0x02),
+        (31, 247, 0x02),
+        (41, 247, 0x02),
+        (56, 247, 0x03),
+        (3, 248, 0x02),
+        (6, 248, 0x02),
+        (10, 248, 0x02),
+        (15, 248, 0x02),
+        (24, 248, 0x02),
+        (31, 248, 0x02),
+        (41, 248, 0x02),
+        (56, 248, 0x03),
+    ],
+    // 220
+    [
+        (2, 250, 0x02),
+        (9, 250, 0x02),
+        (23, 250, 0x02),
+        (40, 250, 0x03),
+        (2, 251, 0x02),
+        (9, 251, 0x02),
+        (23, 251, 0x02),
+        (40, 251, 0x03),
+        (2, 252, 0x02),
+        (9, 252, 0x02),
+        (23, 252, 0x02),
+        (40, 252, 0x03),
+        (2, 253, 0x02),
+        (9, 253, 0x02),
+        (23, 253, 0x02),
+        (40, 253, 0x03),
+    ],
+    // 221
+    [
+        (3, 250, 0x02),
+        (6, 250, 0x02),
+        (10, 250, 0x02),
+        (15, 250, 0x02),
+        (24, 250, 0x02),
+        (31, 250, 0x02),
+        (41, 250, 0x02),
+        (56, 250, 0x03),
+        (3, 251, 0x02),
+        (6, 251, 0x02),
+        (10, 251, 0x02),
+        (15, 251, 0x02),
+        (24, 251, 0x02),
+        (31, 251, 0x02),
+        (41, 251, 0x02),
+        (56, 251, 0x03),
+    ],
+    // 222
+    [
+        (3, 252, 0x02),
+        (6, 252, 0x02),
+        (10, 252, 0x02),
+        (15, 252, 0x02),
+        (24, 252, 0x02),
+        (31, 252, 0x02),
+        (41, 252, 0x02),
+        (56, 252, 0x03),
+        (3, 253, 0x02),
+        (6, 253, 0x02),
+        (10, 253, 0x02),
+        (15, 253, 0x02),
+        (24, 253, 0x02),
+        (31, 253, 0x02),
+        (41, 253, 0x02),
+        (56, 253, 0x03),
+    ],
+    // 223
+    [
+        (0, 254, 0x02),
+        (227, 0, 0x00),
+        (229, 0, 0x00),
+        (230, 0, 0x00),
+        (233, 0, 0x00),
+        (234, 0, 0x00),
+        (236, 0, 0x00),
+        (237, 0, 0x00),
+        (241, 0, 0x00),
+        (242, 0, 0x00),
+        (244, 0, 0x00),
+        (245, 0, 0x00),
+        (248, 0, 0x00),
+        (249, 0, 0x00),
+        (251, 0, 0x00),
+        (252, 0, 0x00),
+    ],
+    // 224
+    [
+        (1, 254, 0x02),
+        (22, 254, 0x03),
+        (0, 2, 0x02),
+        (0, 3, 0x02),
+        (0, 4, 0x02),
+        (0, 5, 0x02),
+        (0, 6, 0x02),
+        (0, 7, 0x02),
+        (0, 8, 0x02),
+        (0, 11, 0x02),
+        (0, 12, 0x02),
+        (0, 14, 0x02),
+        (0, 15, 0x02),
+        (0, 16, 0x02),
+        (0, 17, 0x02),
+        (0, 18, 0x02),
+    ],
+    // 225
+    [
+        (2, 254, 0x02),
+        (9, 254, 0x02),
+        (23, 254, 0x02),
+        (40, 254, 0x03),
+        (1, 2, 0x02),
+        (22, 2, 0x03),
+        (1, 3, 0x02),
+        (22, 3, 0x03),
+        (1, 4, 0x02),
+        (22, 4, 0x03),
+        (1, 5, 0x02),
+        (22, 5, 0x03),
+        (1, 6, 0x02),
+        (22, 6, 0x03),
+        (1, 7, 0x02),
+        (22, 7, 0x03),
+    ],
+    // 226
+    [
+        (3, 254, 0x02),
+        (6, 254, 0x02),
+        (10, 254, 0x02),
+        (15, 254, 0x02),
+        (24, 254, 0x02),
+        (31, 254, 0x02),
+        (41, 254, 0x02),
+        (56, 254, 0x03),
+        (2, 2, 0x02),
+        (9, 2, 0x02),
+        (23, 2, 0x02),
+        (40, 2, 0x03),
+        (2, 3, 0x02),
+        (9, 3, 0x02),
+        (23, 3, 0x02),
+        (40, 3, 0x03),
+    ],
+    // 227
+    [
+        (3, 2, 0x02),
+        (6, 2, 0x02),
+        (10, 2, 0x02),
+        (15, 2, 0x02),
+        (24, 2, 0x02),
+        (31, 2, 0x02),
+        (41, 2, 0x02),
+        (56, 2, 0x03),
+        (3, 3, 0x02),
+        (6, 3, 0x02),
+        (10, 3, 0x02),
+        (15, 3, 0x02),
+        (24, 3, 0x02),
+        (31, 3, 0x02),
+        (41, 3, 0x02),
+        (56, 3, 0x03),
+    ],
+    // 228
+    [
+        (2, 4, 0x02),
+        (9, 4, 0x02),
+        (23, 4, 0x02),
+        (40, 4, 0x03),
+        (2, 5, 0x02),
+        (9, 5, 0x02),
+        (23, 5, 0x02),
+        (40, 5, 0x03),
+        (2, 6, 0x02),
+        (9, 6, 0x02),
+        (23, 6, 0x02),
+        (40, 6, 0x03),
+        (2, 7, 0x02),
+        (9, 7, 0x02),
+        (23, 7, 0x02),
+        (40, 7, 0x03),
+    ],
+    // 229
+    [
+        (3, 4, 0x02),
+        (6, 4, 0x02),
+        (10, 4, 0x02),
+        (15, 4, 0x02),
+        (24, 4, 0x02),
+        (31, 4, 0x02),
+        (41, 4, 0x02),
+        (56, 4, 0x03),
+        (3, 5, 0x02),
+        (6, 5, 0x02),
+        (10, 5, 0x02),
+        (15, 5, 0x02),
+        (24, 5, 0x02),
+        (31, 5, 0x02),
+        (41, 5, 0x02),
+        (56, 5, 0x03),
+    ],
+    // 230
+    [
+        (3, 6, 0x02),
+        (6, 6, 0x02),
+        (10, 6, 0x02),
+        (15, 6, 0x02),
+        (24, 6, 0x02),
+        (31, 6, 0x02),
+        (41, 6, 0x02),
+        (56, 6, 0x03),
+        (3, 7, 0x02),
+        (6, 7, 0x02),
+        (10, 7, 0x02),
+        (15, 7, 0x02),
+        (24, 7, 0x02),
+        (31, 7, 0x02),
+        (41, 7, 0x02),
+        (56, 7, 0x03),
+    ],
+    // 231
+    [
+        (1, 8, 0x02),
+        (22, 8, 0x03),
+        (1, 11, 0x02),
+        (22, 11, 0x03),
+        (1, 12, 0x02),
+        (22, 12, 0x03),
+        (1, 14, 0x02),
+        (22, 14, 0x03),
+        (1, 15, 0x02),
+        (22, 15, 0x03),
+        (1, 16, 0x02),
+        (22, 16, 0x03),
+        (1, 17, 0x02),
+        (22, 17, 0x03),
+        (1, 18, 0x02),
+        (22, 18, 0x03),
+    ],
+    // 232
+    [
+        (2, 8, 0x02),
+        (9, 8, 0x02),
+        (23, 8, 0x02),
+        (40, 8, 0x03),
+        (2, 11, 0x02),
+        (9, 11, 0x02),
+        (23, 11, 0x02),
+        (40, 11, 0x03),
+        (2, 12, 0x02),
+        (9, 12, 0x02),
+        (23, 12, 0x02),
+        (40, 12, 0x03),
+        (2, 14, 0x02),
+        (9, 14, 0x02),
+        (23, 14, 0x02),
+        (40, 14, 0x03),
+    ],
+    // 233
+    [
+        (3, 8, 0x02),
+        (6, 8, 0x02),
+        (10, 8, 0x02),
+        (15, 8, 0x02),
+        (24, 8, 0x02),
+        (31, 8, 0x02),
+        (41, 8, 0x02),
+        (56, 8, 0x03),
+        (3, 11, 0x02),
+        (6, 11, 0x02),
+        (10, 11, 0x02),
+        (15, 11, 0x02),
+        (24, 11, 0x02),
+        (31, 11, 0x02),
+        (41, 11, 0x02),
+        (56, 11, 0x03),
+    ],
+    // 234
+    [
+        (3, 12, 0x02),
+        (6, 12, 0x02),
+        (10, 12, 0x02),
+        (15, 12, 0x02),
+        (24, 12, 0x02),
+        (31, 12, 0x02),
+        (41, 12, 0x02),
+        (56, 12, 0x03),
+        (3, 14, 0x02),
+        (6, 14, 0x02),
+        (10, 14, 0x02),
+        (15, 14, 0x02),
+        (24, 14, 0x02),
+        (31, 14, 0x02),
+        (41, 14, 0x02),
+        (56, 14, 0x03),
+    ],
+    // 235
+    [
+        (2, 15, 0x02),
+        (9, 15, 0x02),
+        (23, 15, 0x02),
+        (40, 15, 0x03),
+        (2, 16, 0x02),
+        (9, 16, 0x02),
+        (23, 16, 0x02),
+        (40, 16, 0x03),
+        (2, 17, 0x02),
+        (9, 17, 0x02),
+        (23, 17, 0x02),
+        (40, 17, 0x03),
+        (2, 18, 0x02),
+        (9, 18, 0x02),
+        (23, 18, 0x02),
+        (40, 18, 0x03),
+    ],
+    // 236
+    [
+        (3, 15, 0x02),
+        (6, 15, 0x02),
+        (10, 15, 0x02),
+        (15, 15, 0x02),
+        (24, 15, 0x02),
+        (31, 15, 0x02),
+        (41, 15, 0x02),
+        (56, 15, 0x03),
+        (3, 16, 0x02),
+        (6, 16, 0x02),
+        (10, 16, 0x02),
+        (15, 16, 0x02),
+        (24, 16, 0x02),
+        (31, 16, 0x02),
+        (41, 16, 0x02),
+        (56, 16, 0x03),
+    ],
+    // 237
+    [
+        (3, 17, 0x02),
+        (6, 17, 0x02),
+        (10, 17, 0x02),
+        (15, 17, 0x02),
+        (24, 17, 0x02),
+        (31, 17, 0x02),
+        (41, 17, 0x02),
+        (56, 17, 0x03),
+        (3, 18, 0x02),
+        (6, 18, 0x02),
+        (10, 18, 0x02),
+        (15, 18, 0x02),
+        (24, 18, 0x02),
+        (31, 18, 0x02),
+        (41, 18, 0x02),
+        (56, 18, 0x03),
+    ],
+    // 238
+    [
+        (0, 19, 0x02),
+        (0, 20, 0x02),
+        (0, 21, 0x02),
+        (0, 23, 0x02),
+        (0, 24, 0x02),
+        (0, 25, 0x02),
+        (0, 26, 0x02),
+        (0, 27, 0x02),
+        (0, 28, 0x02),
+        (0, 29, 0x02),
+        (0, 30, 0x02),
+        (0, 31, 0x02),
+        (0, 127, 0x02),
+        (0, 220, 0x02),
+        (0, 249, 0x02),
+        (253, 0, 0x00),
+    ],
+    // 239
+    [
+        (1, 19, 0x02),
+        (22, 19, 0x03),
+        (1, 20, 0x02),
+        (22, 20, 0x03),
+        (1, 21, 0x02),
+        (22, 21, 0x03),
+        (1, 23, 0x02),
+        (22, 23, 0x03),
+        (1, 24, 0x02),
+        (22, 24, 0x03),
+        (1, 25, 0x02),
+        (22, 25, 0x03),
+        (1, 26, 0x02),
+        (22, 26, 0x03),
+        (1, 27, 0x02),
+        (22, 27, 0x03),
+    ],
+    // 240
+    [
+        (2, 19, 0x02),
+        (9, 19, 0x02),
+        (23, 19, 0x02),
+        (40, 19, 0x03),
+        (2, 20, 0x02),
+        (9, 20, 0x02),
+        (23, 20, 0x02),
+        (40, 20, 0x03),
+        (2, 21, 0x02),
+        (9, 21, 0x02),
+        (23, 21, 0x02),
+        (40, 21, 0x03),
+        (2, 23, 0x02),
+        (9, 23, 0x02),
+        (23, 23, 0x02),
+        (40, 23, 0x03),
+    ],
+    // 241
+    [
+        (3, 19, 0x02),
+        (6, 19, 0x02),
+        (10, 19, 0x02),
+        (15, 19, 0x02),
+        (24, 19, 0x02),
+        (31, 19, 0x02),
+        (41, 19, 0x02),
+        (56, 19, 0x03),
+        (3, 20, 0x02),
+        (6, 20, 0x02),
+        (10, 20, 0x02),
+        (15, 20, 0x02),
+        (24, 20, 0x02),
+        (31, 20, 0x02),
+        (41, 20, 0x02),
+        (56, 20, 0x03),
+    ],
+    // 242
+    [
+        (3, 21, 0x02),
+        (6, 21, 0x02),
+        (10, 21, 0x02),
+        (15, 21, 0x02),
+        (24, 21, 0x02),
+        (31, 21, 0x02),
+        (41, 21, 0x02),
+        (56, 21, 0x03),
+        (3, 23, 0x02),
+        (6, 23, 0x02),
+        (10, 23, 0x02),
+        (15, 23, 0x02),
+        (24, 23, 0x02),
+        (31, 23, 0x02),
+        (41, 23, 0x02),
+        (56, 23, 0x03),
+    ],
+    // 243
+    [
+        (2, 24, 0x02),
+        (9, 24, 0x02),
+        (23, 24, 0x02),
+        (40, 24, 0x03),
+        (2, 25, 0x02),
+        (9, 25, 0x02),
+        (23, 25, 0x02),
+        (40, 25, 0x03),
+        (2, 26, 0x02),
+        (9, 26, 0x02),
+        (23, 26, 0x02),
+        (40, 26, 0x03),
+        (2, 27, 0x02),
+        (9, 27, 0x02),
+        (23, 27, 0x02),
+        (40, 27, 0x03),
+    ],
+    // 244
+    [
+        (3, 24, 0x02),
+        (6, 24, 0x02),
+        (10, 24, 0x02),
+        (15, 24, 0x02),
+        (24, 24, 0x02),
+        (31, 24, 0x02),
+        (41, 24, 0x02),
+        (56, 24, 0x03),
+        (3, 25, 0x02),
+        (6, 25, 0x02),
+        (10, 25, 0x02),
+        (15, 25, 0x02),
+        (24, 25, 0x02),
+        (31, 25, 0x02),
+        (41, 25, 0x02),
+        (56, 25, 0x03),
+    ],
+    // 245
+    [
+        (3, 26, 0x02),
+        (6, 26, 0x02),
+        (10, 26, 0x02),
+        (15, 26, 0x02),
+        (24, 26, 0x02),
+        (31, 26, 0x02),
+        (41, 26, 0x02),
+        (56, 26, 0x03),
+        (3, 27, 0x02),
+        (6, 27, 0x02),
+        (10, 27, 0x02),
+        (15, 27, 0x02),
+        (24, 27, 0x02),
+        (31, 27, 0x02),
+        (41, 27, 0x02),
+        (56, 27, 0x03),
+    ],
+    // 246
+    [
+        (1, 28, 0x02),
+        (22, 28, 0x03),
+        (1, 29, 0x02),
+        (22, 29, 0x03),
+        (1, 30, 0x02),
+        (22, 30, 0x03),
+        (1, 31, 0x02),
+        (22, 31, 0x03),
+        (1, 127, 0x02),
+        (22, 127, 0x03),
+        (1, 220, 0x02),
+        (22, 220, 0x03),
+        (1, 249, 0x02),
+        (22, 249, 0x03),
+        (254, 0, 0x00),
+        (255, 0, 0x00),
+    ],
+    // 247
+    [
+        (2, 28, 0x02),
+        (9, 28, 0x02),
+        (23, 28, 0x02),
+        (40, 28, 0x03),
+        (2, 29, 0x02),
+        (9, 29, 0x02),
+        (23, 29, 0x02),
+        (40, 29, 0x03),
+        (2, 30, 0x02),
+        (9, 30, 0x02),
+        (23, 30, 0x02),
+        (40, 30, 0x03),
+        (2, 31, 0x02),
+        (9, 31, 0x02),
+        (23, 31, 0x02),
+        (40, 31, 0x03),
+    ],
+    // 248
+    [
+        (3, 28, 0x02),
+        (6, 28, 0x02),
+        (10, 28, 0x02),
+        (15, 28, 0x02),
+        (24, 28, 0x02),
+        (31, 28, 0x02),
+        (41, 28, 0x02),
+        (56, 28, 0x03),
+        (3, 29, 0x02),
+        (6, 29, 0x02),
+        (10, 29, 0x02),
+        (15, 29, 0x02),
+        (24, 29, 0x02),
+        (31, 29, 0x02),
+        (41, 29, 0x02),
+        (56, 29, 0x03),
+    ],
+    // 249
+    [
+        (3, 30, 0x02),
+        (6, 30, 0x02),
+        (10, 30, 0x02),
+        (15, 30, 0x02),
+        (24, 30, 0x02),
+        (31, 30, 0x02),
+        (41, 30, 0x02),
+        (56, 30, 0x03),
+        (3, 31, 0x02),
+        (6, 31, 0x02),
+        (10, 31, 0x02),
+        (15, 31, 0x02),
+        (24, 31, 0x02),
+        (31, 31, 0x02),
+        (41, 31, 0x02),
+        (56, 31, 0x03),
+    ],
+    // 250
+    [
+        (2, 127, 0x02),
+        (9, 127, 0x02),
+        (23, 127, 0x02),
+        (40, 127, 0x03),
+        (2, 220, 0x02),
+        (9, 220, 0x02),
+        (23, 220, 0x02),
+        (40, 220, 0x03),
+        (2, 249, 0x02),
+        (9, 249, 0x02),
+        (23, 249, 0x02),
+        (40, 249, 0x03),
+        (0, 10, 0x02),
+        (0, 13, 0x02),
+        (0, 22, 0x02),
+        (0, 0, 0x04),
+    ],
+    // 251
+    [
+        (3, 127, 0x02),
+        (6, 127, 0x02),
+        (10, 127, 0x02),
+        (15, 127, 0x02),
+        (24, 127, 0x02),
+        (31, 127, 0x02),
+        (41, 127, 0x02),
+        (56, 127, 0x03),
+        (3, 220, 0x02),
+        (6, 220, 0x02),
+        (10, 220, 0x02),
+        (15, 220, 0x02),
+        (24, 220, 0x02),
+        (31, 220, 0x02),
+        (41, 220, 0x02),
+        (56, 220, 0x03),
+    ],
+    // 252
+    [
+        (3, 249, 0x02),
+        (6, 249, 0x02),
+        (10, 249, 0x02),
+        (15, 249, 0x02),
+        (24, 249, 0x02),
+        (31, 249, 0x02),
+        (41, 249, 0x02),
+        (56, 249, 0x03),
+        (1, 10, 0x02),
+        (22, 10, 0x03),
+        (1, 13, 0x02),
+        (22, 13, 0x03),
+        (1, 22, 0x02),
+        (22, 22, 0x03),
+        (0, 0, 0x04),
+        (0, 0, 0x05),
+    ],
+    // 253
+    [
+        (2, 10, 0x02),
+        (9, 10, 0x02),
+        (23, 10, 0x02),
+        (40, 10, 0x03),
+        (2, 13, 0x02),
+        (9, 13, 0x02),
+        (23, 13, 0x02),
+        (40, 13, 0x03),
+        (2, 22, 0x02),
+        (9, 22, 0x02),
+        (23, 22, 0x02),
+        (40, 22, 0x03),
+        (0, 0, 0x04),
+        (0, 0, 0x04),
+        (0, 0, 0x04),
+        (0, 0, 0x05),
+    ],
+    // 254
+    [
+        (3, 10, 0x02),
+        (6, 10, 0x02),
+        (10, 10, 0x02),
+        (15, 10, 0x02),
+        (24, 10, 0x02),
+        (31, 10, 0x02),
+        (41, 10, 0x02),
+        (56, 10, 0x03),
+        (3, 13, 0x02),
+        (6, 13, 0x02),
+        (10, 13, 0x02),
+        (15, 13, 0x02),
+        (24, 13, 0x02),
+        (31, 13, 0x02),
+        (41, 13, 0x02),
+        (56, 13, 0x03),
+    ],
+    // 255
+    [
+        (3, 22, 0x02),
+        (6, 22, 0x02),
+        (10, 22, 0x02),
+        (15, 22, 0x02),
+        (24, 22, 0x02),
+        (31, 22, 0x02),
+        (41, 22, 0x02),
+        (56, 22, 0x03),
+        (0, 0, 0x04),
+        (0, 0, 0x04),
+        (0, 0, 0x04),
+        (0, 0, 0x04),
+        (0, 0, 0x04),
+        (0, 0, 0x04),
+        (0, 0, 0x04),
+        (0, 0, 0x05),
+    ],
+];
diff --git a/src/h3/qpack/mod.rs b/src/h3/qpack/mod.rs
new file mode 100644
index 0000000..6f2bdda
--- /dev/null
+++ b/src/h3/qpack/mod.rs
@@ -0,0 +1,162 @@
+// Copyright (C) 2019, Cloudflare, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright notice,
+//       this list of conditions and the following disclaimer.
+//
+//     * Redistributions in binary form must reproduce the above copyright
+//       notice, this list of conditions and the following disclaimer in the
+//       documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+//! HTTP/3 header compression (QPACK).
+
+const INDEXED: u8 = 0b1000_0000;
+const INDEXED_WITH_POST_BASE: u8 = 0b0001_0000;
+const LITERAL: u8 = 0b0010_0000;
+const LITERAL_WITH_NAME_REF: u8 = 0b0100_0000;
+
+/// A specialized [`Result`] type for quiche QPACK operations.
+///
+/// This type is used throughout quiche's QPACK public API for any operation
+/// that can produce an error.
+///
+/// [`Result`]: https://doc.rust-lang.org/std/result/enum.Result.html
+pub type Result<T> = std::result::Result<T, Error>;
+
+/// A QPACK error.
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum Error {
+    /// The provided buffer is too short.
+    BufferTooShort,
+
+    /// The QPACK header block's huffman encoding is invalid.
+    InvalidHuffmanEncoding,
+
+    /// The QPACK static table index provided doesn't exist.
+    InvalidStaticTableIndex,
+
+    /// The decoded QPACK header name or value is not valid.
+    InvalidHeaderValue,
+
+    /// The decoded header list exceeded the size limit.
+    HeaderListTooLarge,
+}
+
+impl std::fmt::Display for Error {
+    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+        write!(f, "{:?}", self)
+    }
+}
+
+impl std::error::Error for Error {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        None
+    }
+}
+
+impl std::convert::From<crate::octets::BufferTooShortError> for Error {
+    fn from(_err: crate::octets::BufferTooShortError) -> Self {
+        Error::BufferTooShort
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::*;
+
+    use super::*;
+
+    #[test]
+    fn encode_decode() {
+        let mut encoded = [0u8; 240];
+
+        let headers = vec![
+            h3::Header::new(":path", "/rsrc.php/v3/yn/r/rIPZ9Qkrdd9.png"),
+            h3::Header::new("accept-encoding", "gzip, deflate, br"),
+            h3::Header::new("accept-language", "en-US,en;q=0.9"),
+            h3::Header::new("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.70 Safari/537.36"),
+            h3::Header::new("accept", "image/webp,image/apng,image/*,*/*;q=0.8"),
+            h3::Header::new("referer", "https://static.xx.fbcdn.net/rsrc.php/v3/yT/l/0,cross/dzXGESIlGQQ.css"),
+            h3::Header::new(":authority", "static.xx.fbcdn.net"),
+            h3::Header::new(":scheme", "https"),
+            h3::Header::new(":method", "GET"),
+        ];
+
+        let mut enc = Encoder::new();
+        assert_eq!(enc.encode(&headers, &mut encoded), Ok(240));
+
+        let mut dec = Decoder::new();
+        assert_eq!(dec.decode(&mut encoded, std::u64::MAX), Ok(headers));
+    }
+
+    #[test]
+    fn lower_case() {
+        let mut encoded = [0u8; 35];
+
+        let headers_expected = vec![
+            crate::h3::Header::new(":status", "200"),
+            crate::h3::Header::new(":path", "/HeLlO"),
+            crate::h3::Header::new("woot", "woot"),
+            crate::h3::Header::new("hello", "WorlD"),
+            crate::h3::Header::new("foo", "BaR"),
+        ];
+
+        // Header.
+        let headers_in = vec![
+            crate::h3::Header::new(":StAtUs", "200"),
+            crate::h3::Header::new(":PaTh", "/HeLlO"),
+            crate::h3::Header::new("WooT", "woot"),
+            crate::h3::Header::new("hello", "WorlD"),
+            crate::h3::Header::new("fOo", "BaR"),
+        ];
+
+        let mut enc = Encoder::new();
+        assert_eq!(enc.encode(&headers_in, &mut encoded), Ok(35));
+
+        let mut dec = Decoder::new();
+        let headers_out = dec.decode(&mut encoded, std::u64::MAX).unwrap();
+
+        assert_eq!(headers_expected, headers_out);
+
+        // HeaderRef.
+        let headers_in = vec![
+            crate::h3::HeaderRef::new(":StAtUs", "200"),
+            crate::h3::HeaderRef::new(":PaTh", "/HeLlO"),
+            crate::h3::HeaderRef::new("WooT", "woot"),
+            crate::h3::HeaderRef::new("hello", "WorlD"),
+            crate::h3::HeaderRef::new("fOo", "BaR"),
+        ];
+
+        let mut enc = Encoder::new();
+        assert_eq!(enc.encode(&headers_in, &mut encoded), Ok(35));
+
+        let mut dec = Decoder::new();
+        let headers_out = dec.decode(&mut encoded, std::u64::MAX).unwrap();
+
+        assert_eq!(headers_expected, headers_out);
+    }
+}
+
+pub use decoder::Decoder;
+pub use encoder::Encoder;
+
+mod decoder;
+mod encoder;
+mod huffman;
+mod static_table;
diff --git a/src/h3/qpack/static_table.rs b/src/h3/qpack/static_table.rs
new file mode 100644
index 0000000..4010d12
--- /dev/null
+++ b/src/h3/qpack/static_table.rs
@@ -0,0 +1,136 @@
+// Copyright (C) 2020, Cloudflare, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright notice,
+//       this list of conditions and the following disclaimer.
+//
+//     * Redistributions in binary form must reproduce the above copyright
+//       notice, this list of conditions and the following disclaimer in the
+//       documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+pub const STATIC_TABLE: [(&str, &str); 99] = [
+    (":authority", ""),
+    (":path", "/"),
+    ("age", "0"),
+    ("content-disposition", ""),
+    ("content-length", "0"),
+    ("cookie", ""),
+    ("date", ""),
+    ("etag", ""),
+    ("if-modified-since", ""),
+    ("if-none-match", ""),
+    ("last-modified", ""),
+    ("link", ""),
+    ("location", ""),
+    ("referer", ""),
+    ("set-cookie", ""),
+    (":method", "CONNECT"),
+    (":method", "DELETE"),
+    (":method", "GET"),
+    (":method", "HEAD"),
+    (":method", "OPTIONS"),
+    (":method", "POST"),
+    (":method", "PUT"),
+    (":scheme", "http"),
+    (":scheme", "https"),
+    (":status", "103"),
+    (":status", "200"),
+    (":status", "304"),
+    (":status", "404"),
+    (":status", "503"),
+    ("accept", "*/*"),
+    ("accept", "application/dns-message"),
+    ("accept-encoding", "gzip, deflate, br"),
+    ("accept-ranges", "bytes"),
+    ("access-control-allow-headers", "cache-control"),
+    ("access-control-allow-headers", "content-type"),
+    ("access-control-allow-origin", "*"),
+    ("cache-control", "max-age=0"),
+    ("cache-control", "max-age=2592000"),
+    ("cache-control", "max-age=604800"),
+    ("cache-control", "no-cache"),
+    ("cache-control", "no-store"),
+    ("cache-control", "public, max-age=31536000"),
+    ("content-encoding", "br"),
+    ("content-encoding", "gzip"),
+    ("content-type", "application/dns-message"),
+    ("content-type", "application/javascript"),
+    ("content-type", "application/json"),
+    ("content-type", "application/x-www-form-urlencoded"),
+    ("content-type", "image/gif"),
+    ("content-type", "image/jpeg"),
+    ("content-type", "image/png"),
+    ("content-type", "text/css"),
+    ("content-type", "text/html; charset=utf-8"),
+    ("content-type", "text/plain"),
+    ("content-type", "text/plain;charset=utf-8"),
+    ("range", "bytes=0-"),
+    ("strict-transport-security", "max-age=31536000"),
+    (
+        "strict-transport-security",
+        "max-age=31536000; includesubdomains",
+    ),
+    (
+        "strict-transport-security",
+        "max-age=31536000; includesubdomains; preload",
+    ),
+    ("vary", "accept-encoding"),
+    ("vary", "origin"),
+    ("x-content-type-options", "nosniff"),
+    ("x-xss-protection", "1; mode=block"),
+    (":status", "100"),
+    (":status", "204"),
+    (":status", "206"),
+    (":status", "302"),
+    (":status", "400"),
+    (":status", "403"),
+    (":status", "421"),
+    (":status", "425"),
+    (":status", "500"),
+    ("accept-language", ""),
+    ("access-control-allow-credentials", "FALSE"),
+    ("access-control-allow-credentials", "TRUE"),
+    ("access-control-allow-headers", "*"),
+    ("access-control-allow-methods", "get"),
+    ("access-control-allow-methods", "get, post, options"),
+    ("access-control-allow-methods", "options"),
+    ("access-control-expose-headers", "content-length"),
+    ("access-control-request-headers", "content-type"),
+    ("access-control-request-method", "get"),
+    ("access-control-request-method", "post"),
+    ("alt-svc", "clear"),
+    ("authorization", ""),
+    (
+        "content-security-policy",
+        "script-src 'none'; object-src 'none'; base-uri 'none'",
+    ),
+    ("early-data", "1"),
+    ("expect-ct", ""),
+    ("forwarded", ""),
+    ("if-range", ""),
+    ("origin", ""),
+    ("purpose", "prefetch"),
+    ("server", ""),
+    ("timing-allow-origin", "*"),
+    ("upgrade-insecure-requests", "1"),
+    ("user-agent", ""),
+    ("x-forwarded-for", ""),
+    ("x-frame-options", "deny"),
+    ("x-frame-options", "sameorigin"),
+];
diff --git a/src/h3/stream.rs b/src/h3/stream.rs
new file mode 100644
index 0000000..f2f8f0c
--- /dev/null
+++ b/src/h3/stream.rs
@@ -0,0 +1,913 @@
+// Copyright (C) 2019, Cloudflare, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright notice,
+//       this list of conditions and the following disclaimer.
+//
+//     * Redistributions in binary form must reproduce the above copyright
+//       notice, this list of conditions and the following disclaimer in the
+//       documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+use super::Error;
+use super::Result;
+
+use crate::octets;
+
+use super::frame;
+
+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;
+
+const MAX_STATE_BUF_SIZE: usize = (1 << 24) - 1;
+
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum Type {
+    Control,
+    Request,
+    Push,
+    QpackEncoder,
+    QpackDecoder,
+    Unknown,
+}
+
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum State {
+    /// Reading the stream's type.
+    StreamType,
+
+    /// Reading the stream's current frame's type.
+    FrameType,
+
+    /// Reading the stream's current frame's payload length.
+    FramePayloadLen,
+
+    /// Reading the stream's current frame's payload.
+    FramePayload,
+
+    /// Reading DATA payload.
+    Data,
+
+    /// Reading the push ID.
+    PushId,
+
+    /// Reading a QPACK instruction.
+    QpackInstruction,
+
+    /// Reading and discarding data.
+    Drain,
+}
+
+impl 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),
+
+            _ => Ok(Type::Unknown),
+        }
+    }
+}
+
+/// An HTTP/3 stream.
+///
+/// This maintains the HTTP/3 state for streams of any type (control, request,
+/// QPACK, ...).
+///
+/// A number of bytes, depending on the current stream's state, is read from the
+/// transport stream into the HTTP/3 stream's "state buffer". This intermediate
+/// buffering is required due to the fact that data read from the transport
+/// might not be complete (e.g. a varint might be split across multiple QUIC
+/// packets).
+///
+/// When enough data to complete the current state has been buffered, it is
+/// consumed from the state buffer and the stream is transitioned to the next
+/// state (see `State` for a list of possible states).
+#[derive(Debug)]
+pub struct Stream {
+    /// The corresponding transport stream's ID.
+    id: u64,
+
+    /// The stream's type (if known).
+    ty: Option<Type>,
+
+    /// The current stream state.
+    state: State,
+
+    /// The buffer holding partial data for the current state.
+    state_buf: Vec<u8>,
+
+    /// The expected amount of bytes required to complete the state.
+    state_len: usize,
+
+    /// The write offset in the state buffer, that is, how many bytes have
+    /// already been read from the transport for the current state. When
+    /// it reaches `stream_len` the state can be completed.
+    state_off: usize,
+
+    /// The type of the frame currently being parsed.
+    frame_type: Option<u64>,
+
+    /// Whether the stream was created locally, or by the peer.
+    is_local: bool,
+
+    /// Whether the stream has been remotely initialized.
+    remote_initialized: bool,
+
+    /// Whether the stream has been locally initialized.
+    local_initialized: bool,
+}
+
+impl Stream {
+    /// Creates a new HTTP/3 stream.
+    ///
+    /// The `is_local` parameter indicates whether the stream was created by the
+    /// local endpoint, or by the peer.
+    pub fn new(id: u64, is_local: bool) -> Stream {
+        let (ty, state) = if crate::stream::is_bidi(id) {
+            // All bidirectional streams are "request" streams, so we don't
+            // need to read the stream type.
+            (Some(Type::Request), State::FrameType)
+        } else {
+            // The stream's type is yet to be determined.
+            (None, State::StreamType)
+        };
+
+        Stream {
+            id,
+            ty,
+
+            state,
+
+            // Pre-allocate a buffer to avoid multiple tiny early allocations.
+            state_buf: vec![0; 16],
+
+            // Expect one byte for the initial state, to parse the initial
+            // varint length.
+            state_len: 1,
+            state_off: 0,
+
+            frame_type: None,
+
+            is_local,
+            remote_initialized: false,
+            local_initialized: false,
+        }
+    }
+
+    pub fn state(&self) -> State {
+        self.state
+    }
+
+    /// Sets the stream's type and transitions to the next state.
+    pub fn set_ty(&mut self, ty: Type) -> Result<()> {
+        assert_eq!(self.state, State::StreamType);
+
+        self.ty = Some(ty);
+
+        let state = match ty {
+            Type::Control | Type::Request => State::FrameType,
+
+            Type::Push => State::PushId,
+
+            Type::QpackEncoder | Type::QpackDecoder => {
+                self.remote_initialized = true;
+
+                State::QpackInstruction
+            },
+
+            Type::Unknown => State::Drain,
+        };
+
+        self.state_transition(state, 1, true)?;
+
+        Ok(())
+    }
+
+    /// Sets the push ID and transitions to the next state.
+    pub fn set_push_id(&mut self, _id: u64) -> Result<()> {
+        assert_eq!(self.state, State::PushId);
+
+        // TODO: implement push ID.
+
+        self.state_transition(State::FrameType, 1, true)?;
+
+        Ok(())
+    }
+
+    /// Sets the frame type and transitions to the next state.
+    pub fn set_frame_type(&mut self, ty: u64) -> Result<()> {
+        assert_eq!(self.state, State::FrameType);
+
+        // Only expect frames on Control, Request and Push streams.
+        match self.ty {
+            Some(Type::Control) => {
+                // Control stream starts uninitialized and only SETTINGS is
+                // accepted in that state. Other frames cause an error. Once
+                // initialized, no more SETTINGS are permitted.
+                match (ty, self.remote_initialized) {
+                    // Initialize control stream.
+                    (frame::SETTINGS_FRAME_TYPE_ID, false) =>
+                        self.remote_initialized = true,
+
+                    // Non-SETTINGS frames not allowed on control stream
+                    // before initialization.
+                    (_, false) => return Err(Error::MissingSettings),
+
+                    // Additional SETTINGS frame.
+                    (frame::SETTINGS_FRAME_TYPE_ID, true) =>
+                        return Err(Error::FrameUnexpected),
+
+                    // Frames that can't be received on control stream
+                    // after initialization.
+                    (frame::DATA_FRAME_TYPE_ID, true) =>
+                        return Err(Error::FrameUnexpected),
+
+                    (frame::HEADERS_FRAME_TYPE_ID, true) =>
+                        return Err(Error::FrameUnexpected),
+
+                    (frame::PUSH_PROMISE_FRAME_TYPE_ID, true) =>
+                        return Err(Error::FrameUnexpected),
+
+                    // All other frames are ignored after initialization.
+                    (_, true) => (),
+                }
+            },
+
+            Some(Type::Request) => {
+                // Request stream starts uninitialized and only HEADERS
+                // is accepted. Other frames cause an error.
+                if !self.is_local {
+                    match (ty, self.remote_initialized) {
+                        (frame::HEADERS_FRAME_TYPE_ID, false) =>
+                            self.remote_initialized = true,
+
+                        (frame::CANCEL_PUSH_FRAME_TYPE_ID, _) =>
+                            return Err(Error::FrameUnexpected),
+
+                        (frame::SETTINGS_FRAME_TYPE_ID, _) =>
+                            return Err(Error::FrameUnexpected),
+
+                        (frame::GOAWAY_FRAME_TYPE_ID, _) =>
+                            return Err(Error::FrameUnexpected),
+
+                        (frame::MAX_PUSH_FRAME_TYPE_ID, _) =>
+                            return Err(Error::FrameUnexpected),
+
+                        // All other frames can be ignored regardless of stream
+                        // state.
+                        (_, false) => (),
+
+                        (_, true) => (),
+                    }
+                }
+            },
+
+            Some(Type::Push) => {
+                match ty {
+                    // Frames that can never be received on request streams.
+                    frame::CANCEL_PUSH_FRAME_TYPE_ID =>
+                        return Err(Error::FrameUnexpected),
+
+                    frame::SETTINGS_FRAME_TYPE_ID =>
+                        return Err(Error::FrameUnexpected),
+
+                    frame::PUSH_PROMISE_FRAME_TYPE_ID =>
+                        return Err(Error::FrameUnexpected),
+
+                    frame::GOAWAY_FRAME_TYPE_ID =>
+                        return Err(Error::FrameUnexpected),
+
+                    frame::MAX_PUSH_FRAME_TYPE_ID =>
+                        return Err(Error::FrameUnexpected),
+
+                    _ => (),
+                }
+            },
+
+            _ => return Err(Error::FrameUnexpected),
+        }
+
+        self.frame_type = Some(ty);
+
+        self.state_transition(State::FramePayloadLen, 1, true)?;
+
+        Ok(())
+    }
+
+    /// Sets the frame's payload length and transitions to the next state.
+    pub fn set_frame_payload_len(&mut self, len: u64) -> Result<()> {
+        assert_eq!(self.state, State::FramePayloadLen);
+
+        // 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)
+        {
+            let (state, resize) = match self.frame_type {
+                Some(frame::DATA_FRAME_TYPE_ID) => (State::Data, false),
+
+                _ => (State::FramePayload, true),
+            };
+
+            self.state_transition(state, len as usize, resize)?;
+
+            return Ok(());
+        }
+
+        Err(Error::InternalError)
+    }
+
+    /// Tries to fill the state buffer by reading data from the corresponding
+    /// transport stream.
+    ///
+    /// When not enough data can be read to complete the state, this returns
+    /// `Error::Done`.
+    pub fn try_fill_buffer(
+        &mut self, conn: &mut crate::Connection,
+    ) -> Result<()> {
+        let buf = &mut self.state_buf[self.state_off..self.state_len];
+
+        let (read, _) = conn.stream_recv(self.id, buf)?;
+
+        trace!(
+            "{} read {} bytes on stream {}",
+            conn.trace_id(),
+            read,
+            self.id,
+        );
+
+        self.state_off += read;
+
+        if !self.state_buffer_complete() {
+            return Err(Error::Done);
+        }
+
+        Ok(())
+    }
+
+    /// Initialize the local part of the stream.
+    pub fn initialize_local(&mut self) {
+        self.local_initialized = true
+    }
+
+    /// Whether the stream has been locally initialized.
+    pub fn local_initialized(&self) -> bool {
+        self.local_initialized
+    }
+
+    /// Tries to fill the state buffer by reading data from the given cursor.
+    ///
+    /// This is intended to replace `try_fill_buffer()` in tests, in order to
+    /// avoid having to setup a transport connection.
+    #[cfg(test)]
+    fn try_fill_buffer_for_tests(
+        &mut self, stream: &mut std::io::Cursor<Vec<u8>>,
+    ) -> Result<()> {
+        let buf = &mut self.state_buf[self.state_off..self.state_len];
+
+        let read = std::io::Read::read(stream, buf).unwrap();
+
+        self.state_off += read;
+
+        if !self.state_buffer_complete() {
+            return Err(Error::Done);
+        }
+
+        Ok(())
+    }
+
+    /// Tries to parse a varint (including length) from the state buffer.
+    pub fn try_consume_varint(&mut self) -> Result<u64> {
+        if self.state_off == 1 {
+            self.state_len = octets::varint_parse_len(self.state_buf[0]);
+            self.state_buf.resize(self.state_len, 0);
+        }
+
+        // Return early if we don't have enough data in the state buffer to
+        // parse the whole varint.
+        if !self.state_buffer_complete() {
+            return Err(Error::Done);
+        }
+
+        let varint = octets::Octets::with_slice(&self.state_buf).get_varint()?;
+
+        Ok(varint)
+    }
+
+    /// Tries to parse a frame from the state buffer.
+    pub fn try_consume_frame(&mut self) -> Result<frame::Frame> {
+        // TODO: properly propagate frame parsing errors.
+        let frame = frame::Frame::from_bytes(
+            self.frame_type.unwrap(),
+            self.state_len as u64,
+            &self.state_buf,
+        )?;
+
+        self.state_transition(State::FrameType, 1, true)?;
+
+        Ok(frame)
+    }
+
+    /// Tries to read DATA payload from the transport stream.
+    pub fn try_consume_data(
+        &mut self, conn: &mut crate::Connection, out: &mut [u8],
+    ) -> Result<usize> {
+        let left = std::cmp::min(out.len(), self.state_len - self.state_off);
+
+        let (len, _) = conn.stream_recv(self.id, &mut out[..left])?;
+
+        self.state_off += len;
+
+        if self.state_buffer_complete() {
+            self.state_transition(State::FrameType, 1, true)?;
+        }
+
+        Ok(len)
+    }
+
+    /// Tries to read DATA payload from the given cursor.
+    ///
+    /// This is intended to replace `try_consume_data()` in tests, in order to
+    /// avoid having to setup a transport connection.
+    #[cfg(test)]
+    fn try_consume_data_for_tests(
+        &mut self, stream: &mut std::io::Cursor<Vec<u8>>, out: &mut [u8],
+    ) -> Result<usize> {
+        let left = std::cmp::min(out.len(), self.state_len - self.state_off);
+
+        let len = std::io::Read::read(stream, &mut out[..left]).unwrap();
+
+        self.state_off += len;
+
+        if self.state_buffer_complete() {
+            self.state_transition(State::FrameType, 1, true)?;
+        }
+
+        Ok(len)
+    }
+
+    /// Returns true if the state buffer has enough data to complete the state.
+    fn state_buffer_complete(&self) -> bool {
+        self.state_off == self.state_len
+    }
+
+    /// Transitions the stream to a new state, and optionally resets the state
+    /// buffer.
+    fn state_transition(
+        &mut self, new_state: State, expected_len: usize, resize: bool,
+    ) -> Result<()> {
+        self.state = new_state;
+        self.state_off = 0;
+        self.state_len = expected_len;
+
+        // Some states don't need the state buffer, so don't resize it if not
+        // necessary.
+        if resize {
+            // A peer can influence the size of the state buffer (e.g. with the
+            // payload size of a GREASE frame), so we need to limit the maximum
+            // size to avoid DoS.
+            if self.state_len > MAX_STATE_BUF_SIZE {
+                return Err(Error::InternalError);
+            }
+
+            self.state_buf.resize(self.state_len, 0);
+        }
+
+        Ok(())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    /// Process incoming SETTINGS frame on control stream.
+    fn control_good() {
+        let mut stream = Stream::new(3, false);
+        assert_eq!(stream.state, State::StreamType);
+
+        let mut d = vec![42; 40];
+        let mut b = octets::OctetsMut::with_slice(&mut d);
+
+        let frame = frame::Frame::Settings {
+            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 mut cursor = std::io::Cursor::new(d);
+
+        // Parse stream type.
+        stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
+
+        let stream_ty = stream.try_consume_varint().unwrap();
+        assert_eq!(stream_ty, HTTP3_CONTROL_STREAM_TYPE_ID);
+        stream
+            .set_ty(Type::deserialize(stream_ty).unwrap())
+            .unwrap();
+        assert_eq!(stream.state, State::FrameType);
+
+        // Parse the SETTINGS frame type.
+        stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
+
+        let frame_ty = stream.try_consume_varint().unwrap();
+        assert_eq!(frame_ty, frame::SETTINGS_FRAME_TYPE_ID);
+
+        stream.set_frame_type(frame_ty).unwrap();
+        assert_eq!(stream.state, State::FramePayloadLen);
+
+        // Parse the SETTINGS frame payload length.
+        stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
+
+        let frame_payload_len = stream.try_consume_varint().unwrap();
+        assert_eq!(frame_payload_len, 6);
+        stream.set_frame_payload_len(frame_payload_len).unwrap();
+        assert_eq!(stream.state, State::FramePayload);
+
+        // Parse the SETTINGS frame payload.
+        stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
+
+        assert_eq!(stream.try_consume_frame(), Ok(frame));
+        assert_eq!(stream.state, State::FrameType);
+    }
+
+    #[test]
+    /// Process duplicate SETTINGS frame on control stream.
+    fn control_bad_multiple_settings() {
+        let mut stream = Stream::new(3, false);
+        assert_eq!(stream.state, State::StreamType);
+
+        let mut d = vec![42; 40];
+        let mut b = octets::OctetsMut::with_slice(&mut d);
+
+        let frame = frame::Frame::Settings {
+            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 mut cursor = std::io::Cursor::new(d);
+
+        // Parse stream type.
+        stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
+
+        let stream_ty = stream.try_consume_varint().unwrap();
+        assert_eq!(stream_ty, HTTP3_CONTROL_STREAM_TYPE_ID);
+        stream
+            .set_ty(Type::deserialize(stream_ty).unwrap())
+            .unwrap();
+        assert_eq!(stream.state, State::FrameType);
+
+        // Parse the SETTINGS frame type.
+        stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
+
+        let frame_ty = stream.try_consume_varint().unwrap();
+        assert_eq!(frame_ty, frame::SETTINGS_FRAME_TYPE_ID);
+
+        stream.set_frame_type(frame_ty).unwrap();
+        assert_eq!(stream.state, State::FramePayloadLen);
+
+        // Parse the SETTINGS frame payload length.
+        stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
+
+        let frame_payload_len = stream.try_consume_varint().unwrap();
+        assert_eq!(frame_payload_len, 6);
+        stream.set_frame_payload_len(frame_payload_len).unwrap();
+        assert_eq!(stream.state, State::FramePayload);
+
+        // Parse the SETTINGS frame payload.
+        stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
+
+        assert_eq!(stream.try_consume_frame(), Ok(frame));
+        assert_eq!(stream.state, State::FrameType);
+
+        // Parse the second SETTINGS frame type.
+        stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
+
+        let frame_ty = stream.try_consume_varint().unwrap();
+        assert_eq!(stream.set_frame_type(frame_ty), Err(Error::FrameUnexpected));
+    }
+
+    #[test]
+    /// Process other frame before SETTINGS frame on control stream.
+    fn control_bad_late_settings() {
+        let mut stream = Stream::new(3, false);
+        assert_eq!(stream.state, State::StreamType);
+
+        let mut d = vec![42; 40];
+        let mut b = octets::OctetsMut::with_slice(&mut d);
+
+        let goaway = frame::Frame::GoAway { id: 0 };
+
+        let settings = frame::Frame::Settings {
+            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 mut cursor = std::io::Cursor::new(d);
+
+        // Parse stream type.
+        stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
+
+        let stream_ty = stream.try_consume_varint().unwrap();
+        assert_eq!(stream_ty, HTTP3_CONTROL_STREAM_TYPE_ID);
+        stream
+            .set_ty(Type::deserialize(stream_ty).unwrap())
+            .unwrap();
+        assert_eq!(stream.state, State::FrameType);
+
+        // Parse GOAWAY.
+        stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
+
+        let frame_ty = stream.try_consume_varint().unwrap();
+        assert_eq!(stream.set_frame_type(frame_ty), Err(Error::MissingSettings));
+    }
+
+    #[test]
+    /// Process not-allowed frame on control stream.
+    fn control_bad_frame() {
+        let mut stream = Stream::new(3, false);
+        assert_eq!(stream.state, State::StreamType);
+
+        let mut d = vec![42; 40];
+        let mut b = octets::OctetsMut::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 {
+            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 mut cursor = std::io::Cursor::new(d);
+
+        // Parse stream type.
+        stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
+
+        let stream_ty = stream.try_consume_varint().unwrap();
+        stream
+            .set_ty(Type::deserialize(stream_ty).unwrap())
+            .unwrap();
+
+        // Parse first SETTINGS frame.
+        stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
+
+        let frame_ty = stream.try_consume_varint().unwrap();
+        stream.set_frame_type(frame_ty).unwrap();
+
+        stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
+
+        let frame_payload_len = stream.try_consume_varint().unwrap();
+        stream.set_frame_payload_len(frame_payload_len).unwrap();
+
+        stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
+
+        assert!(stream.try_consume_frame().is_ok());
+
+        // Parse HEADERS.
+        stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
+
+        let frame_ty = stream.try_consume_varint().unwrap();
+        assert_eq!(stream.set_frame_type(frame_ty), Err(Error::FrameUnexpected));
+    }
+
+    #[test]
+    fn request_no_data() {
+        let mut stream = Stream::new(0, false);
+
+        assert_eq!(stream.ty, Some(Type::Request));
+        assert_eq!(stream.state, State::FrameType);
+
+        assert_eq!(stream.try_consume_varint(), Err(Error::Done));
+    }
+
+    #[test]
+    fn request_good() {
+        let mut stream = Stream::new(0, false);
+
+        let mut d = vec![42; 128];
+        let mut b = octets::OctetsMut::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: payload.clone(),
+        };
+
+        hdrs.to_bytes(&mut b).unwrap();
+        data.to_bytes(&mut b).unwrap();
+
+        let mut cursor = std::io::Cursor::new(d);
+
+        // Parse the HEADERS frame type.
+        stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
+
+        let frame_ty = stream.try_consume_varint().unwrap();
+        assert_eq!(frame_ty, frame::HEADERS_FRAME_TYPE_ID);
+
+        stream.set_frame_type(frame_ty).unwrap();
+        assert_eq!(stream.state, State::FramePayloadLen);
+
+        // Parse the HEADERS frame payload length.
+        stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
+
+        let frame_payload_len = stream.try_consume_varint().unwrap();
+        assert_eq!(frame_payload_len, 12);
+
+        stream.set_frame_payload_len(frame_payload_len).unwrap();
+        assert_eq!(stream.state, State::FramePayload);
+
+        // Parse the HEADERS frame.
+        stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
+
+        assert_eq!(stream.try_consume_frame(), Ok(hdrs));
+        assert_eq!(stream.state, State::FrameType);
+
+        // Parse the DATA frame type.
+        stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
+
+        let frame_ty = stream.try_consume_varint().unwrap();
+        assert_eq!(frame_ty, frame::DATA_FRAME_TYPE_ID);
+
+        stream.set_frame_type(frame_ty).unwrap();
+        assert_eq!(stream.state, State::FramePayloadLen);
+
+        // Parse the DATA frame payload length.
+        stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
+
+        let frame_payload_len = stream.try_consume_varint().unwrap();
+        assert_eq!(frame_payload_len, 12);
+
+        stream.set_frame_payload_len(frame_payload_len).unwrap();
+        assert_eq!(stream.state, State::Data);
+
+        // Parse the DATA payload.
+        let mut recv_buf = vec![0; payload.len()];
+        assert_eq!(
+            stream.try_consume_data_for_tests(&mut cursor, &mut recv_buf),
+            Ok(payload.len())
+        );
+        assert_eq!(payload, recv_buf);
+
+        assert_eq!(stream.state, State::FrameType);
+    }
+
+    #[test]
+    fn push_good() {
+        let mut stream = Stream::new(2, false);
+
+        let mut d = vec![42; 128];
+        let mut b = octets::OctetsMut::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: payload.clone(),
+        };
+
+        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 mut cursor = std::io::Cursor::new(d);
+
+        // Parse stream type.
+        stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
+
+        let stream_ty = stream.try_consume_varint().unwrap();
+        assert_eq!(stream_ty, HTTP3_PUSH_STREAM_TYPE_ID);
+        stream
+            .set_ty(Type::deserialize(stream_ty).unwrap())
+            .unwrap();
+        assert_eq!(stream.state, State::PushId);
+
+        // Parse push ID.
+        stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
+
+        let push_id = stream.try_consume_varint().unwrap();
+        assert_eq!(push_id, 1);
+
+        stream.set_push_id(push_id).unwrap();
+        assert_eq!(stream.state, State::FrameType);
+
+        // Parse the HEADERS frame type.
+        stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
+
+        let frame_ty = stream.try_consume_varint().unwrap();
+        assert_eq!(frame_ty, frame::HEADERS_FRAME_TYPE_ID);
+
+        stream.set_frame_type(frame_ty).unwrap();
+        assert_eq!(stream.state, State::FramePayloadLen);
+
+        // Parse the HEADERS frame payload length.
+        stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
+
+        let frame_payload_len = stream.try_consume_varint().unwrap();
+        assert_eq!(frame_payload_len, 12);
+
+        stream.set_frame_payload_len(frame_payload_len).unwrap();
+        assert_eq!(stream.state, State::FramePayload);
+
+        // Parse the HEADERS frame.
+        stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
+
+        assert_eq!(stream.try_consume_frame(), Ok(hdrs));
+        assert_eq!(stream.state, State::FrameType);
+
+        // Parse the DATA frame type.
+        stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
+
+        let frame_ty = stream.try_consume_varint().unwrap();
+        assert_eq!(frame_ty, frame::DATA_FRAME_TYPE_ID);
+
+        stream.set_frame_type(frame_ty).unwrap();
+        assert_eq!(stream.state, State::FramePayloadLen);
+
+        // Parse the DATA frame payload length.
+        stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
+
+        let frame_payload_len = stream.try_consume_varint().unwrap();
+        assert_eq!(frame_payload_len, 12);
+
+        stream.set_frame_payload_len(frame_payload_len).unwrap();
+        assert_eq!(stream.state, State::Data);
+
+        // Parse the DATA payload.
+        let mut recv_buf = vec![0; payload.len()];
+        assert_eq!(
+            stream.try_consume_data_for_tests(&mut cursor, &mut recv_buf),
+            Ok(payload.len())
+        );
+        assert_eq!(payload, recv_buf);
+
+        assert_eq!(stream.state, State::FrameType);
+    }
+
+    #[test]
+    fn grease() {
+        let mut stream = Stream::new(2, false);
+
+        let mut d = vec![42; 20];
+        let mut b = octets::OctetsMut::with_slice(&mut d);
+
+        b.put_varint(33).unwrap();
+
+        let mut cursor = std::io::Cursor::new(d);
+
+        // Parse stream type.
+        stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
+
+        let stream_ty = stream.try_consume_varint().unwrap();
+        assert_eq!(stream_ty, 33);
+        stream
+            .set_ty(Type::deserialize(stream_ty).unwrap())
+            .unwrap();
+        assert_eq!(stream.state, State::Drain);
+    }
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..79c942b
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,7768 @@
+// Copyright (C) 2018-2019, Cloudflare, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright notice,
+//       this list of conditions and the following disclaimer.
+//
+//     * Redistributions in binary form must reproduce the above copyright
+//       notice, this list of conditions and the following disclaimer in the
+//       documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+//! 🥧 Savoury implementation of the QUIC transport protocol and HTTP/3.
+//!
+//! [quiche] is an implementation of the QUIC transport protocol and HTTP/3 as
+//! specified by the [IETF]. It provides a low level API for processing QUIC
+//! packets and handling connection state. The application is responsible for
+//! providing I/O (e.g. sockets handling) as well as an event loop with support
+//! for timers.
+//!
+//! [quiche]: https://github.com/cloudflare/quiche/
+//! [ietf]: https://quicwg.org/
+//!
+//! ## Connection setup
+//!
+//! The first step in establishing a QUIC connection using quiche is creating a
+//! configuration object:
+//!
+//! ```
+//! let config = quiche::Config::new(quiche::PROTOCOL_VERSION)?;
+//! # Ok::<(), quiche::Error>(())
+//! ```
+//!
+//! This is shared among multiple connections and can be used to configure a
+//! QUIC endpoint.
+//!
+//! On the client-side the [`connect()`] utility function can be used to create
+//! a new connection, while [`accept()`] is for servers:
+//!
+//! ```
+//! # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?;
+//! # let server_name = "quic.tech";
+//! # let scid = [0xba; 16];
+//! // Client connection.
+//! let conn = quiche::connect(Some(&server_name), &scid, &mut config)?;
+//!
+//! // Server connection.
+//! let conn = quiche::accept(&scid, None, &mut config)?;
+//! # Ok::<(), quiche::Error>(())
+//! ```
+//!
+//! ## Handling incoming packets
+//!
+//! Using the connection's [`recv()`] method the application can process
+//! incoming packets that belong to that connection from the network:
+//!
+//! ```no_run
+//! # let mut buf = [0; 512];
+//! # let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap();
+//! # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?;
+//! # let scid = [0xba; 16];
+//! # let mut conn = quiche::accept(&scid, None, &mut config)?;
+//! loop {
+//!     let read = socket.recv(&mut buf).unwrap();
+//!
+//!     let read = match conn.recv(&mut buf[..read]) {
+//!         Ok(v) => v,
+//!
+//!         Err(quiche::Error::Done) => {
+//!             // Done reading.
+//!             break;
+//!         },
+//!
+//!         Err(e) => {
+//!             // An error occurred, handle it.
+//!             break;
+//!         },
+//!     };
+//! }
+//! # Ok::<(), quiche::Error>(())
+//! ```
+//!
+//! ## Generating outgoing packets
+//!
+//! Outgoing packet are generated using the connection's [`send()`] method
+//! instead:
+//!
+//! ```no_run
+//! # let mut out = [0; 512];
+//! # let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap();
+//! # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?;
+//! # let scid = [0xba; 16];
+//! # let mut conn = quiche::accept(&scid, None, &mut config)?;
+//! loop {
+//!     let write = match conn.send(&mut out) {
+//!         Ok(v) => v,
+//!
+//!         Err(quiche::Error::Done) => {
+//!             // Done writing.
+//!             break;
+//!         },
+//!
+//!         Err(e) => {
+//!             // An error occurred, handle it.
+//!             break;
+//!         },
+//!     };
+//!
+//!     socket.send(&out[..write]).unwrap();
+//! }
+//! # Ok::<(), quiche::Error>(())
+//! ```
+//!
+//! When packets are sent, the application is responsible for maintaining a
+//! timer to react to time-based connection events. The timer expiration can be
+//! obtained using the connection's [`timeout()`] method.
+//!
+//! ```
+//! # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?;
+//! # let scid = [0xba; 16];
+//! # let mut conn = quiche::accept(&scid, None, &mut config)?;
+//! let timeout = conn.timeout();
+//! # Ok::<(), quiche::Error>(())
+//! ```
+//!
+//! The application is responsible for providing a timer implementation, which
+//! can be specific to the operating system or networking framework used. When
+//! a timer expires, the connection's [`on_timeout()`] method should be called,
+//! after which additional packets might need to be sent on the network:
+//!
+//! ```no_run
+//! # let mut out = [0; 512];
+//! # let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap();
+//! # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?;
+//! # let scid = [0xba; 16];
+//! # let mut conn = quiche::accept(&scid, None, &mut config)?;
+//! // Timeout expired, handle it.
+//! conn.on_timeout();
+//!
+//! // Send more packets as needed after timeout.
+//! loop {
+//!     let write = match conn.send(&mut out) {
+//!         Ok(v) => v,
+//!
+//!         Err(quiche::Error::Done) => {
+//!             // Done writing.
+//!             break;
+//!         },
+//!
+//!         Err(e) => {
+//!             // An error occurred, handle it.
+//!             break;
+//!         },
+//!     };
+//!
+//!     socket.send(&out[..write]).unwrap();
+//! }
+//! # Ok::<(), quiche::Error>(())
+//! ```
+//!
+//! ## Sending and receiving stream data
+//!
+//! After some back and forth, the connection will complete its handshake and
+//! will be ready for sending or receiving application data.
+//!
+//! Data can be sent on a stream by using the [`stream_send()`] method:
+//!
+//! ```no_run
+//! # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?;
+//! # let scid = [0xba; 16];
+//! # let mut conn = quiche::accept(&scid, None, &mut config)?;
+//! if conn.is_established() {
+//!     // Handshake completed, send some data on stream 0.
+//!     conn.stream_send(0, b"hello", true)?;
+//! }
+//! # Ok::<(), quiche::Error>(())
+//! ```
+//!
+//! The application can check whether there are any readable streams by using
+//! the connection's [`readable()`] method, which returns an iterator over all
+//! the streams that have outstanding data to read.
+//!
+//! The [`stream_recv()`] method can then be used to retrieve the application
+//! data from the readable stream:
+//!
+//! ```no_run
+//! # let mut buf = [0; 512];
+//! # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?;
+//! # let scid = [0xba; 16];
+//! # let mut conn = quiche::accept(&scid, None, &mut config)?;
+//! if conn.is_established() {
+//!     // Iterate over readable streams.
+//!     for stream_id in conn.readable() {
+//!         // Stream is readable, read until there's no more data.
+//!         while let Ok((read, fin)) = conn.stream_recv(stream_id, &mut buf) {
+//!             println!("Got {} bytes on stream {}", read, stream_id);
+//!         }
+//!     }
+//! }
+//! # Ok::<(), quiche::Error>(())
+//! ```
+//!
+//! ## HTTP/3
+//!
+//! The quiche [HTTP/3 module] provides a high level API for sending and
+//! receiving HTTP requests and responses on top of the QUIC transport protocol.
+//!
+//! [`connect()`]: fn.connect.html
+//! [`accept()`]: fn.accept.html
+//! [`recv()`]: struct.Connection.html#method.recv
+//! [`send()`]: struct.Connection.html#method.send
+//! [`timeout()`]: struct.Connection.html#method.timeout
+//! [`on_timeout()`]: struct.Connection.html#method.on_timeout
+//! [`stream_send()`]: struct.Connection.html#method.stream_send
+//! [`readable()`]: struct.Connection.html#method.readable
+//! [`stream_recv()`]: struct.Connection.html#method.stream_recv
+//! [HTTP/3 module]: h3/index.html
+//!
+//! ## Congestion Control
+//!
+//! The quiche library provides a high-level API for configuring which
+//! congestion control algorithm to use throughout the QUIC connection.
+//!
+//! When a QUIC connection is created, the application can optionally choose
+//! which CC algorithm to use. See [`CongestionControlAlgorithm`] for currently
+//! available congestion control algorithms.
+//!
+//! For example:
+//!
+//! ```
+//! let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION).unwrap();
+//! config.set_cc_algorithm(quiche::CongestionControlAlgorithm::Reno);
+//! ```
+//!
+//! Alternatively, you can configure the congestion control algorithm to use
+//! by its name.
+//!
+//! ```
+//! let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION).unwrap();
+//! config.set_cc_algorithm_name("reno").unwrap();
+//! ```
+//!
+//! Note that the CC algorithm should be configured before calling [`connect()`]
+//! or [`accept()`]. Otherwise the connection will use a default CC algorithm.
+//!
+//! [`CongestionControlAlgorithm`]: enum.CongestionControlAlgorithm.html
+
+#![allow(improper_ctypes)]
+#![warn(missing_docs)]
+
+#[macro_use]
+extern crate log;
+
+use std::cmp;
+use std::time;
+
+use std::pin::Pin;
+use std::str::FromStr;
+
+/// The current QUIC wire version.
+pub const PROTOCOL_VERSION: u32 = PROTOCOL_VERSION_DRAFT29;
+
+/// Supported QUIC versions.
+///
+/// Note that the older ones might not be fully supported.
+const PROTOCOL_VERSION_DRAFT27: u32 = 0xff00_001b;
+const PROTOCOL_VERSION_DRAFT28: u32 = 0xff00_001c;
+const PROTOCOL_VERSION_DRAFT29: u32 = 0xff00_001d;
+
+/// The maximum length of a connection ID.
+pub const MAX_CONN_ID_LEN: usize = crate::packet::MAX_CID_LEN as usize;
+
+/// The minimum length of Initial packets sent by a client.
+pub const MIN_CLIENT_INITIAL_LEN: usize = 1200;
+
+#[cfg(not(feature = "fuzzing"))]
+const PAYLOAD_MIN_LEN: usize = 4;
+
+#[cfg(feature = "fuzzing")]
+// Due to the fact that in fuzzing mode we use a zero-length AEAD tag (which
+// would normally be 16 bytes), we need to adjust the minimum payload size to
+// account for that.
+const PAYLOAD_MIN_LEN: usize = 20;
+
+const MAX_AMPLIFICATION_FACTOR: usize = 3;
+
+// The maximum number of tracked packet number ranges that need to be acked.
+//
+// This represents more or less how many ack blocks can fit in a typical packet.
+const MAX_ACK_RANGES: usize = 68;
+
+// The highest possible stream ID allowed.
+const MAX_STREAM_ID: u64 = 1 << 60;
+
+// The default length of DATAGRAM queues.
+const DEFAULT_MAX_DGRAM_QUEUE_LEN: usize = 0;
+
+// The DATAGRAM standard recommends either none or 65536 as maximum DATAGRAM
+// frames size. We enforce the recommendation for forward compatibility.
+const MAX_DGRAM_FRAME_SIZE: u64 = 65536;
+
+/// A specialized [`Result`] type for quiche operations.
+///
+/// This type is used throughout quiche's public API for any operation that
+/// can produce an error.
+///
+/// [`Result`]: https://doc.rust-lang.org/std/result/enum.Result.html
+pub type Result<T> = std::result::Result<T, Error>;
+
+/// A QUIC error.
+#[derive(Clone, Copy, Debug, PartialEq)]
+#[repr(C)]
+pub enum Error {
+    /// There is no more work to do.
+    Done               = -1,
+
+    /// The provided buffer is too short.
+    BufferTooShort     = -2,
+
+    /// The provided packet cannot be parsed because its version is unknown.
+    UnknownVersion     = -3,
+
+    /// The provided packet cannot be parsed because it contains an invalid
+    /// frame.
+    InvalidFrame       = -4,
+
+    /// The provided packet cannot be parsed.
+    InvalidPacket      = -5,
+
+    /// The operation cannot be completed because the connection is in an
+    /// invalid state.
+    InvalidState       = -6,
+
+    /// The operation cannot be completed because the stream is in an
+    /// invalid state.
+    InvalidStreamState = -7,
+
+    /// The peer's transport params cannot be parsed.
+    InvalidTransportParam = -8,
+
+    /// A cryptographic operation failed.
+    CryptoFail         = -9,
+
+    /// The TLS handshake failed.
+    TlsFail            = -10,
+
+    /// The peer violated the local flow control limits.
+    FlowControl        = -11,
+
+    /// The peer violated the local stream limits.
+    StreamLimit        = -12,
+
+    /// The received data exceeds the stream's final size.
+    FinalSize          = -13,
+
+    /// Error in congestion control.
+    CongestionControl  = -14,
+}
+
+impl Error {
+    fn to_wire(self) -> u64 {
+        match self {
+            Error::Done => 0x0,
+            Error::InvalidFrame => 0x7,
+            Error::InvalidStreamState => 0x5,
+            Error::InvalidTransportParam => 0x8,
+            Error::FlowControl => 0x3,
+            Error::StreamLimit => 0x4,
+            Error::FinalSize => 0x6,
+            _ => 0xa,
+        }
+    }
+
+    fn to_c(self) -> libc::ssize_t {
+        self as _
+    }
+}
+
+impl std::fmt::Display for Error {
+    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+        write!(f, "{:?}", self)
+    }
+}
+
+impl std::error::Error for Error {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        None
+    }
+}
+
+impl std::convert::From<octets::BufferTooShortError> for Error {
+    fn from(_err: octets::BufferTooShortError) -> Self {
+        Error::BufferTooShort
+    }
+}
+
+/// The stream's side to shutdown.
+///
+/// This should be used when calling [`stream_shutdown()`].
+///
+/// [`stream_shutdown()`]: struct.Connection.html#method.stream_shutdown
+#[repr(C)]
+pub enum Shutdown {
+    /// Stop receiving stream data.
+    Read  = 0,
+
+    /// Stop sending stream data.
+    Write = 1,
+}
+
+/// Stores configuration shared between multiple connections.
+pub struct Config {
+    local_transport_params: TransportParams,
+
+    version: u32,
+
+    tls_ctx: tls::Context,
+
+    application_protos: Vec<Vec<u8>>,
+
+    grease: bool,
+
+    cc_algorithm: CongestionControlAlgorithm,
+
+    hystart: bool,
+
+    dgram_recv_max_queue_len: usize,
+    dgram_send_max_queue_len: usize,
+}
+
+impl Config {
+    /// Creates a config object with the given version.
+    ///
+    /// ## Examples:
+    ///
+    /// ```
+    /// let config = quiche::Config::new(quiche::PROTOCOL_VERSION)?;
+    /// # Ok::<(), quiche::Error>(())
+    /// ```
+    pub fn new(version: u32) -> Result<Config> {
+        let tls_ctx = tls::Context::new()?;
+
+        Ok(Config {
+            local_transport_params: TransportParams::default(),
+            version,
+            tls_ctx,
+            application_protos: Vec::new(),
+            grease: true,
+            cc_algorithm: CongestionControlAlgorithm::CUBIC,
+            hystart: true,
+
+            dgram_recv_max_queue_len: DEFAULT_MAX_DGRAM_QUEUE_LEN,
+            dgram_send_max_queue_len: DEFAULT_MAX_DGRAM_QUEUE_LEN,
+        })
+    }
+
+    /// Configures the given certificate chain.
+    ///
+    /// The content of `file` is parsed as a PEM-encoded leaf certificate,
+    /// followed by optional intermediate certificates.
+    ///
+    /// ## Examples:
+    ///
+    /// ```no_run
+    /// # let mut config = quiche::Config::new(0xbabababa)?;
+    /// config.load_cert_chain_from_pem_file("/path/to/cert.pem")?;
+    /// # Ok::<(), quiche::Error>(())
+    /// ```
+    pub fn load_cert_chain_from_pem_file(&mut self, file: &str) -> Result<()> {
+        self.tls_ctx.use_certificate_chain_file(file)
+    }
+
+    /// Configures the given private key.
+    ///
+    /// The content of `file` is parsed as a PEM-encoded private key.
+    ///
+    /// ## Examples:
+    ///
+    /// ```no_run
+    /// # let mut config = quiche::Config::new(0xbabababa)?;
+    /// config.load_priv_key_from_pem_file("/path/to/key.pem")?;
+    /// # Ok::<(), quiche::Error>(())
+    /// ```
+    pub fn load_priv_key_from_pem_file(&mut self, file: &str) -> Result<()> {
+        self.tls_ctx.use_privkey_file(file)
+    }
+
+    /// Specifies a file where trusted CA certificates are stored for the
+    /// purposes of certificate verification.
+    ///
+    /// The content of `file` is parsed as a PEM-encoded certificate chain.
+    ///
+    /// ## Examples:
+    ///
+    /// ```no_run
+    /// # let mut config = quiche::Config::new(0xbabababa)?;
+    /// config.load_verify_locations_from_file("/path/to/cert.pem")?;
+    /// # Ok::<(), quiche::Error>(())
+    /// ```
+    pub fn load_verify_locations_from_file(&mut self, file: &str) -> Result<()> {
+        self.tls_ctx.load_verify_locations_from_file(file)
+    }
+
+    /// Specifies a directory where trusted CA certificates are stored for the
+    /// purposes of certificate verification.
+    ///
+    /// The content of `dir` a set of PEM-encoded certificate chains.
+    ///
+    /// ## Examples:
+    ///
+    /// ```no_run
+    /// # let mut config = quiche::Config::new(0xbabababa)?;
+    /// config.load_verify_locations_from_directory("/path/to/certs")?;
+    /// # Ok::<(), quiche::Error>(())
+    /// ```
+    pub fn load_verify_locations_from_directory(
+        &mut self, dir: &str,
+    ) -> Result<()> {
+        self.tls_ctx.load_verify_locations_from_directory(dir)
+    }
+
+    /// Configures whether to verify the peer's certificate.
+    ///
+    /// The default value is `true` for client connections, and `false` for
+    /// server ones.
+    pub fn verify_peer(&mut self, verify: bool) {
+        self.tls_ctx.set_verify(verify);
+    }
+
+    /// Configures whether to send GREASE values.
+    ///
+    /// The default value is `true`.
+    pub fn grease(&mut self, grease: bool) {
+        self.grease = grease;
+    }
+
+    /// Enables logging of secrets.
+    ///
+    /// When logging is enabled, the [`set_keylog()`] method must be called on
+    /// the connection for its cryptographic secrets to be logged in the
+    /// [keylog] format to the specified writer.
+    ///
+    /// [`set_keylog()`]: struct.Connection.html#method.set_keylog
+    /// [keylog]: https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format
+    pub fn log_keys(&mut self) {
+        self.tls_ctx.enable_keylog();
+    }
+
+    /// Enables sending or receiving early data.
+    pub fn enable_early_data(&mut self) {
+        self.tls_ctx.set_early_data_enabled(true);
+    }
+
+    /// Configures the list of supported application protocols.
+    ///
+    /// The list of protocols `protos` must be in wire-format (i.e. a series
+    /// of non-empty, 8-bit length-prefixed strings).
+    ///
+    /// On the client this configures the list of protocols to send to the
+    /// server as part of the ALPN extension.
+    ///
+    /// On the server this configures the list of supported protocols to match
+    /// against the client-supplied list.
+    ///
+    /// Applications must set a value, but no default is provided.
+    ///
+    /// ## Examples:
+    ///
+    /// ```
+    /// # let mut config = quiche::Config::new(0xbabababa)?;
+    /// config.set_application_protos(b"\x08http/1.1\x08http/0.9")?;
+    /// # Ok::<(), quiche::Error>(())
+    /// ```
+    pub fn set_application_protos(&mut self, protos: &[u8]) -> Result<()> {
+        let mut b = octets::Octets::with_slice(&protos);
+
+        let mut protos_list = Vec::new();
+
+        while let Ok(proto) = b.get_bytes_with_u8_length() {
+            protos_list.push(proto.to_vec());
+        }
+
+        self.application_protos = protos_list;
+
+        self.tls_ctx.set_alpn(&self.application_protos)
+    }
+
+    /// Sets the `max_idle_timeout` transport parameter.
+    ///
+    /// The default value is infinite, that is, no timeout is used.
+    pub fn set_max_idle_timeout(&mut self, v: u64) {
+        self.local_transport_params.max_idle_timeout = v;
+    }
+
+    /// Sets the `max_udp_payload_size transport` parameter.
+    ///
+    /// The default value is `65527`.
+    pub fn set_max_udp_payload_size(&mut self, v: u64) {
+        self.local_transport_params.max_udp_payload_size = v;
+    }
+
+    /// Sets the `initial_max_data` transport parameter.
+    ///
+    /// When set to a non-zero value quiche will only allow at most `v` bytes
+    /// of incoming stream data to be buffered for the whole connection (that
+    /// is, data that is not yet read by the application) and will allow more
+    /// data to be received as the buffer is consumed by the application.
+    ///
+    /// The default value is `0`.
+    pub fn set_initial_max_data(&mut self, v: u64) {
+        self.local_transport_params.initial_max_data = v;
+    }
+
+    /// Sets the `initial_max_stream_data_bidi_local` transport parameter.
+    ///
+    /// When set to a non-zero value quiche will only allow at most `v` bytes
+    /// of incoming stream data to be buffered for each locally-initiated
+    /// bidirectional stream (that is, data that is not yet read by the
+    /// application) and will allow more data to be received as the buffer is
+    /// consumed by the application.
+    ///
+    /// The default value is `0`.
+    pub fn set_initial_max_stream_data_bidi_local(&mut self, v: u64) {
+        self.local_transport_params
+            .initial_max_stream_data_bidi_local = v;
+    }
+
+    /// Sets the `initial_max_stream_data_bidi_remote` transport parameter.
+    ///
+    /// When set to a non-zero value quiche will only allow at most `v` bytes
+    /// of incoming stream data to be buffered for each remotely-initiated
+    /// bidirectional stream (that is, data that is not yet read by the
+    /// application) and will allow more data to be received as the buffer is
+    /// consumed by the application.
+    ///
+    /// The default value is `0`.
+    pub fn set_initial_max_stream_data_bidi_remote(&mut self, v: u64) {
+        self.local_transport_params
+            .initial_max_stream_data_bidi_remote = v;
+    }
+
+    /// Sets the `initial_max_stream_data_uni` transport parameter.
+    ///
+    /// When set to a non-zero value quiche will only allow at most `v` bytes
+    /// of incoming stream data to be buffered for each unidirectional stream
+    /// (that is, data that is not yet read by the application) and will allow
+    /// more data to be received as the buffer is consumed by the application.
+    ///
+    /// The default value is `0`.
+    pub fn set_initial_max_stream_data_uni(&mut self, v: u64) {
+        self.local_transport_params.initial_max_stream_data_uni = v;
+    }
+
+    /// Sets the `initial_max_streams_bidi` transport parameter.
+    ///
+    /// When set to a non-zero value quiche will only allow `v` number of
+    /// concurrent remotely-initiated bidirectional streams to be open at any
+    /// given time and will increase the limit automatically as streams are
+    /// completed.
+    ///
+    /// A bidirectional stream is considered completed when all incoming data
+    /// has been read by the application (up to the `fin` offset) or the
+    /// stream's read direction has been shutdown, and all outgoing data has
+    /// been acked by the peer (up to the `fin` offset) or the stream's write
+    /// direction has been shutdown.
+    ///
+    /// The default value is `0`.
+    pub fn set_initial_max_streams_bidi(&mut self, v: u64) {
+        self.local_transport_params.initial_max_streams_bidi = v;
+    }
+
+    /// Sets the `initial_max_streams_uni` transport parameter.
+    ///
+    /// When set to a non-zero value quiche will only allow `v` number of
+    /// concurrent remotely-initiated unidirectional streams to be open at any
+    /// given time and will increase the limit automatically as streams are
+    /// completed.
+    ///
+    /// A unidirectional stream is considered completed when all incoming data
+    /// has been read by the application (up to the `fin` offset) or the
+    /// stream's read direction has been shutdown.
+    ///
+    /// The default value is `0`.
+    pub fn set_initial_max_streams_uni(&mut self, v: u64) {
+        self.local_transport_params.initial_max_streams_uni = v;
+    }
+
+    /// Sets the `ack_delay_exponent` transport parameter.
+    ///
+    /// The default value is `3`.
+    pub fn set_ack_delay_exponent(&mut self, v: u64) {
+        self.local_transport_params.ack_delay_exponent = v;
+    }
+
+    /// Sets the `max_ack_delay` transport parameter.
+    ///
+    /// The default value is `25`.
+    pub fn set_max_ack_delay(&mut self, v: u64) {
+        self.local_transport_params.max_ack_delay = v;
+    }
+
+    /// Sets the `disable_active_migration` transport parameter.
+    ///
+    /// The default value is `false`.
+    pub fn set_disable_active_migration(&mut self, v: bool) {
+        self.local_transport_params.disable_active_migration = v;
+    }
+
+    /// Sets the congestion control algorithm used by string.
+    ///
+    /// The default value is `reno`. On error `Error::CongestionControl`
+    /// will be returned.
+    ///
+    /// ## Examples:
+    ///
+    /// ```
+    /// # let mut config = quiche::Config::new(0xbabababa)?;
+    /// config.set_cc_algorithm_name("reno");
+    /// # Ok::<(), quiche::Error>(())
+    /// ```
+    pub fn set_cc_algorithm_name(&mut self, name: &str) -> Result<()> {
+        self.cc_algorithm = CongestionControlAlgorithm::from_str(name)?;
+
+        Ok(())
+    }
+
+    /// Sets the congestion control algorithm used.
+    ///
+    /// The default value is `CongestionControlAlgorithm::CUBIC`.
+    pub fn set_cc_algorithm(&mut self, algo: CongestionControlAlgorithm) {
+        self.cc_algorithm = algo;
+    }
+
+    /// Configures whether to enable HyStart++.
+    ///
+    /// The default value is `true`.
+    pub fn enable_hystart(&mut self, v: bool) {
+        self.hystart = v;
+    }
+
+    /// Configures whether to enable receiving DATAGRAM frames.
+    ///
+    /// When enabled, the `max_datagram_frame_size` transport parameter is set
+    /// to 65536 as recommended by draft-ietf-quic-datagram-01.
+    ///
+    /// The default is `false`.
+    pub fn enable_dgram(
+        &mut self, enabled: bool, recv_queue_len: usize, send_queue_len: usize,
+    ) {
+        self.local_transport_params.max_datagram_frame_size = if enabled {
+            Some(MAX_DGRAM_FRAME_SIZE)
+        } else {
+            None
+        };
+        self.dgram_recv_max_queue_len = recv_queue_len;
+        self.dgram_send_max_queue_len = send_queue_len;
+    }
+}
+
+/// A QUIC connection.
+pub struct Connection {
+    /// QUIC wire version used for the connection.
+    version: u32,
+
+    /// Peer's connection ID.
+    dcid: Vec<u8>,
+
+    /// Local connection ID.
+    scid: Vec<u8>,
+
+    /// Unique opaque ID for the connection that can be used for logging.
+    trace_id: String,
+
+    /// Packet number spaces.
+    pkt_num_spaces: [packet::PktNumSpace; packet::EPOCH_COUNT],
+
+    /// Peer's transport parameters.
+    peer_transport_params: TransportParams,
+
+    /// Local transport parameters.
+    local_transport_params: TransportParams,
+
+    /// TLS handshake state.
+    handshake: tls::Handshake,
+
+    /// Loss recovery and congestion control state.
+    recovery: recovery::Recovery,
+
+    /// List of supported application protocols.
+    application_protos: Vec<Vec<u8>>,
+
+    /// Total number of received packets.
+    recv_count: usize,
+
+    /// Total number of sent packets.
+    sent_count: usize,
+
+    /// Total number of bytes received from the peer.
+    rx_data: u64,
+
+    /// Local flow control limit for the connection.
+    max_rx_data: u64,
+
+    /// Updated local flow control limit for the connection. This is used to
+    /// trigger sending MAX_DATA frames after a certain threshold.
+    max_rx_data_next: u64,
+
+    /// Whether we send MAX_DATA frame.
+    almost_full: bool,
+
+    /// Total number of bytes sent to the peer.
+    tx_data: u64,
+
+    /// Peer's flow control limit for the connection.
+    max_tx_data: u64,
+
+    /// Total number of bytes the server can send before the peer's address
+    /// is verified.
+    max_send_bytes: usize,
+
+    /// Streams map, indexed by stream ID.
+    streams: stream::StreamMap,
+
+    /// Peer's original destination connection ID. Used by the client to
+    /// validate the server's transport parameter.
+    odcid: Option<Vec<u8>>,
+
+    /// Peer's retry source connection ID. Used by the client during stateless
+    /// retry to validate the server's transport parameter.
+    rscid: Option<Vec<u8>>,
+
+    /// Received address verification token.
+    token: Option<Vec<u8>>,
+
+    /// Error code to be sent to the peer in CONNECTION_CLOSE.
+    error: Option<u64>,
+
+    /// Error code to be sent to the peer in APPLICATION_CLOSE.
+    app_error: Option<u64>,
+
+    /// Error reason to be sent to the peer in APPLICATION_CLOSE.
+    app_reason: Vec<u8>,
+
+    /// Received path challenge.
+    challenge: Option<Vec<u8>>,
+
+    /// The connection-level limit at which send blocking occurred.
+    blocked_limit: Option<u64>,
+
+    /// Idle timeout expiration time.
+    idle_timer: Option<time::Instant>,
+
+    /// Draining timeout expiration time.
+    draining_timer: Option<time::Instant>,
+
+    /// Whether this is a server-side connection.
+    is_server: bool,
+
+    /// Whether the initial secrets have been derived.
+    derived_initial_secrets: bool,
+
+    /// Whether a version negotiation packet has already been received. Only
+    /// relevant for client connections.
+    did_version_negotiation: bool,
+
+    /// Whether stateless retry has been performed.
+    did_retry: bool,
+
+    /// Whether the peer already updated its connection ID.
+    got_peer_conn_id: bool,
+
+    /// Whether the peer's address has been verified.
+    verified_peer_address: bool,
+
+    /// Whether the peer has verified our address.
+    peer_verified_address: bool,
+
+    /// Whether the peer's transport parameters were parsed.
+    parsed_peer_transport_params: bool,
+
+    /// Whether the HANDSHAKE_DONE has been sent.
+    handshake_done_sent: bool,
+
+    /// Whether the connection handshake has been confirmed.
+    handshake_confirmed: bool,
+
+    /// Whether an ack-eliciting packet has been sent since last receiving a
+    /// packet.
+    ack_eliciting_sent: bool,
+
+    /// Whether the connection is closed.
+    closed: bool,
+
+    /// Whether to send GREASE.
+    grease: bool,
+
+    /// TLS keylog writer.
+    keylog: Option<Box<dyn std::io::Write + Send>>,
+
+    /// Qlog streaming output.
+    #[cfg(feature = "qlog")]
+    qlog_streamer: Option<qlog::QlogStreamer>,
+
+    /// Whether peer transport parameters were qlogged.
+    #[cfg(feature = "qlog")]
+    qlogged_peer_params: bool,
+
+    /// DATAGRAM queues.
+    dgram_recv_queue: dgram::DatagramQueue,
+    dgram_send_queue: dgram::DatagramQueue,
+}
+
+/// Creates a new server-side connection.
+///
+/// The `scid` parameter represents the server's source connection ID, while
+/// the optional `odcid` parameter represents the original destination ID the
+/// client sent before a stateless retry (this is only required when using
+/// the [`retry()`] function).
+///
+/// [`retry()`]: fn.retry.html
+///
+/// ## Examples:
+///
+/// ```no_run
+/// # let mut config = quiche::Config::new(0xbabababa)?;
+/// # let scid = [0xba; 16];
+/// let conn = quiche::accept(&scid, None, &mut config)?;
+/// # Ok::<(), quiche::Error>(())
+/// ```
+pub fn accept(
+    scid: &[u8], odcid: Option<&[u8]>, config: &mut Config,
+) -> Result<Pin<Box<Connection>>> {
+    let conn = Connection::new(scid, odcid, config, true)?;
+
+    Ok(conn)
+}
+
+/// Creates a new client-side connection.
+///
+/// The `scid` parameter is used as the connection's source connection ID,
+/// while the optional `server_name` parameter is used to verify the peer's
+/// certificate.
+///
+/// ## Examples:
+///
+/// ```no_run
+/// # let mut config = quiche::Config::new(0xbabababa)?;
+/// # let server_name = "quic.tech";
+/// # let scid = [0xba; 16];
+/// let conn = quiche::connect(Some(&server_name), &scid, &mut config)?;
+/// # Ok::<(), quiche::Error>(())
+/// ```
+pub fn connect(
+    server_name: Option<&str>, scid: &[u8], config: &mut Config,
+) -> Result<Pin<Box<Connection>>> {
+    let conn = Connection::new(scid, None, config, false)?;
+
+    if let Some(server_name) = server_name {
+        conn.handshake.set_host_name(server_name)?;
+    }
+
+    Ok(conn)
+}
+
+/// Writes a version negotiation packet.
+///
+/// The `scid` and `dcid` parameters are the source connection ID and the
+/// destination connection ID extracted from the received client's Initial
+/// packet that advertises an unsupported version.
+///
+/// ## Examples:
+///
+/// ```no_run
+/// # let mut buf = [0; 512];
+/// # let mut out = [0; 512];
+/// # let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap();
+/// let (len, src) = socket.recv_from(&mut buf).unwrap();
+///
+/// let hdr =
+///     quiche::Header::from_slice(&mut buf[..len], quiche::MAX_CONN_ID_LEN)?;
+///
+/// if hdr.version != quiche::PROTOCOL_VERSION {
+///     let len = quiche::negotiate_version(&hdr.scid, &hdr.dcid, &mut out)?;
+///     socket.send_to(&out[..len], &src).unwrap();
+/// }
+/// # Ok::<(), quiche::Error>(())
+/// ```
+pub fn negotiate_version(
+    scid: &[u8], dcid: &[u8], out: &mut [u8],
+) -> Result<usize> {
+    packet::negotiate_version(scid, dcid, out)
+}
+
+/// Writes a stateless retry packet.
+///
+/// The `scid` and `dcid` parameters are the source connection ID and the
+/// destination connection ID extracted from the received client's Initial
+/// packet, while `new_scid` is the server's new source connection ID and
+/// `token` is the address validation token the client needs to echo back.
+///
+/// The application is responsible for generating the address validation
+/// token to be sent to the client, and verifying tokens sent back by the
+/// client. The generated token should include the `dcid` parameter, such
+/// that it can be later extracted from the token and passed to the
+/// [`accept()`] function as its `odcid` parameter.
+///
+/// [`accept()`]: fn.accept.html
+///
+/// ## Examples:
+///
+/// ```no_run
+/// # let mut config = quiche::Config::new(0xbabababa)?;
+/// # let mut buf = [0; 512];
+/// # let mut out = [0; 512];
+/// # let scid = [0xba; 16];
+/// # let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap();
+/// # fn mint_token(hdr: &quiche::Header, src: &std::net::SocketAddr) -> Vec<u8> {
+/// #     vec![]
+/// # }
+/// # fn validate_token<'a>(src: &std::net::SocketAddr, token: &'a [u8]) -> Option<&'a [u8]> {
+/// #     None
+/// # }
+/// let (len, src) = socket.recv_from(&mut buf).unwrap();
+///
+/// let hdr = quiche::Header::from_slice(&mut buf[..len], quiche::MAX_CONN_ID_LEN)?;
+///
+/// let token = hdr.token.as_ref().unwrap();
+///
+/// // No token sent by client, create a new one.
+/// if token.is_empty() {
+///     let new_token = mint_token(&hdr, &src);
+///
+///     let len = quiche::retry(
+///         &hdr.scid, &hdr.dcid, &scid, &new_token, hdr.version, &mut out,
+///     )?;
+///
+///     socket.send_to(&out[..len], &src).unwrap();
+///     return Ok(());
+/// }
+///
+/// // Client sent token, validate it.
+/// let odcid = validate_token(&src, token);
+///
+/// if odcid == None {
+///     // Invalid address validation token.
+///     return Ok(());
+/// }
+///
+/// let conn = quiche::accept(&scid, odcid, &mut config)?;
+/// # Ok::<(), quiche::Error>(())
+/// ```
+pub fn retry(
+    scid: &[u8], dcid: &[u8], new_scid: &[u8], token: &[u8], version: u32,
+    out: &mut [u8],
+) -> Result<usize> {
+    packet::retry(scid, dcid, new_scid, token, version, out)
+}
+
+/// Returns true if the given protocol version is supported.
+pub fn version_is_supported(version: u32) -> bool {
+    matches!(
+        version,
+        PROTOCOL_VERSION_DRAFT27 |
+            PROTOCOL_VERSION_DRAFT28 |
+            PROTOCOL_VERSION_DRAFT29
+    )
+}
+
+/// Pushes a frame to the output packet if there is enough space.
+///
+/// Returns `true` on success, `false` otherwise. In case of failure it means
+/// there is no room to add the frame in the packet. You may retry to add the
+/// frame later.
+macro_rules! push_frame_to_pkt {
+    ($frames:expr, $frame:expr, $payload_len: expr, $left:expr) => {{
+        if $frame.wire_len() <= $left {
+            $payload_len += $frame.wire_len();
+            $left -= $frame.wire_len();
+
+            $frames.push($frame);
+
+            true
+        } else {
+            false
+        }
+    }};
+}
+
+/// Conditional qlog action.
+///
+/// Executes the provided body if the qlog feature is enabled and quiche
+/// has been condifigured with a log writer.
+macro_rules! qlog_with {
+    ($qlog_streamer:expr, $qlog_streamer_ref:ident, $body:block) => {{
+        #[cfg(feature = "qlog")]
+        {
+            if let Some($qlog_streamer_ref) = &mut $qlog_streamer {
+                $body
+            }
+        }
+    }};
+}
+
+impl Connection {
+    fn new(
+        scid: &[u8], odcid: Option<&[u8]>, config: &mut Config, is_server: bool,
+    ) -> Result<Pin<Box<Connection>>> {
+        let tls = config.tls_ctx.new_handshake()?;
+        Connection::with_tls(scid, odcid, config, tls, is_server)
+    }
+
+    fn with_tls(
+        scid: &[u8], odcid: Option<&[u8]>, config: &mut Config,
+        tls: tls::Handshake, is_server: bool,
+    ) -> Result<Pin<Box<Connection>>> {
+        let max_rx_data = config.local_transport_params.initial_max_data;
+
+        let scid_as_hex: Vec<String> =
+            scid.iter().map(|b| format!("{:02x}", b)).collect();
+
+        let mut conn = Box::pin(Connection {
+            version: config.version,
+
+            dcid: Vec::new(),
+            scid: scid.to_vec(),
+
+            trace_id: scid_as_hex.join(""),
+
+            pkt_num_spaces: [
+                packet::PktNumSpace::new(),
+                packet::PktNumSpace::new(),
+                packet::PktNumSpace::new(),
+            ],
+
+            peer_transport_params: TransportParams::default(),
+
+            local_transport_params: config.local_transport_params.clone(),
+
+            handshake: tls,
+
+            recovery: recovery::Recovery::new(&config),
+
+            application_protos: config.application_protos.clone(),
+
+            recv_count: 0,
+            sent_count: 0,
+
+            rx_data: 0,
+            max_rx_data,
+            max_rx_data_next: max_rx_data,
+            almost_full: false,
+
+            tx_data: 0,
+            max_tx_data: 0,
+
+            max_send_bytes: 0,
+
+            streams: stream::StreamMap::new(
+                config.local_transport_params.initial_max_streams_bidi,
+                config.local_transport_params.initial_max_streams_uni,
+            ),
+
+            odcid: None,
+
+            rscid: None,
+
+            token: None,
+
+            error: None,
+
+            app_error: None,
+            app_reason: Vec::new(),
+
+            challenge: None,
+
+            blocked_limit: None,
+
+            idle_timer: None,
+
+            draining_timer: None,
+
+            is_server,
+
+            derived_initial_secrets: false,
+
+            did_version_negotiation: false,
+
+            did_retry: false,
+
+            got_peer_conn_id: false,
+
+            // If we did stateless retry assume the peer's address is verified.
+            verified_peer_address: odcid.is_some(),
+
+            // Assume clients validate the server's address implicitly.
+            peer_verified_address: is_server,
+
+            parsed_peer_transport_params: false,
+
+            handshake_done_sent: false,
+
+            handshake_confirmed: false,
+
+            ack_eliciting_sent: false,
+
+            closed: false,
+
+            grease: config.grease,
+
+            keylog: None,
+
+            #[cfg(feature = "qlog")]
+            qlog_streamer: None,
+
+            #[cfg(feature = "qlog")]
+            qlogged_peer_params: false,
+
+            dgram_recv_queue: dgram::DatagramQueue::new(
+                config.dgram_recv_max_queue_len,
+            ),
+
+            dgram_send_queue: dgram::DatagramQueue::new(
+                config.dgram_send_max_queue_len,
+            ),
+        });
+
+        if let Some(odcid) = odcid {
+            conn.local_transport_params
+                .original_destination_connection_id = Some(odcid.to_vec());
+
+            conn.local_transport_params.retry_source_connection_id =
+                Some(scid.to_vec());
+
+            conn.did_retry = true;
+        }
+
+        conn.local_transport_params.initial_source_connection_id =
+            Some(scid.to_vec());
+
+        conn.handshake.init(&conn)?;
+
+        conn.encode_transport_params()?;
+
+        // Derive initial secrets for the client. We can do this here because
+        // we already generated the random destination connection ID.
+        if !is_server {
+            let mut dcid = [0; 16];
+            rand::rand_bytes(&mut dcid[..]);
+
+            let (aead_open, aead_seal) = crypto::derive_initial_key_material(
+                &dcid,
+                conn.version,
+                conn.is_server,
+            )?;
+
+            conn.dcid.extend_from_slice(&dcid);
+
+            conn.pkt_num_spaces[packet::EPOCH_INITIAL].crypto_open =
+                Some(aead_open);
+            conn.pkt_num_spaces[packet::EPOCH_INITIAL].crypto_seal =
+                Some(aead_seal);
+
+            conn.derived_initial_secrets = true;
+        }
+
+        Ok(conn)
+    }
+
+    /// Sets keylog output to the designated [`Writer`].
+    ///
+    /// This needs to be called as soon as the connection is created, to avoid
+    /// missing some early logs.
+    ///
+    /// [`Writer`]: https://doc.rust-lang.org/std/io/trait.Write.html
+    pub fn set_keylog(&mut self, writer: Box<dyn std::io::Write + Send>) {
+        self.keylog = Some(writer);
+    }
+
+    /// Sets qlog output to the designated [`Writer`].
+    ///
+    /// This needs to be called as soon as the connection is created, to avoid
+    /// missing some early logs.
+    ///
+    /// [`Writer`]: https://doc.rust-lang.org/std/io/trait.Write.html
+    #[cfg(feature = "qlog")]
+    pub fn set_qlog(
+        &mut self, writer: Box<dyn std::io::Write + Send>, title: String,
+        description: String,
+    ) {
+        let vp = if self.is_server {
+            qlog::VantagePointType::Server
+        } else {
+            qlog::VantagePointType::Client
+        };
+
+        let trace = qlog::Trace::new(
+            qlog::VantagePoint {
+                name: None,
+                ty: vp,
+                flow: None,
+            },
+            Some(title.to_string()),
+            Some(description.to_string()),
+            Some(qlog::Configuration {
+                time_offset: Some("0".to_string()),
+                time_units: Some(qlog::TimeUnits::Ms),
+                original_uris: None,
+            }),
+            None,
+        );
+
+        let mut streamer = qlog::QlogStreamer::new(
+            qlog::QLOG_VERSION.to_string(),
+            Some(title),
+            Some(description),
+            None,
+            std::time::Instant::now(),
+            trace,
+            writer,
+        );
+
+        streamer.start_log().ok();
+
+        let ev = self.local_transport_params.to_qlog(
+            qlog::TransportOwner::Local,
+            self.version,
+            self.handshake.alpn_protocol(),
+            self.handshake.cipher(),
+        );
+
+        streamer.add_event(ev).ok();
+
+        self.qlog_streamer = Some(streamer);
+    }
+
+    /// Processes QUIC packets received from the peer.
+    ///
+    /// On success the number of bytes processed from the input buffer is
+    /// returned. On error the connection will be closed by calling [`close()`]
+    /// with the appropriate error code.
+    ///
+    /// Coalesced packets will be processed as necessary.
+    ///
+    /// Note that the contents of the input buffer `buf` might be modified by
+    /// this function due to, for example, in-place decryption.
+    ///
+    /// [`close()`]: struct.Connection.html#method.close
+    ///
+    /// ## Examples:
+    ///
+    /// ```no_run
+    /// # let mut buf = [0; 512];
+    /// # let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap();
+    /// # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?;
+    /// # let scid = [0xba; 16];
+    /// # let mut conn = quiche::accept(&scid, None, &mut config)?;
+    /// loop {
+    ///     let read = socket.recv(&mut buf).unwrap();
+    ///
+    ///     let read = match conn.recv(&mut buf[..read]) {
+    ///         Ok(v) => v,
+    ///
+    ///         Err(e) => {
+    ///             // An error occurred, handle it.
+    ///             break;
+    ///         },
+    ///     };
+    /// }
+    /// # Ok::<(), quiche::Error>(())
+    /// ```
+    pub fn recv(&mut self, buf: &mut [u8]) -> Result<usize> {
+        let len = buf.len();
+
+        // Keep track of how many bytes we received from the client, so we
+        // can limit bytes sent back before address validation, to a multiple
+        // of this. The limit needs to be increased early on, so that if there
+        // is an error there is enough credit to send a CONNECTION_CLOSE.
+        //
+        // It doesn't matter if the packets received were valid or not, we only
+        // need to track the total amount of bytes received.
+        if !self.verified_peer_address {
+            self.max_send_bytes += len * MAX_AMPLIFICATION_FACTOR;
+        }
+
+        let mut done = 0;
+        let mut left = len;
+
+        // Process coalesced packets.
+        while left > 0 {
+            let read = match self.recv_single(&mut buf[len - left..len]) {
+                Ok(v) => v,
+
+                Err(Error::Done) => left,
+
+                Err(e) => {
+                    // In case of error processing the incoming packet, close
+                    // the connection.
+                    self.close(false, e.to_wire(), b"").ok();
+                    return Err(e);
+                },
+            };
+
+            done += read;
+            left -= read;
+        }
+
+        Ok(done)
+    }
+
+    /// Processes a single QUIC packet received from the peer.
+    ///
+    /// On success the number of bytes processed from the input buffer is
+    /// returned. When the [`Done`] error is returned, processing of the
+    /// remainder of the incoming UDP datagram should be interrupted.
+    ///
+    /// On error, an error other than [`Done`] is returned.
+    ///
+    /// [`Done`]: enum.Error.html#variant.Done
+    fn recv_single(&mut self, buf: &mut [u8]) -> Result<usize> {
+        let now = time::Instant::now();
+
+        if buf.is_empty() {
+            return Err(Error::Done);
+        }
+
+        if self.is_closed() || self.draining_timer.is_some() {
+            return Err(Error::Done);
+        }
+
+        let is_closing = self.error.is_some() || self.app_error.is_some();
+
+        if is_closing {
+            return Err(Error::Done);
+        }
+
+        let mut b = octets::OctetsMut::with_slice(buf);
+
+        let mut hdr =
+            Header::from_bytes(&mut b, self.scid.len()).map_err(|e| {
+                drop_pkt_on_err(
+                    e,
+                    self.recv_count,
+                    self.is_server,
+                    &self.trace_id,
+                )
+            })?;
+
+        if hdr.ty == packet::Type::VersionNegotiation {
+            // Version negotiation packets can only be sent by the server.
+            if self.is_server {
+                return Err(Error::Done);
+            }
+
+            // Ignore duplicate version negotiation.
+            if self.did_version_negotiation {
+                return Err(Error::Done);
+            }
+
+            // Ignore version negotiation if any other packet has already been
+            // successfully processed.
+            if self.recv_count > 0 {
+                return Err(Error::Done);
+            }
+
+            if hdr.dcid != self.scid {
+                return Err(Error::Done);
+            }
+
+            if hdr.scid != self.dcid {
+                return Err(Error::Done);
+            }
+
+            trace!("{} rx pkt {:?}", self.trace_id, hdr);
+
+            let versions = hdr.versions.ok_or(Error::Done)?;
+
+            // Ignore version negotiation if the version already selected is
+            // listed.
+            if versions.iter().any(|&v| v == self.version) {
+                return Err(Error::Done);
+            }
+
+            match versions.iter().filter(|&&v| version_is_supported(v)).max() {
+                Some(v) => self.version = *v,
+
+                None => {
+                    // We don't support any of the versions offered.
+                    //
+                    // While a man-in-the-middle attacker might be able to
+                    // inject a version negotiation packet that triggers this
+                    // failure, the window of opportunity is very small and
+                    // this error is quite useful for debugging, so don't just
+                    // ignore the packet.
+                    return Err(Error::UnknownVersion);
+                },
+            };
+
+            self.did_version_negotiation = true;
+
+            // Derive Initial secrets based on the new version.
+            let (aead_open, aead_seal) = crypto::derive_initial_key_material(
+                &self.dcid,
+                self.version,
+                self.is_server,
+            )?;
+
+            // Reset connection state to force sending another Initial packet.
+            self.drop_epoch_state(packet::EPOCH_INITIAL, now);
+            self.got_peer_conn_id = false;
+            self.handshake.clear()?;
+
+            self.pkt_num_spaces[packet::EPOCH_INITIAL].crypto_open =
+                Some(aead_open);
+            self.pkt_num_spaces[packet::EPOCH_INITIAL].crypto_seal =
+                Some(aead_seal);
+
+            // Encode transport parameters again, as the new version might be
+            // using a different format.
+            self.encode_transport_params()?;
+
+            return Err(Error::Done);
+        }
+
+        if hdr.ty == packet::Type::Retry {
+            // Retry packets can only be sent by the server.
+            if self.is_server {
+                return Err(Error::Done);
+            }
+
+            // Ignore duplicate retry.
+            if self.did_retry {
+                return Err(Error::Done);
+            }
+
+            // Check if Retry packet is valid.
+            if packet::verify_retry_integrity(&b, &self.dcid, self.version)
+                .is_err()
+            {
+                return Err(Error::Done);
+            }
+
+            trace!("{} rx pkt {:?}", self.trace_id, hdr);
+
+            self.token = hdr.token;
+            self.did_retry = true;
+
+            // Remember peer's new connection ID.
+            self.odcid = Some(self.dcid.clone());
+
+            self.dcid.resize(hdr.scid.len(), 0);
+            self.dcid.copy_from_slice(&hdr.scid);
+
+            self.rscid = Some(self.dcid.clone());
+
+            // Derive Initial secrets using the new connection ID.
+            let (aead_open, aead_seal) = crypto::derive_initial_key_material(
+                &hdr.scid,
+                self.version,
+                self.is_server,
+            )?;
+
+            // Reset connection state to force sending another Initial packet.
+            self.drop_epoch_state(packet::EPOCH_INITIAL, now);
+            self.got_peer_conn_id = false;
+            self.handshake.clear()?;
+
+            self.pkt_num_spaces[packet::EPOCH_INITIAL].crypto_open =
+                Some(aead_open);
+            self.pkt_num_spaces[packet::EPOCH_INITIAL].crypto_seal =
+                Some(aead_seal);
+
+            return Err(Error::Done);
+        }
+
+        if self.is_server && !self.did_version_negotiation {
+            if !version_is_supported(hdr.version) {
+                return Err(Error::UnknownVersion);
+            }
+
+            self.version = hdr.version;
+            self.did_version_negotiation = true;
+
+            // Encode transport parameters again, as the new version might be
+            // using a different format.
+            self.encode_transport_params()?;
+        }
+
+        if hdr.ty != packet::Type::Short && hdr.version != self.version {
+            // At this point version negotiation was already performed, so
+            // ignore packets that don't match the connection's version.
+            return Err(Error::Done);
+        }
+
+        // Long header packets have an explicit payload length, but short
+        // packets don't so just use the remaining capacity in the buffer.
+        let payload_len = if hdr.ty == packet::Type::Short {
+            b.cap()
+        } else {
+            b.get_varint().map_err(|e| {
+                drop_pkt_on_err(
+                    e.into(),
+                    self.recv_count,
+                    self.is_server,
+                    &self.trace_id,
+                )
+            })? as usize
+        };
+
+        // Derive initial secrets on the server.
+        if !self.derived_initial_secrets {
+            let (aead_open, aead_seal) = crypto::derive_initial_key_material(
+                &hdr.dcid,
+                self.version,
+                self.is_server,
+            )?;
+
+            self.pkt_num_spaces[packet::EPOCH_INITIAL].crypto_open =
+                Some(aead_open);
+            self.pkt_num_spaces[packet::EPOCH_INITIAL].crypto_seal =
+                Some(aead_seal);
+
+            self.derived_initial_secrets = true;
+        }
+
+        // Select packet number space epoch based on the received packet's type.
+        let epoch = hdr.ty.to_epoch()?;
+
+        // Select AEAD context used to open incoming packet.
+        #[allow(clippy::or_fun_call)]
+        let aead = (self.pkt_num_spaces[epoch].crypto_0rtt_open.as_ref())
+            // Only use 0-RTT key if incoming packet is 0-RTT.
+            .filter(|_| hdr.ty == packet::Type::ZeroRTT)
+            // Otherwise use the packet number space's main key.
+            .or(self.pkt_num_spaces[epoch].crypto_open.as_ref())
+            // Finally, discard packet if no usable key is available.
+            //
+            // TODO: buffer 0-RTT/1-RTT packets instead of discarding when the
+            // required key is not available yet, as an optimization.
+            .ok_or_else(|| {
+                drop_pkt_on_err(
+                    Error::CryptoFail,
+                    self.recv_count,
+                    self.is_server,
+                    &self.trace_id,
+                )
+            })?;
+
+        let aead_tag_len = aead.alg().tag_len();
+
+        packet::decrypt_hdr(&mut b, &mut hdr, &aead).map_err(|e| {
+            drop_pkt_on_err(e, self.recv_count, self.is_server, &self.trace_id)
+        })?;
+
+        let pn = packet::decode_pkt_num(
+            self.pkt_num_spaces[epoch].largest_rx_pkt_num,
+            hdr.pkt_num,
+            hdr.pkt_num_len,
+        );
+
+        let pn_len = hdr.pkt_num_len;
+
+        trace!(
+            "{} rx pkt {:?} len={} pn={}",
+            self.trace_id,
+            hdr,
+            payload_len,
+            pn
+        );
+
+        qlog_with!(self.qlog_streamer, q, {
+            let packet_size = b.len();
+
+            let qlog_pkt_hdr = qlog::PacketHeader::with_type(
+                hdr.ty.to_qlog(),
+                pn,
+                Some(packet_size as u64),
+                Some(payload_len as u64),
+                Some(hdr.version),
+                Some(&hdr.scid),
+                Some(&hdr.dcid),
+            );
+
+            q.add_event(qlog::event::Event::packet_received(
+                hdr.ty.to_qlog(),
+                qlog_pkt_hdr,
+                Some(Vec::new()),
+                None,
+                None,
+                None,
+            ))
+            .ok();
+        });
+
+        let mut payload = packet::decrypt_pkt(
+            &mut b,
+            pn,
+            pn_len,
+            payload_len,
+            &aead,
+        )
+        .map_err(|e| {
+            drop_pkt_on_err(e, self.recv_count, self.is_server, &self.trace_id)
+        })?;
+
+        if self.pkt_num_spaces[epoch].recv_pkt_num.contains(pn) {
+            trace!("{} ignored duplicate packet {}", self.trace_id, pn);
+            return Err(Error::Done);
+        }
+
+        if !self.is_server && !self.got_peer_conn_id {
+            if self.odcid.is_none() {
+                self.odcid = Some(self.dcid.clone());
+            }
+
+            // Replace the randomly generated destination connection ID with
+            // the one supplied by the server.
+            self.dcid.resize(hdr.scid.len(), 0);
+            self.dcid.copy_from_slice(&hdr.scid);
+
+            self.got_peer_conn_id = true;
+        }
+
+        if self.is_server && !self.got_peer_conn_id {
+            self.dcid.extend_from_slice(&hdr.scid);
+
+            if !self.did_retry && self.version >= PROTOCOL_VERSION_DRAFT28 {
+                self.local_transport_params
+                    .original_destination_connection_id = Some(hdr.dcid.to_vec());
+
+                self.encode_transport_params()?;
+            }
+
+            self.got_peer_conn_id = true;
+        }
+
+        // To avoid sending an ACK in response to an ACK-only packet, we need
+        // to keep track of whether this packet contains any frame other than
+        // ACK and PADDING.
+        let mut ack_elicited = false;
+
+        // Process packet payload.
+        while payload.cap() > 0 {
+            let frame = frame::Frame::from_bytes(&mut payload, hdr.ty)?;
+
+            qlog_with!(self.qlog_streamer, q, {
+                q.add_frame(frame.to_qlog(), false).ok();
+            });
+
+            if frame.ack_eliciting() {
+                ack_elicited = true;
+            }
+
+            if let Err(e) = self.process_frame(frame, epoch, now) {
+                qlog_with!(self.qlog_streamer, q, {
+                    // Always conclude frame writing on error.
+                    q.finish_frames().ok();
+                });
+
+                return Err(e);
+            }
+        }
+
+        qlog_with!(self.qlog_streamer, q, {
+            // Always conclude frame writing.
+            q.finish_frames().ok();
+        });
+
+        qlog_with!(self.qlog_streamer, q, {
+            let ev = self.recovery.to_qlog();
+            q.add_event(ev).ok();
+        });
+
+        // Only log the remote transport parameters once the connection is
+        // established (i.e. after frames have been fully parsed) and only
+        // once per connection.
+        if self.is_established() {
+            qlog_with!(self.qlog_streamer, q, {
+                if !self.qlogged_peer_params {
+                    let ev = self.peer_transport_params.to_qlog(
+                        qlog::TransportOwner::Remote,
+                        self.version,
+                        self.handshake.alpn_protocol(),
+                        self.handshake.cipher(),
+                    );
+
+                    q.add_event(ev).ok();
+
+                    self.qlogged_peer_params = true;
+                }
+            });
+        }
+
+        // Process acked frames.
+        for acked in self.recovery.acked[epoch].drain(..) {
+            match acked {
+                frame::Frame::ACK { ranges, .. } => {
+                    // Stop acknowledging packets less than or equal to the
+                    // largest acknowledged in the sent ACK frame that, in
+                    // turn, got acked.
+                    if let Some(largest_acked) = ranges.last() {
+                        self.pkt_num_spaces[epoch]
+                            .recv_pkt_need_ack
+                            .remove_until(largest_acked);
+                    }
+                },
+
+                frame::Frame::Crypto { data } => {
+                    self.pkt_num_spaces[epoch]
+                        .crypto_stream
+                        .send
+                        .ack(data.off(), data.len());
+                },
+
+                frame::Frame::Stream { stream_id, data } => {
+                    let stream = match self.streams.get_mut(stream_id) {
+                        Some(v) => v,
+
+                        None => continue,
+                    };
+
+                    stream.send.ack(data.off(), data.len());
+
+                    if stream.is_complete() {
+                        let local = stream.local;
+                        self.streams.collect(stream_id, local);
+                    }
+                },
+
+                _ => (),
+            }
+        }
+
+        // We only record the time of arrival of the largest packet number
+        // that still needs to be acked, to be used for ACK delay calculation.
+        if self.pkt_num_spaces[epoch].recv_pkt_need_ack.last() < Some(pn) {
+            self.pkt_num_spaces[epoch].largest_rx_pkt_time = now;
+        }
+
+        self.pkt_num_spaces[epoch].recv_pkt_num.insert(pn);
+
+        self.pkt_num_spaces[epoch].recv_pkt_need_ack.push_item(pn);
+
+        self.pkt_num_spaces[epoch].ack_elicited =
+            cmp::max(self.pkt_num_spaces[epoch].ack_elicited, ack_elicited);
+
+        self.pkt_num_spaces[epoch].largest_rx_pkt_num =
+            cmp::max(self.pkt_num_spaces[epoch].largest_rx_pkt_num, pn);
+
+        if let Some(idle_timeout) = self.idle_timeout() {
+            self.idle_timer = Some(now + idle_timeout);
+        }
+
+        self.recv_count += 1;
+
+        let read = b.off() + aead_tag_len;
+
+        // An Handshake packet has been received from the client and has been
+        // successfully processed, so we can drop the initial state and consider
+        // the client's address to be verified.
+        if self.is_server && hdr.ty == packet::Type::Handshake {
+            self.drop_epoch_state(packet::EPOCH_INITIAL, now);
+
+            self.verified_peer_address = true;
+        }
+
+        self.ack_eliciting_sent = false;
+
+        Ok(read)
+    }
+
+    /// Writes a single QUIC packet to be sent to the peer.
+    ///
+    /// On success the number of bytes written to the output buffer is
+    /// returned, or [`Done`] if there was nothing to write.
+    ///
+    /// The application should call `send()` multiple times until [`Done`] is
+    /// returned, indicating that there are no more packets to send. It is
+    /// recommended that `send()` be called in the following cases:
+    ///
+    ///  * When the application receives QUIC packets from the peer (that is,
+    ///    any time [`recv()`] is also called).
+    ///
+    ///  * When the connection timer expires (that is, any time [`on_timeout()`]
+    ///    is also called).
+    ///
+    ///  * When the application sends data to the peer (for examples, any time
+    ///    [`stream_send()`] or [`stream_shutdown()`] are called).
+    ///
+    /// [`Done`]: enum.Error.html#variant.Done
+    /// [`recv()`]: struct.Connection.html#method.recv
+    /// [`on_timeout()`]: struct.Connection.html#method.on_timeout
+    /// [`stream_send()`]: struct.Connection.html#method.stream_send
+    /// [`stream_shutdown()`]: struct.Connection.html#method.stream_shutdown
+    ///
+    /// ## Examples:
+    ///
+    /// ```no_run
+    /// # let mut out = [0; 512];
+    /// # let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap();
+    /// # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?;
+    /// # let scid = [0xba; 16];
+    /// # let mut conn = quiche::accept(&scid, None, &mut config)?;
+    /// loop {
+    ///     let write = match conn.send(&mut out) {
+    ///         Ok(v) => v,
+    ///
+    ///         Err(quiche::Error::Done) => {
+    ///             // Done writing.
+    ///             break;
+    ///         },
+    ///
+    ///         Err(e) => {
+    ///             // An error occurred, handle it.
+    ///             break;
+    ///         },
+    ///     };
+    ///
+    ///     socket.send(&out[..write]).unwrap();
+    /// }
+    /// # Ok::<(), quiche::Error>(())
+    /// ```
+    pub fn send(&mut self, out: &mut [u8]) -> Result<usize> {
+        let now = time::Instant::now();
+
+        if out.is_empty() {
+            return Err(Error::BufferTooShort);
+        }
+
+        if self.is_closed() || self.draining_timer.is_some() {
+            return Err(Error::Done);
+        }
+
+        // If the Initial secrets have not been derived yet, there's no point
+        // in trying to send a packet, so return early.
+        if !self.derived_initial_secrets {
+            return Err(Error::Done);
+        }
+
+        let is_closing = self.error.is_some() || self.app_error.is_some();
+
+        if !is_closing {
+            self.do_handshake()?;
+        }
+
+        let mut b = octets::OctetsMut::with_slice(out);
+
+        let epoch = self.write_epoch()?;
+
+        let pkt_type = packet::Type::from_epoch(epoch);
+
+        // Process lost frames.
+        for lost in self.recovery.lost[epoch].drain(..) {
+            match lost {
+                frame::Frame::Crypto { data } => {
+                    self.pkt_num_spaces[epoch].crypto_stream.send.push(data)?;
+                },
+
+                frame::Frame::Stream { stream_id, data } => {
+                    let stream = match self.streams.get_mut(stream_id) {
+                        Some(v) => v,
+
+                        None => continue,
+                    };
+
+                    let was_flushable = stream.is_flushable();
+
+                    let empty_fin = data.is_empty() && data.fin();
+
+                    stream.send.push(data)?;
+
+                    // If the stream is now flushable push it to the flushable
+                    // queue, but only if it wasn't already queued.
+                    //
+                    // Consider the stream flushable also when we are sending a
+                    // zero-length frame that has the fin flag set.
+                    if (stream.is_flushable() || empty_fin) && !was_flushable {
+                        let urgency = stream.urgency;
+                        let incremental = stream.incremental;
+                        self.streams.push_flushable(
+                            stream_id,
+                            urgency,
+                            incremental,
+                        );
+                    }
+                },
+
+                frame::Frame::ACK { .. } => {
+                    self.pkt_num_spaces[epoch].ack_elicited = true;
+                },
+
+                frame::Frame::HandshakeDone => {
+                    self.handshake_done_sent = false;
+                },
+
+                frame::Frame::MaxStreamData { stream_id, .. } => {
+                    if self.streams.get(stream_id).is_some() {
+                        self.streams.mark_almost_full(stream_id, true);
+                    }
+                },
+
+                frame::Frame::MaxData { .. } => {
+                    self.almost_full = true;
+                },
+
+                _ => (),
+            }
+        }
+
+        let mut left = b.cap();
+
+        // Limit output packet size to respect peer's max_packet_size limit.
+        left = cmp::min(left, self.max_send_udp_payload_len());
+
+        // Limit output packet size by congestion window size.
+        left = cmp::min(left, self.recovery.cwnd_available());
+
+        // Limit data sent by the server based on the amount of data received
+        // from the client before its address is validated.
+        if !self.verified_peer_address && self.is_server {
+            left = cmp::min(left, self.max_send_bytes);
+        }
+
+        let pn = self.pkt_num_spaces[epoch].next_pkt_num;
+        let pn_len = packet::pkt_num_len(pn)?;
+
+        // The AEAD overhead at the current encryption level.
+        let crypto_overhead = self.pkt_num_spaces[epoch]
+            .crypto_overhead()
+            .ok_or(Error::Done)?;
+
+        let hdr = Header {
+            ty: pkt_type,
+            version: self.version,
+            dcid: self.dcid.clone(),
+
+            // Don't needlessly clone the source connection ID for 1-RTT packets
+            // as it is not used.
+            scid: if pkt_type != packet::Type::Short {
+                self.scid.clone()
+            } else {
+                Vec::new()
+            },
+
+            pkt_num: 0,
+            pkt_num_len: pn_len,
+
+            // Only clone token for Initial packets, as other packets don't have
+            // this field (Retry doesn't count, as it's not encoded as part of
+            // this code path).
+            token: if pkt_type == packet::Type::Initial {
+                self.token.clone()
+            } else {
+                None
+            },
+
+            versions: None,
+            key_phase: false,
+        };
+
+        hdr.to_bytes(&mut b)?;
+
+        // Calculate the space required for the packet, including the header
+        // the payload length, the packet number and the AEAD overhead.
+        let mut overhead = b.off() + pn_len + crypto_overhead;
+
+        // We assume that the payload length, which is only present in long
+        // header packets, can always be encoded with a 2-byte varint.
+        if pkt_type != packet::Type::Short {
+            overhead += 2;
+        }
+
+        // Make sure we have enough space left for the packet.
+        match left.checked_sub(overhead) {
+            Some(v) => left = v,
+
+            None => {
+                // We can't send more because there isn't enough space available
+                // in the output buffer.
+                //
+                // This usually happens when we try to send a new packet but
+                // failed because cwnd is almost full. In such case app_limited
+                // is set to false here to make cwnd grow when ACK is received.
+                self.recovery.update_app_limited(false);
+                return Err(Error::Done);
+            },
+        }
+
+        let mut frames: Vec<frame::Frame> = Vec::new();
+
+        let mut ack_eliciting = false;
+        let mut in_flight = false;
+        let mut has_data = false;
+
+        let mut payload_len = 0;
+
+        // Create ACK frame.
+        if self.pkt_num_spaces[epoch].recv_pkt_need_ack.len() > 0 &&
+            (self.pkt_num_spaces[epoch].ack_elicited ||
+                self.recovery.loss_probes[epoch] > 0) &&
+            !is_closing
+        {
+            let ack_delay =
+                self.pkt_num_spaces[epoch].largest_rx_pkt_time.elapsed();
+
+            let ack_delay = ack_delay.as_micros() as u64 /
+                2_u64
+                    .pow(self.local_transport_params.ack_delay_exponent as u32);
+
+            let frame = frame::Frame::ACK {
+                ack_delay,
+                ranges: self.pkt_num_spaces[epoch].recv_pkt_need_ack.clone(),
+            };
+
+            if push_frame_to_pkt!(frames, frame, payload_len, left) {
+                self.pkt_num_spaces[epoch].ack_elicited = false;
+            }
+        }
+
+        if pkt_type == packet::Type::Short && !is_closing {
+            // Create HANDSHAKE_DONE frame.
+            if self.is_established() &&
+                !self.handshake_done_sent &&
+                self.is_server
+            {
+                let frame = frame::Frame::HandshakeDone;
+
+                if push_frame_to_pkt!(frames, frame, payload_len, left) {
+                    self.handshake_done_sent = true;
+
+                    ack_eliciting = true;
+                    in_flight = true;
+                }
+            }
+
+            // Create MAX_STREAMS_BIDI frame.
+            if self.streams.should_update_max_streams_bidi() {
+                let frame = frame::Frame::MaxStreamsBidi {
+                    max: self.streams.max_streams_bidi_next(),
+                };
+
+                if push_frame_to_pkt!(frames, frame, payload_len, left) {
+                    self.streams.update_max_streams_bidi();
+
+                    ack_eliciting = true;
+                    in_flight = true;
+                }
+            }
+
+            // Create MAX_STREAMS_UNI frame.
+            if self.streams.should_update_max_streams_uni() {
+                let frame = frame::Frame::MaxStreamsUni {
+                    max: self.streams.max_streams_uni_next(),
+                };
+
+                if push_frame_to_pkt!(frames, frame, payload_len, left) {
+                    self.streams.update_max_streams_uni();
+
+                    ack_eliciting = true;
+                    in_flight = true;
+                }
+            }
+
+            // Create MAX_DATA frame as needed.
+            if self.almost_full {
+                let frame = frame::Frame::MaxData {
+                    max: self.max_rx_data_next,
+                };
+
+                if push_frame_to_pkt!(frames, frame, payload_len, left) {
+                    self.almost_full = false;
+
+                    // Commits the new max_rx_data limit.
+                    self.max_rx_data = self.max_rx_data_next;
+
+                    ack_eliciting = true;
+                    in_flight = true;
+                }
+            }
+
+            // Create DATA_BLOCKED frame.
+            if let Some(limit) = self.blocked_limit {
+                let frame = frame::Frame::DataBlocked { limit };
+
+                if push_frame_to_pkt!(frames, frame, payload_len, left) {
+                    self.blocked_limit = None;
+
+                    ack_eliciting = true;
+                    in_flight = true;
+                }
+            }
+
+            // Create MAX_STREAM_DATA frames as needed.
+            for stream_id in self.streams.almost_full() {
+                let stream = match self.streams.get_mut(stream_id) {
+                    Some(v) => v,
+
+                    None => {
+                        // The stream doesn't exist anymore, so remove it from
+                        // the almost full set.
+                        self.streams.mark_almost_full(stream_id, false);
+                        continue;
+                    },
+                };
+
+                let frame = frame::Frame::MaxStreamData {
+                    stream_id,
+                    max: stream.recv.max_data_next(),
+                };
+
+                if push_frame_to_pkt!(frames, frame, payload_len, left) {
+                    stream.recv.update_max_data();
+
+                    self.streams.mark_almost_full(stream_id, false);
+
+                    ack_eliciting = true;
+                    in_flight = true;
+                }
+            }
+
+            // Create STREAM_DATA_BLOCKED frames as needed.
+            for (stream_id, limit) in self
+                .streams
+                .blocked()
+                .map(|(&k, &v)| (k, v))
+                .collect::<Vec<(u64, u64)>>()
+            {
+                let frame = frame::Frame::StreamDataBlocked { stream_id, limit };
+
+                if push_frame_to_pkt!(frames, frame, payload_len, left) {
+                    self.streams.mark_blocked(stream_id, false, 0);
+
+                    ack_eliciting = true;
+                    in_flight = true;
+                }
+            }
+        }
+
+        // Create CONNECTION_CLOSE frame.
+        if let Some(err) = self.error {
+            let frame = frame::Frame::ConnectionClose {
+                error_code: err,
+                frame_type: 0,
+                reason: Vec::new(),
+            };
+
+            if push_frame_to_pkt!(frames, frame, payload_len, left) {
+                self.draining_timer = Some(now + (self.recovery.pto() * 3));
+
+                ack_eliciting = true;
+                in_flight = true;
+            }
+        }
+
+        // Create APPLICATION_CLOSE frame.
+        if let Some(err) = self.app_error {
+            if pkt_type == packet::Type::Short {
+                let frame = frame::Frame::ApplicationClose {
+                    error_code: err,
+                    reason: self.app_reason.clone(),
+                };
+
+                if push_frame_to_pkt!(frames, frame, payload_len, left) {
+                    self.draining_timer = Some(now + (self.recovery.pto() * 3));
+
+                    ack_eliciting = true;
+                    in_flight = true;
+                }
+            }
+        }
+
+        // Create PATH_RESPONSE frame.
+        if let Some(ref challenge) = self.challenge {
+            let frame = frame::Frame::PathResponse {
+                data: challenge.clone(),
+            };
+
+            if push_frame_to_pkt!(frames, frame, payload_len, left) {
+                self.challenge = None;
+
+                ack_eliciting = true;
+                in_flight = true;
+            }
+        }
+
+        // Create CRYPTO frame.
+        if self.pkt_num_spaces[epoch].crypto_stream.is_flushable() &&
+            left > frame::MAX_CRYPTO_OVERHEAD &&
+            !is_closing
+        {
+            let crypto_len = left - frame::MAX_CRYPTO_OVERHEAD;
+            let crypto_buf = self.pkt_num_spaces[epoch]
+                .crypto_stream
+                .send
+                .pop(crypto_len)?;
+
+            let frame = frame::Frame::Crypto { data: crypto_buf };
+
+            if push_frame_to_pkt!(frames, frame, payload_len, left) {
+                ack_eliciting = true;
+                in_flight = true;
+                has_data = true;
+            }
+        }
+
+        // Create DATAGRAM frame.
+        if pkt_type == packet::Type::Short &&
+            left > frame::MAX_DGRAM_OVERHEAD &&
+            !is_closing
+        {
+            if let Some(max_dgram_payload) = self.dgram_max_writable_len() {
+                while let Some(len) = self.dgram_send_queue.peek_front_len() {
+                    if (len + frame::MAX_DGRAM_OVERHEAD) <= left {
+                        // Front of the queue fits this packet, send it
+                        match self.dgram_send_queue.pop() {
+                            Some(data) => {
+                                let frame = frame::Frame::Datagram { data };
+
+                                if push_frame_to_pkt!(
+                                    frames,
+                                    frame,
+                                    payload_len,
+                                    left
+                                ) {
+                                    ack_eliciting = true;
+                                    in_flight = true;
+                                }
+                            },
+
+                            None => continue,
+                        };
+                    } else if len > max_dgram_payload {
+                        // This dgram frame will never fit. Let's purge it.
+                        self.dgram_send_queue.pop();
+                    } else {
+                        break;
+                    }
+                }
+            }
+        }
+
+        // Create a single STREAM frame for the first stream that is flushable.
+        if pkt_type == packet::Type::Short &&
+            left > frame::MAX_STREAM_OVERHEAD &&
+            !is_closing
+        {
+            while let Some(stream_id) = self.streams.pop_flushable() {
+                let stream = match self.streams.get_mut(stream_id) {
+                    Some(v) => v,
+
+                    None => continue,
+                };
+
+                let off = stream.send.off_front();
+
+                // Try to accurately account for the STREAM frame's overhead,
+                // such that we can fill as much of the packet buffer as
+                // possible.
+                let overhead = 1 +
+                    octets::varint_len(stream_id) +
+                    octets::varint_len(off) +
+                    octets::varint_len(left as u64);
+
+                let max_len = match left.checked_sub(overhead) {
+                    Some(v) => v,
+
+                    None => continue,
+                };
+
+                let stream_buf = stream.send.pop(max_len)?;
+
+                if stream_buf.is_empty() && !stream_buf.fin() {
+                    continue;
+                }
+
+                let frame = frame::Frame::Stream {
+                    stream_id,
+                    data: stream_buf,
+                };
+
+                if push_frame_to_pkt!(frames, frame, payload_len, left) {
+                    ack_eliciting = true;
+                    in_flight = true;
+                    has_data = true;
+                }
+
+                // If the stream is still flushable, push it to the back of the
+                // queue again.
+                if stream.is_flushable() {
+                    let urgency = stream.urgency;
+                    let incremental = stream.incremental;
+                    self.streams.push_flushable(stream_id, urgency, incremental);
+                }
+
+                // When fuzzing, try to coalesce multiple STREAM frames in the
+                // same packet, so it's easier to generate fuzz corpora.
+                if cfg!(feature = "fuzzing") && left > frame::MAX_STREAM_OVERHEAD
+                {
+                    continue;
+                }
+
+                break;
+            }
+        }
+
+        // Create PING for PTO probe if no other ack-elicitng frame is sent.
+        if self.recovery.loss_probes[epoch] > 0 &&
+            !ack_eliciting &&
+            left >= 1 &&
+            !is_closing
+        {
+            let frame = frame::Frame::Ping;
+
+            if push_frame_to_pkt!(frames, frame, payload_len, left) {
+                ack_eliciting = true;
+                in_flight = true;
+            }
+        }
+
+        if ack_eliciting {
+            self.recovery.loss_probes[epoch] =
+                self.recovery.loss_probes[epoch].saturating_sub(1);
+        }
+
+        if frames.is_empty() {
+            // When we reach this point we are not able to write more, so set
+            // app_limited to false.
+            self.recovery.update_app_limited(false);
+            return Err(Error::Done);
+        }
+
+        // Pad the client's initial packet.
+        if !self.is_server && pkt_type == packet::Type::Initial {
+            let pkt_len = pn_len + payload_len + crypto_overhead;
+
+            let frame = frame::Frame::Padding {
+                len: cmp::min(MIN_CLIENT_INITIAL_LEN - pkt_len, left),
+            };
+
+            payload_len += frame.wire_len();
+
+            frames.push(frame);
+
+            in_flight = true;
+        }
+
+        // Pad payload so that it's always at least 4 bytes.
+        if payload_len < PAYLOAD_MIN_LEN {
+            let frame = frame::Frame::Padding {
+                len: PAYLOAD_MIN_LEN - payload_len,
+            };
+
+            payload_len += frame.wire_len();
+
+            frames.push(frame);
+
+            in_flight = true;
+        }
+
+        payload_len += crypto_overhead;
+
+        // Only long header packets have an explicit length field.
+        if pkt_type != packet::Type::Short {
+            let len = pn_len + payload_len;
+            b.put_varint(len as u64)?;
+        }
+
+        packet::encode_pkt_num(pn, &mut b)?;
+
+        let payload_offset = b.off();
+
+        trace!(
+            "{} tx pkt {:?} len={} pn={}",
+            self.trace_id,
+            hdr,
+            payload_len,
+            pn
+        );
+
+        qlog_with!(self.qlog_streamer, q, {
+            let qlog_pkt_hdr = qlog::PacketHeader::with_type(
+                hdr.ty.to_qlog(),
+                pn,
+                Some(payload_len as u64 + payload_offset as u64),
+                Some(payload_len as u64),
+                Some(hdr.version),
+                Some(&hdr.scid),
+                Some(&hdr.dcid),
+            );
+
+            let packet_sent_ev = qlog::event::Event::packet_sent_min(
+                hdr.ty.to_qlog(),
+                qlog_pkt_hdr,
+                Some(Vec::new()),
+            );
+
+            q.add_event(packet_sent_ev).ok();
+        });
+
+        // Encode frames into the output packet.
+        for frame in &mut frames {
+            trace!("{} tx frm {:?}", self.trace_id, frame);
+
+            frame.to_bytes(&mut b)?;
+
+            qlog_with!(self.qlog_streamer, q, {
+                q.add_frame(frame.to_qlog(), false).ok();
+            });
+
+            // Once frames have been serialized they are passed to the Recovery
+            // module which manages retransmission. However, some frames do not
+            // contain retransmittable data, so drop it here.
+            frame.shrink_for_retransmission();
+        }
+
+        qlog_with!(self.qlog_streamer, q, {
+            q.finish_frames().ok();
+        });
+
+        let aead = match self.pkt_num_spaces[epoch].crypto_seal {
+            Some(ref v) => v,
+            None => return Err(Error::InvalidState),
+        };
+
+        let written = packet::encrypt_pkt(
+            &mut b,
+            pn,
+            pn_len,
+            payload_len,
+            payload_offset,
+            aead,
+        )?;
+
+        let sent_pkt = recovery::Sent {
+            pkt_num: pn,
+            frames,
+            time_sent: now,
+            time_acked: None,
+            time_lost: None,
+            size: if ack_eliciting { written } else { 0 },
+            ack_eliciting,
+            in_flight,
+            delivered: 0,
+            delivered_time: now,
+            recent_delivered_packet_sent_time: now,
+            is_app_limited: false,
+            has_data,
+        };
+
+        self.recovery.on_packet_sent(
+            sent_pkt,
+            epoch,
+            self.handshake_status(),
+            now,
+            &self.trace_id,
+        );
+
+        qlog_with!(self.qlog_streamer, q, {
+            let ev = self.recovery.to_qlog();
+            q.add_event(ev).ok();
+        });
+
+        self.pkt_num_spaces[epoch].next_pkt_num += 1;
+
+        self.sent_count += 1;
+
+        if self.dgram_send_queue.byte_size() > self.recovery.cwnd_available() {
+            self.recovery.update_app_limited(false);
+        }
+
+        // On the client, drop initial state after sending an Handshake packet.
+        if !self.is_server && hdr.ty == packet::Type::Handshake {
+            self.drop_epoch_state(packet::EPOCH_INITIAL, now);
+        }
+
+        self.max_send_bytes = self.max_send_bytes.saturating_sub(written);
+
+        // (Re)start the idle timer if we are sending the first ack-eliciting
+        // packet since last receiving a packet.
+        if ack_eliciting && !self.ack_eliciting_sent {
+            if let Some(idle_timeout) = self.idle_timeout() {
+                self.idle_timer = Some(now + idle_timeout);
+            }
+        }
+
+        if ack_eliciting {
+            self.ack_eliciting_sent = true;
+        }
+
+        Ok(written)
+    }
+
+    // Returns the maximum len of a packet to be sent. This is max_packet_size
+    // as sent by the peer, except during the handshake when we haven't parsed
+    // transport parameters yet, so use a default value then.
+    fn max_send_udp_payload_len(&self) -> usize {
+        if self.is_established() {
+            // We cap the maximum packet size to 16KB or so, so that it can be
+            // always encoded with a 2-byte varint.
+            cmp::min(16383, self.peer_transport_params.max_udp_payload_size)
+                as usize
+        } else {
+            // Allow for 1200 bytes (minimum QUIC packet size) during the
+            // handshake.
+            MIN_CLIENT_INITIAL_LEN
+        }
+    }
+
+    /// Reads contiguous data from a stream into the provided slice.
+    ///
+    /// The slice must be sized by the caller and will be populated up to its
+    /// capacity.
+    ///
+    /// On success the amount of bytes read and a flag indicating the fin state
+    /// is returned as a tuple, or [`Done`] if there is no data to read.
+    ///
+    /// [`Done`]: enum.Error.html#variant.Done
+    ///
+    /// ## Examples:
+    ///
+    /// ```no_run
+    /// # let mut buf = [0; 512];
+    /// # let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap();
+    /// # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?;
+    /// # let scid = [0xba; 16];
+    /// # let mut conn = quiche::accept(&scid, None, &mut config)?;
+    /// # let stream_id = 0;
+    /// while let Ok((read, fin)) = conn.stream_recv(stream_id, &mut buf) {
+    ///     println!("Got {} bytes on stream {}", read, stream_id);
+    /// }
+    /// # Ok::<(), quiche::Error>(())
+    /// ```
+    pub fn stream_recv(
+        &mut self, stream_id: u64, out: &mut [u8],
+    ) -> Result<(usize, bool)> {
+        // We can't read on our own unidirectional streams.
+        if !stream::is_bidi(stream_id) &&
+            stream::is_local(stream_id, self.is_server)
+        {
+            return Err(Error::InvalidStreamState);
+        }
+
+        let stream = self
+            .streams
+            .get_mut(stream_id)
+            .ok_or(Error::InvalidStreamState)?;
+
+        if !stream.is_readable() {
+            return Err(Error::Done);
+        }
+
+        #[cfg(feature = "qlog")]
+        let offset = stream.recv.off_front();
+
+        let (read, fin) = stream.recv.pop(out)?;
+
+        self.max_rx_data_next = self.max_rx_data_next.saturating_add(read as u64);
+
+        let readable = stream.is_readable();
+
+        let complete = stream.is_complete();
+
+        let local = stream.local;
+
+        if stream.recv.almost_full() {
+            self.streams.mark_almost_full(stream_id, true);
+        }
+
+        if !readable {
+            self.streams.mark_readable(stream_id, false);
+        }
+
+        if complete {
+            self.streams.collect(stream_id, local);
+        }
+
+        qlog_with!(self.qlog_streamer, q, {
+            let ev = qlog::event::Event::h3_data_moved(
+                stream_id.to_string(),
+                Some(offset.to_string()),
+                Some(read as u64),
+                Some(qlog::H3DataRecipient::Transport),
+                None,
+                None,
+            );
+            q.add_event(ev).ok();
+        });
+
+        if self.should_update_max_data() {
+            self.almost_full = true;
+        }
+
+        Ok((read, fin))
+    }
+
+    /// Writes data to a stream.
+    ///
+    /// On success the number of bytes written is returned, or [`Done`] if no
+    /// data was written (e.g. because the stream has no capacity).
+    ///
+    /// Note that in order to avoid buffering an infinite amount of data in the
+    /// stream's send buffer, streams are only allowed to buffer outgoing data
+    /// up to the amount that the peer allows it to send (that is, up to the
+    /// stream's outgoing flow control capacity).
+    ///
+    /// This means that the number of written bytes returned can be lower than
+    /// the length of the input buffer when the stream doesn't have enough
+    /// capacity for the operation to complete. The application should retry the
+    /// operation once the stream is reported as writable again.
+    ///
+    /// Applications should call this method only after the handshake is
+    /// completed (whenever [`is_established()`] returns `true`) or during
+    /// early data if enabled (whenever [`is_in_early_data()`] returns `true`).
+    ///
+    /// [`Done`]: enum.Error.html#variant.Done
+    /// [`is_established()`]: struct.Connection.html#method.is_established
+    /// [`is_in_early_data()`]: struct.Connection.html#method.is_in_early_data
+    ///
+    /// ## Examples:
+    ///
+    /// ```no_run
+    /// # let mut buf = [0; 512];
+    /// # let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap();
+    /// # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?;
+    /// # let scid = [0xba; 16];
+    /// # let mut conn = quiche::accept(&scid, None, &mut config)?;
+    /// # let stream_id = 0;
+    /// conn.stream_send(stream_id, b"hello", true)?;
+    /// # Ok::<(), quiche::Error>(())
+    /// ```
+    pub fn stream_send(
+        &mut self, stream_id: u64, buf: &[u8], fin: bool,
+    ) -> Result<usize> {
+        // We can't write on the peer's unidirectional streams.
+        if !stream::is_bidi(stream_id) &&
+            !stream::is_local(stream_id, self.is_server)
+        {
+            return Err(Error::InvalidStreamState);
+        }
+
+        // Mark the connection as blocked if the connection-level flow control
+        // limit doesn't let us buffer all the data.
+        //
+        // Note that this is separate from "send capacity" as that also takes
+        // congestion control into consideration.
+        if self.max_tx_data - self.tx_data < buf.len() as u64 {
+            self.blocked_limit = Some(self.max_tx_data);
+        }
+
+        // Truncate the input buffer based on the connection's send capacity if
+        // necessary.
+        let cap = self.send_capacity();
+
+        let (buf, fin) = if cap < buf.len() {
+            (&buf[..cap], false)
+        } else {
+            (buf, fin)
+        };
+
+        // Get existing stream or create a new one.
+        let stream = self.get_or_create_stream(stream_id, true)?;
+
+        #[cfg(feature = "qlog")]
+        let offset = stream.send.off_back();
+
+        let was_flushable = stream.is_flushable();
+
+        let sent = stream.send.push_slice(buf, fin)?;
+
+        let urgency = stream.urgency;
+        let incremental = stream.incremental;
+
+        let flushable = stream.is_flushable();
+
+        let writable = stream.is_writable();
+
+        let empty_fin = buf.is_empty() && fin;
+
+        if sent < buf.len() {
+            let max_off = stream.send.max_off();
+
+            self.streams.mark_blocked(stream_id, true, max_off);
+        } else {
+            self.streams.mark_blocked(stream_id, false, 0);
+        }
+
+        // If the stream is now flushable push it to the flushable queue, but
+        // only if it wasn't already queued.
+        //
+        // Consider the stream flushable also when we are sending a zero-length
+        // frame that has the fin flag set.
+        if (flushable || empty_fin) && !was_flushable {
+            self.streams.push_flushable(stream_id, urgency, incremental);
+        }
+
+        if !writable {
+            self.streams.mark_writable(stream_id, false);
+        }
+
+        self.tx_data += sent as u64;
+
+        self.recovery.rate_check_app_limited();
+
+        qlog_with!(self.qlog_streamer, q, {
+            let ev = qlog::event::Event::h3_data_moved(
+                stream_id.to_string(),
+                Some(offset.to_string()),
+                Some(sent as u64),
+                None,
+                Some(qlog::H3DataRecipient::Transport),
+                None,
+            );
+            q.add_event(ev).ok();
+        });
+
+        Ok(sent)
+    }
+
+    /// Sets the priority for a stream.
+    ///
+    /// A stream's priority determines the order in which stream data is sent
+    /// on the wire (streams with lower priority are sent first). Streams are
+    /// created with a default priority of `127`.
+    ///
+    /// The target stream is created if it did not exist before calling this
+    /// method.
+    pub fn stream_priority(
+        &mut self, stream_id: u64, urgency: u8, incremental: bool,
+    ) -> Result<()> {
+        // Get existing stream or create a new one, but if the stream
+        // has already been closed and collected, ignore the prioritization.
+        let stream = match self.get_or_create_stream(stream_id, true) {
+            Ok(v) => v,
+
+            Err(Error::Done) => return Ok(()),
+
+            Err(e) => return Err(e),
+        };
+
+        if stream.urgency == urgency && stream.incremental == incremental {
+            return Ok(());
+        }
+
+        stream.urgency = urgency;
+        stream.incremental = incremental;
+
+        // TODO: reprioritization
+
+        Ok(())
+    }
+
+    /// Shuts down reading or writing from/to the specified stream.
+    ///
+    /// When the `direction` argument is set to [`Shutdown::Read`], outstanding
+    /// data in the stream's receive buffer is dropped, and no additional data
+    /// is added to it. Data received after calling this method is still
+    /// validated and acked but not stored, and [`stream_recv()`] will not
+    /// return it to the application.
+    ///
+    /// When the `direction` argument is set to [`Shutdown::Write`], outstanding
+    /// data in the stream's send buffer is dropped, and no additional data
+    /// is added to it. Data passed to [`stream_send()`] after calling this
+    /// method will be ignored.
+    ///
+    /// [`Shutdown::Read`]: enum.Shutdown.html#variant.Read
+    /// [`Shutdown::Write`]: enum.Shutdown.html#variant.Write
+    /// [`stream_recv()`]: struct.Connection.html#method.stream_recv
+    /// [`stream_send()`]: struct.Connection.html#method.stream_send
+    pub fn stream_shutdown(
+        &mut self, stream_id: u64, direction: Shutdown, _err: u64,
+    ) -> Result<()> {
+        // Get existing stream.
+        let stream = self.streams.get_mut(stream_id).ok_or(Error::Done)?;
+
+        match direction {
+            // TODO: send STOP_SENDING
+            Shutdown::Read => {
+                stream.recv.shutdown()?;
+
+                // Once shutdown, the stream is guaranteed to be non-readable.
+                self.streams.mark_readable(stream_id, false);
+            },
+
+            // TODO: send RESET_STREAM
+            Shutdown::Write => {
+                stream.send.shutdown()?;
+
+                // Once shutdown, the stream is guaranteed to be non-writable.
+                self.streams.mark_writable(stream_id, false);
+            },
+        }
+
+        Ok(())
+    }
+
+    /// Returns the stream's send capacity in bytes.
+    pub fn stream_capacity(&self, stream_id: u64) -> Result<usize> {
+        if let Some(stream) = self.streams.get(stream_id) {
+            let cap = cmp::min(self.send_capacity(), stream.send.cap());
+            return Ok(cap);
+        };
+
+        Err(Error::InvalidStreamState)
+    }
+
+    /// Returns true if all the data has been read from the specified stream.
+    ///
+    /// This instructs the application that all the data received from the
+    /// peer on the stream has been read, and there won't be anymore in the
+    /// future.
+    ///
+    /// Basically this returns true when the peer either set the `fin` flag
+    /// for the stream, or sent `RESET_STREAM`.
+    pub fn stream_finished(&self, stream_id: u64) -> bool {
+        let stream = match self.streams.get(stream_id) {
+            Some(v) => v,
+
+            None => return true,
+        };
+
+        stream.recv.is_fin()
+    }
+
+    /// Initializes the stream's application data.
+    ///
+    /// This can be used by applications to store per-stream information without
+    /// having to maintain their own stream map.
+    ///
+    /// Stream data can only be initialized once. Additional calls to this
+    /// method will return [`Done`].
+    ///
+    /// [`Done`]: enum.Error.html#variant.Done
+    pub fn stream_init_application_data<T>(
+        &mut self, stream_id: u64, data: T,
+    ) -> Result<()>
+    where
+        T: std::any::Any + Send,
+    {
+        // Get existing stream.
+        let stream = self.streams.get_mut(stream_id).ok_or(Error::Done)?;
+
+        if stream.data.is_some() {
+            return Err(Error::Done);
+        }
+
+        stream.data = Some(Box::new(data));
+
+        Ok(())
+    }
+
+    /// Returns the stream's application data, if any was initialized.
+    ///
+    /// This returns a reference to the application data that was initialized
+    /// by calling [`stream_init_application_data()`].
+    ///
+    /// [`stream_init_application_data()`]:
+    /// struct.Connection.html#method.stream_init_application_data
+    pub fn stream_application_data(
+        &mut self, stream_id: u64,
+    ) -> Option<&mut dyn std::any::Any> {
+        // Get existing stream.
+        let stream = self.streams.get_mut(stream_id)?;
+
+        if let Some(ref mut stream_data) = stream.data {
+            return Some(stream_data.as_mut());
+        }
+
+        None
+    }
+
+    /// Returns an iterator over streams that have outstanding data to read.
+    ///
+    /// Note that the iterator will only include streams that were readable at
+    /// the time the iterator itself was created (i.e. when `readable()` was
+    /// called). To account for newly readable streams, the iterator needs to
+    /// be created again.
+    ///
+    /// ## Examples:
+    ///
+    /// ```no_run
+    /// # let mut buf = [0; 512];
+    /// # let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap();
+    /// # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?;
+    /// # let scid = [0xba; 16];
+    /// # let mut conn = quiche::accept(&scid, None, &mut config)?;
+    /// // Iterate over readable streams.
+    /// for stream_id in conn.readable() {
+    ///     // Stream is readable, read until there's no more data.
+    ///     while let Ok((read, fin)) = conn.stream_recv(stream_id, &mut buf) {
+    ///         println!("Got {} bytes on stream {}", read, stream_id);
+    ///     }
+    /// }
+    /// # Ok::<(), quiche::Error>(())
+    /// ```
+    pub fn readable(&self) -> StreamIter {
+        self.streams.readable()
+    }
+
+    /// Returns an iterator over streams that can be written to.
+    ///
+    /// A "writable" stream is a stream that has enough flow control capacity to
+    /// send data to the peer. To avoid buffering an infinite amount of data,
+    /// streams are only allowed to buffer outgoing data up to the amount that
+    /// the peer allows to send.
+    ///
+    /// Note that the iterator will only include streams that were writable at
+    /// the time the iterator itself was created (i.e. when `writable()` was
+    /// called). To account for newly writable streams, the iterator needs to
+    /// be created again.
+    ///
+    /// ## Examples:
+    ///
+    /// ```no_run
+    /// # let mut buf = [0; 512];
+    /// # let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap();
+    /// # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?;
+    /// # let scid = [0xba; 16];
+    /// # let mut conn = quiche::accept(&scid, None, &mut config)?;
+    /// // Iterate over writable streams.
+    /// for stream_id in conn.writable() {
+    ///     // Stream is writable, write some data.
+    ///     if let Ok(written) = conn.stream_send(stream_id, &buf, false) {
+    ///         println!("Written {} bytes on stream {}", written, stream_id);
+    ///     }
+    /// }
+    /// # Ok::<(), quiche::Error>(())
+    /// ```
+    pub fn writable(&self) -> StreamIter {
+        // If there is not enough connection-level send capacity, none of the
+        // streams are writable, so return an empty iterator.
+        if self.send_capacity() == 0 {
+            return StreamIter::default();
+        }
+
+        self.streams.writable()
+    }
+
+    /// Reads the first received DATAGRAM.
+    ///
+    /// On success the DATAGRAM's data is returned along with its size.
+    ///
+    /// [`Done`] is returned if there is no data to read.
+    ///
+    /// [`BufferTooShort`] is returned if the provided buffer is too small for
+    /// the DATAGRAM.
+    ///
+    /// [`Done`]: enum.Error.html#variant.Done
+    /// [`BufferTooShort`]: enum.Error.html#variant.BufferTooShort
+    ///
+    /// ## Examples:
+    ///
+    /// ```no_run
+    /// # let mut buf = [0; 512];
+    /// # let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap();
+    /// # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?;
+    /// # let scid = [0xba; 16];
+    /// # let mut conn = quiche::accept(&scid, None, &mut config)?;
+    /// let mut dgram_buf = [0; 512];
+    /// while let Ok((len)) = conn.dgram_recv(&mut dgram_buf) {
+    ///     println!("Got {} bytes of DATAGRAM", len);
+    /// }
+    /// # Ok::<(), quiche::Error>(())
+    /// ```
+    pub fn dgram_recv(&mut self, buf: &mut [u8]) -> Result<usize> {
+        match self.dgram_recv_queue.pop() {
+            Some(d) => {
+                if d.len() > buf.len() {
+                    return Err(Error::BufferTooShort);
+                }
+
+                buf[..d.len()].copy_from_slice(&d);
+                Ok(d.len())
+            },
+
+            None => Err(Error::Done),
+        }
+    }
+
+    /// Reads the first received DATAGRAM without removing it from the queue.
+    ///
+    /// On success the DATAGRAM's data is returned along with the actual number
+    /// of bytes peeked. The requested length cannot exceed the DATAGRAM's
+    /// actual length.
+    ///
+    /// [`Done`] is returned if there is no data to read.
+    ///
+    /// [`BufferTooShort`] is returned if the provided buffer is smaller the
+    /// number of bytes to peek.
+    ///
+    /// [`Done`]: enum.Error.html#variant.Done
+    /// [`BufferTooShort`]: enum.Error.html#variant.BufferTooShort
+    pub fn dgram_recv_peek(&self, buf: &mut [u8], len: usize) -> Result<usize> {
+        self.dgram_recv_queue.peek_front_bytes(buf, len)
+    }
+
+    /// Returns the length of the first stored DATAGRAM.
+    pub fn dgram_recv_front_len(&self) -> Option<usize> {
+        self.dgram_recv_queue.peek_front_len()
+    }
+
+    /// Sends data in a DATAGRAM frame.
+    ///
+    /// [`Done`] is returned if no data was written.
+    /// [`InvalidState`] is returned if the peer does not support DATAGRAM.
+    /// [`BufferTooShort`] is returned if the DATAGRAM frame length is larger
+    /// than peer's supported DATAGRAM frame length. Use
+    /// [`dgram_max_writable_len()`] to get the largest supported DATAGRAM
+    /// frame length.
+    ///
+    /// Note that there is no flow control of DATAGRAM frames, so in order to
+    /// avoid buffering an infinite amount of frames we apply an internal
+    /// limit.
+    ///
+    /// [`Done`]: enum.Error.html#variant.Done
+    /// [`InvalidState`]: enum.Error.html#variant.InvalidState
+    /// [`BufferTooShort`]: enum.Error.html#variant.BufferTooShort
+    /// [`dgram_max_writable_len()`]:
+    /// struct.Connection.html#method.dgram_max_writable_len
+    ///
+    /// ## Examples:
+    ///
+    /// ```no_run
+    /// # let mut buf = [0; 512];
+    /// # let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap();
+    /// # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?;
+    /// # let scid = [0xba; 16];
+    /// # let mut conn = quiche::accept(&scid, None, &mut config)?;
+    /// conn.dgram_send(b"hello")?;
+    /// # Ok::<(), quiche::Error>(())
+    /// ```
+    pub fn dgram_send(&mut self, buf: &[u8]) -> Result<()> {
+        let max_payload_len = match self.dgram_max_writable_len() {
+            Some(v) => v as usize,
+            None => {
+                return Err(Error::InvalidState);
+            },
+        };
+
+        if buf.len() > max_payload_len {
+            return Err(Error::BufferTooShort);
+        }
+
+        self.dgram_send_queue.push(buf)?;
+
+        if self.dgram_send_queue.byte_size() > self.recovery.cwnd_available() {
+            self.recovery.update_app_limited(false);
+        }
+
+        Ok(())
+    }
+
+    /// Purges queued outgoing DATAGRAMs matching the predicate.
+    ///
+    /// In other words, remove all elements `e` such that `f(&e)` returns true.
+    ///
+    /// ## Examples:
+    /// ```no_run
+    /// # let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap();
+    /// # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?;
+    /// # let scid = [0xba; 16];
+    /// # let mut conn = quiche::accept(&scid, None, &mut config)?;
+    /// conn.dgram_send(b"hello")?;
+    /// conn.dgram_purge_outgoing(&|d: &[u8]| -> bool { d[0] == 0 });
+    /// # Ok::<(), quiche::Error>(())
+    /// ```
+    pub fn dgram_purge_outgoing<F: Fn(&[u8]) -> bool>(&mut self, f: F) {
+        self.dgram_send_queue.purge(f);
+    }
+
+    /// Returns the maximum DATAGRAM payload that can be sent.
+    ///
+    /// [`None`] is returned if the peer hasn't advertised a maximum DATAGRAM
+    /// frame size.
+    ///
+    /// ## Examples:
+    ///
+    /// ```no_run
+    /// # let mut buf = [0; 512];
+    /// # let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap();
+    /// # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?;
+    /// # let scid = [0xba; 16];
+    /// # let mut conn = quiche::accept(&scid, None, &mut config)?;
+    /// if let Some(payload_size) = conn.dgram_max_writable_len() {
+    ///     if payload_size > 5 {
+    ///         conn.dgram_send(b"hello")?;
+    ///     }
+    /// }
+    /// # Ok::<(), quiche::Error>(())
+    /// ```
+    pub fn dgram_max_writable_len(&self) -> Option<usize> {
+        match self.peer_transport_params.max_datagram_frame_size {
+            None => None,
+            Some(peer_frame_len) => {
+                // Start from the maximum packet size...
+                let mut max_len = self.max_send_udp_payload_len();
+                // ...subtract the Short packet header overhead...
+                // (1 byte of pkt_len + len of dcid)
+                max_len = max_len.saturating_sub(1 + self.dcid.len());
+                // ...subtract the packet number (max len)...
+                max_len = max_len.saturating_sub(packet::MAX_PKT_NUM_LEN);
+                // ...subtract the crypto overhead...
+                max_len = max_len.saturating_sub(
+                    self.pkt_num_spaces[packet::EPOCH_APPLICATION]
+                        .crypto_overhead()?,
+                );
+                // ...clamp to what peer can support...
+                max_len = cmp::min(peer_frame_len as usize, max_len);
+                // ...subtract frame overhead, checked for underflow.
+                max_len.checked_sub(frame::MAX_DGRAM_OVERHEAD)
+            },
+        }
+    }
+
+    fn dgram_enabled(&self) -> bool {
+        self.local_transport_params
+            .max_datagram_frame_size
+            .is_none()
+    }
+
+    /// Returns the amount of time until the next timeout event.
+    ///
+    /// Once the given duration has elapsed, the [`on_timeout()`] method should
+    /// be called. A timeout of `None` means that the timer should be disarmed.
+    ///
+    /// [`on_timeout()`]: struct.Connection.html#method.on_timeout
+    pub fn timeout(&self) -> Option<time::Duration> {
+        if self.is_closed() {
+            return None;
+        }
+
+        let timeout = if self.draining_timer.is_some() {
+            // Draining timer takes precedence over all other timers. If it is
+            // set it means the connection is closing so there's no point in
+            // processing the other timers.
+            self.draining_timer
+        } else {
+            // Use the lowest timer value (i.e. "sooner") among idle and loss
+            // detection timers. If they are both unset (i.e. `None`) then the
+            // result is `None`, but if at least one of them is set then a
+            // `Some(...)` value is returned.
+            let timers = [self.idle_timer, self.recovery.loss_detection_timer()];
+
+            timers.iter().filter_map(|&x| x).min()
+        };
+
+        if let Some(timeout) = timeout {
+            let now = time::Instant::now();
+
+            if timeout <= now {
+                return Some(time::Duration::new(0, 0));
+            }
+
+            return Some(timeout.duration_since(now));
+        }
+
+        None
+    }
+
+    /// Processes a timeout event.
+    ///
+    /// If no timeout has occurred it does nothing.
+    pub fn on_timeout(&mut self) {
+        let now = time::Instant::now();
+
+        if let Some(draining_timer) = self.draining_timer {
+            if draining_timer <= now {
+                trace!("{} draining timeout expired", self.trace_id);
+
+                qlog_with!(self.qlog_streamer, q, {
+                    q.finish_log().ok();
+                });
+
+                self.closed = true;
+            }
+
+            // Draining timer takes precedence over all other timers. If it is
+            // set it means the connection is closing so there's no point in
+            // processing the other timers.
+            return;
+        }
+
+        if let Some(timer) = self.idle_timer {
+            if timer <= now {
+                trace!("{} idle timeout expired", self.trace_id);
+
+                qlog_with!(self.qlog_streamer, q, {
+                    q.finish_log().ok();
+                });
+
+                self.closed = true;
+                return;
+            }
+        }
+
+        if let Some(timer) = self.recovery.loss_detection_timer() {
+            if timer <= now {
+                trace!("{} loss detection timeout expired", self.trace_id);
+
+                self.recovery.on_loss_detection_timeout(
+                    self.handshake_status(),
+                    now,
+                    &self.trace_id,
+                );
+
+                qlog_with!(self.qlog_streamer, q, {
+                    let ev = self.recovery.to_qlog();
+                    q.add_event(ev).ok();
+                });
+
+                return;
+            }
+        }
+    }
+
+    /// Closes the connection with the given error and reason.
+    ///
+    /// The `app` parameter specifies whether an application close should be
+    /// sent to the peer. Otherwise a normal connection close is sent.
+    ///
+    /// Returns [`Done`] if the connection had already been closed.
+    ///
+    /// Note that the connection will not be closed immediately. An application
+    /// should continue calling [`recv()`], [`send()`] and [`timeout()`] as
+    /// normal, until the [`is_closed()`] method returns `true`.
+    ///
+    /// [`Done`]: enum.Error.html#variant.Done
+    /// [`recv()`]: struct.Connection.html#method.recv
+    /// [`send()`]: struct.Connection.html#method.send
+    /// [`timeout()`]: struct.Connection.html#method.timeout
+    /// [`is_closed()`]: struct.Connection.html#method.is_closed
+    pub fn close(&mut self, app: bool, err: u64, reason: &[u8]) -> Result<()> {
+        if self.is_closed() || self.draining_timer.is_some() {
+            return Err(Error::Done);
+        }
+
+        if self.error.is_some() || self.app_error.is_some() {
+            return Err(Error::Done);
+        }
+
+        if app {
+            self.app_error = Some(err);
+            self.app_reason.extend_from_slice(reason);
+        } else {
+            self.error = Some(err);
+        }
+
+        // When no packet was successfully processed close connection immediately.
+        if self.recv_count == 0 {
+            self.closed = true;
+        }
+
+        Ok(())
+    }
+
+    /// Returns a string uniquely representing the connection.
+    ///
+    /// This can be used for logging purposes to differentiate between multiple
+    /// connections.
+    pub fn trace_id(&self) -> &str {
+        &self.trace_id
+    }
+
+    /// Returns the negotiated ALPN protocol.
+    ///
+    /// If no protocol has been negotiated, the returned value is empty.
+    pub fn application_proto(&self) -> &[u8] {
+        self.handshake.alpn_protocol()
+    }
+
+    /// Returns the peer's leaf certificate (if any) as a DER-encoded buffer.
+    pub fn peer_cert(&self) -> Option<Vec<u8>> {
+        self.handshake.peer_cert()
+    }
+
+    /// Returns true if the connection handshake is complete.
+    pub fn is_established(&self) -> bool {
+        self.handshake.is_completed()
+    }
+
+    /// Returns true if the connection is resumed.
+    pub fn is_resumed(&self) -> bool {
+        self.handshake.is_resumed()
+    }
+
+    /// Returns true if the connection has a pending handshake that has
+    /// progressed enough to send or receive early data.
+    pub fn is_in_early_data(&self) -> bool {
+        self.handshake.is_in_early_data()
+    }
+
+    /// Returns true if the connection is closed.
+    ///
+    /// If this returns true, the connection object can be dropped.
+    pub fn is_closed(&self) -> bool {
+        self.closed
+    }
+
+    /// Collects and returns statistics about the connection.
+    pub fn stats(&self) -> Stats {
+        Stats {
+            recv: self.recv_count,
+            sent: self.sent_count,
+            lost: self.recovery.lost_count,
+            cwnd: self.recovery.cwnd(),
+            rtt: self.recovery.rtt(),
+            delivery_rate: self.recovery.delivery_rate(),
+        }
+    }
+
+    fn encode_transport_params(&mut self) -> Result<()> {
+        let mut raw_params = [0; 128];
+
+        let raw_params = TransportParams::encode(
+            &self.local_transport_params,
+            self.is_server,
+            &mut raw_params,
+        )?;
+
+        self.handshake.set_quic_transport_params(raw_params)?;
+
+        Ok(())
+    }
+
+    /// Continues the handshake.
+    ///
+    /// If the connection is already established, it does nothing.
+    fn do_handshake(&mut self) -> Result<()> {
+        // Handshake is already complete, there's nothing to do.
+        if self.is_established() {
+            return Ok(());
+        }
+
+        match self.handshake.do_handshake() {
+            Ok(_) => (),
+
+            Err(Error::Done) => return Ok(()),
+
+            Err(e) => return Err(e),
+        };
+
+        if self.application_proto().is_empty() {
+            // Send no_application_proto TLS alert when no protocol
+            // can be negotiated.
+            self.error = Some(0x178);
+            return Err(Error::TlsFail);
+        }
+
+        trace!("{} connection established: proto={:?} cipher={:?} curve={:?} sigalg={:?} resumed={} {:?}",
+               &self.trace_id,
+               std::str::from_utf8(self.application_proto()),
+               self.handshake.cipher(),
+               self.handshake.curve(),
+               self.handshake.sigalg(),
+               self.is_resumed(),
+               self.peer_transport_params);
+
+        Ok(())
+    }
+
+    /// Selects the packet number space for outgoing packets.
+    fn write_epoch(&self) -> Result<packet::Epoch> {
+        // On error send packet in the latest epoch available, but only send
+        // 1-RTT ones when the handshake is completed.
+        if self.error.is_some() {
+            let epoch = match self.handshake.write_level() {
+                crypto::Level::Initial => packet::EPOCH_INITIAL,
+                crypto::Level::ZeroRTT => unreachable!(),
+                crypto::Level::Handshake => packet::EPOCH_HANDSHAKE,
+                crypto::Level::OneRTT => packet::EPOCH_APPLICATION,
+            };
+
+            if epoch == packet::EPOCH_APPLICATION && !self.is_established() {
+                // Downgrade the epoch to handshake as the handshake is not
+                // completed yet.
+                return Ok(packet::EPOCH_HANDSHAKE);
+            }
+
+            return Ok(epoch);
+        }
+
+        for epoch in packet::EPOCH_INITIAL..packet::EPOCH_COUNT {
+            // Only send packets in a space when we have the send keys for it.
+            if self.pkt_num_spaces[epoch].crypto_seal.is_none() {
+                continue;
+            }
+
+            // We are ready to send data for this packet number space.
+            if self.pkt_num_spaces[epoch].ready() {
+                return Ok(epoch);
+            }
+
+            // There are lost frames in this packet number space.
+            if !self.recovery.lost[epoch].is_empty() {
+                return Ok(epoch);
+            }
+
+            // We need to send PTO probe packets.
+            if self.recovery.loss_probes[epoch] > 0 {
+                return Ok(epoch);
+            }
+        }
+
+        // If there are flushable, almost full or blocked streams, use the
+        // Application epoch.
+        if (self.is_established() || self.is_in_early_data()) &&
+            (self.almost_full ||
+                self.blocked_limit.is_some() ||
+                self.dgram_send_queue.has_pending() ||
+                self.streams.should_update_max_streams_bidi() ||
+                self.streams.should_update_max_streams_uni() ||
+                self.streams.has_flushable() ||
+                self.streams.has_almost_full() ||
+                self.streams.has_blocked())
+        {
+            return Ok(packet::EPOCH_APPLICATION);
+        }
+
+        Err(Error::Done)
+    }
+
+    /// Returns the mutable stream with the given ID if it exists, or creates
+    /// a new one otherwise.
+    fn get_or_create_stream(
+        &mut self, id: u64, local: bool,
+    ) -> Result<&mut stream::Stream> {
+        self.streams.get_or_create(
+            id,
+            &self.local_transport_params,
+            &self.peer_transport_params,
+            local,
+            self.is_server,
+        )
+    }
+
+    /// Processes an incoming frame.
+    fn process_frame(
+        &mut self, frame: frame::Frame, epoch: packet::Epoch, now: time::Instant,
+    ) -> Result<()> {
+        trace!("{} rx frm {:?}", self.trace_id, frame);
+
+        match frame {
+            frame::Frame::Padding { .. } => (),
+
+            frame::Frame::Ping => (),
+
+            frame::Frame::ACK { ranges, ack_delay } => {
+                let ack_delay = ack_delay
+                    .checked_mul(2_u64.pow(
+                        self.peer_transport_params.ack_delay_exponent as u32,
+                    ))
+                    .ok_or(Error::InvalidFrame)?;
+
+                if epoch == packet::EPOCH_HANDSHAKE {
+                    self.peer_verified_address = true;
+                }
+
+                // When we receive an ACK for a 1-RTT packet after handshake
+                // completion, it means the handshake has been confirmed.
+                if epoch == packet::EPOCH_APPLICATION && self.is_established() {
+                    self.peer_verified_address = true;
+
+                    self.handshake_confirmed = true;
+                }
+
+                self.recovery.on_ack_received(
+                    &ranges,
+                    ack_delay,
+                    epoch,
+                    self.handshake_status(),
+                    now,
+                    &self.trace_id,
+                )?;
+
+                // Once the handshake is confirmed, we can drop Handshake keys.
+                if self.handshake_confirmed {
+                    self.drop_epoch_state(packet::EPOCH_HANDSHAKE, now);
+                }
+            },
+
+            frame::Frame::ResetStream {
+                stream_id,
+                final_size,
+                ..
+            } => {
+                // Peer can't send on our unidirectional streams.
+                if !stream::is_bidi(stream_id) &&
+                    stream::is_local(stream_id, self.is_server)
+                {
+                    return Err(Error::InvalidStreamState);
+                }
+
+                // Get existing stream or create a new one, but if the stream
+                // has already been closed and collected, ignore the frame.
+                //
+                // This can happen if e.g. an ACK frame is lost, and the peer
+                // retransmits another frame before it realizes that the stream
+                // is gone.
+                //
+                // Note that it makes it impossible to check if the frame is
+                // illegal, since we have no state, but since we ignore the
+                // frame, it should be fine.
+                let stream = match self.get_or_create_stream(stream_id, false) {
+                    Ok(v) => v,
+
+                    Err(Error::Done) => return Ok(()),
+
+                    Err(e) => return Err(e),
+                };
+
+                self.rx_data += stream.recv.reset(final_size)? as u64;
+
+                if self.rx_data > self.max_rx_data {
+                    return Err(Error::FlowControl);
+                }
+            },
+
+            frame::Frame::StopSending { stream_id, .. } => {
+                // STOP_SENDING on a receive-only stream is a fatal error.
+                if !stream::is_local(stream_id, self.is_server) &&
+                    !stream::is_bidi(stream_id)
+                {
+                    return Err(Error::InvalidStreamState);
+                }
+            },
+
+            frame::Frame::Crypto { data } => {
+                // Push the data to the stream so it can be re-ordered.
+                self.pkt_num_spaces[epoch].crypto_stream.recv.push(data)?;
+
+                // Feed crypto data to the TLS state, if there's data
+                // available at the expected offset.
+                let mut crypto_buf = [0; 512];
+
+                let level = crypto::Level::from_epoch(epoch);
+
+                let stream = &mut self.pkt_num_spaces[epoch].crypto_stream;
+
+                while let Ok((read, _)) = stream.recv.pop(&mut crypto_buf) {
+                    let recv_buf = &crypto_buf[..read];
+                    self.handshake.provide_data(level, &recv_buf)?;
+                }
+
+                self.do_handshake()?;
+
+                // Try to parse transport parameters as soon as the first flight
+                // of handshake data is processed.
+                //
+                // This is potentially dangerous as the handshake hasn't been
+                // completed yet, though it's required to be able to send data
+                // in 0.5 RTT.
+                let raw_params = self.handshake.quic_transport_params();
+
+                if !self.parsed_peer_transport_params && !raw_params.is_empty() {
+                    let peer_params =
+                        TransportParams::decode(&raw_params, self.is_server)?;
+
+                    if self.version >= PROTOCOL_VERSION_DRAFT28 {
+                        // Validate initial_source_connection_id.
+                        match &peer_params.initial_source_connection_id {
+                            Some(v) if v != &self.dcid =>
+                                return Err(Error::InvalidTransportParam),
+
+                            Some(_) => (),
+
+                            // initial_source_connection_id must be sent by
+                            // both endpoints.
+                            None => return Err(Error::InvalidTransportParam),
+                        }
+
+                        // Validate original_destination_connection_id.
+                        if let Some(odcid) = &self.odcid {
+                            match &peer_params.original_destination_connection_id
+                            {
+                                Some(v) if v != odcid =>
+                                    return Err(Error::InvalidTransportParam),
+
+                                Some(_) => (),
+
+                                // original_destination_connection_id must be
+                                // sent by the server.
+                                None if !self.is_server =>
+                                    return Err(Error::InvalidTransportParam),
+
+                                None => (),
+                            }
+                        }
+
+                        // Validate retry_source_connection_id.
+                        if let Some(rscid) = &self.rscid {
+                            match &peer_params.retry_source_connection_id {
+                                Some(v) if v != rscid =>
+                                    return Err(Error::InvalidTransportParam),
+
+                                Some(_) => (),
+
+                                // retry_source_connection_id must be sent by
+                                // the server.
+                                None => return Err(Error::InvalidTransportParam),
+                            }
+                        }
+                    } else {
+                        // Legacy validation of the original connection ID when
+                        // stateless retry is performed, for drafts < 28.
+                        if self.did_retry &&
+                            peer_params.original_destination_connection_id !=
+                                self.odcid
+                        {
+                            return Err(Error::InvalidTransportParam);
+                        }
+                    }
+
+                    // Update flow control limits.
+                    self.max_tx_data = peer_params.initial_max_data;
+
+                    self.streams.update_peer_max_streams_bidi(
+                        peer_params.initial_max_streams_bidi,
+                    );
+                    self.streams.update_peer_max_streams_uni(
+                        peer_params.initial_max_streams_uni,
+                    );
+
+                    self.recovery.max_ack_delay =
+                        time::Duration::from_millis(peer_params.max_ack_delay);
+
+                    self.peer_transport_params = peer_params;
+
+                    self.parsed_peer_transport_params = true;
+                }
+            },
+
+            // TODO: implement stateless retry
+            frame::Frame::NewToken { .. } => (),
+
+            frame::Frame::Stream { stream_id, data } => {
+                // Peer can't send on our unidirectional streams.
+                if !stream::is_bidi(stream_id) &&
+                    stream::is_local(stream_id, self.is_server)
+                {
+                    return Err(Error::InvalidStreamState);
+                }
+
+                let max_rx_data_left = self.max_rx_data - self.rx_data;
+
+                // Get existing stream or create a new one, but if the stream
+                // has already been closed and collected, ignore the frame.
+                //
+                // This can happen if e.g. an ACK frame is lost, and the peer
+                // retransmits another frame before it realizes that the stream
+                // is gone.
+                //
+                // Note that it makes it impossible to check if the frame is
+                // illegal, since we have no state, but since we ignore the
+                // frame, it should be fine.
+                let stream = match self.get_or_create_stream(stream_id, false) {
+                    Ok(v) => v,
+
+                    Err(Error::Done) => return Ok(()),
+
+                    Err(e) => return Err(e),
+                };
+
+                // Check for the connection-level flow control limit.
+                let max_off_delta =
+                    data.max_off().saturating_sub(stream.recv.max_off());
+
+                if max_off_delta > max_rx_data_left {
+                    return Err(Error::FlowControl);
+                }
+
+                stream.recv.push(data)?;
+
+                if stream.is_readable() {
+                    self.streams.mark_readable(stream_id, true);
+                }
+
+                self.rx_data += max_off_delta;
+            },
+
+            frame::Frame::MaxData { max } => {
+                self.max_tx_data = cmp::max(self.max_tx_data, max);
+            },
+
+            frame::Frame::MaxStreamData { stream_id, max } => {
+                // Get existing stream or create a new one, but if the stream
+                // has already been closed and collected, ignore the frame.
+                //
+                // This can happen if e.g. an ACK frame is lost, and the peer
+                // retransmits another frame before it realizes that the stream
+                // is gone.
+                //
+                // Note that it makes it impossible to check if the frame is
+                // illegal, since we have no state, but since we ignore the
+                // frame, it should be fine.
+                let stream = match self.get_or_create_stream(stream_id, false) {
+                    Ok(v) => v,
+
+                    Err(Error::Done) => return Ok(()),
+
+                    Err(e) => return Err(e),
+                };
+
+                let was_flushable = stream.is_flushable();
+
+                stream.send.update_max_data(max);
+
+                let writable = stream.is_writable();
+
+                // If the stream is now flushable push it to the flushable queue,
+                // but only if it wasn't already queued.
+                if stream.is_flushable() && !was_flushable {
+                    let urgency = stream.urgency;
+                    let incremental = stream.incremental;
+                    self.streams.push_flushable(stream_id, urgency, incremental);
+                }
+
+                if writable {
+                    self.streams.mark_writable(stream_id, true);
+                }
+            },
+
+            frame::Frame::MaxStreamsBidi { max } => {
+                if max > MAX_STREAM_ID {
+                    return Err(Error::InvalidFrame);
+                }
+
+                self.streams.update_peer_max_streams_bidi(max);
+            },
+
+            frame::Frame::MaxStreamsUni { max } => {
+                if max > MAX_STREAM_ID {
+                    return Err(Error::InvalidFrame);
+                }
+
+                self.streams.update_peer_max_streams_uni(max);
+            },
+
+            frame::Frame::DataBlocked { .. } => (),
+
+            frame::Frame::StreamDataBlocked { .. } => (),
+
+            frame::Frame::StreamsBlockedBidi { limit } =>
+                if limit > MAX_STREAM_ID {
+                    return Err(Error::InvalidFrame);
+                },
+
+            frame::Frame::StreamsBlockedUni { limit } =>
+                if limit > MAX_STREAM_ID {
+                    return Err(Error::InvalidFrame);
+                },
+
+            // TODO: implement connection migration
+            frame::Frame::NewConnectionId { .. } => (),
+
+            // TODO: implement connection migration
+            frame::Frame::RetireConnectionId { .. } => (),
+
+            frame::Frame::PathChallenge { data } => {
+                self.challenge = Some(data);
+            },
+
+            frame::Frame::PathResponse { .. } => (),
+
+            frame::Frame::ConnectionClose { .. } => {
+                self.draining_timer = Some(now + (self.recovery.pto() * 3));
+            },
+
+            frame::Frame::ApplicationClose { .. } => {
+                self.draining_timer = Some(now + (self.recovery.pto() * 3));
+            },
+
+            frame::Frame::HandshakeDone => {
+                if self.is_server {
+                    return Err(Error::InvalidPacket);
+                }
+
+                self.peer_verified_address = true;
+
+                self.handshake_confirmed = true;
+
+                // Once the handshake is confirmed, we can drop Handshake keys.
+                self.drop_epoch_state(packet::EPOCH_HANDSHAKE, now);
+            },
+
+            frame::Frame::Datagram { data } => {
+                // Close the connection if DATAGRAMs are not enabled.
+                // quiche always advertises support for 64K sized DATAGRAM
+                // frames, as recommended by the standard, so we don't need a
+                // size check.
+                if self.dgram_enabled() {
+                    return Err(Error::InvalidState);
+                }
+
+                // If recv queue is full, discard oldest
+                if self.dgram_recv_queue.is_full() {
+                    self.dgram_recv_queue.pop();
+                }
+
+                self.dgram_recv_queue.push(&data)?;
+            },
+        }
+
+        Ok(())
+    }
+
+    /// Drops the keys and recovery state for the given epoch.
+    fn drop_epoch_state(&mut self, epoch: packet::Epoch, now: time::Instant) {
+        if self.pkt_num_spaces[epoch].crypto_open.is_none() {
+            return;
+        }
+
+        self.pkt_num_spaces[epoch].crypto_open = None;
+        self.pkt_num_spaces[epoch].crypto_seal = None;
+        self.pkt_num_spaces[epoch].clear();
+
+        self.recovery.on_pkt_num_space_discarded(
+            epoch,
+            self.handshake_status(),
+            now,
+        );
+
+        trace!("{} dropped epoch {} state", self.trace_id, epoch);
+    }
+
+    /// Returns true if the connection-level flow control needs to be updated.
+    ///
+    /// This happens when the new max data limit is at least double the amount
+    /// of data that can be received before blocking.
+    fn should_update_max_data(&self) -> bool {
+        self.max_rx_data_next != self.max_rx_data &&
+            self.max_rx_data_next / 2 > self.max_rx_data - self.rx_data
+    }
+
+    /// Returns the idle timeout value.
+    ///
+    /// `None` is returned if both end-points disabled the idle timeout.
+    fn idle_timeout(&mut self) -> Option<time::Duration> {
+        // If the transport parameter is set to 0, then the respective endpoint
+        // decided to disable the idle timeout. If both are disabled we should
+        // not set any timeout.
+        if self.local_transport_params.max_idle_timeout == 0 &&
+            self.peer_transport_params.max_idle_timeout == 0
+        {
+            return None;
+        }
+
+        // If the local endpoint or the peer disabled the idle timeout, use the
+        // other peer's value, otherwise use the minimum of the two values.
+        let idle_timeout = if self.local_transport_params.max_idle_timeout == 0 {
+            self.peer_transport_params.max_idle_timeout
+        } else if self.peer_transport_params.max_idle_timeout == 0 {
+            self.local_transport_params.max_idle_timeout
+        } else {
+            cmp::min(
+                self.local_transport_params.max_idle_timeout,
+                self.peer_transport_params.max_idle_timeout,
+            )
+        };
+
+        let idle_timeout = time::Duration::from_millis(idle_timeout);
+        let idle_timeout = cmp::max(idle_timeout, 3 * self.recovery.pto());
+
+        Some(idle_timeout)
+    }
+
+    /// Returns the connection's overall send capacity.
+    fn send_capacity(&self) -> usize {
+        let cap = self.max_tx_data - self.tx_data;
+        cmp::min(cap, self.recovery.cwnd_available() as u64) as usize
+    }
+
+    /// Returns the connection's handshake status for use in loss recovery.
+    fn handshake_status(&self) -> recovery::HandshakeStatus {
+        recovery::HandshakeStatus {
+            has_handshake_keys: self.pkt_num_spaces[packet::EPOCH_HANDSHAKE]
+                .has_keys(),
+
+            peer_verified_address: self.peer_verified_address,
+
+            completed: self.is_established(),
+        }
+    }
+}
+
+/// Maps an `Error` to `Error::Done`, or itself.
+///
+/// When a received packet that hasn't yet been authenticated triggers a failure
+/// it should, in most cases, be ignored, instead of raising a connection error,
+/// to avoid potential man-in-the-middle and man-on-the-side attacks.
+///
+/// However, if no other packet was previously received, the connection should
+/// indeed be closed as the received packet might just be network background
+/// noise, and it shouldn't keep resources occupied indefinitely.
+///
+/// This function maps an error to `Error::Done` to ignore a packet failure
+/// without aborting the connection, except when no other packet was previously
+/// received, in which case the error itself is returned, but only on the
+/// server-side as the client will already have armed the idle timer.
+///
+/// This must only be used for errors preceding packet authentication. Failures
+/// happening after a packet has been authenticated should still cause the
+/// connection to be aborted.
+fn drop_pkt_on_err(
+    e: Error, recv_count: usize, is_server: bool, trace_id: &str,
+) -> Error {
+    // On the server, if no other packet has been successflully processed, abort
+    // the connection to avoid keeping the connection open when only junk is
+    // received.
+    if is_server && recv_count == 0 {
+        return e;
+    }
+
+    trace!("{} dropped invalid packet", trace_id);
+
+    // Ignore other invalid packets that haven't been authenticated to prevent
+    // man-in-the-middle and man-on-the-side attacks.
+    Error::Done
+}
+
+/// Statistics about the connection.
+///
+/// A connections's statistics can be collected using the [`stats()`] method.
+///
+/// [`stats()`]: struct.Connection.html#method.stats
+#[derive(Clone)]
+pub struct Stats {
+    /// The number of QUIC packets received on this connection.
+    pub recv: usize,
+
+    /// The number of QUIC packets sent on this connection.
+    pub sent: usize,
+
+    /// The number of QUIC packets that were lost.
+    pub lost: usize,
+
+    /// The estimated round-trip time of the connection.
+    pub rtt: time::Duration,
+
+    /// The size of the connection's congestion window in bytes.
+    pub cwnd: usize,
+
+    /// The estimated data delivery rate in bytes/s.
+    pub delivery_rate: u64,
+}
+
+impl std::fmt::Debug for Stats {
+    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+        write!(
+            f,
+            "recv={} sent={} lost={} rtt={:?} cwnd={} delivery_rate={}",
+            self.recv,
+            self.sent,
+            self.lost,
+            self.rtt,
+            self.cwnd,
+            self.delivery_rate
+        )
+    }
+}
+
+#[derive(Clone, Debug, PartialEq)]
+struct TransportParams {
+    pub original_destination_connection_id: Option<Vec<u8>>,
+    pub max_idle_timeout: u64,
+    pub stateless_reset_token: Option<Vec<u8>>,
+    pub max_udp_payload_size: u64,
+    pub initial_max_data: u64,
+    pub initial_max_stream_data_bidi_local: u64,
+    pub initial_max_stream_data_bidi_remote: u64,
+    pub initial_max_stream_data_uni: u64,
+    pub initial_max_streams_bidi: u64,
+    pub initial_max_streams_uni: u64,
+    pub ack_delay_exponent: u64,
+    pub max_ack_delay: u64,
+    pub disable_active_migration: bool,
+    // pub preferred_address: ...,
+    pub active_conn_id_limit: u64,
+    pub initial_source_connection_id: Option<Vec<u8>>,
+    pub retry_source_connection_id: Option<Vec<u8>>,
+    pub max_datagram_frame_size: Option<u64>,
+}
+
+impl Default for TransportParams {
+    fn default() -> TransportParams {
+        TransportParams {
+            original_destination_connection_id: None,
+            max_idle_timeout: 0,
+            stateless_reset_token: None,
+            max_udp_payload_size: 65527,
+            initial_max_data: 0,
+            initial_max_stream_data_bidi_local: 0,
+            initial_max_stream_data_bidi_remote: 0,
+            initial_max_stream_data_uni: 0,
+            initial_max_streams_bidi: 0,
+            initial_max_streams_uni: 0,
+            ack_delay_exponent: 3,
+            max_ack_delay: 25,
+            disable_active_migration: false,
+            active_conn_id_limit: 2,
+            initial_source_connection_id: None,
+            retry_source_connection_id: None,
+            max_datagram_frame_size: None,
+        }
+    }
+}
+
+impl TransportParams {
+    fn decode(buf: &[u8], is_server: bool) -> Result<TransportParams> {
+        let mut params = octets::Octets::with_slice(buf);
+
+        let mut tp = TransportParams::default();
+
+        while params.cap() > 0 {
+            let id = params.get_varint()?;
+
+            let mut val = params.get_bytes_with_varint_length()?;
+
+            // TODO: forbid duplicated param
+
+            match id {
+                0x0000 => {
+                    if is_server {
+                        return Err(Error::InvalidTransportParam);
+                    }
+
+                    tp.original_destination_connection_id = Some(val.to_vec());
+                },
+
+                0x0001 => {
+                    tp.max_idle_timeout = val.get_varint()?;
+                },
+
+                0x0002 => {
+                    if is_server {
+                        return Err(Error::InvalidTransportParam);
+                    }
+
+                    tp.stateless_reset_token = Some(val.get_bytes(16)?.to_vec());
+                },
+
+                0x0003 => {
+                    tp.max_udp_payload_size = val.get_varint()?;
+
+                    if tp.max_udp_payload_size < 1200 {
+                        return Err(Error::InvalidTransportParam);
+                    }
+                },
+
+                0x0004 => {
+                    tp.initial_max_data = val.get_varint()?;
+                },
+
+                0x0005 => {
+                    tp.initial_max_stream_data_bidi_local = val.get_varint()?;
+                },
+
+                0x0006 => {
+                    tp.initial_max_stream_data_bidi_remote = val.get_varint()?;
+                },
+
+                0x0007 => {
+                    tp.initial_max_stream_data_uni = val.get_varint()?;
+                },
+
+                0x0008 => {
+                    let max = val.get_varint()?;
+
+                    if max > MAX_STREAM_ID {
+                        return Err(Error::InvalidTransportParam);
+                    }
+
+                    tp.initial_max_streams_bidi = max;
+                },
+
+                0x0009 => {
+                    let max = val.get_varint()?;
+
+                    if max > MAX_STREAM_ID {
+                        return Err(Error::InvalidTransportParam);
+                    }
+
+                    tp.initial_max_streams_uni = max;
+                },
+
+                0x000a => {
+                    let ack_delay_exponent = val.get_varint()?;
+
+                    if ack_delay_exponent > 20 {
+                        return Err(Error::InvalidTransportParam);
+                    }
+
+                    tp.ack_delay_exponent = ack_delay_exponent;
+                },
+
+                0x000b => {
+                    let max_ack_delay = val.get_varint()?;
+
+                    if max_ack_delay >= 2_u64.pow(14) {
+                        return Err(Error::InvalidTransportParam);
+                    }
+
+                    tp.max_ack_delay = max_ack_delay;
+                },
+
+                0x000c => {
+                    tp.disable_active_migration = true;
+                },
+
+                0x000d => {
+                    if is_server {
+                        return Err(Error::InvalidTransportParam);
+                    }
+
+                    // TODO: decode preferred_address
+                },
+
+                0x000e => {
+                    let limit = val.get_varint()?;
+
+                    if limit < 2 {
+                        return Err(Error::InvalidTransportParam);
+                    }
+
+                    tp.active_conn_id_limit = limit;
+                },
+
+                0x000f => {
+                    tp.initial_source_connection_id = Some(val.to_vec());
+                },
+
+                0x00010 => {
+                    if is_server {
+                        return Err(Error::InvalidTransportParam);
+                    }
+
+                    tp.retry_source_connection_id = Some(val.to_vec());
+                },
+
+                0x0020 => {
+                    tp.max_datagram_frame_size = Some(val.get_varint()?);
+                },
+
+                // Ignore unknown parameters.
+                _ => (),
+            }
+        }
+
+        Ok(tp)
+    }
+
+    fn encode_param(
+        b: &mut octets::OctetsMut, ty: u64, len: usize,
+    ) -> Result<()> {
+        b.put_varint(ty)?;
+        b.put_varint(len as u64)?;
+
+        Ok(())
+    }
+
+    fn encode<'a>(
+        tp: &TransportParams, is_server: bool, out: &'a mut [u8],
+    ) -> Result<&'a mut [u8]> {
+        let mut b = octets::OctetsMut::with_slice(out);
+
+        if is_server {
+            if let Some(ref odcid) = tp.original_destination_connection_id {
+                TransportParams::encode_param(&mut b, 0x0000, odcid.len())?;
+                b.put_bytes(&odcid)?;
+            }
+        };
+
+        if tp.max_idle_timeout != 0 {
+            TransportParams::encode_param(
+                &mut b,
+                0x0001,
+                octets::varint_len(tp.max_idle_timeout),
+            )?;
+            b.put_varint(tp.max_idle_timeout)?;
+        }
+
+        if is_server {
+            if let Some(ref token) = tp.stateless_reset_token {
+                TransportParams::encode_param(&mut b, 0x0002, token.len())?;
+                b.put_bytes(&token)?;
+            }
+        }
+
+        if tp.max_udp_payload_size != 0 {
+            TransportParams::encode_param(
+                &mut b,
+                0x0003,
+                octets::varint_len(tp.max_udp_payload_size),
+            )?;
+            b.put_varint(tp.max_udp_payload_size)?;
+        }
+
+        if tp.initial_max_data != 0 {
+            TransportParams::encode_param(
+                &mut b,
+                0x0004,
+                octets::varint_len(tp.initial_max_data),
+            )?;
+            b.put_varint(tp.initial_max_data)?;
+        }
+
+        if tp.initial_max_stream_data_bidi_local != 0 {
+            TransportParams::encode_param(
+                &mut b,
+                0x0005,
+                octets::varint_len(tp.initial_max_stream_data_bidi_local),
+            )?;
+            b.put_varint(tp.initial_max_stream_data_bidi_local)?;
+        }
+
+        if tp.initial_max_stream_data_bidi_remote != 0 {
+            TransportParams::encode_param(
+                &mut b,
+                0x0006,
+                octets::varint_len(tp.initial_max_stream_data_bidi_remote),
+            )?;
+            b.put_varint(tp.initial_max_stream_data_bidi_remote)?;
+        }
+
+        if tp.initial_max_stream_data_uni != 0 {
+            TransportParams::encode_param(
+                &mut b,
+                0x0007,
+                octets::varint_len(tp.initial_max_stream_data_uni),
+            )?;
+            b.put_varint(tp.initial_max_stream_data_uni)?;
+        }
+
+        if tp.initial_max_streams_bidi != 0 {
+            TransportParams::encode_param(
+                &mut b,
+                0x0008,
+                octets::varint_len(tp.initial_max_streams_bidi),
+            )?;
+            b.put_varint(tp.initial_max_streams_bidi)?;
+        }
+
+        if tp.initial_max_streams_uni != 0 {
+            TransportParams::encode_param(
+                &mut b,
+                0x0009,
+                octets::varint_len(tp.initial_max_streams_uni),
+            )?;
+            b.put_varint(tp.initial_max_streams_uni)?;
+        }
+
+        if tp.ack_delay_exponent != 0 {
+            TransportParams::encode_param(
+                &mut b,
+                0x000a,
+                octets::varint_len(tp.ack_delay_exponent),
+            )?;
+            b.put_varint(tp.ack_delay_exponent)?;
+        }
+
+        if tp.max_ack_delay != 0 {
+            TransportParams::encode_param(
+                &mut b,
+                0x000b,
+                octets::varint_len(tp.max_ack_delay),
+            )?;
+            b.put_varint(tp.max_ack_delay)?;
+        }
+
+        if tp.disable_active_migration {
+            TransportParams::encode_param(&mut b, 0x000c, 0)?;
+        }
+
+        // TODO: encode preferred_address
+
+        if tp.active_conn_id_limit != 2 {
+            TransportParams::encode_param(
+                &mut b,
+                0x000e,
+                octets::varint_len(tp.active_conn_id_limit),
+            )?;
+            b.put_varint(tp.active_conn_id_limit)?;
+        }
+
+        if let Some(scid) = &tp.initial_source_connection_id {
+            TransportParams::encode_param(&mut b, 0x000f, scid.len())?;
+            b.put_bytes(&scid)?;
+        }
+
+        if is_server {
+            if let Some(scid) = &tp.retry_source_connection_id {
+                TransportParams::encode_param(&mut b, 0x0010, scid.len())?;
+                b.put_bytes(&scid)?;
+            }
+        }
+
+        if let Some(max_datagram_frame_size) = tp.max_datagram_frame_size {
+            TransportParams::encode_param(
+                &mut b,
+                0x0020,
+                octets::varint_len(max_datagram_frame_size),
+            )?;
+            b.put_varint(max_datagram_frame_size)?;
+        }
+
+        let out_len = b.off();
+
+        Ok(&mut out[..out_len])
+    }
+
+    /// Creates a qlog event for connection transport parameters and TLS fields
+    #[cfg(feature = "qlog")]
+    pub fn to_qlog(
+        &self, owner: qlog::TransportOwner, version: u32, alpn: &[u8],
+        cipher: Option<crypto::Algorithm>,
+    ) -> qlog::event::Event {
+        let ocid = qlog::HexSlice::maybe_string(
+            self.original_destination_connection_id.as_ref(),
+        );
+        let stateless_reset_token =
+            qlog::HexSlice::maybe_string(self.stateless_reset_token.as_ref());
+
+        qlog::event::Event::transport_parameters_set(
+            Some(owner),
+            None, // resumption
+            None, // early data
+            String::from_utf8(alpn.to_vec()).ok(),
+            Some(format!("{:x?}", version)),
+            Some(format!("{:?}", cipher)),
+            ocid,
+            stateless_reset_token,
+            Some(self.disable_active_migration),
+            Some(self.max_idle_timeout),
+            Some(self.max_udp_payload_size),
+            Some(self.ack_delay_exponent),
+            Some(self.max_ack_delay),
+            Some(self.active_conn_id_limit),
+            Some(self.initial_max_data.to_string()),
+            Some(self.initial_max_stream_data_bidi_local.to_string()),
+            Some(self.initial_max_stream_data_bidi_remote.to_string()),
+            Some(self.initial_max_stream_data_uni.to_string()),
+            Some(self.initial_max_streams_bidi.to_string()),
+            Some(self.initial_max_streams_uni.to_string()),
+            None, // preferred address
+        )
+    }
+}
+
+#[doc(hidden)]
+pub mod testing {
+    use super::*;
+
+    pub struct Pipe {
+        pub client: Pin<Box<Connection>>,
+        pub server: Pin<Box<Connection>>,
+    }
+
+    impl Pipe {
+        pub fn default() -> Result<Pipe> {
+            let mut config = Config::new(crate::PROTOCOL_VERSION)?;
+            config.load_cert_chain_from_pem_file("examples/cert.crt")?;
+            config.load_priv_key_from_pem_file("examples/cert.key")?;
+            config.set_application_protos(b"\x06proto1\x06proto2")?;
+            config.set_initial_max_data(30);
+            config.set_initial_max_stream_data_bidi_local(15);
+            config.set_initial_max_stream_data_bidi_remote(15);
+            config.set_initial_max_stream_data_uni(10);
+            config.set_initial_max_streams_bidi(3);
+            config.set_initial_max_streams_uni(3);
+            config.set_max_idle_timeout(180_000);
+            config.verify_peer(false);
+
+            Pipe::with_config(&mut config)
+        }
+
+        pub fn with_config(config: &mut Config) -> Result<Pipe> {
+            let mut client_scid = [0; 16];
+            rand::rand_bytes(&mut client_scid[..]);
+
+            let mut server_scid = [0; 16];
+            rand::rand_bytes(&mut server_scid[..]);
+
+            Ok(Pipe {
+                client: connect(Some("quic.tech"), &client_scid, config)?,
+                server: accept(&server_scid, None, config)?,
+            })
+        }
+
+        pub fn with_client_config(client_config: &mut Config) -> Result<Pipe> {
+            let mut client_scid = [0; 16];
+            rand::rand_bytes(&mut client_scid[..]);
+
+            let mut server_scid = [0; 16];
+            rand::rand_bytes(&mut server_scid[..]);
+
+            let mut config = Config::new(crate::PROTOCOL_VERSION)?;
+            config.load_cert_chain_from_pem_file("examples/cert.crt")?;
+            config.load_priv_key_from_pem_file("examples/cert.key")?;
+            config.set_application_protos(b"\x06proto1\x06proto2")?;
+            config.set_initial_max_data(30);
+            config.set_initial_max_stream_data_bidi_local(15);
+            config.set_initial_max_stream_data_bidi_remote(15);
+            config.set_initial_max_streams_bidi(3);
+            config.set_initial_max_streams_uni(3);
+
+            Ok(Pipe {
+                client: connect(Some("quic.tech"), &client_scid, client_config)?,
+                server: accept(&server_scid, None, &mut config)?,
+            })
+        }
+
+        pub fn with_server_config(server_config: &mut Config) -> Result<Pipe> {
+            let mut client_scid = [0; 16];
+            rand::rand_bytes(&mut client_scid[..]);
+
+            let mut server_scid = [0; 16];
+            rand::rand_bytes(&mut server_scid[..]);
+
+            let mut config = Config::new(crate::PROTOCOL_VERSION)?;
+            config.set_application_protos(b"\x06proto1\x06proto2")?;
+            config.set_initial_max_data(30);
+            config.set_initial_max_stream_data_bidi_local(15);
+            config.set_initial_max_stream_data_bidi_remote(15);
+            config.set_initial_max_streams_bidi(3);
+            config.set_initial_max_streams_uni(3);
+
+            Ok(Pipe {
+                client: connect(Some("quic.tech"), &client_scid, &mut config)?,
+                server: accept(&server_scid, None, server_config)?,
+            })
+        }
+
+        pub fn handshake(&mut self, buf: &mut [u8]) -> Result<()> {
+            let mut len = self.client.send(buf)?;
+
+            while !self.client.is_established() && !self.server.is_established() {
+                len = recv_send(&mut self.server, buf, len)?;
+                len = recv_send(&mut self.client, buf, len)?;
+            }
+
+            recv_send(&mut self.server, buf, len)?;
+
+            Ok(())
+        }
+
+        pub fn flush_client(&mut self, buf: &mut [u8]) -> Result<()> {
+            loop {
+                let len = match self.client.send(buf) {
+                    Ok(v) => v,
+
+                    Err(Error::Done) => break,
+
+                    Err(e) => return Err(e),
+                };
+
+                match self.server.recv(&mut buf[..len]) {
+                    Ok(_) => (),
+
+                    Err(Error::Done) => (),
+
+                    Err(e) => return Err(e),
+                }
+            }
+
+            Ok(())
+        }
+
+        pub fn flush_server(&mut self, buf: &mut [u8]) -> Result<()> {
+            loop {
+                let len = match self.server.send(buf) {
+                    Ok(v) => v,
+
+                    Err(Error::Done) => break,
+
+                    Err(e) => return Err(e),
+                };
+
+                match self.client.recv(&mut buf[..len]) {
+                    Ok(_) => (),
+
+                    Err(Error::Done) => (),
+
+                    Err(e) => return Err(e),
+                }
+            }
+
+            Ok(())
+        }
+
+        pub fn advance(&mut self, buf: &mut [u8]) -> Result<()> {
+            let mut client_done = false;
+            let mut server_done = false;
+
+            let mut len = 0;
+
+            while !client_done || !server_done {
+                len = recv_send(&mut self.client, buf, len)?;
+                client_done = len == 0;
+
+                len = recv_send(&mut self.server, buf, len)?;
+                server_done = len == 0;
+            }
+
+            Ok(())
+        }
+
+        pub fn send_pkt_to_server(
+            &mut self, pkt_type: packet::Type, frames: &[frame::Frame],
+            buf: &mut [u8],
+        ) -> Result<usize> {
+            let written = encode_pkt(&mut self.client, pkt_type, frames, buf)?;
+            recv_send(&mut self.server, buf, written)
+        }
+    }
+
+    pub fn recv_send(
+        conn: &mut Connection, buf: &mut [u8], len: usize,
+    ) -> Result<usize> {
+        let mut left = len;
+
+        while left > 0 {
+            match conn.recv(&mut buf[len - left..len]) {
+                Ok(read) => left -= read,
+
+                Err(Error::Done) => break,
+
+                Err(e) => return Err(e),
+            }
+        }
+
+        assert_eq!(left, 0);
+
+        let mut off = 0;
+
+        while off < buf.len() {
+            match conn.send(&mut buf[off..]) {
+                Ok(write) => off += write,
+
+                Err(Error::Done) => break,
+
+                Err(e) => return Err(e),
+            }
+        }
+
+        Ok(off)
+    }
+
+    pub fn encode_pkt(
+        conn: &mut Connection, pkt_type: packet::Type, frames: &[frame::Frame],
+        buf: &mut [u8],
+    ) -> Result<usize> {
+        let mut b = octets::OctetsMut::with_slice(buf);
+
+        let epoch = pkt_type.to_epoch()?;
+
+        let space = &mut conn.pkt_num_spaces[epoch];
+
+        let pn = space.next_pkt_num;
+        let pn_len = packet::pkt_num_len(pn)?;
+
+        let hdr = Header {
+            ty: pkt_type,
+            version: conn.version,
+            dcid: conn.dcid.clone(),
+            scid: conn.scid.clone(),
+            pkt_num: 0,
+            pkt_num_len: pn_len,
+            token: conn.token.clone(),
+            versions: None,
+            key_phase: false,
+        };
+
+        hdr.to_bytes(&mut b)?;
+
+        let payload_len = frames.iter().fold(0, |acc, x| acc + x.wire_len()) +
+            space.crypto_overhead().unwrap();
+
+        if pkt_type != packet::Type::Short {
+            let len = pn_len + payload_len;
+            b.put_varint(len as u64)?;
+        }
+
+        packet::encode_pkt_num(pn, &mut b)?;
+
+        let payload_offset = b.off();
+
+        for frame in frames {
+            frame.to_bytes(&mut b)?;
+        }
+
+        let aead = match space.crypto_seal {
+            Some(ref v) => v,
+            None => return Err(Error::InvalidState),
+        };
+
+        let written = packet::encrypt_pkt(
+            &mut b,
+            pn,
+            pn_len,
+            payload_len,
+            payload_offset,
+            aead,
+        )?;
+
+        space.next_pkt_num += 1;
+
+        Ok(written)
+    }
+
+    pub fn decode_pkt(
+        conn: &mut Connection, buf: &mut [u8], len: usize,
+    ) -> Result<Vec<frame::Frame>> {
+        let mut b = octets::OctetsMut::with_slice(&mut buf[..len]);
+
+        let mut hdr = Header::from_bytes(&mut b, conn.scid.len()).unwrap();
+
+        let epoch = hdr.ty.to_epoch()?;
+
+        let aead = conn.pkt_num_spaces[epoch].crypto_open.as_ref().unwrap();
+
+        let payload_len = b.cap();
+
+        packet::decrypt_hdr(&mut b, &mut hdr, &aead).unwrap();
+
+        let pn = packet::decode_pkt_num(
+            conn.pkt_num_spaces[epoch].largest_rx_pkt_num,
+            hdr.pkt_num,
+            hdr.pkt_num_len,
+        );
+
+        let mut payload =
+            packet::decrypt_pkt(&mut b, pn, hdr.pkt_num_len, payload_len, aead)
+                .unwrap();
+
+        let mut frames = Vec::new();
+
+        while payload.cap() > 0 {
+            let frame = frame::Frame::from_bytes(&mut payload, hdr.ty)?;
+            frames.push(frame);
+        }
+
+        Ok(frames)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn transport_params() {
+        // Server encodes, client decodes.
+        let tp = TransportParams {
+            original_destination_connection_id: None,
+            max_idle_timeout: 30,
+            stateless_reset_token: Some(vec![0xba; 16]),
+            max_udp_payload_size: 23_421,
+            initial_max_data: 424_645_563,
+            initial_max_stream_data_bidi_local: 154_323_123,
+            initial_max_stream_data_bidi_remote: 6_587_456,
+            initial_max_stream_data_uni: 2_461_234,
+            initial_max_streams_bidi: 12_231,
+            initial_max_streams_uni: 18_473,
+            ack_delay_exponent: 20,
+            max_ack_delay: 2_u64.pow(14) - 1,
+            disable_active_migration: true,
+            active_conn_id_limit: 8,
+            initial_source_connection_id: Some(b"woot woot".to_vec()),
+            retry_source_connection_id: Some(b"retry".to_vec()),
+            max_datagram_frame_size: Some(32),
+        };
+
+        let mut raw_params = [42; 256];
+        let raw_params =
+            TransportParams::encode(&tp, true, &mut raw_params).unwrap();
+        assert_eq!(raw_params.len(), 94);
+
+        let new_tp = TransportParams::decode(&raw_params, false).unwrap();
+
+        assert_eq!(new_tp, tp);
+
+        // Client encodes, server decodes.
+        let tp = TransportParams {
+            original_destination_connection_id: None,
+            max_idle_timeout: 30,
+            stateless_reset_token: None,
+            max_udp_payload_size: 23_421,
+            initial_max_data: 424_645_563,
+            initial_max_stream_data_bidi_local: 154_323_123,
+            initial_max_stream_data_bidi_remote: 6_587_456,
+            initial_max_stream_data_uni: 2_461_234,
+            initial_max_streams_bidi: 12_231,
+            initial_max_streams_uni: 18_473,
+            ack_delay_exponent: 20,
+            max_ack_delay: 2_u64.pow(14) - 1,
+            disable_active_migration: true,
+            active_conn_id_limit: 8,
+            initial_source_connection_id: Some(b"woot woot".to_vec()),
+            retry_source_connection_id: None,
+            max_datagram_frame_size: Some(32),
+        };
+
+        let mut raw_params = [42; 256];
+        let raw_params =
+            TransportParams::encode(&tp, false, &mut raw_params).unwrap();
+        assert_eq!(raw_params.len(), 69);
+
+        let new_tp = TransportParams::decode(&raw_params, true).unwrap();
+
+        assert_eq!(new_tp, tp);
+    }
+
+    #[test]
+    fn unknown_version() {
+        let mut buf = [0; 65535];
+
+        let mut config = Config::new(0xbabababa).unwrap();
+        config.verify_peer(false);
+
+        let mut pipe = testing::Pipe::with_client_config(&mut config).unwrap();
+
+        assert_eq!(pipe.handshake(&mut buf), Err(Error::UnknownVersion));
+    }
+
+    #[test]
+    fn version_negotiation() {
+        let mut buf = [0; 65535];
+
+        let mut config = Config::new(0xbabababa).unwrap();
+        config
+            .set_application_protos(b"\x06proto1\x06proto2")
+            .unwrap();
+        config.verify_peer(false);
+
+        let mut pipe = testing::Pipe::with_client_config(&mut config).unwrap();
+
+        let mut len = pipe.client.send(&mut buf).unwrap();
+
+        let hdr = packet::Header::from_slice(&mut buf[..len], 0).unwrap();
+        len = crate::negotiate_version(&hdr.scid, &hdr.dcid, &mut buf).unwrap();
+
+        assert_eq!(pipe.client.recv(&mut buf[..len]), Ok(len));
+
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+        assert_eq!(pipe.client.version, PROTOCOL_VERSION);
+        assert_eq!(pipe.server.version, PROTOCOL_VERSION);
+    }
+
+    #[test]
+    fn verify_custom_root() {
+        let mut buf = [0; 65535];
+
+        let mut config = Config::new(PROTOCOL_VERSION).unwrap();
+        config.verify_peer(true);
+        config
+            .load_verify_locations_from_file("examples/rootca.crt")
+            .unwrap();
+        config
+            .set_application_protos(b"\x06proto1\x06proto2")
+            .unwrap();
+
+        let mut pipe = testing::Pipe::with_client_config(&mut config).unwrap();
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+    }
+
+    #[test]
+    fn missing_initial_source_connection_id() {
+        let mut buf = [0; 65535];
+
+        let mut pipe = testing::Pipe::default().unwrap();
+
+        // Reset initial_source_connection_id.
+        pipe.client
+            .local_transport_params
+            .initial_source_connection_id = None;
+        assert_eq!(pipe.client.encode_transport_params(), Ok(()));
+
+        // Client sends initial flight.
+        let len = pipe.client.send(&mut buf).unwrap();
+
+        // Server rejects transport parameters.
+        assert_eq!(
+            testing::recv_send(&mut pipe.server, &mut buf, len),
+            Err(Error::InvalidTransportParam)
+        );
+    }
+
+    #[test]
+    fn invalid_initial_source_connection_id() {
+        let mut buf = [0; 65535];
+
+        let mut pipe = testing::Pipe::default().unwrap();
+
+        // Scramble initial_source_connection_id.
+        pipe.client
+            .local_transport_params
+            .initial_source_connection_id = Some(b"bogus value".to_vec());
+        assert_eq!(pipe.client.encode_transport_params(), Ok(()));
+
+        // Client sends initial flight.
+        let len = pipe.client.send(&mut buf).unwrap();
+
+        // Server rejects transport parameters.
+        assert_eq!(
+            testing::recv_send(&mut pipe.server, &mut buf, len),
+            Err(Error::InvalidTransportParam)
+        );
+    }
+
+    #[test]
+    fn handshake() {
+        let mut buf = [0; 65535];
+
+        let mut pipe = testing::Pipe::default().unwrap();
+
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+        assert_eq!(
+            pipe.client.application_proto(),
+            pipe.server.application_proto()
+        );
+    }
+
+    #[test]
+    fn handshake_confirmation() {
+        let mut buf = [0; 65535];
+
+        let mut pipe = testing::Pipe::default().unwrap();
+
+        // Client sends initial flight.
+        let mut len = pipe.client.send(&mut buf).unwrap();
+
+        // Server sends initial flight.
+        len = testing::recv_send(&mut pipe.server, &mut buf, len).unwrap();
+
+        assert!(!pipe.client.is_established());
+        assert!(!pipe.client.handshake_confirmed);
+
+        assert!(!pipe.server.is_established());
+        assert!(!pipe.server.handshake_confirmed);
+
+        // Client sends Handshake packet and completes handshake.
+        len = testing::recv_send(&mut pipe.client, &mut buf, len).unwrap();
+
+        assert!(pipe.client.is_established());
+        assert!(!pipe.client.handshake_confirmed);
+
+        assert!(!pipe.server.is_established());
+        assert!(!pipe.server.handshake_confirmed);
+
+        // Server completes handshake and sends HANDSHAKE_DONE.
+        len = testing::recv_send(&mut pipe.server, &mut buf, len).unwrap();
+
+        assert!(pipe.client.is_established());
+        assert!(!pipe.client.handshake_confirmed);
+
+        assert!(pipe.server.is_established());
+        assert!(!pipe.server.handshake_confirmed);
+
+        // Client acks 1-RTT packet, and confirms handshake.
+        len = testing::recv_send(&mut pipe.client, &mut buf, len).unwrap();
+
+        assert!(pipe.client.is_established());
+        assert!(pipe.client.handshake_confirmed);
+
+        assert!(pipe.server.is_established());
+        assert!(!pipe.server.handshake_confirmed);
+
+        // Server handshake is confirmed.
+        testing::recv_send(&mut pipe.server, &mut buf, len).unwrap();
+
+        assert!(pipe.client.is_established());
+        assert!(pipe.client.handshake_confirmed);
+
+        assert!(pipe.server.is_established());
+        assert!(pipe.server.handshake_confirmed);
+    }
+
+    #[test]
+    fn handshake_alpn_mismatch() {
+        let mut buf = [0; 65535];
+
+        let mut config = Config::new(PROTOCOL_VERSION).unwrap();
+        config
+            .set_application_protos(b"\x06proto3\x06proto4")
+            .unwrap();
+        config.verify_peer(false);
+
+        let mut pipe = testing::Pipe::with_client_config(&mut config).unwrap();
+
+        assert_eq!(pipe.handshake(&mut buf), Err(Error::TlsFail));
+
+        assert_eq!(pipe.client.application_proto(), b"");
+        assert_eq!(pipe.server.application_proto(), b"");
+    }
+
+    #[test]
+    fn limit_handshake_data() {
+        let mut buf = [0; 65535];
+
+        let mut config = Config::new(PROTOCOL_VERSION).unwrap();
+        config
+            .load_cert_chain_from_pem_file("examples/cert-big.crt")
+            .unwrap();
+        config
+            .load_priv_key_from_pem_file("examples/cert.key")
+            .unwrap();
+        config
+            .set_application_protos(b"\x06proto1\06proto2")
+            .unwrap();
+
+        let mut pipe = testing::Pipe::with_server_config(&mut config).unwrap();
+
+        let client_sent = pipe.client.send(&mut buf).unwrap();
+        let server_sent =
+            testing::recv_send(&mut pipe.server, &mut buf, client_sent).unwrap();
+
+        assert_eq!(server_sent, (client_sent - 1) * MAX_AMPLIFICATION_FACTOR);
+    }
+
+    #[test]
+    fn stream() {
+        let mut buf = [0; 65535];
+
+        let mut pipe = testing::Pipe::default().unwrap();
+
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+        assert_eq!(pipe.client.stream_send(4, b"hello, world", true), Ok(12));
+
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        assert!(!pipe.server.stream_finished(4));
+
+        let mut r = pipe.server.readable();
+        assert_eq!(r.next(), Some(4));
+        assert_eq!(r.next(), None);
+
+        let mut b = [0; 15];
+        assert_eq!(pipe.server.stream_recv(4, &mut b), Ok((12, true)));
+        assert_eq!(&b[..12], b"hello, world");
+
+        assert!(pipe.server.stream_finished(4));
+    }
+
+    #[test]
+    fn stream_send_on_32bit_arch() {
+        let mut buf = [0; 65535];
+
+        let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap();
+        config
+            .load_cert_chain_from_pem_file("examples/cert.crt")
+            .unwrap();
+        config
+            .load_priv_key_from_pem_file("examples/cert.key")
+            .unwrap();
+        config
+            .set_application_protos(b"\x06proto1\x06proto2")
+            .unwrap();
+        config.set_initial_max_data(2_u64.pow(32) + 5);
+        config.set_initial_max_stream_data_bidi_local(15);
+        config.set_initial_max_stream_data_bidi_remote(15);
+        config.set_initial_max_stream_data_uni(10);
+        config.set_initial_max_streams_bidi(3);
+        config.set_initial_max_streams_uni(0);
+        config.verify_peer(false);
+
+        let mut pipe = testing::Pipe::with_config(&mut config).unwrap();
+
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+        // In 32bit arch, send_capacity() should be min(2^32+5, cwnd),
+        // not min(5, cwnd)
+        assert_eq!(pipe.client.stream_send(4, b"hello, world", true), Ok(12));
+
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        assert!(!pipe.server.stream_finished(4));
+    }
+
+    #[test]
+    fn empty_stream_frame() {
+        let mut buf = [0; 65535];
+
+        let mut pipe = testing::Pipe::default().unwrap();
+
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+        let frames = [frame::Frame::Stream {
+            stream_id: 4,
+            data: stream::RangeBuf::from(b"aaaaa", 0, false),
+        }];
+
+        let pkt_type = packet::Type::Short;
+        assert_eq!(pipe.send_pkt_to_server(pkt_type, &frames, &mut buf), Ok(39));
+
+        let mut readable = pipe.server.readable();
+        assert_eq!(readable.next(), Some(4));
+
+        assert_eq!(pipe.server.stream_recv(4, &mut buf), Ok((5, false)));
+
+        let frames = [frame::Frame::Stream {
+            stream_id: 4,
+            data: stream::RangeBuf::from(b"", 5, true),
+        }];
+
+        let pkt_type = packet::Type::Short;
+        assert_eq!(pipe.send_pkt_to_server(pkt_type, &frames, &mut buf), Ok(39));
+
+        let mut readable = pipe.server.readable();
+        assert_eq!(readable.next(), Some(4));
+
+        assert_eq!(pipe.server.stream_recv(4, &mut buf), Ok((0, true)));
+
+        let frames = [frame::Frame::Stream {
+            stream_id: 4,
+            data: stream::RangeBuf::from(b"", 15, true),
+        }];
+
+        let pkt_type = packet::Type::Short;
+        assert_eq!(
+            pipe.send_pkt_to_server(pkt_type, &frames, &mut buf),
+            Err(Error::FinalSize)
+        );
+    }
+
+    #[test]
+    fn flow_control_limit() {
+        let mut buf = [0; 65535];
+
+        let mut pipe = testing::Pipe::default().unwrap();
+
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+        let frames = [
+            frame::Frame::Stream {
+                stream_id: 4,
+                data: stream::RangeBuf::from(b"aaaaaaaaaaaaaaa", 0, false),
+            },
+            frame::Frame::Stream {
+                stream_id: 8,
+                data: stream::RangeBuf::from(b"aaaaaaaaaaaaaaa", 0, false),
+            },
+            frame::Frame::Stream {
+                stream_id: 12,
+                data: stream::RangeBuf::from(b"a", 0, false),
+            },
+        ];
+
+        let pkt_type = packet::Type::Short;
+        assert_eq!(
+            pipe.send_pkt_to_server(pkt_type, &frames, &mut buf),
+            Err(Error::FlowControl),
+        );
+    }
+
+    #[test]
+    fn flow_control_limit_dup() {
+        let mut buf = [0; 65535];
+
+        let mut pipe = testing::Pipe::default().unwrap();
+
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+        let frames = [
+            // One byte less than stream limit.
+            frame::Frame::Stream {
+                stream_id: 4,
+                data: stream::RangeBuf::from(b"aaaaaaaaaaaaaa", 0, false),
+            },
+            // Same stream, but one byte more.
+            frame::Frame::Stream {
+                stream_id: 4,
+                data: stream::RangeBuf::from(b"aaaaaaaaaaaaaaa", 0, false),
+            },
+            frame::Frame::Stream {
+                stream_id: 12,
+                data: stream::RangeBuf::from(b"aaaaaaaaaaaaaaa", 0, false),
+            },
+        ];
+
+        let pkt_type = packet::Type::Short;
+        assert!(pipe.send_pkt_to_server(pkt_type, &frames, &mut buf).is_ok());
+    }
+
+    #[test]
+    fn flow_control_update() {
+        let mut buf = [0; 65535];
+
+        let mut pipe = testing::Pipe::default().unwrap();
+
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+        let frames = [
+            frame::Frame::Stream {
+                stream_id: 4,
+                data: stream::RangeBuf::from(b"aaaaaaaaaaaaaaa", 0, false),
+            },
+            frame::Frame::Stream {
+                stream_id: 8,
+                data: stream::RangeBuf::from(b"a", 0, false),
+            },
+        ];
+
+        let pkt_type = packet::Type::Short;
+
+        assert!(pipe.send_pkt_to_server(pkt_type, &frames, &mut buf).is_ok());
+
+        pipe.server.stream_recv(4, &mut buf).unwrap();
+        pipe.server.stream_recv(8, &mut buf).unwrap();
+
+        let frames = [frame::Frame::Stream {
+            stream_id: 8,
+            data: stream::RangeBuf::from(b"a", 1, false),
+        }];
+
+        let len = pipe
+            .send_pkt_to_server(pkt_type, &frames, &mut buf)
+            .unwrap();
+
+        assert!(len > 0);
+
+        let frames =
+            testing::decode_pkt(&mut pipe.client, &mut buf, len).unwrap();
+        let mut iter = frames.iter();
+
+        // Ignore ACK.
+        iter.next().unwrap();
+
+        assert_eq!(iter.next(), Some(&frame::Frame::MaxData { max: 46 }));
+    }
+
+    #[test]
+    fn stream_flow_control_limit_bidi() {
+        let mut buf = [0; 65535];
+
+        let mut pipe = testing::Pipe::default().unwrap();
+
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+        let frames = [frame::Frame::Stream {
+            stream_id: 4,
+            data: stream::RangeBuf::from(b"aaaaaaaaaaaaaaaa", 0, true),
+        }];
+
+        let pkt_type = packet::Type::Short;
+        assert_eq!(
+            pipe.send_pkt_to_server(pkt_type, &frames, &mut buf),
+            Err(Error::FlowControl),
+        );
+    }
+
+    #[test]
+    fn stream_flow_control_limit_uni() {
+        let mut buf = [0; 65535];
+
+        let mut pipe = testing::Pipe::default().unwrap();
+
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+        let frames = [frame::Frame::Stream {
+            stream_id: 2,
+            data: stream::RangeBuf::from(b"aaaaaaaaaaa", 0, true),
+        }];
+
+        let pkt_type = packet::Type::Short;
+        assert_eq!(
+            pipe.send_pkt_to_server(pkt_type, &frames, &mut buf),
+            Err(Error::FlowControl),
+        );
+    }
+
+    #[test]
+    fn stream_flow_control_update() {
+        let mut buf = [0; 65535];
+
+        let mut pipe = testing::Pipe::default().unwrap();
+
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+        let frames = [frame::Frame::Stream {
+            stream_id: 4,
+            data: stream::RangeBuf::from(b"aaaaaaa", 0, false),
+        }];
+
+        let pkt_type = packet::Type::Short;
+
+        assert!(pipe.send_pkt_to_server(pkt_type, &frames, &mut buf).is_ok());
+
+        pipe.server.stream_recv(4, &mut buf).unwrap();
+
+        let frames = [frame::Frame::Stream {
+            stream_id: 4,
+            data: stream::RangeBuf::from(b"a", 7, false),
+        }];
+
+        let len = pipe
+            .send_pkt_to_server(pkt_type, &frames, &mut buf)
+            .unwrap();
+
+        assert!(len > 0);
+
+        let frames =
+            testing::decode_pkt(&mut pipe.client, &mut buf, len).unwrap();
+        let mut iter = frames.iter();
+
+        // Ignore ACK.
+        iter.next().unwrap();
+
+        assert_eq!(
+            iter.next(),
+            Some(&frame::Frame::MaxStreamData {
+                stream_id: 4,
+                max: 22,
+            })
+        );
+    }
+
+    #[test]
+    fn stream_limit_bidi() {
+        let mut buf = [0; 65535];
+
+        let mut pipe = testing::Pipe::default().unwrap();
+
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+        let frames = [
+            frame::Frame::Stream {
+                stream_id: 4,
+                data: stream::RangeBuf::from(b"a", 0, false),
+            },
+            frame::Frame::Stream {
+                stream_id: 8,
+                data: stream::RangeBuf::from(b"a", 0, false),
+            },
+            frame::Frame::Stream {
+                stream_id: 12,
+                data: stream::RangeBuf::from(b"a", 0, false),
+            },
+            frame::Frame::Stream {
+                stream_id: 16,
+                data: stream::RangeBuf::from(b"a", 0, false),
+            },
+            frame::Frame::Stream {
+                stream_id: 20,
+                data: stream::RangeBuf::from(b"a", 0, false),
+            },
+            frame::Frame::Stream {
+                stream_id: 24,
+                data: stream::RangeBuf::from(b"a", 0, false),
+            },
+            frame::Frame::Stream {
+                stream_id: 28,
+                data: stream::RangeBuf::from(b"a", 0, false),
+            },
+        ];
+
+        let pkt_type = packet::Type::Short;
+        assert_eq!(
+            pipe.send_pkt_to_server(pkt_type, &frames, &mut buf),
+            Err(Error::StreamLimit),
+        );
+    }
+
+    #[test]
+    fn stream_limit_max_bidi() {
+        let mut buf = [0; 65535];
+
+        let mut pipe = testing::Pipe::default().unwrap();
+
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+        let frames = [frame::Frame::MaxStreamsBidi { max: MAX_STREAM_ID }];
+
+        let pkt_type = packet::Type::Short;
+        assert!(pipe.send_pkt_to_server(pkt_type, &frames, &mut buf).is_ok());
+
+        let frames = [frame::Frame::MaxStreamsBidi {
+            max: MAX_STREAM_ID + 1,
+        }];
+
+        let pkt_type = packet::Type::Short;
+        assert_eq!(
+            pipe.send_pkt_to_server(pkt_type, &frames, &mut buf),
+            Err(Error::InvalidFrame),
+        );
+    }
+
+    #[test]
+    fn stream_limit_uni() {
+        let mut buf = [0; 65535];
+
+        let mut pipe = testing::Pipe::default().unwrap();
+
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+        let frames = [
+            frame::Frame::Stream {
+                stream_id: 2,
+                data: stream::RangeBuf::from(b"a", 0, false),
+            },
+            frame::Frame::Stream {
+                stream_id: 6,
+                data: stream::RangeBuf::from(b"a", 0, false),
+            },
+            frame::Frame::Stream {
+                stream_id: 10,
+                data: stream::RangeBuf::from(b"a", 0, false),
+            },
+            frame::Frame::Stream {
+                stream_id: 14,
+                data: stream::RangeBuf::from(b"a", 0, false),
+            },
+            frame::Frame::Stream {
+                stream_id: 18,
+                data: stream::RangeBuf::from(b"a", 0, false),
+            },
+            frame::Frame::Stream {
+                stream_id: 22,
+                data: stream::RangeBuf::from(b"a", 0, false),
+            },
+            frame::Frame::Stream {
+                stream_id: 26,
+                data: stream::RangeBuf::from(b"a", 0, false),
+            },
+        ];
+
+        let pkt_type = packet::Type::Short;
+        assert_eq!(
+            pipe.send_pkt_to_server(pkt_type, &frames, &mut buf),
+            Err(Error::StreamLimit),
+        );
+    }
+
+    #[test]
+    fn stream_limit_max_uni() {
+        let mut buf = [0; 65535];
+
+        let mut pipe = testing::Pipe::default().unwrap();
+
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+        let frames = [frame::Frame::MaxStreamsUni { max: MAX_STREAM_ID }];
+
+        let pkt_type = packet::Type::Short;
+        assert!(pipe.send_pkt_to_server(pkt_type, &frames, &mut buf).is_ok());
+
+        let frames = [frame::Frame::MaxStreamsUni {
+            max: MAX_STREAM_ID + 1,
+        }];
+
+        let pkt_type = packet::Type::Short;
+        assert_eq!(
+            pipe.send_pkt_to_server(pkt_type, &frames, &mut buf),
+            Err(Error::InvalidFrame),
+        );
+    }
+
+    #[test]
+    fn streams_blocked_max_bidi() {
+        let mut buf = [0; 65535];
+
+        let mut pipe = testing::Pipe::default().unwrap();
+
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+        let frames = [frame::Frame::StreamsBlockedBidi {
+            limit: MAX_STREAM_ID,
+        }];
+
+        let pkt_type = packet::Type::Short;
+        assert!(pipe.send_pkt_to_server(pkt_type, &frames, &mut buf).is_ok());
+
+        let frames = [frame::Frame::StreamsBlockedBidi {
+            limit: MAX_STREAM_ID + 1,
+        }];
+
+        let pkt_type = packet::Type::Short;
+        assert_eq!(
+            pipe.send_pkt_to_server(pkt_type, &frames, &mut buf),
+            Err(Error::InvalidFrame),
+        );
+    }
+
+    #[test]
+    fn streams_blocked_max_uni() {
+        let mut buf = [0; 65535];
+
+        let mut pipe = testing::Pipe::default().unwrap();
+
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+        let frames = [frame::Frame::StreamsBlockedUni {
+            limit: MAX_STREAM_ID,
+        }];
+
+        let pkt_type = packet::Type::Short;
+        assert!(pipe.send_pkt_to_server(pkt_type, &frames, &mut buf).is_ok());
+
+        let frames = [frame::Frame::StreamsBlockedUni {
+            limit: MAX_STREAM_ID + 1,
+        }];
+
+        let pkt_type = packet::Type::Short;
+        assert_eq!(
+            pipe.send_pkt_to_server(pkt_type, &frames, &mut buf),
+            Err(Error::InvalidFrame),
+        );
+    }
+
+    #[test]
+    fn stream_data_overlap() {
+        let mut buf = [0; 65535];
+
+        let mut pipe = testing::Pipe::default().unwrap();
+
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+        let frames = [
+            frame::Frame::Stream {
+                stream_id: 0,
+                data: stream::RangeBuf::from(b"aaaaa", 0, false),
+            },
+            frame::Frame::Stream {
+                stream_id: 0,
+                data: stream::RangeBuf::from(b"bbbbb", 3, false),
+            },
+            frame::Frame::Stream {
+                stream_id: 0,
+                data: stream::RangeBuf::from(b"ccccc", 6, false),
+            },
+        ];
+
+        let pkt_type = packet::Type::Short;
+        assert!(pipe.send_pkt_to_server(pkt_type, &frames, &mut buf).is_ok());
+
+        let mut b = [0; 15];
+        assert_eq!(pipe.server.stream_recv(0, &mut b), Ok((11, false)));
+        assert_eq!(&b[..11], b"aaaaabbbccc");
+    }
+
+    #[test]
+    fn stream_data_overlap_with_reordering() {
+        let mut buf = [0; 65535];
+
+        let mut pipe = testing::Pipe::default().unwrap();
+
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+        let frames = [
+            frame::Frame::Stream {
+                stream_id: 0,
+                data: stream::RangeBuf::from(b"aaaaa", 0, false),
+            },
+            frame::Frame::Stream {
+                stream_id: 0,
+                data: stream::RangeBuf::from(b"ccccc", 6, false),
+            },
+            frame::Frame::Stream {
+                stream_id: 0,
+                data: stream::RangeBuf::from(b"bbbbb", 3, false),
+            },
+        ];
+
+        let pkt_type = packet::Type::Short;
+        assert!(pipe.send_pkt_to_server(pkt_type, &frames, &mut buf).is_ok());
+
+        let mut b = [0; 15];
+        assert_eq!(pipe.server.stream_recv(0, &mut b), Ok((11, false)));
+        assert_eq!(&b[..11], b"aaaaabccccc");
+    }
+
+    #[test]
+    fn reset_stream_flow_control() {
+        let mut buf = [0; 65535];
+
+        let mut pipe = testing::Pipe::default().unwrap();
+
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+        let frames = [
+            frame::Frame::Stream {
+                stream_id: 4,
+                data: stream::RangeBuf::from(b"aaaaaaaaaaaaaaa", 0, false),
+            },
+            frame::Frame::Stream {
+                stream_id: 8,
+                data: stream::RangeBuf::from(b"a", 0, false),
+            },
+            frame::Frame::ResetStream {
+                stream_id: 8,
+                error_code: 0,
+                final_size: 15,
+            },
+            frame::Frame::Stream {
+                stream_id: 12,
+                data: stream::RangeBuf::from(b"a", 0, false),
+            },
+        ];
+
+        let pkt_type = packet::Type::Short;
+        assert_eq!(
+            pipe.send_pkt_to_server(pkt_type, &frames, &mut buf),
+            Err(Error::FlowControl),
+        );
+    }
+
+    #[test]
+    fn path_challenge() {
+        let mut buf = [0; 65535];
+
+        let mut pipe = testing::Pipe::default().unwrap();
+
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+        let frames = [frame::Frame::PathChallenge {
+            data: vec![0xba; 8],
+        }];
+
+        let pkt_type = packet::Type::Short;
+
+        let len = pipe
+            .send_pkt_to_server(pkt_type, &frames, &mut buf)
+            .unwrap();
+
+        assert!(len > 0);
+
+        let frames =
+            testing::decode_pkt(&mut pipe.client, &mut buf, len).unwrap();
+        let mut iter = frames.iter();
+
+        // Ignore ACK.
+        iter.next().unwrap();
+
+        assert_eq!(
+            iter.next(),
+            Some(&frame::Frame::PathResponse {
+                data: vec![0xba; 8],
+            })
+        );
+    }
+
+    #[test]
+    /// Simulates reception of an early 1-RTT packet on the server, by
+    /// delaying the client's Handshake packet that completes the handshake.
+    fn early_1rtt_packet() {
+        let mut buf = [0; 65535];
+
+        let mut pipe = testing::Pipe::default().unwrap();
+
+        // Client sends initial flight
+        let mut len = pipe.client.send(&mut buf).unwrap();
+
+        // Server sends initial flight..
+        len = testing::recv_send(&mut pipe.server, &mut buf, len).unwrap();
+
+        // Client sends Handshake packet.
+        len = testing::recv_send(&mut pipe.client, &mut buf, len).unwrap();
+
+        // Emulate handshake packet delay by not making server process client
+        // packet.
+        let mut delayed = (&buf[..len]).to_vec();
+        testing::recv_send(&mut pipe.server, &mut buf, 0).unwrap();
+
+        assert!(pipe.client.is_established());
+
+        // Send 1-RTT packet #0.
+        let frames = [frame::Frame::Stream {
+            stream_id: 0,
+            data: stream::RangeBuf::from(b"hello, world", 0, true),
+        }];
+
+        let pkt_type = packet::Type::Short;
+        let written =
+            testing::encode_pkt(&mut pipe.client, pkt_type, &frames, &mut buf)
+                .unwrap();
+        assert_eq!(pipe.server.recv(&mut buf[..written]), Ok(written));
+
+        // Send 1-RTT packet #1.
+        let frames = [frame::Frame::Stream {
+            stream_id: 4,
+            data: stream::RangeBuf::from(b"hello, world", 0, true),
+        }];
+
+        let written =
+            testing::encode_pkt(&mut pipe.client, pkt_type, &frames, &mut buf)
+                .unwrap();
+        assert_eq!(pipe.server.recv(&mut buf[..written]), Ok(written));
+
+        assert!(!pipe.server.is_established());
+
+        // Client sent 1-RTT packets 0 and 1, but server hasn't received them.
+        //
+        // Note that `largest_rx_pkt_num` is initialized to 0, so we need to
+        // send another 1-RTT packet to make this check meaningful.
+        assert_eq!(
+            pipe.server.pkt_num_spaces[packet::EPOCH_APPLICATION]
+                .largest_rx_pkt_num,
+            0
+        );
+
+        // Process delayed packet.
+        pipe.server.recv(&mut delayed).unwrap();
+
+        assert!(pipe.server.is_established());
+
+        assert_eq!(
+            pipe.server.pkt_num_spaces[packet::EPOCH_APPLICATION]
+                .largest_rx_pkt_num,
+            0
+        );
+    }
+
+    #[test]
+    fn stream_shutdown_read() {
+        let mut buf = [0; 65535];
+
+        let mut pipe = testing::Pipe::default().unwrap();
+
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+        assert_eq!(pipe.client.stream_send(4, b"hello, world", false), Ok(12));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        let mut r = pipe.server.readable();
+        assert_eq!(r.next(), Some(4));
+        assert_eq!(r.next(), None);
+
+        assert_eq!(pipe.server.stream_shutdown(4, Shutdown::Read, 0), Ok(()));
+
+        let mut r = pipe.server.readable();
+        assert_eq!(r.next(), None);
+
+        assert_eq!(pipe.client.stream_send(4, b"bye", false), Ok(3));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        let mut r = pipe.server.readable();
+        assert_eq!(r.next(), None);
+
+        assert_eq!(
+            pipe.server.stream_shutdown(4, Shutdown::Read, 0),
+            Err(Error::Done)
+        );
+    }
+
+    #[test]
+    fn stream_shutdown_write() {
+        let mut buf = [0; 65535];
+
+        let mut pipe = testing::Pipe::default().unwrap();
+
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+        assert_eq!(pipe.client.stream_send(4, b"hello, world", false), Ok(12));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        let mut r = pipe.server.readable();
+        assert_eq!(r.next(), Some(4));
+        assert_eq!(r.next(), None);
+
+        let mut b = [0; 15];
+        pipe.server.stream_recv(4, &mut b).unwrap();
+
+        assert_eq!(pipe.client.stream_send(4, b"a", false), Ok(1));
+        assert_eq!(pipe.client.stream_shutdown(4, Shutdown::Write, 0), Ok(()));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        let mut r = pipe.server.readable();
+        assert_eq!(r.next(), None);
+
+        assert_eq!(pipe.client.stream_send(4, b"bye", false), Ok(3));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        let mut r = pipe.server.readable();
+        assert_eq!(r.next(), None);
+
+        assert_eq!(
+            pipe.client.stream_shutdown(4, Shutdown::Write, 0),
+            Err(Error::Done)
+        );
+    }
+
+    #[test]
+    /// Tests that the order of flushable streams scheduled on the wire is the
+    /// same as the order of `stream_send()` calls done by the application.
+    fn stream_round_robin() {
+        let mut buf = [0; 65535];
+
+        let mut pipe = testing::Pipe::default().unwrap();
+
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+        assert_eq!(pipe.client.stream_send(8, b"aaaaa", false), Ok(5));
+        assert_eq!(pipe.client.stream_send(0, b"aaaaa", false), Ok(5));
+        assert_eq!(pipe.client.stream_send(4, b"aaaaa", false), Ok(5));
+
+        let len = pipe.client.send(&mut buf).unwrap();
+
+        let frames =
+            testing::decode_pkt(&mut pipe.server, &mut buf, len).unwrap();
+
+        assert_eq!(
+            frames.iter().next(),
+            Some(&frame::Frame::Stream {
+                stream_id: 8,
+                data: stream::RangeBuf::from(b"aaaaa", 0, false),
+            })
+        );
+
+        let len = pipe.client.send(&mut buf).unwrap();
+
+        let frames =
+            testing::decode_pkt(&mut pipe.server, &mut buf, len).unwrap();
+
+        assert_eq!(
+            frames.iter().next(),
+            Some(&frame::Frame::Stream {
+                stream_id: 0,
+                data: stream::RangeBuf::from(b"aaaaa", 0, false),
+            })
+        );
+
+        let len = pipe.client.send(&mut buf).unwrap();
+
+        let frames =
+            testing::decode_pkt(&mut pipe.server, &mut buf, len).unwrap();
+
+        assert_eq!(
+            frames.iter().next(),
+            Some(&frame::Frame::Stream {
+                stream_id: 4,
+                data: stream::RangeBuf::from(b"aaaaa", 0, false),
+            })
+        );
+    }
+
+    #[test]
+    /// Tests the readable iterator.
+    fn stream_readable() {
+        let mut buf = [0; 65535];
+
+        let mut pipe = testing::Pipe::default().unwrap();
+
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+        // No readable streams.
+        let mut r = pipe.client.readable();
+        assert_eq!(r.next(), None);
+
+        assert_eq!(pipe.client.stream_send(4, b"aaaaa", false), Ok(5));
+
+        let mut r = pipe.client.readable();
+        assert_eq!(r.next(), None);
+
+        let mut r = pipe.server.readable();
+        assert_eq!(r.next(), None);
+
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        // Server received stream.
+        let mut r = pipe.server.readable();
+        assert_eq!(r.next(), Some(4));
+        assert_eq!(r.next(), None);
+
+        assert_eq!(
+            pipe.server.stream_send(4, b"aaaaaaaaaaaaaaa", false),
+            Ok(15)
+        );
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        let mut r = pipe.client.readable();
+        assert_eq!(r.next(), Some(4));
+        assert_eq!(r.next(), None);
+
+        // Client drains stream.
+        let mut b = [0; 15];
+        pipe.client.stream_recv(4, &mut b).unwrap();
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        let mut r = pipe.client.readable();
+        assert_eq!(r.next(), None);
+
+        // Server shuts down stream.
+        let mut r = pipe.server.readable();
+        assert_eq!(r.next(), Some(4));
+        assert_eq!(r.next(), None);
+
+        assert_eq!(pipe.server.stream_shutdown(4, Shutdown::Read, 0), Ok(()));
+
+        let mut r = pipe.server.readable();
+        assert_eq!(r.next(), None);
+
+        // Client creates multiple streams.
+        assert_eq!(pipe.client.stream_send(8, b"aaaaa", false), Ok(5));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        assert_eq!(pipe.client.stream_send(12, b"aaaaa", false), Ok(5));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        let mut r = pipe.server.readable();
+        assert_eq!(r.len(), 2);
+
+        assert!(r.next().is_some());
+        assert!(r.next().is_some());
+        assert!(r.next().is_none());
+
+        assert_eq!(r.len(), 0);
+    }
+
+    #[test]
+    /// Tests the writable iterator.
+    fn stream_writable() {
+        let mut buf = [0; 65535];
+
+        let mut pipe = testing::Pipe::default().unwrap();
+
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+        // No writable streams.
+        let mut w = pipe.client.writable();
+        assert_eq!(w.next(), None);
+
+        assert_eq!(pipe.client.stream_send(4, b"aaaaa", false), Ok(5));
+
+        // Client created stream.
+        let mut w = pipe.client.writable();
+        assert_eq!(w.next(), Some(4));
+        assert_eq!(w.next(), None);
+
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        // Server created stream.
+        let mut w = pipe.server.writable();
+        assert_eq!(w.next(), Some(4));
+        assert_eq!(w.next(), None);
+
+        assert_eq!(
+            pipe.server.stream_send(4, b"aaaaaaaaaaaaaaa", false),
+            Ok(15)
+        );
+
+        // Server stream is full.
+        let mut w = pipe.server.writable();
+        assert_eq!(w.next(), None);
+
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        // Client drains stream.
+        let mut b = [0; 15];
+        pipe.client.stream_recv(4, &mut b).unwrap();
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        // Server stream is writable again.
+        let mut w = pipe.server.writable();
+        assert_eq!(w.next(), Some(4));
+        assert_eq!(w.next(), None);
+
+        // Server suts down stream.
+        assert_eq!(pipe.server.stream_shutdown(4, Shutdown::Write, 0), Ok(()));
+
+        let mut w = pipe.server.writable();
+        assert_eq!(w.next(), None);
+
+        // Client creates multiple streams.
+        assert_eq!(pipe.client.stream_send(8, b"aaaaa", false), Ok(5));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        assert_eq!(pipe.client.stream_send(12, b"aaaaa", false), Ok(5));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        let mut w = pipe.server.writable();
+        assert_eq!(w.len(), 2);
+
+        assert!(w.next().is_some());
+        assert!(w.next().is_some());
+        assert!(w.next().is_none());
+
+        assert_eq!(w.len(), 0);
+
+        // Server finishes stream.
+        assert_eq!(pipe.server.stream_send(12, b"aaaaa", true), Ok(5));
+
+        let mut w = pipe.server.writable();
+        assert_eq!(w.next(), Some(8));
+        assert_eq!(w.next(), None);
+    }
+
+    #[test]
+    /// Tests that we don't exceed the per-connection flow control limit set by
+    /// the peer.
+    fn flow_control_limit_send() {
+        let mut buf = [0; 65535];
+
+        let mut pipe = testing::Pipe::default().unwrap();
+
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+        assert_eq!(
+            pipe.client.stream_send(0, b"aaaaaaaaaaaaaaa", false),
+            Ok(15)
+        );
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+        assert_eq!(
+            pipe.client.stream_send(4, b"aaaaaaaaaaaaaaa", false),
+            Ok(15)
+        );
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+        assert_eq!(pipe.client.stream_send(8, b"a", false), Ok(0));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        let mut r = pipe.server.readable();
+        assert!(r.next().is_some());
+        assert!(r.next().is_some());
+        assert!(r.next().is_none());
+    }
+
+    #[test]
+    /// Tests that invalid packets received before any other valid ones cause
+    /// the server to close the connection immediately.
+    fn invalid_initial_server() {
+        let mut buf = [0; 65535];
+        let mut pipe = testing::Pipe::default().unwrap();
+
+        let frames = [frame::Frame::Padding { len: 10 }];
+
+        let written = testing::encode_pkt(
+            &mut pipe.client,
+            packet::Type::Initial,
+            &frames,
+            &mut buf,
+        )
+        .unwrap();
+
+        // Corrupt the packets's last byte to make decryption fail (the last
+        // byte is part of the AEAD tag, so changing it means that the packet
+        // cannot be authenticated during decryption).
+        buf[written - 1] = !buf[written - 1];
+
+        assert_eq!(pipe.server.timeout(), None);
+
+        assert_eq!(
+            pipe.server.recv(&mut buf[..written]),
+            Err(Error::CryptoFail)
+        );
+
+        assert!(pipe.server.is_closed());
+    }
+
+    #[test]
+    /// Tests that invalid Initial packets received to cause
+    /// the client to close the connection immediately.
+    fn invalid_initial_client() {
+        let mut buf = [0; 65535];
+        let mut pipe = testing::Pipe::default().unwrap();
+
+        // Client sends initial flight.
+        let len = pipe.client.send(&mut buf).unwrap();
+
+        // Server sends initial flight.
+        assert_eq!(pipe.server.recv(&mut buf[..len]), Ok(1200));
+
+        let frames = [frame::Frame::Padding { len: 10 }];
+
+        let written = testing::encode_pkt(
+            &mut pipe.server,
+            packet::Type::Initial,
+            &frames,
+            &mut buf,
+        )
+        .unwrap();
+
+        // Corrupt the packets's last byte to make decryption fail (the last
+        // byte is part of the AEAD tag, so changing it means that the packet
+        // cannot be authenticated during decryption).
+        buf[written - 1] = !buf[written - 1];
+
+        // Client will ignore invalid packet.
+        assert_eq!(pipe.client.recv(&mut buf[..written]), Ok(68));
+
+        // The connection should be alive...
+        assert_eq!(pipe.client.is_closed(), false);
+
+        // ...and the idle timeout should be armed.
+        assert!(pipe.client.idle_timer.is_some());
+    }
+
+    #[test]
+    /// Tests that packets with invalid payload length received before any other
+    /// valid packet cause the server to close the connection immediately.
+    fn invalid_initial_payload() {
+        let mut buf = [0; 65535];
+        let mut pipe = testing::Pipe::default().unwrap();
+
+        let mut b = octets::OctetsMut::with_slice(&mut buf);
+
+        let epoch = packet::Type::Initial.to_epoch().unwrap();
+
+        let pn = 0;
+        let pn_len = packet::pkt_num_len(pn).unwrap();
+
+        let hdr = Header {
+            ty: packet::Type::Initial,
+            version: pipe.client.version,
+            dcid: pipe.client.dcid.clone(),
+            scid: pipe.client.scid.clone(),
+            pkt_num: 0,
+            pkt_num_len: pn_len,
+            token: pipe.client.token.clone(),
+            versions: None,
+            key_phase: false,
+        };
+
+        hdr.to_bytes(&mut b).unwrap();
+
+        // Payload length is invalid!!!
+        let payload_len = 4096;
+
+        let len = pn_len + payload_len;
+        b.put_varint(len as u64).unwrap();
+
+        packet::encode_pkt_num(pn, &mut b).unwrap();
+
+        let payload_offset = b.off();
+
+        let frames = [frame::Frame::Padding { len: 10 }];
+
+        for frame in &frames {
+            frame.to_bytes(&mut b).unwrap();
+        }
+
+        let space = &mut pipe.client.pkt_num_spaces[epoch];
+
+        // Use correct payload length when encrypting the packet.
+        let payload_len = frames.iter().fold(0, |acc, x| acc + x.wire_len()) +
+            space.crypto_overhead().unwrap();
+
+        let aead = space.crypto_seal.as_ref().unwrap();
+
+        let written = packet::encrypt_pkt(
+            &mut b,
+            pn,
+            pn_len,
+            payload_len,
+            payload_offset,
+            aead,
+        )
+        .unwrap();
+
+        assert_eq!(pipe.server.timeout(), None);
+
+        assert_eq!(
+            pipe.server.recv(&mut buf[..written]),
+            Err(Error::BufferTooShort)
+        );
+
+        assert!(pipe.server.is_closed());
+    }
+
+    #[test]
+    /// Tests that invalid packets don't cause the connection to be closed.
+    fn invalid_packet() {
+        let mut buf = [0; 65535];
+        let mut pipe = testing::Pipe::default().unwrap();
+
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+        let frames = [frame::Frame::Padding { len: 10 }];
+
+        let written = testing::encode_pkt(
+            &mut pipe.client,
+            packet::Type::Short,
+            &frames,
+            &mut buf,
+        )
+        .unwrap();
+
+        // Corrupt the packets's last byte to make decryption fail (the last
+        // byte is part of the AEAD tag, so changing it means that the packet
+        // cannot be authenticated during decryption).
+        buf[written - 1] = !buf[written - 1];
+
+        assert_eq!(pipe.server.recv(&mut buf[..written]), Ok(written));
+
+        // Corrupt the packets's first byte to make the header fail decoding.
+        buf[0] = 255;
+
+        assert_eq!(pipe.server.recv(&mut buf[..written]), Ok(written));
+    }
+
+    #[test]
+    /// Tests that the MAX_STREAMS frame is sent for bidirectional streams.
+    fn stream_limit_update_bidi() {
+        let mut buf = [0; 65535];
+
+        let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap();
+        config
+            .load_cert_chain_from_pem_file("examples/cert.crt")
+            .unwrap();
+        config
+            .load_priv_key_from_pem_file("examples/cert.key")
+            .unwrap();
+        config
+            .set_application_protos(b"\x06proto1\x06proto2")
+            .unwrap();
+        config.set_initial_max_data(30);
+        config.set_initial_max_stream_data_bidi_local(15);
+        config.set_initial_max_stream_data_bidi_remote(15);
+        config.set_initial_max_stream_data_uni(10);
+        config.set_initial_max_streams_bidi(3);
+        config.set_initial_max_streams_uni(0);
+        config.verify_peer(false);
+
+        let mut pipe = testing::Pipe::with_config(&mut config).unwrap();
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+        // Client sends stream data.
+        assert_eq!(pipe.client.stream_send(0, b"a", false), Ok(1));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        assert_eq!(pipe.client.stream_send(4, b"a", false), Ok(1));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        assert_eq!(pipe.client.stream_send(4, b"b", true), Ok(1));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        assert_eq!(pipe.client.stream_send(0, b"b", true), Ok(1));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        // Server reads stream data.
+        let mut b = [0; 15];
+        pipe.server.stream_recv(0, &mut b).unwrap();
+        pipe.server.stream_recv(4, &mut b).unwrap();
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        // Server sends stream data, with fin.
+        assert_eq!(pipe.server.stream_send(0, b"a", false), Ok(1));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        assert_eq!(pipe.server.stream_send(4, b"a", false), Ok(1));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        assert_eq!(pipe.server.stream_send(4, b"b", true), Ok(1));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        assert_eq!(pipe.server.stream_send(0, b"b", true), Ok(1));
+
+        // Server sends MAX_STREAMS.
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        // Client tries to create new streams.
+        assert_eq!(pipe.client.stream_send(8, b"a", false), Ok(1));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        assert_eq!(pipe.client.stream_send(12, b"a", false), Ok(1));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        assert_eq!(pipe.client.stream_send(16, b"a", false), Ok(1));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        assert_eq!(
+            pipe.client.stream_send(20, b"a", false),
+            Err(Error::StreamLimit)
+        );
+
+        assert_eq!(pipe.server.readable().len(), 3);
+    }
+
+    #[test]
+    /// Tests that the MAX_STREAMS frame is sent for unirectional streams.
+    fn stream_limit_update_uni() {
+        let mut buf = [0; 65535];
+
+        let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap();
+        config
+            .load_cert_chain_from_pem_file("examples/cert.crt")
+            .unwrap();
+        config
+            .load_priv_key_from_pem_file("examples/cert.key")
+            .unwrap();
+        config
+            .set_application_protos(b"\x06proto1\x06proto2")
+            .unwrap();
+        config.set_initial_max_data(30);
+        config.set_initial_max_stream_data_bidi_local(15);
+        config.set_initial_max_stream_data_bidi_remote(15);
+        config.set_initial_max_stream_data_uni(10);
+        config.set_initial_max_streams_bidi(0);
+        config.set_initial_max_streams_uni(3);
+        config.verify_peer(false);
+
+        let mut pipe = testing::Pipe::with_config(&mut config).unwrap();
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+        // Client sends stream data.
+        assert_eq!(pipe.client.stream_send(2, b"a", false), Ok(1));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        assert_eq!(pipe.client.stream_send(6, b"a", false), Ok(1));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        assert_eq!(pipe.client.stream_send(6, b"b", true), Ok(1));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        assert_eq!(pipe.client.stream_send(2, b"b", true), Ok(1));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        // Server reads stream data.
+        let mut b = [0; 15];
+        pipe.server.stream_recv(2, &mut b).unwrap();
+        pipe.server.stream_recv(6, &mut b).unwrap();
+
+        // Server sends MAX_STREAMS.
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        // Client tries to create new streams.
+        assert_eq!(pipe.client.stream_send(10, b"a", false), Ok(1));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        assert_eq!(pipe.client.stream_send(14, b"a", false), Ok(1));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        assert_eq!(pipe.client.stream_send(18, b"a", false), Ok(1));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        assert_eq!(
+            pipe.client.stream_send(22, b"a", false),
+            Err(Error::StreamLimit)
+        );
+
+        assert_eq!(pipe.server.readable().len(), 3);
+    }
+
+    #[test]
+    /// Tests that the stream's fin flag is properly flushed even if there's no
+    /// data in the buffer, and that the buffer becomes readable on the other
+    /// side.
+    fn stream_zero_length_fin() {
+        let mut buf = [0; 65535];
+
+        let mut pipe = testing::Pipe::default().unwrap();
+
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+        assert_eq!(
+            pipe.client.stream_send(0, b"aaaaaaaaaaaaaaa", false),
+            Ok(15)
+        );
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        let mut r = pipe.server.readable();
+        assert_eq!(r.next(), Some(0));
+        assert!(r.next().is_none());
+
+        let mut b = [0; 15];
+        pipe.server.stream_recv(0, &mut b).unwrap();
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        // Client sends zero-length frame.
+        assert_eq!(pipe.client.stream_send(0, b"", true), Ok(0));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        // Stream should be readable on the server after receiving empty fin.
+        let mut r = pipe.server.readable();
+        assert_eq!(r.next(), Some(0));
+        assert!(r.next().is_none());
+
+        let mut b = [0; 15];
+        pipe.server.stream_recv(0, &mut b).unwrap();
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        // Client sends zero-length frame (again).
+        assert_eq!(pipe.client.stream_send(0, b"", true), Ok(0));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        // Stream should _not_ be readable on the server after receiving empty
+        // fin, because it was already finished.
+        let mut r = pipe.server.readable();
+        assert_eq!(r.next(), None);
+    }
+
+    #[test]
+    /// Tests that completed streams are garbage collected.
+    fn collect_streams() {
+        let mut buf = [0; 65535];
+
+        let mut pipe = testing::Pipe::default().unwrap();
+
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+        assert_eq!(pipe.client.streams.len(), 0);
+        assert_eq!(pipe.server.streams.len(), 0);
+
+        assert_eq!(pipe.client.stream_send(0, b"aaaaa", true), Ok(5));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        assert!(!pipe.client.stream_finished(0));
+        assert!(!pipe.server.stream_finished(0));
+
+        assert_eq!(pipe.client.streams.len(), 1);
+        assert_eq!(pipe.server.streams.len(), 1);
+
+        let mut b = [0; 5];
+        pipe.server.stream_recv(0, &mut b).unwrap();
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        assert_eq!(pipe.server.stream_send(0, b"aaaaa", true), Ok(5));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        assert!(!pipe.client.stream_finished(0));
+        assert!(pipe.server.stream_finished(0));
+
+        assert_eq!(pipe.client.streams.len(), 1);
+        assert_eq!(pipe.server.streams.len(), 0);
+
+        let mut b = [0; 5];
+        pipe.client.stream_recv(0, &mut b).unwrap();
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        assert_eq!(pipe.client.streams.len(), 0);
+        assert_eq!(pipe.server.streams.len(), 0);
+
+        assert!(pipe.client.stream_finished(0));
+        assert!(pipe.server.stream_finished(0));
+
+        assert_eq!(pipe.client.stream_send(0, b"", true), Err(Error::Done));
+
+        let frames = [frame::Frame::Stream {
+            stream_id: 0,
+            data: stream::RangeBuf::from(b"aa", 0, false),
+        }];
+
+        let pkt_type = packet::Type::Short;
+        assert_eq!(pipe.send_pkt_to_server(pkt_type, &frames, &mut buf), Ok(39));
+    }
+
+    #[test]
+    fn config_set_cc_algorithm_name() {
+        let mut config = Config::new(PROTOCOL_VERSION).unwrap();
+
+        assert_eq!(config.set_cc_algorithm_name("reno"), Ok(()));
+
+        // Unknown name.
+        assert_eq!(
+            config.set_cc_algorithm_name("???"),
+            Err(Error::CongestionControl)
+        );
+    }
+
+    #[test]
+    fn peer_cert() {
+        let mut buf = [0; 65535];
+
+        let mut pipe = testing::Pipe::default().unwrap();
+
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+        match pipe.client.peer_cert() {
+            Some(c) => assert_eq!(c.len(), 753),
+
+            None => panic!("missing server certificate"),
+        }
+    }
+
+    #[test]
+    fn retry() {
+        let mut buf = [0; 65535];
+
+        let mut config = Config::new(PROTOCOL_VERSION).unwrap();
+        config
+            .load_cert_chain_from_pem_file("examples/cert.crt")
+            .unwrap();
+        config
+            .load_priv_key_from_pem_file("examples/cert.key")
+            .unwrap();
+        config
+            .set_application_protos(b"\x06proto1\06proto2")
+            .unwrap();
+
+        let mut pipe = testing::Pipe::with_server_config(&mut config).unwrap();
+
+        // Client sends initial flight.
+        let mut len = pipe.client.send(&mut buf).unwrap();
+
+        // Server sends Retry packet.
+        let hdr = Header::from_slice(&mut buf[..len], MAX_CONN_ID_LEN).unwrap();
+
+        let odcid = hdr.dcid.to_vec();
+
+        let mut scid = [0; MAX_CONN_ID_LEN];
+        rand::rand_bytes(&mut scid[..]);
+
+        let token = b"quiche test retry token";
+
+        len = packet::retry(
+            &hdr.scid,
+            &hdr.dcid,
+            &scid,
+            token,
+            hdr.version,
+            &mut buf,
+        )
+        .unwrap();
+
+        // Client receives Retry and sends new Initial.
+        assert_eq!(pipe.client.recv(&mut buf[..len]), Ok(len));
+
+        len = pipe.client.send(&mut buf).unwrap();
+
+        let hdr = Header::from_slice(&mut buf[..len], MAX_CONN_ID_LEN).unwrap();
+        assert_eq!(&hdr.token.unwrap(), token);
+
+        // Server accepts connection and send first flight.
+        pipe.server = accept(&scid, Some(&odcid), &mut config).unwrap();
+
+        len = testing::recv_send(&mut pipe.server, &mut buf, len).unwrap();
+        len = testing::recv_send(&mut pipe.client, &mut buf, len).unwrap();
+        testing::recv_send(&mut pipe.server, &mut buf, len).unwrap();
+
+        assert!(pipe.client.is_established());
+        assert!(pipe.server.is_established());
+    }
+
+    #[test]
+    fn missing_retry_source_connection_id() {
+        let mut buf = [0; 65535];
+
+        let mut config = Config::new(PROTOCOL_VERSION).unwrap();
+        config
+            .load_cert_chain_from_pem_file("examples/cert.crt")
+            .unwrap();
+        config
+            .load_priv_key_from_pem_file("examples/cert.key")
+            .unwrap();
+        config
+            .set_application_protos(b"\x06proto1\06proto2")
+            .unwrap();
+
+        let mut pipe = testing::Pipe::with_server_config(&mut config).unwrap();
+
+        // Client sends initial flight.
+        let mut len = pipe.client.send(&mut buf).unwrap();
+
+        // Server sends Retry packet.
+        let hdr = Header::from_slice(&mut buf[..len], MAX_CONN_ID_LEN).unwrap();
+
+        let mut scid = [0; MAX_CONN_ID_LEN];
+        rand::rand_bytes(&mut scid[..]);
+
+        let token = b"quiche test retry token";
+
+        len = packet::retry(
+            &hdr.scid,
+            &hdr.dcid,
+            &scid,
+            token,
+            hdr.version,
+            &mut buf,
+        )
+        .unwrap();
+
+        // Client receives Retry and sends new Initial.
+        assert_eq!(pipe.client.recv(&mut buf[..len]), Ok(len));
+
+        len = pipe.client.send(&mut buf).unwrap();
+
+        // Server accepts connection and send first flight. But original
+        // destination connection ID is ignored.
+        pipe.server = accept(&scid, None, &mut config).unwrap();
+
+        len = testing::recv_send(&mut pipe.server, &mut buf, len).unwrap();
+
+        assert_eq!(
+            pipe.client.recv(&mut buf[..len]),
+            Err(Error::InvalidTransportParam)
+        );
+    }
+
+    #[test]
+    fn invalid_retry_source_connection_id() {
+        let mut buf = [0; 65535];
+
+        let mut config = Config::new(PROTOCOL_VERSION).unwrap();
+        config
+            .load_cert_chain_from_pem_file("examples/cert.crt")
+            .unwrap();
+        config
+            .load_priv_key_from_pem_file("examples/cert.key")
+            .unwrap();
+        config
+            .set_application_protos(b"\x06proto1\06proto2")
+            .unwrap();
+
+        let mut pipe = testing::Pipe::with_server_config(&mut config).unwrap();
+
+        // Client sends initial flight.
+        let mut len = pipe.client.send(&mut buf).unwrap();
+
+        // Server sends Retry packet.
+        let hdr = Header::from_slice(&mut buf[..len], MAX_CONN_ID_LEN).unwrap();
+
+        let mut scid = [0; MAX_CONN_ID_LEN];
+        rand::rand_bytes(&mut scid[..]);
+
+        let token = b"quiche test retry token";
+
+        len = packet::retry(
+            &hdr.scid,
+            &hdr.dcid,
+            &scid,
+            token,
+            hdr.version,
+            &mut buf,
+        )
+        .unwrap();
+
+        // Client receives Retry and sends new Initial.
+        assert_eq!(pipe.client.recv(&mut buf[..len]), Ok(len));
+
+        len = pipe.client.send(&mut buf).unwrap();
+
+        // Server accepts connection and send first flight. But original
+        // destination connection ID is invalid.
+        pipe.server = accept(&scid, Some(b"bogus value"), &mut config).unwrap();
+
+        len = testing::recv_send(&mut pipe.server, &mut buf, len).unwrap();
+
+        assert_eq!(
+            pipe.client.recv(&mut buf[..len]),
+            Err(Error::InvalidTransportParam)
+        );
+    }
+
+    fn check_send(_: &mut impl Send) {}
+
+    #[test]
+    fn connection_must_be_send() {
+        let mut pipe = testing::Pipe::default().unwrap();
+        check_send(&mut pipe.client);
+    }
+
+    #[test]
+    fn data_blocked() {
+        let mut buf = [0; 65535];
+
+        let mut pipe = testing::Pipe::default().unwrap();
+
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+        assert_eq!(pipe.client.stream_send(0, b"aaaaaaaaaa", false), Ok(10));
+        assert_eq!(pipe.client.blocked_limit, None);
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        assert_eq!(pipe.client.stream_send(4, b"aaaaaaaaaa", false), Ok(10));
+        assert_eq!(pipe.client.blocked_limit, None);
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        assert_eq!(pipe.client.stream_send(8, b"aaaaaaaaaaa", false), Ok(10));
+        assert_eq!(pipe.client.blocked_limit, Some(30));
+
+        let len = pipe.client.send(&mut buf).unwrap();
+        assert_eq!(pipe.client.blocked_limit, None);
+
+        let frames =
+            testing::decode_pkt(&mut pipe.server, &mut buf, len).unwrap();
+
+        let mut iter = frames.iter();
+
+        assert_eq!(iter.next(), Some(&frame::Frame::DataBlocked { limit: 30 }));
+
+        assert_eq!(
+            iter.next(),
+            Some(&frame::Frame::Stream {
+                stream_id: 8,
+                data: stream::RangeBuf::from(b"aaaaaaaaaa", 0, false),
+            })
+        );
+
+        assert_eq!(iter.next(), None);
+    }
+
+    #[test]
+    fn stream_data_blocked() {
+        let mut buf = [0; 65535];
+
+        let mut pipe = testing::Pipe::default().unwrap();
+
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+        assert_eq!(pipe.client.stream_send(0, b"aaaaa", false), Ok(5));
+        assert_eq!(pipe.client.streams.blocked().len(), 0);
+
+        assert_eq!(pipe.client.stream_send(0, b"aaaaa", false), Ok(5));
+        assert_eq!(pipe.client.streams.blocked().len(), 0);
+
+        assert_eq!(pipe.client.stream_send(0, b"aaaaaa", false), Ok(5));
+        assert_eq!(pipe.client.streams.blocked().len(), 1);
+
+        let len = pipe.client.send(&mut buf).unwrap();
+        assert_eq!(pipe.client.streams.blocked().len(), 0);
+
+        let frames =
+            testing::decode_pkt(&mut pipe.server, &mut buf, len).unwrap();
+
+        let mut iter = frames.iter();
+
+        assert_eq!(
+            iter.next(),
+            Some(&frame::Frame::StreamDataBlocked {
+                stream_id: 0,
+                limit: 15,
+            })
+        );
+
+        assert_eq!(
+            iter.next(),
+            Some(&frame::Frame::Stream {
+                stream_id: 0,
+                data: stream::RangeBuf::from(b"aaaaaaaaaaaaaaa", 0, false),
+            })
+        );
+
+        assert_eq!(iter.next(), None);
+
+        // Send from another stream, make sure we don't send STREAM_DATA_BLOCKED
+        // again.
+        assert_eq!(pipe.client.stream_send(4, b"a", false), Ok(1));
+
+        let len = pipe.client.send(&mut buf).unwrap();
+        assert_eq!(pipe.client.streams.blocked().len(), 0);
+
+        let frames =
+            testing::decode_pkt(&mut pipe.server, &mut buf, len).unwrap();
+
+        let mut iter = frames.iter();
+
+        assert_eq!(
+            iter.next(),
+            Some(&frame::Frame::Stream {
+                stream_id: 4,
+                data: stream::RangeBuf::from(b"a", 0, false),
+            })
+        );
+
+        assert_eq!(iter.next(), None);
+
+        // Send again from blocked stream and make sure it is marked as blocked
+        // again.
+        assert_eq!(pipe.client.stream_send(0, b"aaaaaa", false), Ok(0));
+        assert_eq!(pipe.client.streams.blocked().len(), 1);
+
+        let len = pipe.client.send(&mut buf).unwrap();
+        assert_eq!(pipe.client.streams.blocked().len(), 0);
+
+        let frames =
+            testing::decode_pkt(&mut pipe.server, &mut buf, len).unwrap();
+
+        let mut iter = frames.iter();
+
+        assert_eq!(
+            iter.next(),
+            Some(&frame::Frame::StreamDataBlocked {
+                stream_id: 0,
+                limit: 15,
+            })
+        );
+
+        assert_eq!(iter.next(), Some(&frame::Frame::Padding { len: 1 }));
+
+        assert_eq!(iter.next(), None);
+    }
+
+    #[test]
+    fn app_limited_true() {
+        let mut buf = [0; 65535];
+
+        let mut config = Config::new(PROTOCOL_VERSION).unwrap();
+        config
+            .set_application_protos(b"\x06proto1\x06proto2")
+            .unwrap();
+        config.set_initial_max_data(50000);
+        config.set_initial_max_stream_data_bidi_local(50000);
+        config.set_initial_max_stream_data_bidi_remote(50000);
+        config.set_max_udp_payload_size(1200);
+        config.verify_peer(false);
+
+        let mut pipe = testing::Pipe::with_client_config(&mut config).unwrap();
+
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+        // Client sends stream data.
+        assert_eq!(pipe.client.stream_send(0, b"a", true), Ok(1));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        // Server reads stream data.
+        let mut b = [0; 15];
+        pipe.server.stream_recv(0, &mut b).unwrap();
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        // Server sends stream data smaller than cwnd.
+        let send_buf = [0; 10000];
+        assert_eq!(pipe.server.stream_send(0, &send_buf, false), Ok(10000));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        // app_limited should be true because we send less than cwnd.
+        assert_eq!(pipe.server.recovery.app_limited(), true);
+    }
+
+    #[test]
+    fn app_limited_false() {
+        let mut buf = [0; 65535];
+
+        let mut config = Config::new(PROTOCOL_VERSION).unwrap();
+        config
+            .set_application_protos(b"\x06proto1\x06proto2")
+            .unwrap();
+        config.set_initial_max_data(50000);
+        config.set_initial_max_stream_data_bidi_local(50000);
+        config.set_initial_max_stream_data_bidi_remote(50000);
+        config.set_max_udp_payload_size(1200);
+        config.verify_peer(false);
+
+        let mut pipe = testing::Pipe::with_client_config(&mut config).unwrap();
+
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+        // Client sends stream data.
+        assert_eq!(pipe.client.stream_send(0, b"a", true), Ok(1));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        // Server reads stream data.
+        let mut b = [0; 15];
+        pipe.server.stream_recv(0, &mut b).unwrap();
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        // Server sends stream data bigger than cwnd.
+        let send_buf1 = [0; 20000];
+        assert_eq!(pipe.server.stream_send(0, &send_buf1, false), Ok(14085));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        // We can't create a new packet header because there is no room by cwnd.
+        // app_limited should be false because we can't send more by cwnd.
+        assert_eq!(pipe.server.recovery.app_limited(), false);
+    }
+
+    #[test]
+    fn app_limited_false_no_frame() {
+        let mut buf = [0; 65535];
+
+        let mut config = Config::new(PROTOCOL_VERSION).unwrap();
+        config
+            .set_application_protos(b"\x06proto1\x06proto2")
+            .unwrap();
+        config.set_initial_max_data(50000);
+        config.set_initial_max_stream_data_bidi_local(50000);
+        config.set_initial_max_stream_data_bidi_remote(50000);
+        config.set_max_udp_payload_size(1405);
+        config.verify_peer(false);
+
+        let mut pipe = testing::Pipe::with_client_config(&mut config).unwrap();
+
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+        // Client sends stream data.
+        assert_eq!(pipe.client.stream_send(0, b"a", true), Ok(1));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        // Server reads stream data.
+        let mut b = [0; 15];
+        pipe.server.stream_recv(0, &mut b).unwrap();
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        // Server sends stream data bigger than cwnd.
+        let send_buf1 = [0; 20000];
+        assert_eq!(pipe.server.stream_send(0, &send_buf1, false), Ok(14085));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        // We can't create a new packet header because there is no room by cwnd.
+        // app_limited should be false because we can't send more by cwnd.
+        assert_eq!(pipe.server.recovery.app_limited(), false);
+    }
+
+    #[test]
+    fn app_limited_false_no_header() {
+        let mut buf = [0; 65535];
+
+        let mut config = Config::new(PROTOCOL_VERSION).unwrap();
+        config
+            .set_application_protos(b"\x06proto1\x06proto2")
+            .unwrap();
+        config.set_initial_max_data(50000);
+        config.set_initial_max_stream_data_bidi_local(50000);
+        config.set_initial_max_stream_data_bidi_remote(50000);
+        config.set_max_udp_payload_size(1406);
+        config.verify_peer(false);
+
+        let mut pipe = testing::Pipe::with_client_config(&mut config).unwrap();
+
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+        // Client sends stream data.
+        assert_eq!(pipe.client.stream_send(0, b"a", true), Ok(1));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        // Server reads stream data.
+        let mut b = [0; 15];
+        pipe.server.stream_recv(0, &mut b).unwrap();
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        // Server sends stream data bigger than cwnd.
+        let send_buf1 = [0; 20000];
+        assert_eq!(pipe.server.stream_send(0, &send_buf1, false), Ok(14085));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        // We can't create a new frame because there is no room by cwnd.
+        // app_limited should be false because we can't send more by cwnd.
+        assert_eq!(pipe.server.recovery.app_limited(), false);
+    }
+
+    #[test]
+    fn limit_ack_ranges() {
+        let mut buf = [0; 65535];
+
+        let mut pipe = testing::Pipe::default().unwrap();
+
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+        let epoch = packet::EPOCH_APPLICATION;
+
+        assert_eq!(pipe.server.pkt_num_spaces[epoch].recv_pkt_need_ack.len(), 0);
+
+        let frames = [frame::Frame::Ping, frame::Frame::Padding { len: 3 }];
+
+        let pkt_type = packet::Type::Short;
+
+        let mut last_packet_sent = 0;
+
+        for _ in 0..512 {
+            let recv_count = pipe.server.recv_count;
+
+            last_packet_sent = pipe.client.pkt_num_spaces[epoch].next_pkt_num;
+
+            pipe.send_pkt_to_server(pkt_type, &frames, &mut buf)
+                .unwrap();
+
+            assert_eq!(pipe.server.recv_count, recv_count + 1);
+
+            // Skip packet number.
+            pipe.client.pkt_num_spaces[epoch].next_pkt_num += 1;
+        }
+
+        assert_eq!(
+            pipe.server.pkt_num_spaces[epoch].recv_pkt_need_ack.len(),
+            MAX_ACK_RANGES
+        );
+
+        assert_eq!(
+            pipe.server.pkt_num_spaces[epoch].recv_pkt_need_ack.first(),
+            Some(last_packet_sent - ((MAX_ACK_RANGES as u64) - 1) * 2)
+        );
+
+        assert_eq!(
+            pipe.server.pkt_num_spaces[epoch].recv_pkt_need_ack.last(),
+            Some(last_packet_sent)
+        );
+    }
+
+    #[test]
+    /// Tests that streams are correctly scheduled based on their priority.
+    fn stream_priority() {
+        // Limit 1-RTT packet size to avoid congestion control interference.
+        const MAX_TEST_PACKET_SIZE: usize = 540;
+
+        let mut buf = [0; 65535];
+
+        let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap();
+        config
+            .load_cert_chain_from_pem_file("examples/cert.crt")
+            .unwrap();
+        config
+            .load_priv_key_from_pem_file("examples/cert.key")
+            .unwrap();
+        config
+            .set_application_protos(b"\x06proto1\x06proto2")
+            .unwrap();
+        config.set_initial_max_data(1_000_000);
+        config.set_initial_max_stream_data_bidi_local(1_000_000);
+        config.set_initial_max_stream_data_bidi_remote(1_000_000);
+        config.set_initial_max_stream_data_uni(0);
+        config.set_initial_max_streams_bidi(100);
+        config.set_initial_max_streams_uni(0);
+        config.verify_peer(false);
+
+        let mut pipe = testing::Pipe::with_config(&mut config).unwrap();
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+        assert_eq!(pipe.client.stream_send(0, b"a", false), Ok(1));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        assert_eq!(pipe.client.stream_send(4, b"a", false), Ok(1));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        assert_eq!(pipe.client.stream_send(8, b"a", false), Ok(1));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        assert_eq!(pipe.client.stream_send(12, b"a", false), Ok(1));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        assert_eq!(pipe.client.stream_send(16, b"a", false), Ok(1));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        assert_eq!(pipe.client.stream_send(20, b"a", false), Ok(1));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        let mut b = [0; 1];
+
+        let out = [b'b'; 500];
+
+        // Server prioritizes streams as follows:
+        //  * Stream 8 and 16 have the same priority but are non-incremental.
+        //  * Stream 4, 12 and 20 have the same priority but 20 is non-incremental
+        //    and 4 and 12 are incremental.
+        //  * Stream 0 is on its own.
+
+        pipe.server.stream_recv(0, &mut b).unwrap();
+        assert_eq!(pipe.server.stream_priority(0, 255, true), Ok(()));
+        pipe.server.stream_send(0, &out, false).unwrap();
+        pipe.server.stream_send(0, &out, false).unwrap();
+        pipe.server.stream_send(0, &out, false).unwrap();
+
+        pipe.server.stream_recv(12, &mut b).unwrap();
+        assert_eq!(pipe.server.stream_priority(12, 42, true), Ok(()));
+        pipe.server.stream_send(12, &out, false).unwrap();
+        pipe.server.stream_send(12, &out, false).unwrap();
+        pipe.server.stream_send(12, &out, false).unwrap();
+
+        pipe.server.stream_recv(16, &mut b).unwrap();
+        assert_eq!(pipe.server.stream_priority(16, 10, false), Ok(()));
+        pipe.server.stream_send(16, &out, false).unwrap();
+        pipe.server.stream_send(16, &out, false).unwrap();
+        pipe.server.stream_send(16, &out, false).unwrap();
+
+        pipe.server.stream_recv(4, &mut b).unwrap();
+        assert_eq!(pipe.server.stream_priority(4, 42, true), Ok(()));
+        pipe.server.stream_send(4, &out, false).unwrap();
+        pipe.server.stream_send(4, &out, false).unwrap();
+        pipe.server.stream_send(4, &out, false).unwrap();
+
+        pipe.server.stream_recv(8, &mut b).unwrap();
+        assert_eq!(pipe.server.stream_priority(8, 10, false), Ok(()));
+        pipe.server.stream_send(8, &out, false).unwrap();
+        pipe.server.stream_send(8, &out, false).unwrap();
+        pipe.server.stream_send(8, &out, false).unwrap();
+
+        pipe.server.stream_recv(20, &mut b).unwrap();
+        assert_eq!(pipe.server.stream_priority(20, 42, false), Ok(()));
+        pipe.server.stream_send(20, &out, false).unwrap();
+        pipe.server.stream_send(20, &out, false).unwrap();
+        pipe.server.stream_send(20, &out, false).unwrap();
+
+        // First is stream 8.
+        let mut off = 0;
+
+        for _ in 1..=3 {
+            let len = pipe.server.send(&mut buf[..MAX_TEST_PACKET_SIZE]).unwrap();
+
+            let frames =
+                testing::decode_pkt(&mut pipe.client, &mut buf, len).unwrap();
+            let stream = frames.iter().next().unwrap();
+
+            assert_eq!(stream, &frame::Frame::Stream {
+                stream_id: 8,
+                data: stream::RangeBuf::from(&out, off, false),
+            });
+
+            off = match stream {
+                frame::Frame::Stream { data, .. } => data.max_off(),
+
+                _ => unreachable!(),
+            };
+        }
+
+        // Then is stream 16.
+        let mut off = 0;
+
+        for _ in 1..=3 {
+            let len = pipe.server.send(&mut buf[..MAX_TEST_PACKET_SIZE]).unwrap();
+
+            let frames =
+                testing::decode_pkt(&mut pipe.client, &mut buf, len).unwrap();
+            let stream = frames.iter().next().unwrap();
+
+            assert_eq!(stream, &frame::Frame::Stream {
+                stream_id: 16,
+                data: stream::RangeBuf::from(&out, off, false),
+            });
+
+            off = match stream {
+                frame::Frame::Stream { data, .. } => data.max_off(),
+
+                _ => unreachable!(),
+            };
+        }
+
+        // Then is stream 20.
+        let mut off = 0;
+
+        for _ in 1..=3 {
+            let len = pipe.server.send(&mut buf[..MAX_TEST_PACKET_SIZE]).unwrap();
+
+            let frames =
+                testing::decode_pkt(&mut pipe.client, &mut buf, len).unwrap();
+            let stream = frames.iter().next().unwrap();
+
+            assert_eq!(stream, &frame::Frame::Stream {
+                stream_id: 20,
+                data: stream::RangeBuf::from(&out, off, false),
+            });
+
+            off = match stream {
+                frame::Frame::Stream { data, .. } => data.max_off(),
+
+                _ => unreachable!(),
+            };
+        }
+
+        // Then are stream 12 and 4, with the same priority, incrementally.
+        let mut off = 0;
+
+        for _ in 1..=3 {
+            let len = pipe.server.send(&mut buf[..MAX_TEST_PACKET_SIZE]).unwrap();
+
+            let frames =
+                testing::decode_pkt(&mut pipe.client, &mut buf, len).unwrap();
+
+            assert_eq!(
+                frames.iter().next(),
+                Some(&frame::Frame::Stream {
+                    stream_id: 12,
+                    data: stream::RangeBuf::from(&out, off, false),
+                })
+            );
+
+            let len = pipe.server.send(&mut buf[..MAX_TEST_PACKET_SIZE]).unwrap();
+
+            let frames =
+                testing::decode_pkt(&mut pipe.client, &mut buf, len).unwrap();
+
+            let stream = frames.iter().next().unwrap();
+
+            assert_eq!(stream, &frame::Frame::Stream {
+                stream_id: 4,
+                data: stream::RangeBuf::from(&out, off, false),
+            });
+
+            off = match stream {
+                frame::Frame::Stream { data, .. } => data.max_off(),
+
+                _ => unreachable!(),
+            };
+        }
+
+        // Final is stream 0.
+        let mut off = 0;
+
+        for _ in 1..=3 {
+            let len = pipe.server.send(&mut buf[..MAX_TEST_PACKET_SIZE]).unwrap();
+
+            let frames =
+                testing::decode_pkt(&mut pipe.client, &mut buf, len).unwrap();
+            let stream = frames.iter().next().unwrap();
+
+            assert_eq!(stream, &frame::Frame::Stream {
+                stream_id: 0,
+                data: stream::RangeBuf::from(&out, off, false),
+            });
+
+            off = match stream {
+                frame::Frame::Stream { data, .. } => data.max_off(),
+
+                _ => unreachable!(),
+            };
+        }
+
+        assert_eq!(pipe.server.send(&mut buf), Err(Error::Done));
+    }
+
+    #[test]
+    /// Tests that changing a stream's priority is correctly propagated.
+    ///
+    /// Re-prioritization is not supported, so this should fail.
+    #[should_panic]
+    fn stream_reprioritize() {
+        let mut buf = [0; 65535];
+
+        let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap();
+        config
+            .load_cert_chain_from_pem_file("examples/cert.crt")
+            .unwrap();
+        config
+            .load_priv_key_from_pem_file("examples/cert.key")
+            .unwrap();
+        config
+            .set_application_protos(b"\x06proto1\x06proto2")
+            .unwrap();
+        config.set_initial_max_data(30);
+        config.set_initial_max_stream_data_bidi_local(15);
+        config.set_initial_max_stream_data_bidi_remote(15);
+        config.set_initial_max_stream_data_uni(0);
+        config.set_initial_max_streams_bidi(5);
+        config.set_initial_max_streams_uni(0);
+        config.verify_peer(false);
+
+        let mut pipe = testing::Pipe::with_config(&mut config).unwrap();
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+        assert_eq!(pipe.client.stream_send(0, b"a", false), Ok(1));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        assert_eq!(pipe.client.stream_send(4, b"a", false), Ok(1));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        assert_eq!(pipe.client.stream_send(8, b"a", false), Ok(1));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        assert_eq!(pipe.client.stream_send(12, b"a", false), Ok(1));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        let mut b = [0; 1];
+
+        pipe.server.stream_recv(0, &mut b).unwrap();
+        assert_eq!(pipe.server.stream_priority(0, 255, true), Ok(()));
+        pipe.server.stream_send(0, b"b", false).unwrap();
+
+        pipe.server.stream_recv(12, &mut b).unwrap();
+        assert_eq!(pipe.server.stream_priority(12, 42, true), Ok(()));
+        pipe.server.stream_send(12, b"b", false).unwrap();
+
+        pipe.server.stream_recv(8, &mut b).unwrap();
+        assert_eq!(pipe.server.stream_priority(8, 10, true), Ok(()));
+        pipe.server.stream_send(8, b"b", false).unwrap();
+
+        pipe.server.stream_recv(4, &mut b).unwrap();
+        assert_eq!(pipe.server.stream_priority(4, 42, true), Ok(()));
+        pipe.server.stream_send(4, b"b", false).unwrap();
+
+        // Stream 0 is re-prioritized!!!
+        assert_eq!(pipe.server.stream_priority(0, 20, true), Ok(()));
+
+        // First is stream 8.
+        let len = pipe.server.send(&mut buf).unwrap();
+
+        let frames =
+            testing::decode_pkt(&mut pipe.client, &mut buf, len).unwrap();
+
+        assert_eq!(
+            frames.iter().next(),
+            Some(&frame::Frame::Stream {
+                stream_id: 8,
+                data: stream::RangeBuf::from(b"b", 0, false),
+            })
+        );
+
+        // Then is stream 0.
+        let len = pipe.server.send(&mut buf).unwrap();
+
+        let frames =
+            testing::decode_pkt(&mut pipe.client, &mut buf, len).unwrap();
+
+        assert_eq!(
+            frames.iter().next(),
+            Some(&frame::Frame::Stream {
+                stream_id: 0,
+                data: stream::RangeBuf::from(b"b", 0, false),
+            })
+        );
+
+        // Then are stream 12 and 4, with the same priority.
+        let len = pipe.server.send(&mut buf).unwrap();
+
+        let frames =
+            testing::decode_pkt(&mut pipe.client, &mut buf, len).unwrap();
+
+        assert_eq!(
+            frames.iter().next(),
+            Some(&frame::Frame::Stream {
+                stream_id: 12,
+                data: stream::RangeBuf::from(b"b", 0, false),
+            })
+        );
+
+        let len = pipe.server.send(&mut buf).unwrap();
+
+        let frames =
+            testing::decode_pkt(&mut pipe.client, &mut buf, len).unwrap();
+
+        assert_eq!(
+            frames.iter().next(),
+            Some(&frame::Frame::Stream {
+                stream_id: 4,
+                data: stream::RangeBuf::from(b"b", 0, false),
+            })
+        );
+
+        assert_eq!(pipe.server.send(&mut buf), Err(Error::Done));
+    }
+
+    #[test]
+    /// Tests that old data is retransmitted on PTO.
+    fn early_retransmit() {
+        let mut buf = [0; 65535];
+
+        let mut pipe = testing::Pipe::default().unwrap();
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+        // Client sends stream data.
+        assert_eq!(pipe.client.stream_send(0, b"a", false), Ok(1));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        // Client sends more stream data, but packet is lost
+        assert_eq!(pipe.client.stream_send(4, b"b", false), Ok(1));
+        assert!(pipe.client.send(&mut buf).is_ok());
+
+        // Wait until PTO expires. Since the RTT is very low, wait a bit more.
+        let timer = pipe.client.timeout().unwrap();
+        std::thread::sleep(timer + time::Duration::from_millis(1));
+
+        pipe.client.on_timeout();
+
+        let epoch = packet::EPOCH_APPLICATION;
+        assert_eq!(pipe.client.recovery.loss_probes[epoch], 1);
+
+        // Client retransmits stream data in PTO probe.
+        let len = pipe.client.send(&mut buf).unwrap();
+        assert_eq!(pipe.client.recovery.loss_probes[epoch], 0);
+
+        let frames =
+            testing::decode_pkt(&mut pipe.server, &mut buf, len).unwrap();
+
+        let mut iter = frames.iter();
+
+        // Skip ACK frame.
+        iter.next();
+
+        assert_eq!(
+            iter.next(),
+            Some(&frame::Frame::Stream {
+                stream_id: 4,
+                data: stream::RangeBuf::from(b"b", 0, false),
+            })
+        );
+    }
+
+    #[test]
+    /// Tests that client avoids handshake deadlock by arming PTO.
+    fn handshake_anti_deadlock() {
+        let mut buf = [0; 65535];
+
+        let mut config = Config::new(PROTOCOL_VERSION).unwrap();
+        config
+            .load_cert_chain_from_pem_file("examples/cert-big.crt")
+            .unwrap();
+        config
+            .load_priv_key_from_pem_file("examples/cert.key")
+            .unwrap();
+        config
+            .set_application_protos(b"\x06proto1\06proto2")
+            .unwrap();
+
+        let mut pipe = testing::Pipe::with_server_config(&mut config).unwrap();
+
+        assert_eq!(pipe.client.handshake_status().has_handshake_keys, false);
+        assert_eq!(pipe.client.handshake_status().peer_verified_address, false);
+        assert_eq!(pipe.server.handshake_status().has_handshake_keys, false);
+        assert_eq!(pipe.server.handshake_status().peer_verified_address, true);
+
+        // Client sends padded Initial.
+        let len = pipe.client.send(&mut buf).unwrap();
+        assert_eq!(len, 1200);
+
+        // Server receives client's Initial and sends own Initial and Handshake
+        // until it's blocked by the anti-amplification limit.
+        let len = testing::recv_send(&mut pipe.server, &mut buf, len).unwrap();
+        assert_eq!(pipe.server.send(&mut buf[len..]), Err(Error::Done));
+
+        assert_eq!(pipe.client.handshake_status().has_handshake_keys, false);
+        assert_eq!(pipe.client.handshake_status().peer_verified_address, false);
+        assert_eq!(pipe.server.handshake_status().has_handshake_keys, true);
+        assert_eq!(pipe.server.handshake_status().peer_verified_address, true);
+
+        // Client receives the server flight and sends Handshake ACK, but it is
+        // lost.
+        assert!(testing::recv_send(&mut pipe.client, &mut buf, len).is_ok());
+
+        assert_eq!(pipe.client.handshake_status().has_handshake_keys, true);
+        assert_eq!(pipe.client.handshake_status().peer_verified_address, false);
+        assert_eq!(pipe.server.handshake_status().has_handshake_keys, true);
+        assert_eq!(pipe.server.handshake_status().peer_verified_address, true);
+
+        // Make sure client's PTO timer is armed.
+        assert!(pipe.client.timeout().is_some());
+    }
+
+    #[test]
+    /// Tests that packets with corrupted type (from Handshake to Initial) are
+    /// properly ignored.
+    fn handshake_packet_type_corruption() {
+        let mut buf = [0; 65535];
+
+        let mut pipe = testing::Pipe::default().unwrap();
+
+        // Client sends padded Initial.
+        let len = pipe.client.send(&mut buf).unwrap();
+        assert_eq!(len, 1200);
+
+        // Server receives client's Initial and sends own Initial and Handshake.
+        let len = testing::recv_send(&mut pipe.server, &mut buf, len).unwrap();
+        assert_eq!(pipe.client.recv(&mut buf[..len]), Ok(len));
+
+        // Client sends Initial packet with ACK.
+        let len = pipe.client.send(&mut buf).unwrap();
+
+        let hdr = Header::from_slice(&mut buf[..len], 0).unwrap();
+        assert_eq!(hdr.ty, Type::Initial);
+
+        assert_eq!(pipe.server.recv(&mut buf[..len]), Ok(len));
+
+        // Client sends Handshake packet.
+        let len = pipe.client.send(&mut buf).unwrap();
+
+        let hdr = Header::from_slice(&mut buf[..len], 0).unwrap();
+        assert_eq!(hdr.ty, Type::Handshake);
+
+        // Packet type is corrupted to Initial..
+        buf[0] &= !(0x20);
+
+        let hdr = Header::from_slice(&mut buf[..len], 0).unwrap();
+        assert_eq!(hdr.ty, Type::Initial);
+
+        // Server receives corrupted packet without returning an error.
+        assert_eq!(pipe.server.recv(&mut buf[..len]), Ok(len));
+    }
+
+    #[test]
+    fn dgram_send_fails_invalidstate() {
+        let mut buf = [0; 65535];
+
+        let mut pipe = testing::Pipe::default().unwrap();
+
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+        assert_eq!(
+            pipe.client.dgram_send(b"hello, world"),
+            Err(Error::InvalidState)
+        );
+    }
+
+    #[test]
+    fn dgram_send_app_limited() {
+        let mut buf = [0; 65535];
+        let send_buf = [0xcf; 1000];
+
+        let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap();
+        config
+            .load_cert_chain_from_pem_file("examples/cert.crt")
+            .unwrap();
+        config
+            .load_priv_key_from_pem_file("examples/cert.key")
+            .unwrap();
+        config
+            .set_application_protos(b"\x06proto1\x06proto2")
+            .unwrap();
+        config.set_initial_max_data(30);
+        config.set_initial_max_stream_data_bidi_local(15);
+        config.set_initial_max_stream_data_bidi_remote(15);
+        config.set_initial_max_stream_data_uni(10);
+        config.set_initial_max_streams_bidi(3);
+        config.set_initial_max_streams_uni(3);
+        config.enable_dgram(true, 1000, 1000);
+        config.set_max_udp_payload_size(1200);
+        config.verify_peer(false);
+
+        let mut pipe = testing::Pipe::with_config(&mut config).unwrap();
+
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        for _ in 0..1000 {
+            assert_eq!(pipe.client.dgram_send(&send_buf), Ok(()));
+        }
+
+        assert!(!pipe.client.recovery.app_limited());
+        assert_eq!(pipe.client.dgram_send_queue.byte_size(), 1_000_000);
+
+        let len = pipe.client.send(&mut buf).unwrap();
+
+        assert_ne!(pipe.client.dgram_send_queue.byte_size(), 0);
+        assert_ne!(pipe.client.dgram_send_queue.byte_size(), 1_000_000);
+        assert!(!pipe.client.recovery.app_limited());
+
+        testing::recv_send(&mut pipe.client, &mut buf, len).unwrap();
+        testing::recv_send(&mut pipe.server, &mut buf, len).unwrap();
+
+        assert_ne!(pipe.client.dgram_send_queue.byte_size(), 0);
+        assert_ne!(pipe.client.dgram_send_queue.byte_size(), 1_000_000);
+
+        assert!(!pipe.client.recovery.app_limited());
+    }
+
+    #[test]
+    fn dgram_single_datagram() {
+        let mut buf = [0; 65535];
+
+        let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap();
+        config
+            .load_cert_chain_from_pem_file("examples/cert.crt")
+            .unwrap();
+        config
+            .load_priv_key_from_pem_file("examples/cert.key")
+            .unwrap();
+        config
+            .set_application_protos(b"\x06proto1\x06proto2")
+            .unwrap();
+        config.set_initial_max_data(30);
+        config.set_initial_max_stream_data_bidi_local(15);
+        config.set_initial_max_stream_data_bidi_remote(15);
+        config.set_initial_max_stream_data_uni(10);
+        config.set_initial_max_streams_bidi(3);
+        config.set_initial_max_streams_uni(3);
+        config.enable_dgram(true, 10, 10);
+        config.verify_peer(false);
+
+        let mut pipe = testing::Pipe::with_config(&mut config).unwrap();
+
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+        assert_eq!(pipe.client.dgram_send(b"hello, world"), Ok(()));
+
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        let result1 = pipe.server.dgram_recv(&mut buf);
+        assert_eq!(result1, Ok(12));
+
+        let result2 = pipe.server.dgram_recv(&mut buf);
+        assert_eq!(result2, Err(Error::Done));
+    }
+
+    #[test]
+    fn dgram_multiple_datagrams() {
+        let mut buf = [0; 65535];
+
+        let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap();
+        config
+            .load_cert_chain_from_pem_file("examples/cert.crt")
+            .unwrap();
+        config
+            .load_priv_key_from_pem_file("examples/cert.key")
+            .unwrap();
+        config
+            .set_application_protos(b"\x06proto1\x06proto2")
+            .unwrap();
+        config.set_initial_max_data(30);
+        config.set_initial_max_stream_data_bidi_local(15);
+        config.set_initial_max_stream_data_bidi_remote(15);
+        config.set_initial_max_stream_data_uni(10);
+        config.set_initial_max_streams_bidi(3);
+        config.set_initial_max_streams_uni(3);
+        config.enable_dgram(true, 10, 10);
+        config.verify_peer(false);
+
+        let mut pipe = testing::Pipe::with_config(&mut config).unwrap();
+
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+        assert_eq!(pipe.client.dgram_send(b"hello, world"), Ok(()));
+        assert_eq!(pipe.client.dgram_send(b"ciao, mondo"), Ok(()));
+        assert_eq!(pipe.client.dgram_send(b"hola, mundo"), Ok(()));
+
+        pipe.client
+            .dgram_purge_outgoing(|d: &[u8]| -> bool { d[0] == b'c' });
+
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        let result1 = pipe.server.dgram_recv(&mut buf);
+        assert_eq!(result1, Ok(12));
+        assert_eq!(buf[0], b'h');
+        assert_eq!(buf[1], b'e');
+
+        let result2 = pipe.server.dgram_recv(&mut buf);
+        assert_eq!(result2, Ok(11));
+        assert_eq!(buf[0], b'h');
+        assert_eq!(buf[1], b'o');
+
+        let result3 = pipe.server.dgram_recv(&mut buf);
+        assert_eq!(result3, Err(Error::Done));
+    }
+
+    #[test]
+    fn dgram_send_queue_overflow() {
+        let mut buf = [0; 65535];
+
+        let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap();
+        config
+            .load_cert_chain_from_pem_file("examples/cert.crt")
+            .unwrap();
+        config
+            .load_priv_key_from_pem_file("examples/cert.key")
+            .unwrap();
+        config
+            .set_application_protos(b"\x06proto1\x06proto2")
+            .unwrap();
+        config.set_initial_max_data(30);
+        config.set_initial_max_stream_data_bidi_local(15);
+        config.set_initial_max_stream_data_bidi_remote(15);
+        config.set_initial_max_stream_data_uni(10);
+        config.set_initial_max_streams_bidi(3);
+        config.set_initial_max_streams_uni(3);
+        config.enable_dgram(true, 10, 2);
+        config.verify_peer(false);
+
+        let mut pipe = testing::Pipe::with_config(&mut config).unwrap();
+
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        assert_eq!(pipe.client.dgram_send(b"hello, world"), Ok(()));
+        assert_eq!(pipe.client.dgram_send(b"ciao, mondo"), Ok(()));
+        assert_eq!(pipe.client.dgram_send(b"hola, mundo"), Err(Error::Done));
+
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        let result1 = pipe.server.dgram_recv(&mut buf);
+        assert_eq!(result1, Ok(12));
+        assert_eq!(buf[0], b'h');
+        assert_eq!(buf[1], b'e');
+
+        let result2 = pipe.server.dgram_recv(&mut buf);
+        assert_eq!(result2, Ok(11));
+        assert_eq!(buf[0], b'c');
+        assert_eq!(buf[1], b'i');
+
+        let result3 = pipe.server.dgram_recv(&mut buf);
+        assert_eq!(result3, Err(Error::Done));
+    }
+
+    #[test]
+    fn dgram_recv_queue_overflow() {
+        let mut buf = [0; 65535];
+
+        let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap();
+        config
+            .load_cert_chain_from_pem_file("examples/cert.crt")
+            .unwrap();
+        config
+            .load_priv_key_from_pem_file("examples/cert.key")
+            .unwrap();
+        config
+            .set_application_protos(b"\x06proto1\x06proto2")
+            .unwrap();
+        config.set_initial_max_data(30);
+        config.set_initial_max_stream_data_bidi_local(15);
+        config.set_initial_max_stream_data_bidi_remote(15);
+        config.set_initial_max_stream_data_uni(10);
+        config.set_initial_max_streams_bidi(3);
+        config.set_initial_max_streams_uni(3);
+        config.enable_dgram(true, 2, 10);
+        config.set_max_udp_payload_size(1200);
+        config.verify_peer(false);
+
+        let mut pipe = testing::Pipe::with_config(&mut config).unwrap();
+
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+        assert_eq!(pipe.client.dgram_send(b"hello, world"), Ok(()));
+        assert_eq!(pipe.client.dgram_send(b"ciao, mondo"), Ok(()));
+        assert_eq!(pipe.client.dgram_send(b"hola, mundo"), Ok(()));
+
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        let result1 = pipe.server.dgram_recv(&mut buf);
+        assert_eq!(result1, Ok(11));
+        assert_eq!(buf[0], b'c');
+        assert_eq!(buf[1], b'i');
+
+        let result2 = pipe.server.dgram_recv(&mut buf);
+        assert_eq!(result2, Ok(11));
+        assert_eq!(buf[0], b'h');
+        assert_eq!(buf[1], b'o');
+
+        let result3 = pipe.server.dgram_recv(&mut buf);
+        assert_eq!(result3, Err(Error::Done));
+    }
+
+    #[test]
+    fn dgram_send_max_size() {
+        let mut buf = [0; MAX_DGRAM_FRAME_SIZE as usize];
+
+        let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap();
+        config
+            .load_cert_chain_from_pem_file("examples/cert.crt")
+            .unwrap();
+        config
+            .load_priv_key_from_pem_file("examples/cert.key")
+            .unwrap();
+        config
+            .set_application_protos(b"\x06proto1\x06proto2")
+            .unwrap();
+        config.set_initial_max_data(30);
+        config.set_initial_max_stream_data_bidi_local(15);
+        config.set_initial_max_stream_data_bidi_remote(15);
+        config.set_initial_max_stream_data_uni(10);
+        config.set_initial_max_streams_bidi(3);
+        config.set_initial_max_streams_uni(3);
+        config.enable_dgram(true, 10, 10);
+        config.set_max_udp_payload_size(1452);
+        config.verify_peer(false);
+
+        let mut pipe = testing::Pipe::with_config(&mut config).unwrap();
+
+        // Before handshake (before peer settings) we don't know max dgram size
+        assert_eq!(pipe.client.dgram_max_writable_len(), None);
+
+        assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+        let max_dgram_size = pipe.client.dgram_max_writable_len().unwrap();
+
+        let dgram_packet: Vec<u8> = vec![42; max_dgram_size];
+
+        assert_eq!(pipe.client.dgram_send(&dgram_packet), Ok(()));
+
+        assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+        let result1 = pipe.server.dgram_recv(&mut buf);
+        assert_eq!(result1, Ok(max_dgram_size));
+
+        let result2 = pipe.server.dgram_recv(&mut buf);
+        assert_eq!(result2, Err(Error::Done));
+    }
+}
+
+pub use crate::packet::Header;
+pub use crate::packet::Type;
+pub use crate::recovery::CongestionControlAlgorithm;
+pub use crate::stream::StreamIter;
+
+mod crypto;
+mod dgram;
+mod ffi;
+mod frame;
+pub mod h3;
+mod minmax;
+mod octets;
+mod packet;
+mod rand;
+mod ranges;
+mod recovery;
+mod stream;
+mod tls;
diff --git a/src/minmax.rs b/src/minmax.rs
new file mode 100644
index 0000000..a8a23fd
--- /dev/null
+++ b/src/minmax.rs
@@ -0,0 +1,435 @@
+// Copyright (C) 2020, Cloudflare, Inc.
+// Copyright (C) 2017, Google, Inc.
+//
+// Use of this source code is governed by the following BSD-style license:
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// lib/minmax.c: windowed min/max tracker
+//
+// Kathleen Nichols' algorithm for tracking the minimum (or maximum)
+// value of a data stream over some fixed time interval.  (E.g.,
+// the minimum RTT over the past five minutes.) It uses constant
+// space and constant time per update yet almost always delivers
+// the same minimum as an implementation that has to keep all the
+// data in the window.
+//
+// The algorithm keeps track of the best, 2nd best & 3rd best min
+// values, maintaining an invariant that the measurement time of
+// the n'th best >= n-1'th best. It also makes sure that the three
+// values are widely separated in the time window since that bounds
+// the worse case error when that data is monotonically increasing
+// over the window.
+//
+// Upon getting a new min, we can forget everything earlier because
+// it has no value - the new min is <= everything else in the window
+// by definition and it's the most recent. So we restart fresh on
+// every new min and overwrites 2nd & 3rd choices. The same property
+// holds for 2nd & 3rd best.
+
+use std::time::Duration;
+use std::time::Instant;
+
+#[derive(Copy, Clone)]
+struct MinmaxSample<T> {
+    time: Instant,
+    value: T,
+}
+
+pub struct Minmax<T> {
+    estimate: [MinmaxSample<T>; 3],
+}
+
+impl<T: PartialOrd + Copy> Minmax<T> {
+    pub fn new(val: T) -> Self {
+        Minmax {
+            estimate: [MinmaxSample {
+                time: Instant::now(),
+                value: val,
+            }; 3],
+        }
+    }
+
+    /// Resets the estimates to the given value.
+    pub fn reset(&mut self, time: Instant, meas: T) -> T {
+        let val = MinmaxSample { time, value: meas };
+
+        for i in self.estimate.iter_mut() {
+            *i = val;
+        }
+
+        self.estimate[0].value
+    }
+
+    /// Updates the min estimate based on the given measurement, and returns it.
+    pub fn running_min(&mut self, win: Duration, time: Instant, meas: T) -> T {
+        let val = MinmaxSample { time, value: meas };
+
+        let delta_time = time.duration_since(self.estimate[2].time);
+
+        // Reset if there's nothing in the window or a new min value is found.
+        if val.value <= self.estimate[0].value || delta_time > win {
+            return self.reset(time, meas);
+        }
+
+        if val.value <= self.estimate[1].value {
+            self.estimate[2] = val;
+            self.estimate[1] = val;
+        } else if val.value <= self.estimate[2].value {
+            self.estimate[2] = val;
+        }
+
+        self.subwin_update(win, time, meas)
+    }
+
+    /// Updates the max estimate based on the given measurement, and returns it.
+    pub fn _running_max(&mut self, win: Duration, time: Instant, meas: T) -> T {
+        let val = MinmaxSample { time, value: meas };
+
+        let delta_time = time.duration_since(self.estimate[2].time);
+
+        // Reset if there's nothing in the window or a new max value is found.
+        if val.value >= self.estimate[0].value || delta_time > win {
+            return self.reset(time, meas);
+        }
+
+        if val.value >= self.estimate[1].value {
+            self.estimate[2] = val;
+            self.estimate[1] = val;
+        } else if val.value >= self.estimate[2].value {
+            self.estimate[2] = val
+        }
+
+        self.subwin_update(win, time, meas)
+    }
+
+    /// As time advances, update the 1st, 2nd and 3rd estimates.
+    fn subwin_update(&mut self, win: Duration, time: Instant, meas: T) -> T {
+        let val = MinmaxSample { time, value: meas };
+
+        let delta_time = time.duration_since(self.estimate[0].time);
+
+        if delta_time > win {
+            // Passed entire window without a new val so make 2nd estimate the
+            // new val & 3rd estimate the new 2nd choice. we may have to iterate
+            // this since our 2nd estimate may also be outside the window (we
+            // checked on entry that the third estimate was in the window).
+            self.estimate[0] = self.estimate[1];
+            self.estimate[1] = self.estimate[2];
+            self.estimate[2] = val;
+
+            if time.duration_since(self.estimate[0].time) > win {
+                self.estimate[0] = self.estimate[1];
+                self.estimate[1] = self.estimate[2];
+                self.estimate[2] = val;
+            }
+        } else if self.estimate[1].time == self.estimate[0].time &&
+            delta_time > win.div_f32(4.0)
+        {
+            // We've passed a quarter of the window without a new val so take a
+            // 2nd estimate from the 2nd quarter of the window.
+            self.estimate[2] = val;
+            self.estimate[1] = val;
+        } else if self.estimate[2].time == self.estimate[1].time &&
+            delta_time > win.div_f32(2.0)
+        {
+            // We've passed half the window without finding a new val so take a
+            // 3rd estimate from the last half of the window.
+            self.estimate[2] = val;
+        }
+
+        self.estimate[0].value
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn reset_filter_rtt() {
+        let mut f = Minmax::new(Duration::new(0, 0));
+        let now = Instant::now();
+        let rtt = Duration::from_millis(50);
+
+        let rtt_min = f.reset(now, rtt);
+        assert_eq!(rtt_min, rtt);
+
+        assert_eq!(f.estimate[0].time, now);
+        assert_eq!(f.estimate[0].value, rtt);
+
+        assert_eq!(f.estimate[1].time, now);
+        assert_eq!(f.estimate[1].value, rtt);
+
+        assert_eq!(f.estimate[2].time, now);
+        assert_eq!(f.estimate[2].value, rtt);
+    }
+
+    #[test]
+    fn reset_filter_bandwidth() {
+        let mut f = Minmax::new(0);
+        let now = Instant::now();
+        let bw = 2000;
+
+        let bw_min = f.reset(now, bw);
+        assert_eq!(bw_min, bw);
+
+        assert_eq!(f.estimate[0].time, now);
+        assert_eq!(f.estimate[0].value, bw);
+
+        assert_eq!(f.estimate[1].time, now);
+        assert_eq!(f.estimate[1].value, bw);
+
+        assert_eq!(f.estimate[2].time, now);
+        assert_eq!(f.estimate[2].value, bw);
+    }
+
+    #[test]
+    fn get_windowed_min_rtt() {
+        let mut f = Minmax::new(Duration::new(0, 0));
+        let rtt_25 = Duration::from_millis(25);
+        let rtt_24 = Duration::from_millis(24);
+        let win = Duration::from_millis(500);
+        let mut time = Instant::now();
+
+        let mut rtt_min = f.reset(time, rtt_25);
+        assert_eq!(rtt_min, rtt_25);
+
+        time += Duration::from_millis(250);
+        rtt_min = f.running_min(win, time, rtt_24);
+        assert_eq!(rtt_min, rtt_24);
+        assert_eq!(f.estimate[1].value, rtt_24);
+        assert_eq!(f.estimate[2].value, rtt_24);
+
+        time += Duration::from_millis(600);
+        rtt_min = f.running_min(win, time, rtt_25);
+        assert_eq!(rtt_min, rtt_25);
+        assert_eq!(f.estimate[1].value, rtt_25);
+        assert_eq!(f.estimate[2].value, rtt_25);
+    }
+
+    #[test]
+    fn get_windowed_min_bandwidth() {
+        let mut f = Minmax::new(0);
+        let bw_200 = 200;
+        let bw_500 = 500;
+        let win = Duration::from_millis(500);
+        let mut time = Instant::now();
+
+        let mut bw_min = f.reset(time, bw_500);
+        assert_eq!(bw_min, bw_500);
+
+        time += Duration::from_millis(250);
+        bw_min = f.running_min(win, time, bw_200);
+        assert_eq!(bw_min, bw_200);
+        assert_eq!(f.estimate[1].value, bw_200);
+        assert_eq!(f.estimate[2].value, bw_200);
+
+        time += Duration::from_millis(600);
+        bw_min = f.running_min(win, time, bw_500);
+        assert_eq!(bw_min, bw_500);
+        assert_eq!(f.estimate[1].value, bw_500);
+        assert_eq!(f.estimate[2].value, bw_500);
+    }
+
+    #[test]
+    fn get_windowed_max_rtt() {
+        let mut f = Minmax::new(Duration::new(0, 0));
+        let rtt_25 = Duration::from_millis(25);
+        let rtt_24 = Duration::from_millis(24);
+        let win = Duration::from_millis(500);
+        let mut time = Instant::now();
+
+        let mut rtt_max = f.reset(time, rtt_24);
+        assert_eq!(rtt_max, rtt_24);
+
+        time += Duration::from_millis(250);
+        rtt_max = f._running_max(win, time, rtt_25);
+        assert_eq!(rtt_max, rtt_25);
+        assert_eq!(f.estimate[1].value, rtt_25);
+        assert_eq!(f.estimate[2].value, rtt_25);
+
+        time += Duration::from_millis(600);
+        rtt_max = f._running_max(win, time, rtt_24);
+        assert_eq!(rtt_max, rtt_24);
+        assert_eq!(f.estimate[1].value, rtt_24);
+        assert_eq!(f.estimate[2].value, rtt_24);
+    }
+
+    #[test]
+    fn get_windowed_max_bandwidth() {
+        let mut f = Minmax::new(0);
+        let bw_200 = 200;
+        let bw_500 = 500;
+        let win = Duration::from_millis(500);
+        let mut time = Instant::now();
+
+        let mut bw_max = f.reset(time, bw_200);
+        assert_eq!(bw_max, bw_200);
+
+        time += Duration::from_millis(5000);
+        bw_max = f._running_max(win, time, bw_500);
+        assert_eq!(bw_max, bw_500);
+        assert_eq!(f.estimate[1].value, bw_500);
+        assert_eq!(f.estimate[2].value, bw_500);
+
+        time += Duration::from_millis(600);
+        bw_max = f._running_max(win, time, bw_200);
+        assert_eq!(bw_max, bw_200);
+        assert_eq!(f.estimate[1].value, bw_200);
+        assert_eq!(f.estimate[2].value, bw_200);
+    }
+
+    #[test]
+    fn get_windowed_min_estimates_rtt() {
+        let mut f = Minmax::new(Duration::new(0, 0));
+        let rtt_25 = Duration::from_millis(25);
+        let rtt_24 = Duration::from_millis(24);
+        let rtt_23 = Duration::from_millis(23);
+        let rtt_22 = Duration::from_millis(22);
+        let win = Duration::from_secs(1);
+        let mut time = Instant::now();
+
+        let mut rtt_min = f.reset(time, rtt_23);
+        assert_eq!(rtt_min, rtt_23);
+
+        time += Duration::from_millis(300);
+        rtt_min = f.running_min(win, time, rtt_24);
+        assert_eq!(rtt_min, rtt_23);
+        assert_eq!(f.estimate[1].value, rtt_24);
+        assert_eq!(f.estimate[2].value, rtt_24);
+
+        time += Duration::from_millis(300);
+        rtt_min = f.running_min(win, time, rtt_25);
+        assert_eq!(rtt_min, rtt_23);
+        assert_eq!(f.estimate[1].value, rtt_24);
+        assert_eq!(f.estimate[2].value, rtt_25);
+
+        time += Duration::from_millis(300);
+        rtt_min = f.running_min(win, time, rtt_22);
+        assert_eq!(rtt_min, rtt_22);
+        assert_eq!(f.estimate[1].value, rtt_22);
+        assert_eq!(f.estimate[2].value, rtt_22);
+    }
+
+    #[test]
+    fn get_windowed_min_estimates_bandwidth() {
+        let mut f = Minmax::new(0);
+        let bw_500 = 500;
+        let bw_400 = 400;
+        let bw_300 = 300;
+        let bw_200 = 200;
+        let win = Duration::from_secs(1);
+        let mut time = Instant::now();
+
+        let mut bw_min = f.reset(time, bw_300);
+        assert_eq!(bw_min, bw_300);
+
+        time += Duration::from_millis(300);
+        bw_min = f.running_min(win, time, bw_400);
+        assert_eq!(bw_min, bw_300);
+        assert_eq!(f.estimate[1].value, bw_400);
+        assert_eq!(f.estimate[2].value, bw_400);
+
+        time += Duration::from_millis(300);
+        bw_min = f.running_min(win, time, bw_500);
+        assert_eq!(bw_min, bw_300);
+        assert_eq!(f.estimate[1].value, bw_400);
+        assert_eq!(f.estimate[2].value, bw_500);
+
+        time += Duration::from_millis(300);
+        bw_min = f.running_min(win, time, bw_200);
+        assert_eq!(bw_min, bw_200);
+        assert_eq!(f.estimate[1].value, bw_200);
+        assert_eq!(f.estimate[2].value, bw_200);
+    }
+
+    #[test]
+    fn get_windowed_max_estimates_rtt() {
+        let mut f = Minmax::new(Duration::new(0, 0));
+        let rtt_25 = Duration::from_millis(25);
+        let rtt_24 = Duration::from_millis(24);
+        let rtt_23 = Duration::from_millis(23);
+        let rtt_26 = Duration::from_millis(26);
+        let win = Duration::from_secs(1);
+        let mut time = Instant::now();
+
+        let mut rtt_max = f.reset(time, rtt_25);
+        assert_eq!(rtt_max, rtt_25);
+
+        time += Duration::from_millis(300);
+        rtt_max = f._running_max(win, time, rtt_24);
+        assert_eq!(rtt_max, rtt_25);
+        assert_eq!(f.estimate[1].value, rtt_24);
+        assert_eq!(f.estimate[2].value, rtt_24);
+
+        time += Duration::from_millis(300);
+        rtt_max = f._running_max(win, time, rtt_23);
+        assert_eq!(rtt_max, rtt_25);
+        assert_eq!(f.estimate[1].value, rtt_24);
+        assert_eq!(f.estimate[2].value, rtt_23);
+
+        time += Duration::from_millis(300);
+        rtt_max = f._running_max(win, time, rtt_26);
+        assert_eq!(rtt_max, rtt_26);
+        assert_eq!(f.estimate[1].value, rtt_26);
+        assert_eq!(f.estimate[2].value, rtt_26);
+    }
+
+    #[test]
+    fn get_windowed_max_estimates_bandwidth() {
+        let mut f = Minmax::new(0);
+        let bw_500 = 500;
+        let bw_400 = 400;
+        let bw_300 = 300;
+        let bw_600 = 600;
+        let win = Duration::from_secs(1);
+        let mut time = Instant::now();
+
+        let mut bw_max = f.reset(time, bw_500);
+        assert_eq!(bw_max, bw_500);
+
+        time += Duration::from_millis(300);
+        bw_max = f._running_max(win, time, bw_400);
+        assert_eq!(bw_max, bw_500);
+        assert_eq!(f.estimate[1].value, bw_400);
+        assert_eq!(f.estimate[2].value, bw_400);
+
+        time += Duration::from_millis(300);
+        bw_max = f._running_max(win, time, bw_300);
+        assert_eq!(bw_max, bw_500);
+        assert_eq!(f.estimate[1].value, bw_400);
+        assert_eq!(f.estimate[2].value, bw_300);
+
+        time += Duration::from_millis(300);
+        bw_max = f._running_max(win, time, bw_600);
+        assert_eq!(bw_max, bw_600);
+        assert_eq!(f.estimate[1].value, bw_600);
+        assert_eq!(f.estimate[2].value, bw_600);
+    }
+}
diff --git a/src/octets.rs b/src/octets.rs
new file mode 100644
index 0000000..2b36707
--- /dev/null
+++ b/src/octets.rs
@@ -0,0 +1,1261 @@
+// Copyright (C) 2018-2019, Cloudflare, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright notice,
+//       this list of conditions and the following disclaimer.
+//
+//     * Redistributions in binary form must reproduce the above copyright
+//       notice, this list of conditions and the following disclaimer in the
+//       documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#![allow(dead_code)]
+
+/// Zero-copy abstraction for parsing and constructing network packets.
+use std::mem;
+use std::ptr;
+
+/// A specialized [`Result`] type for [`OctetsMut`] operations.
+///
+/// [`Result`]: https://doc.rust-lang.org/std/result/enum.Result.html
+/// [`OctetsMut`]: struct.OctetsMut.html
+pub type Result<T> = std::result::Result<T, BufferTooShortError>;
+
+/// An error indicating that the provided [`OctetsMut`] is not big enough.
+///
+/// [`OctetsMut`]: struct.OctetsMut.html
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub struct BufferTooShortError;
+
+impl std::fmt::Display for BufferTooShortError {
+    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+        write!(f, "BufferTooShortError")
+    }
+}
+
+impl std::error::Error for BufferTooShortError {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        None
+    }
+}
+
+macro_rules! peek_u {
+    ($b:expr, $ty:ty, $len:expr) => {{
+        let len = $len;
+        let src = &$b.buf[$b.off..];
+
+        if src.len() < len {
+            return Err(BufferTooShortError);
+        }
+
+        let mut out: $ty = 0;
+        unsafe {
+            let dst = &mut out as *mut $ty as *mut u8;
+            let off = (mem::size_of::<$ty>() - len) as isize;
+
+            ptr::copy_nonoverlapping(src.as_ptr(), dst.offset(off), len);
+        };
+
+        Ok(<$ty>::from_be(out))
+    }};
+}
+
+macro_rules! get_u {
+    ($b:expr, $ty:ty, $len:expr) => {{
+        let out = peek_u!($b, $ty, $len);
+
+        $b.off += $len;
+
+        out
+    }};
+}
+
+macro_rules! put_u {
+    ($b:expr, $ty:ty, $v:expr, $len:expr) => {{
+        let len = $len;
+
+        if $b.buf.len() < $b.off + len {
+            return Err(BufferTooShortError);
+        }
+
+        let v = $v;
+
+        #[allow(clippy::range_plus_one)]
+        let dst = &mut $b.buf[$b.off..($b.off + len)];
+
+        unsafe {
+            let src = &<$ty>::to_be(v) as *const $ty as *const u8;
+            let off = (mem::size_of::<$ty>() - len) as isize;
+
+            ptr::copy_nonoverlapping(src.offset(off), dst.as_mut_ptr(), len);
+        }
+
+        $b.off += $len;
+
+        Ok(dst)
+    }};
+}
+
+/// A zero-copy immutable byte buffer.
+///
+/// `Octets` wraps an in-memory buffer of bytes and provides utility functions
+/// for manipulating it. The underlying buffer is provided by the user and is
+/// not copied when creating an `Octets`. Operations are panic-free and will
+/// avoid indexing the buffer past its end.
+///
+/// Additionally, an offset (initially set to the start of the buffer) is
+/// incremented as bytes are read from / written to the buffer, to allow for
+/// sequential operations.
+#[derive(Debug, PartialEq)]
+pub struct Octets<'a> {
+    buf: &'a [u8],
+    off: usize,
+}
+
+impl<'a> Octets<'a> {
+    /// Creates an `Octets` from the given slice, without copying.
+    ///
+    /// Since there's no copy, the input slice needs to be mutable to allow
+    /// modifications.
+    pub fn with_slice(buf: &'a [u8]) -> Self {
+        Octets { buf, off: 0 }
+    }
+
+    /// Reads an unsigned 8-bit integer from the current offset and advances
+    /// the buffer.
+    pub fn get_u8(&mut self) -> Result<u8> {
+        get_u!(self, u8, 1)
+    }
+
+    /// Reads an unsigned 8-bit integer from the current offset without
+    /// advancing the buffer.
+    pub fn peek_u8(&mut self) -> Result<u8> {
+        peek_u!(self, u8, 1)
+    }
+
+    /// Reads an unsigned 16-bit integer in network byte-order from the current
+    /// offset and advances the buffer.
+    pub fn get_u16(&mut self) -> Result<u16> {
+        get_u!(self, u16, 2)
+    }
+
+    /// Reads an unsigned 24-bit integer in network byte-order from the current
+    /// offset and advances the buffer.
+    pub fn get_u24(&mut self) -> Result<u32> {
+        get_u!(self, u32, 3)
+    }
+
+    /// Reads an unsigned 32-bit integer in network byte-order from the current
+    /// offset and advances the buffer.
+    pub fn get_u32(&mut self) -> Result<u32> {
+        get_u!(self, u32, 4)
+    }
+
+    /// Reads an unsigned 64-bit integer in network byte-order from the current
+    /// offset and advances the buffer.
+    pub fn get_u64(&mut self) -> Result<u64> {
+        get_u!(self, u64, 8)
+    }
+
+    /// Reads an unsigned variable-length integer in network byte-order from
+    /// the current offset and advances the buffer.
+    pub fn get_varint(&mut self) -> Result<u64> {
+        let first = self.peek_u8()?;
+
+        let len = varint_parse_len(first);
+
+        if len > self.cap() {
+            return Err(BufferTooShortError);
+        }
+
+        let mut vec = self.get_bytes(len)?.to_vec();
+
+        // Mask the 2 most significant bits to remove the encoded length.
+        vec[0] &= 0x3f;
+
+        let mut b = OctetsMut::with_slice(&mut vec);
+
+        let out = match len {
+            1 => u64::from(b.get_u8()?),
+            2 => u64::from(b.get_u16()?),
+            4 => u64::from(b.get_u32()?),
+            8 => b.get_u64()?,
+            _ => unreachable!(),
+        };
+
+        Ok(out)
+    }
+
+    /// Reads `len` bytes from the current offset without copying and advances
+    /// the buffer.
+    pub fn get_bytes(&mut self, len: usize) -> Result<Octets> {
+        if self.cap() < len {
+            return Err(BufferTooShortError);
+        }
+
+        let out = Octets {
+            buf: &self.buf[self.off..self.off + len],
+            off: 0,
+        };
+
+        self.off += len;
+
+        Ok(out)
+    }
+
+    /// Reads `len` bytes from the current offset without copying and advances
+    /// the buffer, where `len` is an unsigned 8-bit integer prefix.
+    pub fn get_bytes_with_u8_length(&mut self) -> Result<Octets> {
+        let len = self.get_u8()?;
+        self.get_bytes(len as usize)
+    }
+
+    /// Reads `len` bytes from the current offset without copying and advances
+    /// the buffer, where `len` is an unsigned 16-bit integer prefix in network
+    /// byte-order.
+    pub fn get_bytes_with_u16_length(&mut self) -> Result<Octets> {
+        let len = self.get_u16()?;
+        self.get_bytes(len as usize)
+    }
+
+    /// Reads `len` bytes from the current offset without copying and advances
+    /// the buffer, where `len` is an unsigned variable-length integer prefix
+    /// in network byte-order.
+    pub fn get_bytes_with_varint_length(&mut self) -> Result<Octets> {
+        let len = self.get_varint()?;
+        self.get_bytes(len as usize)
+    }
+
+    /// Reads `len` bytes from the current offset without copying and without
+    /// advancing the buffer.
+    pub fn peek_bytes(&self, len: usize) -> Result<Octets> {
+        if self.cap() < len {
+            return Err(BufferTooShortError);
+        }
+
+        let out = Octets {
+            buf: &self.buf[self.off..self.off + len],
+            off: 0,
+        };
+
+        Ok(out)
+    }
+
+    /// Returns a slice of `len` elements from the current offset.
+    pub fn slice(&'a self, len: usize) -> Result<&'a [u8]> {
+        if len > self.cap() {
+            return Err(BufferTooShortError);
+        }
+
+        Ok(&self.buf[self.off..self.off + len])
+    }
+
+    /// Returns a slice of `len` elements from the end of the buffer.
+    pub fn slice_last(&'a self, len: usize) -> Result<&'a [u8]> {
+        if len > self.cap() {
+            return Err(BufferTooShortError);
+        }
+
+        let cap = self.cap();
+        Ok(&self.buf[cap - len..])
+    }
+
+    /// Returns the remaining capacity in the buffer.
+    pub fn cap(&self) -> usize {
+        self.buf.len() - self.off
+    }
+
+    /// Returns the total length of the buffer.
+    pub fn len(&self) -> usize {
+        self.buf.len()
+    }
+
+    /// Returns the current offset of the buffer.
+    pub fn off(&self) -> usize {
+        self.off
+    }
+
+    /// Returns a reference to the internal buffer.
+    pub fn buf(&self) -> &[u8] {
+        self.buf
+    }
+
+    /// Copies the buffer from the current offset into a new `Vec<u8>`.
+    pub fn to_vec(&self) -> Vec<u8> {
+        self.as_ref().to_vec()
+    }
+}
+
+impl<'a> AsRef<[u8]> for Octets<'a> {
+    fn as_ref(&self) -> &[u8] {
+        &self.buf[self.off..]
+    }
+}
+
+/// A zero-copy mutable byte buffer.
+///
+/// Like `Octets` but mutable.
+#[derive(Debug, PartialEq)]
+pub struct OctetsMut<'a> {
+    buf: &'a mut [u8],
+    off: usize,
+}
+
+impl<'a> OctetsMut<'a> {
+    /// Creates an `OctetsMut` from the given slice, without copying.
+    ///
+    /// Since there's no copy, the input slice needs to be mutable to allow
+    /// modifications.
+    pub fn with_slice(buf: &'a mut [u8]) -> Self {
+        OctetsMut { buf, off: 0 }
+    }
+
+    /// Reads an unsigned 8-bit integer from the current offset and advances
+    /// the buffer.
+    pub fn get_u8(&mut self) -> Result<u8> {
+        get_u!(self, u8, 1)
+    }
+
+    /// Reads an unsigned 8-bit integer from the current offset without
+    /// advancing the buffer.
+    pub fn peek_u8(&mut self) -> Result<u8> {
+        peek_u!(self, u8, 1)
+    }
+
+    /// Writes an unsigned 8-bit integer at the current offset and advances
+    /// the buffer.
+    pub fn put_u8(&mut self, v: u8) -> Result<&mut [u8]> {
+        put_u!(self, u8, v, 1)
+    }
+
+    /// Reads an unsigned 16-bit integer in network byte-order from the current
+    /// offset and advances the buffer.
+    pub fn get_u16(&mut self) -> Result<u16> {
+        get_u!(self, u16, 2)
+    }
+
+    /// Writes an unsigned 16-bit integer in network byte-order at the current
+    /// offset and advances the buffer.
+    pub fn put_u16(&mut self, v: u16) -> Result<&mut [u8]> {
+        put_u!(self, u16, v, 2)
+    }
+
+    /// Reads an unsigned 24-bit integer in network byte-order from the current
+    /// offset and advances the buffer.
+    pub fn get_u24(&mut self) -> Result<u32> {
+        get_u!(self, u32, 3)
+    }
+
+    /// Writes an unsigned 24-bit integer in network byte-order at the current
+    /// offset and advances the buffer.
+    pub fn put_u24(&mut self, v: u32) -> Result<&mut [u8]> {
+        put_u!(self, u32, v, 3)
+    }
+
+    /// Reads an unsigned 32-bit integer in network byte-order from the current
+    /// offset and advances the buffer.
+    pub fn get_u32(&mut self) -> Result<u32> {
+        get_u!(self, u32, 4)
+    }
+
+    /// Writes an unsigned 32-bit integer in network byte-order at the current
+    /// offset and advances the buffer.
+    pub fn put_u32(&mut self, v: u32) -> Result<&mut [u8]> {
+        put_u!(self, u32, v, 4)
+    }
+
+    /// Reads an unsigned 64-bit integer in network byte-order from the current
+    /// offset and advances the buffer.
+    pub fn get_u64(&mut self) -> Result<u64> {
+        get_u!(self, u64, 8)
+    }
+
+    /// Writes an unsigned 64-bit integer in network byte-order at the current
+    /// offset and advances the buffer.
+    pub fn put_u64(&mut self, v: u64) -> Result<&mut [u8]> {
+        put_u!(self, u64, v, 8)
+    }
+
+    /// Reads an unsigned variable-length integer in network byte-order from
+    /// the current offset and advances the buffer.
+    pub fn get_varint(&mut self) -> Result<u64> {
+        let first = self.peek_u8()?;
+
+        let len = varint_parse_len(first);
+
+        if len > self.cap() {
+            return Err(BufferTooShortError);
+        }
+
+        let mut vec = self.get_bytes(len)?.to_vec();
+
+        // Mask the 2 most significant bits to remove the encoded length.
+        vec[0] &= 0x3f;
+
+        let mut b = OctetsMut::with_slice(&mut vec);
+
+        let out = match len {
+            1 => u64::from(b.get_u8()?),
+            2 => u64::from(b.get_u16()?),
+            4 => u64::from(b.get_u32()?),
+            8 => b.get_u64()?,
+            _ => unreachable!(),
+        };
+
+        Ok(out)
+    }
+
+    /// Writes an unsigned variable-length integer in network byte-order at the
+    /// current offset and advances the buffer.
+    pub fn put_varint(&mut self, v: u64) -> Result<&mut [u8]> {
+        self.put_varint_with_len(v, varint_len(v))
+    }
+
+    /// Writes an unsigned variable-length integer of the specified length, in
+    /// network byte-order at the current offset and advances the buffer.
+    pub fn put_varint_with_len(
+        &mut self, v: u64, len: usize,
+    ) -> Result<&mut [u8]> {
+        if self.cap() < len {
+            return Err(BufferTooShortError);
+        }
+
+        let buf = match len {
+            1 => self.put_u8(v as u8)?,
+
+            2 => {
+                let buf = self.put_u16(v as u16)?;
+                buf[0] |= 0x40;
+                buf
+            },
+
+            4 => {
+                let buf = self.put_u32(v as u32)?;
+                buf[0] |= 0x80;
+                buf
+            },
+
+            8 => {
+                let buf = self.put_u64(v)?;
+                buf[0] |= 0xc0;
+                buf
+            },
+
+            _ => panic!("value is too large for varint"),
+        };
+
+        Ok(buf)
+    }
+
+    /// Reads `len` bytes from the current offset without copying and advances
+    /// the buffer.
+    pub fn get_bytes(&mut self, len: usize) -> Result<Octets> {
+        if self.cap() < len {
+            return Err(BufferTooShortError);
+        }
+
+        let out = Octets {
+            buf: &self.buf[self.off..self.off + len],
+            off: 0,
+        };
+
+        self.off += len;
+
+        Ok(out)
+    }
+
+    /// Reads `len` bytes from the current offset without copying and advances
+    /// the buffer.
+    pub fn get_bytes_mut(&mut self, len: usize) -> Result<OctetsMut> {
+        if self.cap() < len {
+            return Err(BufferTooShortError);
+        }
+
+        let out = OctetsMut {
+            buf: &mut self.buf[self.off..self.off + len],
+            off: 0,
+        };
+
+        self.off += len;
+
+        Ok(out)
+    }
+
+    /// Reads `len` bytes from the current offset without copying and advances
+    /// the buffer, where `len` is an unsigned 8-bit integer prefix.
+    pub fn get_bytes_with_u8_length(&mut self) -> Result<Octets> {
+        let len = self.get_u8()?;
+        self.get_bytes(len as usize)
+    }
+
+    /// Reads `len` bytes from the current offset without copying and advances
+    /// the buffer, where `len` is an unsigned 16-bit integer prefix in network
+    /// byte-order.
+    pub fn get_bytes_with_u16_length(&mut self) -> Result<Octets> {
+        let len = self.get_u16()?;
+        self.get_bytes(len as usize)
+    }
+
+    /// Reads `len` bytes from the current offset without copying and advances
+    /// the buffer, where `len` is an unsigned variable-length integer prefix
+    /// in network byte-order.
+    pub fn get_bytes_with_varint_length(&mut self) -> Result<Octets> {
+        let len = self.get_varint()?;
+        self.get_bytes(len as usize)
+    }
+
+    /// Reads `len` bytes from the current offset without copying and without
+    /// advancing the buffer.
+    pub fn peek_bytes(&mut self, len: usize) -> Result<Octets> {
+        if self.cap() < len {
+            return Err(BufferTooShortError);
+        }
+
+        let out = Octets {
+            buf: &self.buf[self.off..self.off + len],
+            off: 0,
+        };
+
+        Ok(out)
+    }
+
+    /// Reads `len` bytes from the current offset without copying and without
+    /// advancing the buffer.
+    pub fn peek_bytes_mut(&mut self, len: usize) -> Result<OctetsMut> {
+        if self.cap() < len {
+            return Err(BufferTooShortError);
+        }
+
+        let out = OctetsMut {
+            buf: &mut self.buf[self.off..self.off + len],
+            off: 0,
+        };
+
+        Ok(out)
+    }
+
+    /// Writes `len` bytes from the current offset without copying and advances
+    /// the buffer.
+    pub fn put_bytes(&mut self, v: &[u8]) -> Result<()> {
+        let len = v.len();
+
+        if self.cap() < len {
+            return Err(BufferTooShortError);
+        }
+
+        if len == 0 {
+            return Ok(());
+        }
+
+        self.as_mut()[..len].copy_from_slice(v);
+
+        self.off += len;
+
+        Ok(())
+    }
+
+    /// Splits the buffer in two at the given absolute offset.
+    pub fn split_at(&mut self, off: usize) -> Result<(OctetsMut, OctetsMut)> {
+        if self.len() < off {
+            return Err(BufferTooShortError);
+        }
+
+        let (left, right) = self.buf.split_at_mut(off);
+
+        let first = OctetsMut { buf: left, off: 0 };
+
+        let last = OctetsMut { buf: right, off: 0 };
+
+        Ok((first, last))
+    }
+
+    /// Returns a slice of `len` elements from the current offset.
+    pub fn slice(&'a mut self, len: usize) -> Result<&'a mut [u8]> {
+        if len > self.cap() {
+            return Err(BufferTooShortError);
+        }
+
+        Ok(&mut self.buf[self.off..self.off + len])
+    }
+
+    /// Returns a slice of `len` elements from the end of the buffer.
+    pub fn slice_last(&'a mut self, len: usize) -> Result<&'a mut [u8]> {
+        if len > self.cap() {
+            return Err(BufferTooShortError);
+        }
+
+        let cap = self.cap();
+        Ok(&mut self.buf[cap - len..])
+    }
+
+    /// Returns the remaining capacity in the buffer.
+    pub fn cap(&self) -> usize {
+        self.buf.len() - self.off
+    }
+
+    /// Returns the total length of the buffer.
+    pub fn len(&self) -> usize {
+        self.buf.len()
+    }
+
+    /// Returns the current offset of the buffer.
+    pub fn off(&self) -> usize {
+        self.off
+    }
+
+    /// Returns a reference to the internal buffer.
+    pub fn buf(&self) -> &[u8] {
+        self.buf
+    }
+
+    /// Copies the buffer from the current offset into a new `Vec<u8>`.
+    pub fn to_vec(&self) -> Vec<u8> {
+        self.as_ref().to_vec()
+    }
+}
+
+impl<'a> AsRef<[u8]> for OctetsMut<'a> {
+    fn as_ref(&self) -> &[u8] {
+        &self.buf[self.off..]
+    }
+}
+
+impl<'a> AsMut<[u8]> for OctetsMut<'a> {
+    fn as_mut(&mut self) -> &mut [u8] {
+        &mut self.buf[self.off..]
+    }
+}
+
+/// Returns how many bytes it would take to encode `v` as a variable-length
+/// integer.
+pub fn varint_len(v: u64) -> usize {
+    if v <= 63 {
+        1
+    } else if v <= 16383 {
+        2
+    } else if v <= 1_073_741_823 {
+        4
+    } else if v <= 4_611_686_018_427_387_903 {
+        8
+    } else {
+        unreachable!()
+    }
+}
+
+/// Returns how long the variable-length integer is, given its first byte.
+pub fn varint_parse_len(first: u8) -> usize {
+    match first >> 6 {
+        0 => 1,
+        1 => 2,
+        2 => 4,
+        3 => 8,
+        _ => unreachable!(),
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn get_u() {
+        let d = [
+            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
+        ];
+
+        let mut b = Octets::with_slice(&d);
+        assert_eq!(b.cap(), 18);
+        assert_eq!(b.off(), 0);
+
+        assert_eq!(b.get_u8().unwrap(), 1);
+        assert_eq!(b.cap(), 17);
+        assert_eq!(b.off(), 1);
+
+        assert_eq!(b.get_u16().unwrap(), 0x203);
+        assert_eq!(b.cap(), 15);
+        assert_eq!(b.off(), 3);
+
+        assert_eq!(b.get_u24().unwrap(), 0x40506);
+        assert_eq!(b.cap(), 12);
+        assert_eq!(b.off(), 6);
+
+        assert_eq!(b.get_u32().unwrap(), 0x0708090a);
+        assert_eq!(b.cap(), 8);
+        assert_eq!(b.off(), 10);
+
+        assert_eq!(b.get_u64().unwrap(), 0x0b0c0d0e0f101112);
+        assert_eq!(b.cap(), 0);
+        assert_eq!(b.off(), 18);
+
+        assert!(b.get_u8().is_err());
+        assert!(b.get_u16().is_err());
+        assert!(b.get_u24().is_err());
+        assert!(b.get_u32().is_err());
+        assert!(b.get_u64().is_err());
+    }
+
+    #[test]
+    fn get_u_mut() {
+        let mut d = [
+            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
+        ];
+
+        let mut b = OctetsMut::with_slice(&mut d);
+        assert_eq!(b.cap(), 18);
+        assert_eq!(b.off(), 0);
+
+        assert_eq!(b.get_u8().unwrap(), 1);
+        assert_eq!(b.cap(), 17);
+        assert_eq!(b.off(), 1);
+
+        assert_eq!(b.get_u16().unwrap(), 0x203);
+        assert_eq!(b.cap(), 15);
+        assert_eq!(b.off(), 3);
+
+        assert_eq!(b.get_u24().unwrap(), 0x40506);
+        assert_eq!(b.cap(), 12);
+        assert_eq!(b.off(), 6);
+
+        assert_eq!(b.get_u32().unwrap(), 0x0708090a);
+        assert_eq!(b.cap(), 8);
+        assert_eq!(b.off(), 10);
+
+        assert_eq!(b.get_u64().unwrap(), 0x0b0c0d0e0f101112);
+        assert_eq!(b.cap(), 0);
+        assert_eq!(b.off(), 18);
+
+        assert!(b.get_u8().is_err());
+        assert!(b.get_u16().is_err());
+        assert!(b.get_u24().is_err());
+        assert!(b.get_u32().is_err());
+        assert!(b.get_u64().is_err());
+    }
+
+    #[test]
+    fn peek_u() {
+        let d = [1, 2];
+
+        let mut b = Octets::with_slice(&d);
+        assert_eq!(b.cap(), 2);
+        assert_eq!(b.off(), 0);
+
+        assert_eq!(b.peek_u8().unwrap(), 1);
+        assert_eq!(b.cap(), 2);
+        assert_eq!(b.off(), 0);
+
+        assert_eq!(b.peek_u8().unwrap(), 1);
+        assert_eq!(b.cap(), 2);
+        assert_eq!(b.off(), 0);
+
+        b.get_u16().unwrap();
+
+        assert!(b.peek_u8().is_err());
+    }
+
+    #[test]
+    fn peek_u_mut() {
+        let mut d = [1, 2];
+
+        let mut b = OctetsMut::with_slice(&mut d);
+        assert_eq!(b.cap(), 2);
+        assert_eq!(b.off(), 0);
+
+        assert_eq!(b.peek_u8().unwrap(), 1);
+        assert_eq!(b.cap(), 2);
+        assert_eq!(b.off(), 0);
+
+        assert_eq!(b.peek_u8().unwrap(), 1);
+        assert_eq!(b.cap(), 2);
+        assert_eq!(b.off(), 0);
+
+        b.get_u16().unwrap();
+
+        assert!(b.peek_u8().is_err());
+    }
+
+    #[test]
+    fn get_bytes() {
+        let d = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
+        let mut b = Octets::with_slice(&d);
+        assert_eq!(b.cap(), 10);
+        assert_eq!(b.off(), 0);
+
+        assert_eq!(b.get_bytes(5).unwrap().as_ref(), [1, 2, 3, 4, 5]);
+        assert_eq!(b.cap(), 5);
+        assert_eq!(b.off(), 5);
+
+        assert_eq!(b.get_bytes(3).unwrap().as_ref(), [6, 7, 8]);
+        assert_eq!(b.cap(), 2);
+        assert_eq!(b.off(), 8);
+
+        assert!(b.get_bytes(3).is_err());
+        assert_eq!(b.cap(), 2);
+        assert_eq!(b.off(), 8);
+
+        assert_eq!(b.get_bytes(2).unwrap().as_ref(), [9, 10]);
+        assert_eq!(b.cap(), 0);
+        assert_eq!(b.off(), 10);
+
+        assert!(b.get_bytes(2).is_err());
+    }
+
+    #[test]
+    fn get_bytes_mut() {
+        let mut d = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
+        let mut b = OctetsMut::with_slice(&mut d);
+        assert_eq!(b.cap(), 10);
+        assert_eq!(b.off(), 0);
+
+        assert_eq!(b.get_bytes(5).unwrap().as_ref(), [1, 2, 3, 4, 5]);
+        assert_eq!(b.cap(), 5);
+        assert_eq!(b.off(), 5);
+
+        assert_eq!(b.get_bytes(3).unwrap().as_ref(), [6, 7, 8]);
+        assert_eq!(b.cap(), 2);
+        assert_eq!(b.off(), 8);
+
+        assert!(b.get_bytes(3).is_err());
+        assert_eq!(b.cap(), 2);
+        assert_eq!(b.off(), 8);
+
+        assert_eq!(b.get_bytes(2).unwrap().as_ref(), [9, 10]);
+        assert_eq!(b.cap(), 0);
+        assert_eq!(b.off(), 10);
+
+        assert!(b.get_bytes(2).is_err());
+    }
+
+    #[test]
+    fn peek_bytes() {
+        let d = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
+        let mut b = Octets::with_slice(&d);
+        assert_eq!(b.cap(), 10);
+        assert_eq!(b.off(), 0);
+
+        assert_eq!(b.peek_bytes(5).unwrap().as_ref(), [1, 2, 3, 4, 5]);
+        assert_eq!(b.cap(), 10);
+        assert_eq!(b.off(), 0);
+
+        assert_eq!(b.peek_bytes(5).unwrap().as_ref(), [1, 2, 3, 4, 5]);
+        assert_eq!(b.cap(), 10);
+        assert_eq!(b.off(), 0);
+
+        b.get_bytes(5).unwrap();
+    }
+
+    #[test]
+    fn peek_bytes_mut() {
+        let mut d = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
+        let mut b = OctetsMut::with_slice(&mut d);
+        assert_eq!(b.cap(), 10);
+        assert_eq!(b.off(), 0);
+
+        assert_eq!(b.peek_bytes(5).unwrap().as_ref(), [1, 2, 3, 4, 5]);
+        assert_eq!(b.cap(), 10);
+        assert_eq!(b.off(), 0);
+
+        assert_eq!(b.peek_bytes(5).unwrap().as_ref(), [1, 2, 3, 4, 5]);
+        assert_eq!(b.cap(), 10);
+        assert_eq!(b.off(), 0);
+
+        b.get_bytes(5).unwrap();
+    }
+
+    #[test]
+    fn get_varint() {
+        let d = [0xc2, 0x19, 0x7c, 0x5e, 0xff, 0x14, 0xe8, 0x8c];
+        let mut b = Octets::with_slice(&d);
+        assert_eq!(b.get_varint().unwrap(), 151288809941952652);
+        assert_eq!(b.cap(), 0);
+        assert_eq!(b.off(), 8);
+
+        let d = [0x9d, 0x7f, 0x3e, 0x7d];
+        let mut b = Octets::with_slice(&d);
+        assert_eq!(b.get_varint().unwrap(), 494878333);
+        assert_eq!(b.cap(), 0);
+        assert_eq!(b.off(), 4);
+
+        let d = [0x7b, 0xbd];
+        let mut b = Octets::with_slice(&d);
+        assert_eq!(b.get_varint().unwrap(), 15293);
+        assert_eq!(b.cap(), 0);
+        assert_eq!(b.off(), 2);
+
+        let d = [0x40, 0x25];
+        let mut b = Octets::with_slice(&d);
+        assert_eq!(b.get_varint().unwrap(), 37);
+        assert_eq!(b.cap(), 0);
+        assert_eq!(b.off(), 2);
+
+        let d = [0x25];
+        let mut b = Octets::with_slice(&d);
+        assert_eq!(b.get_varint().unwrap(), 37);
+        assert_eq!(b.cap(), 0);
+        assert_eq!(b.off(), 1);
+    }
+
+    #[test]
+    fn get_varint_mut() {
+        let mut d = [0xc2, 0x19, 0x7c, 0x5e, 0xff, 0x14, 0xe8, 0x8c];
+        let mut b = OctetsMut::with_slice(&mut d);
+        assert_eq!(b.get_varint().unwrap(), 151288809941952652);
+        assert_eq!(b.cap(), 0);
+        assert_eq!(b.off(), 8);
+
+        let mut d = [0x9d, 0x7f, 0x3e, 0x7d];
+        let mut b = OctetsMut::with_slice(&mut d);
+        assert_eq!(b.get_varint().unwrap(), 494878333);
+        assert_eq!(b.cap(), 0);
+        assert_eq!(b.off(), 4);
+
+        let mut d = [0x7b, 0xbd];
+        let mut b = OctetsMut::with_slice(&mut d);
+        assert_eq!(b.get_varint().unwrap(), 15293);
+        assert_eq!(b.cap(), 0);
+        assert_eq!(b.off(), 2);
+
+        let mut d = [0x40, 0x25];
+        let mut b = OctetsMut::with_slice(&mut d);
+        assert_eq!(b.get_varint().unwrap(), 37);
+        assert_eq!(b.cap(), 0);
+        assert_eq!(b.off(), 2);
+
+        let mut d = [0x25];
+        let mut b = OctetsMut::with_slice(&mut d);
+        assert_eq!(b.get_varint().unwrap(), 37);
+        assert_eq!(b.cap(), 0);
+        assert_eq!(b.off(), 1);
+    }
+
+    #[test]
+    fn put_varint() {
+        let mut d = [0; 8];
+        {
+            let mut b = OctetsMut::with_slice(&mut d);
+            assert!(b.put_varint(151288809941952652).is_ok());
+            assert_eq!(b.cap(), 0);
+            assert_eq!(b.off(), 8);
+        }
+        let exp = [0xc2, 0x19, 0x7c, 0x5e, 0xff, 0x14, 0xe8, 0x8c];
+        assert_eq!(&d, &exp);
+
+        let mut d = [0; 4];
+        {
+            let mut b = OctetsMut::with_slice(&mut d);
+            assert!(b.put_varint(494878333).is_ok());
+            assert_eq!(b.cap(), 0);
+            assert_eq!(b.off(), 4);
+        }
+        let exp = [0x9d, 0x7f, 0x3e, 0x7d];
+        assert_eq!(&d, &exp);
+
+        let mut d = [0; 2];
+        {
+            let mut b = OctetsMut::with_slice(&mut d);
+            assert!(b.put_varint(15293).is_ok());
+            assert_eq!(b.cap(), 0);
+            assert_eq!(b.off(), 2);
+        }
+        let exp = [0x7b, 0xbd];
+        assert_eq!(&d, &exp);
+
+        let mut d = [0; 1];
+        {
+            let mut b = OctetsMut::with_slice(&mut d);
+            assert!(b.put_varint(37).is_ok());
+            assert_eq!(b.cap(), 0);
+            assert_eq!(b.off(), 1);
+        }
+        let exp = [0x25];
+        assert_eq!(&d, &exp);
+
+        let mut d = [0; 3];
+        {
+            let mut b = OctetsMut::with_slice(&mut d);
+            assert!(b.put_varint(151288809941952652).is_err());
+            assert_eq!(b.cap(), 3);
+            assert_eq!(b.off(), 0);
+        }
+        let exp = [0; 3];
+        assert_eq!(&d, &exp);
+    }
+
+    #[test]
+    #[should_panic]
+    fn varint_too_large() {
+        let mut d = [0; 3];
+        let mut b = OctetsMut::with_slice(&mut d);
+        assert!(b.put_varint(std::u64::MAX).is_err());
+    }
+
+    #[test]
+    fn put_u() {
+        let mut d = [0; 18];
+
+        {
+            let mut b = OctetsMut::with_slice(&mut d);
+            assert_eq!(b.cap(), 18);
+            assert_eq!(b.off(), 0);
+
+            assert!(b.put_u8(1).is_ok());
+            assert_eq!(b.cap(), 17);
+            assert_eq!(b.off(), 1);
+
+            assert!(b.put_u16(0x203).is_ok());
+            assert_eq!(b.cap(), 15);
+            assert_eq!(b.off(), 3);
+
+            assert!(b.put_u24(0x40506).is_ok());
+            assert_eq!(b.cap(), 12);
+            assert_eq!(b.off(), 6);
+
+            assert!(b.put_u32(0x0708090a).is_ok());
+            assert_eq!(b.cap(), 8);
+            assert_eq!(b.off(), 10);
+
+            assert!(b.put_u64(0x0b0c0d0e0f101112).is_ok());
+            assert_eq!(b.cap(), 0);
+            assert_eq!(b.off(), 18);
+
+            assert!(b.put_u8(1).is_err());
+        }
+
+        let exp = [
+            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
+        ];
+        assert_eq!(&d, &exp);
+    }
+
+    #[test]
+    fn put_bytes() {
+        let mut d = [0; 5];
+
+        {
+            let mut b = OctetsMut::with_slice(&mut d);
+            assert_eq!(b.cap(), 5);
+            assert_eq!(b.off(), 0);
+
+            let p = [0x0a, 0x0b, 0x0c, 0x0d, 0x0e];
+            assert!(b.put_bytes(&p).is_ok());
+            assert_eq!(b.cap(), 0);
+            assert_eq!(b.off(), 5);
+
+            assert!(b.put_u8(1).is_err());
+        }
+
+        let exp = [0xa, 0xb, 0xc, 0xd, 0xe];
+        assert_eq!(&d, &exp);
+    }
+
+    #[test]
+    fn split() {
+        let mut d = b"helloworld".to_vec();
+
+        let mut b = OctetsMut::with_slice(&mut d);
+        assert_eq!(b.cap(), 10);
+        assert_eq!(b.off(), 0);
+        assert_eq!(b.as_ref(), b"helloworld");
+
+        assert!(b.get_bytes(5).is_ok());
+        assert_eq!(b.cap(), 5);
+        assert_eq!(b.off(), 5);
+        assert_eq!(b.as_ref(), b"world");
+
+        let off = b.off();
+
+        let (first, last) = b.split_at(off).unwrap();
+        assert_eq!(first.cap(), 5);
+        assert_eq!(first.off(), 0);
+        assert_eq!(first.as_ref(), b"hello");
+
+        assert_eq!(last.cap(), 5);
+        assert_eq!(last.off(), 0);
+        assert_eq!(last.as_ref(), b"world");
+    }
+
+    #[test]
+    fn split_at() {
+        let mut d = b"helloworld".to_vec();
+
+        {
+            let mut b = OctetsMut::with_slice(&mut d);
+            let (first, second) = b.split_at(5).unwrap();
+
+            let mut exp1 = b"hello".to_vec();
+            assert_eq!(first.as_ref(), &mut exp1[..]);
+
+            let mut exp2 = b"world".to_vec();
+            assert_eq!(second.as_ref(), &mut exp2[..]);
+        }
+
+        {
+            let mut b = OctetsMut::with_slice(&mut d);
+            let (first, second) = b.split_at(10).unwrap();
+
+            let mut exp1 = b"helloworld".to_vec();
+            assert_eq!(first.as_ref(), &mut exp1[..]);
+
+            let mut exp2 = b"".to_vec();
+            assert_eq!(second.as_ref(), &mut exp2[..]);
+        }
+
+        {
+            let mut b = OctetsMut::with_slice(&mut d);
+            let (first, second) = b.split_at(9).unwrap();
+
+            let mut exp1 = b"helloworl".to_vec();
+            assert_eq!(first.as_ref(), &mut exp1[..]);
+
+            let mut exp2 = b"d".to_vec();
+            assert_eq!(second.as_ref(), &mut exp2[..]);
+        }
+
+        {
+            let mut b = OctetsMut::with_slice(&mut d);
+            assert!(b.split_at(11).is_err());
+        }
+    }
+
+    #[test]
+    fn slice() {
+        let d = b"helloworld".to_vec();
+
+        {
+            let b = Octets::with_slice(&d);
+            let exp = b"hello".to_vec();
+            assert_eq!(b.slice(5), Ok(&exp[..]));
+        }
+
+        {
+            let b = Octets::with_slice(&d);
+            let exp = b"".to_vec();
+            assert_eq!(b.slice(0), Ok(&exp[..]));
+        }
+
+        {
+            let mut b = Octets::with_slice(&d);
+            b.get_bytes(5).unwrap();
+
+            let exp = b"world".to_vec();
+            assert_eq!(b.slice(5), Ok(&exp[..]));
+        }
+
+        {
+            let b = Octets::with_slice(&d);
+            assert!(b.slice(11).is_err());
+        }
+    }
+
+    #[test]
+    fn slice_mut() {
+        let mut d = b"helloworld".to_vec();
+
+        {
+            let mut b = OctetsMut::with_slice(&mut d);
+            let mut exp = b"hello".to_vec();
+            assert_eq!(b.slice(5), Ok(&mut exp[..]));
+        }
+
+        {
+            let mut b = OctetsMut::with_slice(&mut d);
+            let mut exp = b"".to_vec();
+            assert_eq!(b.slice(0), Ok(&mut exp[..]));
+        }
+
+        {
+            let mut b = OctetsMut::with_slice(&mut d);
+            b.get_bytes(5).unwrap();
+
+            let mut exp = b"world".to_vec();
+            assert_eq!(b.slice(5), Ok(&mut exp[..]));
+        }
+
+        {
+            let mut b = OctetsMut::with_slice(&mut d);
+            assert!(b.slice(11).is_err());
+        }
+    }
+
+    #[test]
+    fn slice_last() {
+        let d = b"helloworld".to_vec();
+
+        {
+            let b = Octets::with_slice(&d);
+            let exp = b"orld".to_vec();
+            assert_eq!(b.slice_last(4), Ok(&exp[..]));
+        }
+
+        {
+            let b = Octets::with_slice(&d);
+            let exp = b"d".to_vec();
+            assert_eq!(b.slice_last(1), Ok(&exp[..]));
+        }
+
+        {
+            let b = Octets::with_slice(&d);
+            let exp = b"".to_vec();
+            assert_eq!(b.slice_last(0), Ok(&exp[..]));
+        }
+
+        {
+            let b = Octets::with_slice(&d);
+            let exp = b"helloworld".to_vec();
+            assert_eq!(b.slice_last(10), Ok(&exp[..]));
+        }
+
+        {
+            let b = Octets::with_slice(&d);
+            assert!(b.slice_last(11).is_err());
+        }
+    }
+
+    #[test]
+    fn slice_last_mut() {
+        let mut d = b"helloworld".to_vec();
+
+        {
+            let mut b = OctetsMut::with_slice(&mut d);
+            let mut exp = b"orld".to_vec();
+            assert_eq!(b.slice_last(4), Ok(&mut exp[..]));
+        }
+
+        {
+            let mut b = OctetsMut::with_slice(&mut d);
+            let mut exp = b"d".to_vec();
+            assert_eq!(b.slice_last(1), Ok(&mut exp[..]));
+        }
+
+        {
+            let mut b = OctetsMut::with_slice(&mut d);
+            let mut exp = b"".to_vec();
+            assert_eq!(b.slice_last(0), Ok(&mut exp[..]));
+        }
+
+        {
+            let mut b = OctetsMut::with_slice(&mut d);
+            let mut exp = b"helloworld".to_vec();
+            assert_eq!(b.slice_last(10), Ok(&mut exp[..]));
+        }
+
+        {
+            let mut b = OctetsMut::with_slice(&mut d);
+            assert!(b.slice_last(11).is_err());
+        }
+    }
+}
diff --git a/src/packet.rs b/src/packet.rs
new file mode 100644
index 0000000..0534df5
--- /dev/null
+++ b/src/packet.rs
@@ -0,0 +1,2204 @@
+// Copyright (C) 2018-2019, Cloudflare, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright notice,
+//       this list of conditions and the following disclaimer.
+//
+//     * Redistributions in binary form must reproduce the above copyright
+//       notice, this list of conditions and the following disclaimer in the
+//       documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// 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::time;
+
+use ring::aead;
+
+use crate::Error;
+use crate::Result;
+
+use crate::crypto;
+use crate::octets;
+use crate::rand;
+use crate::ranges;
+use crate::stream;
+
+const FORM_BIT: u8 = 0x80;
+const FIXED_BIT: u8 = 0x40;
+const KEY_PHASE_BIT: u8 = 0x04;
+
+const TYPE_MASK: u8 = 0x30;
+const PKT_NUM_MASK: u8 = 0x03;
+
+pub const MAX_CID_LEN: u8 = 20;
+
+pub const MAX_PKT_NUM_LEN: usize = 4;
+
+const SAMPLE_LEN: usize = 16;
+
+pub const EPOCH_INITIAL: usize = 0;
+pub const EPOCH_HANDSHAKE: usize = 1;
+pub const EPOCH_APPLICATION: usize = 2;
+pub const EPOCH_COUNT: usize = 3;
+
+/// Packet number space epoch.
+///
+/// This should only ever be one of `EPOCH_INITIAL`, `EPOCH_HANDSHAKE` or
+/// `EPOCH_APPLICATION`, and can be used to index state specific to a packet
+/// number space in `Connection` and `Recovery`.
+pub type Epoch = usize;
+
+/// QUIC packet type.
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum Type {
+    /// Initial packet.
+    Initial,
+
+    /// Retry packet.
+    Retry,
+
+    /// Handshake packet.
+    Handshake,
+
+    /// 0-RTT packet.
+    ZeroRTT,
+
+    /// Version negotiation packet.
+    VersionNegotiation,
+
+    /// 1-RTT short header packet.
+    Short,
+}
+
+impl Type {
+    pub(crate) fn from_epoch(e: Epoch) -> Type {
+        match e {
+            EPOCH_INITIAL => Type::Initial,
+
+            EPOCH_HANDSHAKE => Type::Handshake,
+
+            EPOCH_APPLICATION => Type::Short,
+
+            _ => unreachable!(),
+        }
+    }
+
+    pub(crate) fn to_epoch(self) -> Result<Epoch> {
+        match self {
+            Type::Initial => Ok(EPOCH_INITIAL),
+
+            Type::ZeroRTT => Ok(EPOCH_APPLICATION),
+
+            Type::Handshake => Ok(EPOCH_HANDSHAKE),
+
+            Type::Short => Ok(EPOCH_APPLICATION),
+
+            _ => Err(Error::InvalidPacket),
+        }
+    }
+
+    #[cfg(feature = "qlog")]
+    pub(crate) fn to_qlog(self) -> qlog::PacketType {
+        match self {
+            Type::Initial => qlog::PacketType::Initial,
+
+            Type::Retry => qlog::PacketType::Retry,
+
+            Type::Handshake => qlog::PacketType::Handshake,
+
+            Type::ZeroRTT => qlog::PacketType::ZeroRtt,
+
+            Type::VersionNegotiation => qlog::PacketType::VersionNegotiation,
+
+            Type::Short => qlog::PacketType::OneRtt,
+        }
+    }
+}
+
+/// A QUIC packet's header.
+#[derive(Clone, PartialEq)]
+pub struct Header {
+    /// The type of the packet.
+    pub ty: Type,
+
+    /// The version of the packet.
+    pub version: u32,
+
+    /// The destination connection ID of the packet.
+    pub dcid: Vec<u8>,
+
+    /// The source connection ID of the packet.
+    pub scid: Vec<u8>,
+
+    /// The packet number. It's only meaningful after the header protection is
+    /// removed.
+    pub(crate) pkt_num: u64,
+
+    /// The length of the packet number. It's only meaningful after the header
+    /// protection is removed.
+    pub(crate) pkt_num_len: usize,
+
+    /// The address verification token of the packet. Only present in `Initial`
+    /// and `Retry` packets.
+    pub token: Option<Vec<u8>>,
+
+    /// The list of versions in the packet. Only present in
+    /// `VersionNegotiation` packets.
+    pub versions: Option<Vec<u32>>,
+
+    /// The key phase bit of the packet. It's only meaningful after the header
+    /// protection is removed.
+    pub(crate) key_phase: bool,
+}
+
+impl Header {
+    /// Parses a QUIC packet header from the given buffer.
+    ///
+    /// The `dcid_len` parameter is the length of the destination connection ID,
+    /// required to parse short header packets.
+    ///
+    /// ## Examples:
+    ///
+    /// ```no_run
+    /// # const LOCAL_CONN_ID_LEN: usize = 16;
+    /// # let mut buf = [0; 512];
+    /// # let mut out = [0; 512];
+    /// # let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap();
+    /// let (len, src) = socket.recv_from(&mut buf).unwrap();
+    ///
+    /// let hdr = quiche::Header::from_slice(&mut buf[..len], LOCAL_CONN_ID_LEN)?;
+    /// # Ok::<(), quiche::Error>(())
+    /// ```
+    pub fn from_slice(buf: &mut [u8], dcid_len: usize) -> Result<Header> {
+        let mut b = octets::OctetsMut::with_slice(buf);
+        Header::from_bytes(&mut b, dcid_len)
+    }
+
+    pub(crate) fn from_bytes(
+        b: &mut octets::OctetsMut, dcid_len: usize,
+    ) -> Result<Header> {
+        let first = b.get_u8()?;
+
+        if !Header::is_long(first) {
+            // Decode short header.
+            let dcid = b.get_bytes(dcid_len)?;
+
+            return Ok(Header {
+                ty: Type::Short,
+                version: 0,
+                dcid: dcid.to_vec(),
+                scid: Vec::new(),
+                pkt_num: 0,
+                pkt_num_len: 0,
+                token: None,
+                versions: None,
+                key_phase: false,
+            });
+        }
+
+        // Decode long header.
+        let version = b.get_u32()?;
+
+        let ty = if version == 0 {
+            Type::VersionNegotiation
+        } else {
+            match (first & TYPE_MASK) >> 4 {
+                0x00 => Type::Initial,
+                0x01 => Type::ZeroRTT,
+                0x02 => Type::Handshake,
+                0x03 => Type::Retry,
+                _ => return Err(Error::InvalidPacket),
+            }
+        };
+
+        let dcid_len = b.get_u8()?;
+        if crate::version_is_supported(version) && dcid_len > MAX_CID_LEN {
+            return Err(Error::InvalidPacket);
+        }
+        let dcid = b.get_bytes(dcid_len as usize)?.to_vec();
+
+        let scid_len = b.get_u8()?;
+        if crate::version_is_supported(version) && scid_len > MAX_CID_LEN {
+            return Err(Error::InvalidPacket);
+        }
+        let scid = b.get_bytes(scid_len as usize)?.to_vec();
+
+        // End of invariants.
+
+        let mut token: Option<Vec<u8>> = None;
+        let mut versions: Option<Vec<u32>> = None;
+
+        match ty {
+            Type::Initial => {
+                token = Some(b.get_bytes_with_varint_length()?.to_vec());
+            },
+
+            Type::Retry => {
+                // Exclude the integrity tag from the token.
+                if b.cap() < aead::AES_128_GCM.tag_len() {
+                    return Err(Error::InvalidPacket);
+                }
+
+                let token_len = b.cap() - aead::AES_128_GCM.tag_len();
+                token = Some(b.get_bytes(token_len)?.to_vec());
+            },
+
+            Type::VersionNegotiation => {
+                let mut list: Vec<u32> = Vec::new();
+
+                while b.cap() > 0 {
+                    let version = b.get_u32()?;
+                    list.push(version);
+                }
+
+                versions = Some(list);
+            },
+
+            _ => (),
+        };
+
+        Ok(Header {
+            ty,
+            version,
+            dcid,
+            scid,
+            pkt_num: 0,
+            pkt_num_len: 0,
+            token,
+            versions,
+            key_phase: false,
+        })
+    }
+
+    pub(crate) fn to_bytes(&self, out: &mut octets::OctetsMut) -> Result<()> {
+        let mut first = 0;
+
+        // Encode pkt num length.
+        first |= self.pkt_num_len.saturating_sub(1) as u8;
+
+        // Encode short header.
+        if self.ty == Type::Short {
+            // Unset form bit for short header.
+            first &= !FORM_BIT;
+
+            // Set fixed bit.
+            first |= FIXED_BIT;
+
+            // Set key phase bit.
+            if self.key_phase {
+                first |= KEY_PHASE_BIT;
+            } else {
+                first &= !KEY_PHASE_BIT;
+            }
+
+            out.put_u8(first)?;
+            out.put_bytes(&self.dcid)?;
+
+            return Ok(());
+        }
+
+        // Encode long header.
+        let ty: u8 = match self.ty {
+            Type::Initial => 0x00,
+            Type::ZeroRTT => 0x01,
+            Type::Handshake => 0x02,
+            Type::Retry => 0x03,
+            _ => return Err(Error::InvalidPacket),
+        };
+
+        first |= FORM_BIT | FIXED_BIT | (ty << 4);
+
+        out.put_u8(first)?;
+
+        out.put_u32(self.version)?;
+
+        out.put_u8(self.dcid.len() as u8)?;
+        out.put_bytes(&self.dcid)?;
+
+        out.put_u8(self.scid.len() as u8)?;
+        out.put_bytes(&self.scid)?;
+
+        // Only Initial and Retry packets have a token.
+        match self.ty {
+            Type::Initial => {
+                match self.token {
+                    Some(ref v) => {
+                        out.put_varint(v.len() as u64)?;
+                        out.put_bytes(v)?;
+                    },
+
+                    // No token, so length = 0.
+                    None => {
+                        out.put_varint(0)?;
+                    },
+                }
+            },
+
+            Type::Retry => {
+                // Retry packets don't have a token length.
+                out.put_bytes(self.token.as_ref().unwrap())?;
+            },
+
+            _ => (),
+        }
+
+        Ok(())
+    }
+
+    /// Returns true if the packet has a long header.
+    ///
+    /// The `b` parameter represents the first byte of the QUIC header.
+    fn is_long(b: u8) -> bool {
+        b & FORM_BIT != 0
+    }
+}
+
+impl std::fmt::Debug for Header {
+    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+        write!(f, "{:?}", self.ty)?;
+
+        if self.ty != Type::Short {
+            write!(f, " version={:x}", self.version)?;
+        }
+
+        write!(f, " dcid=")?;
+        for b in &self.dcid {
+            write!(f, "{:02x}", b)?;
+        }
+
+        if self.ty != Type::Short {
+            write!(f, " scid=")?;
+            for b in &self.scid {
+                write!(f, "{:02x}", b)?;
+            }
+        }
+
+        if let Some(ref token) = self.token {
+            write!(f, " token=")?;
+            for b in token {
+                write!(f, "{:02x}", b)?;
+            }
+        }
+
+        if let Some(ref versions) = self.versions {
+            write!(f, " versions={:x?}", versions)?;
+        }
+
+        if self.ty == Type::Short {
+            write!(f, " key_phase={}", self.key_phase)?;
+        }
+
+        Ok(())
+    }
+}
+
+pub fn pkt_num_len(pn: u64) -> Result<usize> {
+    let len = if pn < u64::from(std::u8::MAX) {
+        1
+    } else if pn < u64::from(std::u16::MAX) {
+        2
+    } else if pn < u64::from(std::u32::MAX) {
+        4
+    } else {
+        return Err(Error::InvalidPacket);
+    };
+
+    Ok(len)
+}
+
+pub fn decrypt_hdr(
+    b: &mut octets::OctetsMut, hdr: &mut Header, aead: &crypto::Open,
+) -> Result<()> {
+    let mut first = {
+        let (first_buf, _) = b.split_at(1)?;
+        first_buf.as_ref()[0]
+    };
+
+    let mut pn_and_sample = b.peek_bytes_mut(MAX_PKT_NUM_LEN + SAMPLE_LEN)?;
+
+    let (mut ciphertext, sample) = pn_and_sample.split_at(MAX_PKT_NUM_LEN)?;
+
+    let ciphertext = ciphertext.as_mut();
+
+    let mask = aead.new_mask(sample.as_ref())?;
+
+    if Header::is_long(first) {
+        first ^= mask[0] & 0x0f;
+    } else {
+        first ^= mask[0] & 0x1f;
+    }
+
+    let pn_len = usize::from((first & PKT_NUM_MASK) + 1);
+
+    let ciphertext = &mut ciphertext[..pn_len];
+
+    for i in 0..pn_len {
+        ciphertext[i] ^= mask[i + 1];
+    }
+
+    // Extract packet number corresponding to the decoded length.
+    let pn = match pn_len {
+        1 => u64::from(b.get_u8()?),
+
+        2 => u64::from(b.get_u16()?),
+
+        3 => u64::from(b.get_u24()?),
+
+        4 => u64::from(b.get_u32()?),
+
+        _ => return Err(Error::InvalidPacket),
+    };
+
+    // Write decrypted first byte back into the input buffer.
+    let (mut first_buf, _) = b.split_at(1)?;
+    first_buf.as_mut()[0] = first;
+
+    hdr.pkt_num = pn;
+    hdr.pkt_num_len = pn_len;
+
+    if hdr.ty == Type::Short {
+        hdr.key_phase = (first & KEY_PHASE_BIT) != 0;
+    }
+
+    Ok(())
+}
+
+pub fn decode_pkt_num(largest_pn: u64, truncated_pn: u64, pn_len: usize) -> u64 {
+    let pn_nbits = pn_len * 8;
+    let expected_pn = largest_pn + 1;
+    let pn_win = 1 << pn_nbits;
+    let pn_hwin = pn_win / 2;
+    let pn_mask = pn_win - 1;
+    let candidate_pn = (expected_pn & !pn_mask) | truncated_pn;
+
+    if candidate_pn + pn_hwin <= expected_pn && candidate_pn < (1 << 62) - pn_win
+    {
+        return candidate_pn + pn_win;
+    }
+
+    if candidate_pn > expected_pn + pn_hwin && candidate_pn >= pn_win {
+        return candidate_pn - pn_win;
+    }
+
+    candidate_pn
+}
+
+pub fn decrypt_pkt<'a>(
+    b: &'a mut octets::OctetsMut, pn: u64, pn_len: usize, payload_len: usize,
+    aead: &crypto::Open,
+) -> Result<octets::Octets<'a>> {
+    let payload_offset = b.off();
+
+    let (header, mut payload) = b.split_at(payload_offset)?;
+
+    let payload_len = payload_len
+        .checked_sub(pn_len)
+        .ok_or(Error::InvalidPacket)?;
+
+    let mut ciphertext = payload.peek_bytes_mut(payload_len)?;
+
+    let payload_len =
+        aead.open_with_u64_counter(pn, header.as_ref(), ciphertext.as_mut())?;
+
+    Ok(b.get_bytes(payload_len)?)
+}
+
+pub fn encrypt_hdr(
+    b: &mut octets::OctetsMut, pn_len: usize, payload: &[u8], aead: &crypto::Seal,
+) -> Result<()> {
+    let sample = &payload[4 - pn_len..16 + (4 - pn_len)];
+
+    let mask = aead.new_mask(sample)?;
+
+    let (mut first, mut rest) = b.split_at(1)?;
+
+    let first = first.as_mut();
+
+    if Header::is_long(first[0]) {
+        first[0] ^= mask[0] & 0x0f;
+    } else {
+        first[0] ^= mask[0] & 0x1f;
+    }
+
+    let pn_buf = rest.slice_last(pn_len)?;
+    for i in 0..pn_len {
+        pn_buf[i] ^= mask[i + 1];
+    }
+
+    Ok(())
+}
+
+pub fn encrypt_pkt(
+    b: &mut octets::OctetsMut, pn: u64, pn_len: usize, payload_len: usize,
+    payload_offset: usize, aead: &crypto::Seal,
+) -> Result<usize> {
+    let (mut header, mut payload) = b.split_at(payload_offset)?;
+
+    // Encrypt + authenticate payload.
+    let ciphertext = payload.slice(payload_len)?;
+    aead.seal_with_u64_counter(pn, header.as_ref(), ciphertext)?;
+
+    encrypt_hdr(&mut header, pn_len, ciphertext, aead)?;
+
+    Ok(payload_offset + payload_len)
+}
+
+pub fn encode_pkt_num(pn: u64, b: &mut octets::OctetsMut) -> Result<()> {
+    let len = pkt_num_len(pn)?;
+
+    match len {
+        1 => b.put_u8(pn as u8)?,
+
+        2 => b.put_u16(pn as u16)?,
+
+        3 => b.put_u24(pn as u32)?,
+
+        4 => b.put_u32(pn as u32)?,
+
+        _ => return Err(Error::InvalidPacket),
+    };
+
+    Ok(())
+}
+
+pub fn negotiate_version(
+    scid: &[u8], dcid: &[u8], out: &mut [u8],
+) -> Result<usize> {
+    let mut b = octets::OctetsMut::with_slice(out);
+
+    let first = rand::rand_u8() | FORM_BIT;
+
+    b.put_u8(first)?;
+    b.put_u32(0)?;
+
+    b.put_u8(scid.len() as u8)?;
+    b.put_bytes(&scid)?;
+    b.put_u8(dcid.len() as u8)?;
+    b.put_bytes(&dcid)?;
+    b.put_u32(crate::PROTOCOL_VERSION_DRAFT29)?;
+    b.put_u32(crate::PROTOCOL_VERSION_DRAFT28)?;
+    b.put_u32(crate::PROTOCOL_VERSION_DRAFT27)?;
+
+    Ok(b.off())
+}
+
+pub fn retry(
+    scid: &[u8], dcid: &[u8], new_scid: &[u8], token: &[u8], version: u32,
+    out: &mut [u8],
+) -> Result<usize> {
+    let mut b = octets::OctetsMut::with_slice(out);
+
+    if !crate::version_is_supported(version) {
+        return Err(Error::UnknownVersion);
+    }
+
+    let hdr = Header {
+        ty: Type::Retry,
+        version,
+        dcid: scid.to_vec(),
+        scid: new_scid.to_vec(),
+        pkt_num: 0,
+        pkt_num_len: 0,
+        token: Some(token.to_vec()),
+        versions: None,
+        key_phase: false,
+    };
+
+    hdr.to_bytes(&mut b)?;
+
+    let tag = compute_retry_integrity_tag(&b, dcid, version)?;
+
+    b.put_bytes(tag.as_ref())?;
+
+    Ok(b.off())
+}
+
+pub fn verify_retry_integrity(
+    b: &octets::OctetsMut, odcid: &[u8], version: u32,
+) -> Result<()> {
+    let tag = compute_retry_integrity_tag(b, odcid, version)?;
+
+    ring::constant_time::verify_slices_are_equal(
+        &b.as_ref()[..aead::AES_128_GCM.tag_len()],
+        tag.as_ref(),
+    )
+    .map_err(|_| Error::CryptoFail)?;
+
+    Ok(())
+}
+
+fn compute_retry_integrity_tag(
+    b: &octets::OctetsMut, odcid: &[u8], version: u32,
+) -> Result<aead::Tag> {
+    const RETRY_INTEGRITY_KEY: [u8; 16] = [
+        0xcc, 0xce, 0x18, 0x7e, 0xd0, 0x9a, 0x09, 0xd0, 0x57, 0x28, 0x15, 0x5a,
+        0x6c, 0xb9, 0x6b, 0xe1,
+    ];
+
+    const RETRY_INTEGRITY_NONCE: [u8; aead::NONCE_LEN] = [
+        0xe5, 0x49, 0x30, 0xf9, 0x7f, 0x21, 0x36, 0xf0, 0x53, 0x0a, 0x8c, 0x1c,
+    ];
+
+    const RETRY_INTEGRITY_KEY_OLD: [u8; 16] = [
+        0x4d, 0x32, 0xec, 0xdb, 0x2a, 0x21, 0x33, 0xc8, 0x41, 0xe4, 0x04, 0x3d,
+        0xf2, 0x7d, 0x44, 0x30,
+    ];
+
+    const RETRY_INTEGRITY_NONCE_OLD: [u8; aead::NONCE_LEN] = [
+        0x4d, 0x16, 0x11, 0xd0, 0x55, 0x13, 0xa5, 0x52, 0xc5, 0x87, 0xd5, 0x75,
+    ];
+
+    let (key, nonce) = match version {
+        crate::PROTOCOL_VERSION_DRAFT27 | crate::PROTOCOL_VERSION_DRAFT28 =>
+            (&RETRY_INTEGRITY_KEY_OLD, RETRY_INTEGRITY_NONCE_OLD),
+
+        _ => (&RETRY_INTEGRITY_KEY, RETRY_INTEGRITY_NONCE),
+    };
+
+    let hdr_len = b.off();
+
+    let mut pseudo = vec![0; 1 + odcid.len() + hdr_len];
+
+    let mut pb = octets::OctetsMut::with_slice(&mut pseudo);
+
+    pb.put_u8(odcid.len() as u8)?;
+    pb.put_bytes(odcid)?;
+    pb.put_bytes(&b.buf()[..hdr_len])?;
+
+    let key = aead::LessSafeKey::new(
+        aead::UnboundKey::new(&aead::AES_128_GCM, key)
+            .map_err(|_| Error::CryptoFail)?,
+    );
+
+    let nonce = aead::Nonce::assume_unique_for_key(nonce);
+
+    let aad = aead::Aad::from(&pseudo);
+
+    key.seal_in_place_separate_tag(nonce, aad, &mut [])
+        .map_err(|_| Error::CryptoFail)
+}
+
+pub struct PktNumSpace {
+    pub largest_rx_pkt_num: u64,
+
+    pub largest_rx_pkt_time: time::Instant,
+
+    pub next_pkt_num: u64,
+
+    pub recv_pkt_need_ack: ranges::RangeSet,
+
+    pub recv_pkt_num: PktNumWindow,
+
+    pub ack_elicited: bool,
+
+    pub crypto_open: Option<crypto::Open>,
+    pub crypto_seal: Option<crypto::Seal>,
+
+    pub crypto_0rtt_open: Option<crypto::Open>,
+    pub crypto_0rtt_seal: Option<crypto::Seal>,
+
+    pub crypto_stream: stream::Stream,
+}
+
+impl PktNumSpace {
+    pub fn new() -> PktNumSpace {
+        PktNumSpace {
+            largest_rx_pkt_num: 0,
+
+            largest_rx_pkt_time: time::Instant::now(),
+
+            next_pkt_num: 0,
+
+            recv_pkt_need_ack: ranges::RangeSet::new(crate::MAX_ACK_RANGES),
+
+            recv_pkt_num: PktNumWindow::default(),
+
+            ack_elicited: false,
+
+            crypto_open: None,
+            crypto_seal: None,
+
+            crypto_0rtt_open: None,
+            crypto_0rtt_seal: None,
+
+            crypto_stream: stream::Stream::new(
+                std::u64::MAX,
+                std::u64::MAX,
+                true,
+                true,
+            ),
+        }
+    }
+
+    pub fn clear(&mut self) {
+        self.crypto_stream =
+            stream::Stream::new(std::u64::MAX, std::u64::MAX, true, true);
+
+        self.ack_elicited = false;
+    }
+
+    pub fn crypto_overhead(&self) -> Option<usize> {
+        Some(self.crypto_seal.as_ref()?.alg().tag_len())
+    }
+
+    pub fn ready(&self) -> bool {
+        self.crypto_stream.is_flushable() || self.ack_elicited
+    }
+
+    pub fn has_keys(&self) -> bool {
+        self.crypto_open.is_some() && self.crypto_seal.is_some()
+    }
+}
+
+#[derive(Clone, Copy, Default)]
+pub struct PktNumWindow {
+    lower: u64,
+    window: u128,
+}
+
+impl PktNumWindow {
+    pub fn insert(&mut self, seq: u64) {
+        // Packet is on the left end of the window.
+        if seq < self.lower {
+            return;
+        }
+
+        // Packet is on the right end of the window.
+        if seq > self.upper() {
+            let diff = seq - self.upper();
+            self.lower += diff;
+
+            self.window = self.window.checked_shl(diff as u32).unwrap_or(0);
+        }
+
+        let mask = 1_u128 << (self.upper() - seq);
+        self.window |= mask;
+    }
+
+    pub fn contains(&mut self, seq: u64) -> bool {
+        // Packet is on the right end of the window.
+        if seq > self.upper() {
+            return false;
+        }
+
+        // Packet is on the left end of the window.
+        if seq < self.lower {
+            return true;
+        }
+
+        let mask = 1_u128 << (self.upper() - seq);
+        self.window & mask != 0
+    }
+
+    fn upper(&self) -> u64 {
+        self.lower
+            .saturating_add(std::mem::size_of::<u128>() as u64 * 8) -
+            1
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    use crate::crypto;
+    use crate::octets;
+
+    #[test]
+    fn retry() {
+        let hdr = Header {
+            ty: Type::Retry,
+            version: 0xafafafaf,
+            dcid: vec![0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba],
+            scid: vec![0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb],
+            pkt_num: 0,
+            pkt_num_len: 0,
+            token: Some(vec![0xba; 24]),
+            versions: None,
+            key_phase: false,
+        };
+
+        let mut d = [0; 63];
+
+        let mut b = octets::OctetsMut::with_slice(&mut d);
+        assert!(hdr.to_bytes(&mut b).is_ok());
+
+        // Add fake retry integrity token.
+        b.put_bytes(&vec![0xba; 16]).unwrap();
+
+        let mut b = octets::OctetsMut::with_slice(&mut d);
+        assert_eq!(Header::from_bytes(&mut b, 9).unwrap(), hdr);
+    }
+
+    #[test]
+    fn initial() {
+        let hdr = Header {
+            ty: Type::Initial,
+            version: 0xafafafaf,
+            dcid: vec![0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba],
+            scid: vec![0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb],
+            pkt_num: 0,
+            pkt_num_len: 0,
+            token: Some(vec![0x05, 0x06, 0x07, 0x08]),
+            versions: None,
+            key_phase: false,
+        };
+
+        let mut d = [0; 50];
+
+        let mut b = octets::OctetsMut::with_slice(&mut d);
+        assert!(hdr.to_bytes(&mut b).is_ok());
+
+        let mut b = octets::OctetsMut::with_slice(&mut d);
+        assert_eq!(Header::from_bytes(&mut b, 9).unwrap(), hdr);
+    }
+
+    #[test]
+    fn initial_v1_dcid_too_long() {
+        let hdr = Header {
+            ty: Type::Initial,
+            version: crate::PROTOCOL_VERSION,
+            dcid: vec![
+                0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+                0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+            ],
+            scid: vec![0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb],
+            pkt_num: 0,
+            pkt_num_len: 0,
+            token: Some(vec![0x05, 0x06, 0x07, 0x08]),
+            versions: None,
+            key_phase: false,
+        };
+
+        let mut d = [0; 50];
+
+        let mut b = octets::OctetsMut::with_slice(&mut d);
+        assert!(hdr.to_bytes(&mut b).is_ok());
+
+        let mut b = octets::OctetsMut::with_slice(&mut d);
+        assert_eq!(Header::from_bytes(&mut b, 21), Err(Error::InvalidPacket));
+    }
+
+    #[test]
+    fn initial_v1_scid_too_long() {
+        let hdr = Header {
+            ty: Type::Initial,
+            version: crate::PROTOCOL_VERSION,
+            dcid: vec![0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba],
+            scid: vec![
+                0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+                0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+            ],
+            pkt_num: 0,
+            pkt_num_len: 0,
+            token: Some(vec![0x05, 0x06, 0x07, 0x08]),
+            versions: None,
+            key_phase: false,
+        };
+
+        let mut d = [0; 50];
+
+        let mut b = octets::OctetsMut::with_slice(&mut d);
+        assert!(hdr.to_bytes(&mut b).is_ok());
+
+        let mut b = octets::OctetsMut::with_slice(&mut d);
+        assert_eq!(Header::from_bytes(&mut b, 9), Err(Error::InvalidPacket));
+    }
+
+    #[test]
+    fn initial_non_v1_scid_long() {
+        let hdr = Header {
+            ty: Type::Initial,
+            version: 0xafafafaf,
+            dcid: vec![0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba],
+            scid: vec![
+                0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+                0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+            ],
+            pkt_num: 0,
+            pkt_num_len: 0,
+            token: Some(vec![0x05, 0x06, 0x07, 0x08]),
+            versions: None,
+            key_phase: false,
+        };
+
+        let mut d = [0; 50];
+
+        let mut b = octets::OctetsMut::with_slice(&mut d);
+        assert!(hdr.to_bytes(&mut b).is_ok());
+
+        let mut b = octets::OctetsMut::with_slice(&mut d);
+        assert_eq!(Header::from_bytes(&mut b, 9).unwrap(), hdr);
+    }
+
+    #[test]
+    fn handshake() {
+        let hdr = Header {
+            ty: Type::Handshake,
+            version: 0xafafafaf,
+            dcid: vec![0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba],
+            scid: vec![0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb],
+            pkt_num: 0,
+            pkt_num_len: 0,
+            token: None,
+            versions: None,
+            key_phase: false,
+        };
+
+        let mut d = [0; 50];
+
+        let mut b = octets::OctetsMut::with_slice(&mut d);
+        assert!(hdr.to_bytes(&mut b).is_ok());
+
+        let mut b = octets::OctetsMut::with_slice(&mut d);
+        assert_eq!(Header::from_bytes(&mut b, 9).unwrap(), hdr);
+    }
+
+    #[test]
+    fn application() {
+        let hdr = Header {
+            ty: Type::Short,
+            version: 0,
+            dcid: vec![0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba],
+            scid: vec![],
+            pkt_num: 0,
+            pkt_num_len: 0,
+            token: None,
+            versions: None,
+            key_phase: false,
+        };
+
+        let mut d = [0; 50];
+
+        let mut b = octets::OctetsMut::with_slice(&mut d);
+        assert!(hdr.to_bytes(&mut b).is_ok());
+
+        let mut b = octets::OctetsMut::with_slice(&mut d);
+        assert_eq!(Header::from_bytes(&mut b, 9).unwrap(), hdr);
+    }
+
+    #[test]
+    fn pkt_num_decode() {
+        let pn = decode_pkt_num(0xa82f30ea, 0x9b32, 2);
+        assert_eq!(pn, 0xa82f9b32);
+    }
+
+    #[test]
+    fn pkt_num_window() {
+        let mut win = PktNumWindow::default();
+        assert_eq!(win.lower, 0);
+        assert!(!win.contains(0));
+        assert!(!win.contains(1));
+
+        win.insert(0);
+        assert_eq!(win.lower, 0);
+        assert!(win.contains(0));
+        assert!(!win.contains(1));
+
+        win.insert(1);
+        assert_eq!(win.lower, 0);
+        assert!(win.contains(0));
+        assert!(win.contains(1));
+
+        win.insert(3);
+        assert_eq!(win.lower, 0);
+        assert!(win.contains(0));
+        assert!(win.contains(1));
+        assert!(!win.contains(2));
+        assert!(win.contains(3));
+
+        win.insert(10);
+        assert_eq!(win.lower, 0);
+        assert!(win.contains(0));
+        assert!(win.contains(1));
+        assert!(!win.contains(2));
+        assert!(win.contains(3));
+        assert!(!win.contains(4));
+        assert!(!win.contains(5));
+        assert!(!win.contains(6));
+        assert!(!win.contains(7));
+        assert!(!win.contains(8));
+        assert!(!win.contains(9));
+        assert!(win.contains(10));
+
+        win.insert(132);
+        assert_eq!(win.lower, 5);
+        assert!(win.contains(0));
+        assert!(win.contains(1));
+        assert!(win.contains(2));
+        assert!(win.contains(3));
+        assert!(win.contains(4));
+        assert!(!win.contains(5));
+        assert!(!win.contains(6));
+        assert!(!win.contains(7));
+        assert!(!win.contains(8));
+        assert!(!win.contains(9));
+        assert!(win.contains(10));
+        assert!(!win.contains(128));
+        assert!(!win.contains(130));
+        assert!(!win.contains(131));
+        assert!(win.contains(132));
+
+        win.insert(1024);
+        assert_eq!(win.lower, 897);
+        assert!(win.contains(0));
+        assert!(win.contains(1));
+        assert!(win.contains(2));
+        assert!(win.contains(3));
+        assert!(win.contains(4));
+        assert!(win.contains(5));
+        assert!(win.contains(6));
+        assert!(win.contains(7));
+        assert!(win.contains(8));
+        assert!(win.contains(9));
+        assert!(win.contains(10));
+        assert!(win.contains(128));
+        assert!(win.contains(130));
+        assert!(win.contains(132));
+        assert!(win.contains(896));
+        assert!(!win.contains(897));
+        assert!(!win.contains(1022));
+        assert!(!win.contains(1023));
+        assert!(win.contains(1024));
+        assert!(!win.contains(1025));
+        assert!(!win.contains(1026));
+
+        win.insert(std::u64::MAX - 1);
+        assert!(win.contains(0));
+        assert!(win.contains(1));
+        assert!(win.contains(2));
+        assert!(win.contains(3));
+        assert!(win.contains(4));
+        assert!(win.contains(5));
+        assert!(win.contains(6));
+        assert!(win.contains(7));
+        assert!(win.contains(8));
+        assert!(win.contains(9));
+        assert!(win.contains(10));
+        assert!(win.contains(128));
+        assert!(win.contains(130));
+        assert!(win.contains(132));
+        assert!(win.contains(896));
+        assert!(win.contains(897));
+        assert!(win.contains(1022));
+        assert!(win.contains(1023));
+        assert!(win.contains(1024));
+        assert!(win.contains(1025));
+        assert!(win.contains(1026));
+        assert!(!win.contains(std::u64::MAX - 2));
+        assert!(win.contains(std::u64::MAX - 1));
+    }
+
+    fn assert_decrypt_initial_pkt(
+        pkt: &mut [u8], dcid: &[u8], is_server: bool, expected_frames: &[u8],
+        expected_pn: u64, expected_pn_len: usize,
+    ) {
+        let mut b = octets::OctetsMut::with_slice(pkt);
+
+        let mut hdr = Header::from_bytes(&mut b, 0).unwrap();
+        assert_eq!(hdr.ty, Type::Initial);
+
+        let payload_len = b.get_varint().unwrap() as usize;
+
+        let (aead, _) =
+            crypto::derive_initial_key_material(dcid, hdr.version, is_server)
+                .unwrap();
+
+        decrypt_hdr(&mut b, &mut hdr, &aead).unwrap();
+        assert_eq!(hdr.pkt_num_len, expected_pn_len);
+
+        let pn = decode_pkt_num(0, hdr.pkt_num, hdr.pkt_num_len);
+        assert_eq!(pn, expected_pn);
+
+        let payload =
+            decrypt_pkt(&mut b, pn, hdr.pkt_num_len, payload_len, &aead).unwrap();
+
+        let payload = payload.as_ref();
+        assert_eq!(&payload[..expected_frames.len()], expected_frames);
+    }
+
+    #[test]
+    fn decrypt_client_initial() {
+        let mut pkt = [
+            0xc5, 0xff, 0x00, 0x00, 0x1d, 0x08, 0x83, 0x94, 0xc8, 0xf0, 0x3e,
+            0x51, 0x57, 0x08, 0x00, 0x00, 0x44, 0x9e, 0x4a, 0x95, 0x24, 0x5b,
+            0xfb, 0x66, 0xbc, 0x5f, 0x93, 0x03, 0x2b, 0x7d, 0xdd, 0x89, 0xfe,
+            0x0f, 0xf1, 0x5d, 0x9c, 0x4f, 0x70, 0x50, 0xfc, 0xcd, 0xb7, 0x1c,
+            0x1c, 0xd8, 0x05, 0x12, 0xd4, 0x43, 0x16, 0x43, 0xa5, 0x3a, 0xaf,
+            0xa1, 0xb0, 0xb5, 0x18, 0xb4, 0x49, 0x68, 0xb1, 0x8b, 0x8d, 0x3e,
+            0x7a, 0x4d, 0x04, 0xc3, 0x0b, 0x3e, 0xd9, 0x41, 0x03, 0x25, 0xb2,
+            0xab, 0xb2, 0xda, 0xfb, 0x1c, 0x12, 0xf8, 0xb7, 0x04, 0x79, 0xeb,
+            0x8d, 0xf9, 0x8a, 0xbc, 0xaf, 0x95, 0xdd, 0x8f, 0x3d, 0x1c, 0x78,
+            0x66, 0x0f, 0xbc, 0x71, 0x9f, 0x88, 0xb2, 0x3c, 0x8a, 0xef, 0x67,
+            0x71, 0xf3, 0xd5, 0x0e, 0x10, 0xfd, 0xfb, 0x4c, 0x9d, 0x92, 0x38,
+            0x6d, 0x44, 0x48, 0x1b, 0x6c, 0x52, 0xd5, 0x9e, 0x55, 0x38, 0xd3,
+            0xd3, 0x94, 0x2d, 0xe9, 0xf1, 0x3a, 0x7f, 0x8b, 0x70, 0x2d, 0xc3,
+            0x17, 0x24, 0x18, 0x0d, 0xa9, 0xdf, 0x22, 0x71, 0x4d, 0x01, 0x00,
+            0x3f, 0xc5, 0xe3, 0xd1, 0x65, 0xc9, 0x50, 0xe6, 0x30, 0xb8, 0x54,
+            0x0f, 0xbd, 0x81, 0xc9, 0xdf, 0x0e, 0xe6, 0x3f, 0x94, 0x99, 0x70,
+            0x26, 0xc4, 0xf2, 0xe1, 0x88, 0x7a, 0x2d, 0xef, 0x79, 0x05, 0x0a,
+            0xc2, 0xd8, 0x6b, 0xa3, 0x18, 0xe0, 0xb3, 0xad, 0xc4, 0xc5, 0xaa,
+            0x18, 0xbc, 0xf6, 0x3c, 0x7c, 0xf8, 0xe8, 0x5f, 0x56, 0x92, 0x49,
+            0x81, 0x3a, 0x22, 0x36, 0xa7, 0xe7, 0x22, 0x69, 0x44, 0x7c, 0xd1,
+            0xc7, 0x55, 0xe4, 0x51, 0xf5, 0xe7, 0x74, 0x70, 0xeb, 0x3d, 0xe6,
+            0x4c, 0x88, 0x49, 0xd2, 0x92, 0x82, 0x06, 0x98, 0x02, 0x9c, 0xfa,
+            0x18, 0xe5, 0xd6, 0x61, 0x76, 0xfe, 0x6e, 0x5b, 0xa4, 0xed, 0x18,
+            0x02, 0x6f, 0x90, 0x90, 0x0a, 0x5b, 0x49, 0x80, 0xe2, 0xf5, 0x8e,
+            0x39, 0x15, 0x1d, 0x5c, 0xd6, 0x85, 0xb1, 0x09, 0x29, 0x63, 0x6d,
+            0x4f, 0x02, 0xe7, 0xfa, 0xd2, 0xa5, 0xa4, 0x58, 0x24, 0x9f, 0x5c,
+            0x02, 0x98, 0xa6, 0xd5, 0x3a, 0xcb, 0xe4, 0x1a, 0x7f, 0xc8, 0x3f,
+            0xa7, 0xcc, 0x01, 0x97, 0x3f, 0x7a, 0x74, 0xd1, 0x23, 0x7a, 0x51,
+            0x97, 0x4e, 0x09, 0x76, 0x36, 0xb6, 0x20, 0x39, 0x97, 0xf9, 0x21,
+            0xd0, 0x7b, 0xc1, 0x94, 0x0a, 0x6f, 0x2d, 0x0d, 0xe9, 0xf5, 0xa1,
+            0x14, 0x32, 0x94, 0x61, 0x59, 0xed, 0x6c, 0xc2, 0x1d, 0xf6, 0x5c,
+            0x4d, 0xdd, 0x11, 0x15, 0xf8, 0x64, 0x27, 0x25, 0x9a, 0x19, 0x6c,
+            0x71, 0x48, 0xb2, 0x5b, 0x64, 0x78, 0xb0, 0xdc, 0x77, 0x66, 0xe1,
+            0xc4, 0xd1, 0xb1, 0xf5, 0x15, 0x9f, 0x90, 0xea, 0xbc, 0x61, 0x63,
+            0x62, 0x26, 0x24, 0x46, 0x42, 0xee, 0x14, 0x8b, 0x46, 0x4c, 0x9e,
+            0x61, 0x9e, 0xe5, 0x0a, 0x5e, 0x3d, 0xdc, 0x83, 0x62, 0x27, 0xca,
+            0xd9, 0x38, 0x98, 0x7c, 0x4e, 0xa3, 0xc1, 0xfa, 0x7c, 0x75, 0xbb,
+            0xf8, 0x8d, 0x89, 0xe9, 0xad, 0xa6, 0x42, 0xb2, 0xb8, 0x8f, 0xe8,
+            0x10, 0x7b, 0x7e, 0xa3, 0x75, 0xb1, 0xb6, 0x48, 0x89, 0xa4, 0xe9,
+            0xe5, 0xc3, 0x8a, 0x1c, 0x89, 0x6c, 0xe2, 0x75, 0xa5, 0x65, 0x8d,
+            0x25, 0x0e, 0x2d, 0x76, 0xe1, 0xed, 0x3a, 0x34, 0xce, 0x7e, 0x3a,
+            0x3f, 0x38, 0x3d, 0x0c, 0x99, 0x6d, 0x0b, 0xed, 0x10, 0x6c, 0x28,
+            0x99, 0xca, 0x6f, 0xc2, 0x63, 0xef, 0x04, 0x55, 0xe7, 0x4b, 0xb6,
+            0xac, 0x16, 0x40, 0xea, 0x7b, 0xfe, 0xdc, 0x59, 0xf0, 0x3f, 0xee,
+            0x0e, 0x17, 0x25, 0xea, 0x15, 0x0f, 0xf4, 0xd6, 0x9a, 0x76, 0x60,
+            0xc5, 0x54, 0x21, 0x19, 0xc7, 0x1d, 0xe2, 0x70, 0xae, 0x7c, 0x3e,
+            0xcf, 0xd1, 0xaf, 0x2c, 0x4c, 0xe5, 0x51, 0x98, 0x69, 0x49, 0xcc,
+            0x34, 0xa6, 0x6b, 0x3e, 0x21, 0x6b, 0xfe, 0x18, 0xb3, 0x47, 0xe6,
+            0xc0, 0x5f, 0xd0, 0x50, 0xf8, 0x59, 0x12, 0xdb, 0x30, 0x3a, 0x8f,
+            0x05, 0x4e, 0xc2, 0x3e, 0x38, 0xf4, 0x4d, 0x1c, 0x72, 0x5a, 0xb6,
+            0x41, 0xae, 0x92, 0x9f, 0xec, 0xc8, 0xe3, 0xce, 0xfa, 0x56, 0x19,
+            0xdf, 0x42, 0x31, 0xf5, 0xb4, 0xc0, 0x09, 0xfa, 0x0c, 0x0b, 0xbc,
+            0x60, 0xbc, 0x75, 0xf7, 0x6d, 0x06, 0xef, 0x15, 0x4f, 0xc8, 0x57,
+            0x70, 0x77, 0xd9, 0xd6, 0xa1, 0xd2, 0xbd, 0x9b, 0xf0, 0x81, 0xdc,
+            0x78, 0x3e, 0xce, 0x60, 0x11, 0x1b, 0xea, 0x7d, 0xa9, 0xe5, 0xa9,
+            0x74, 0x80, 0x69, 0xd0, 0x78, 0xb2, 0xbe, 0xf4, 0x8d, 0xe0, 0x4c,
+            0xab, 0xe3, 0x75, 0x5b, 0x19, 0x7d, 0x52, 0xb3, 0x20, 0x46, 0x94,
+            0x9e, 0xca, 0xa3, 0x10, 0x27, 0x4b, 0x4a, 0xac, 0x0d, 0x00, 0x8b,
+            0x19, 0x48, 0xc1, 0x08, 0x2c, 0xdf, 0xe2, 0x08, 0x3e, 0x38, 0x6d,
+            0x4f, 0xd8, 0x4c, 0x0e, 0xd0, 0x66, 0x6d, 0x3e, 0xe2, 0x6c, 0x45,
+            0x15, 0xc4, 0xfe, 0xe7, 0x34, 0x33, 0xac, 0x70, 0x3b, 0x69, 0x0a,
+            0x9f, 0x7b, 0xf2, 0x78, 0xa7, 0x74, 0x86, 0xac, 0xe4, 0x4c, 0x48,
+            0x9a, 0x0c, 0x7a, 0xc8, 0xdf, 0xe4, 0xd1, 0xa5, 0x8f, 0xb3, 0xa7,
+            0x30, 0xb9, 0x93, 0xff, 0x0f, 0x0d, 0x61, 0xb4, 0xd8, 0x95, 0x57,
+            0x83, 0x1e, 0xb4, 0xc7, 0x52, 0xff, 0xd3, 0x9c, 0x10, 0xf6, 0xb9,
+            0xf4, 0x6d, 0x8d, 0xb2, 0x78, 0xda, 0x62, 0x4f, 0xd8, 0x00, 0xe4,
+            0xaf, 0x85, 0x54, 0x8a, 0x29, 0x4c, 0x15, 0x18, 0x89, 0x3a, 0x87,
+            0x78, 0xc4, 0xf6, 0xd6, 0xd7, 0x3c, 0x93, 0xdf, 0x20, 0x09, 0x60,
+            0x10, 0x4e, 0x06, 0x2b, 0x38, 0x8e, 0xa9, 0x7d, 0xcf, 0x40, 0x16,
+            0xbc, 0xed, 0x7f, 0x62, 0xb4, 0xf0, 0x62, 0xcb, 0x6c, 0x04, 0xc2,
+            0x06, 0x93, 0xd9, 0xa0, 0xe3, 0xb7, 0x4b, 0xa8, 0xfe, 0x74, 0xcc,
+            0x01, 0x23, 0x78, 0x84, 0xf4, 0x0d, 0x76, 0x5a, 0xe5, 0x6a, 0x51,
+            0x68, 0x8d, 0x98, 0x5c, 0xf0, 0xce, 0xae, 0xf4, 0x30, 0x45, 0xed,
+            0x8c, 0x3f, 0x0c, 0x33, 0xbc, 0xed, 0x08, 0x53, 0x7f, 0x68, 0x82,
+            0x61, 0x3a, 0xcd, 0x3b, 0x08, 0xd6, 0x65, 0xfc, 0xe9, 0xdd, 0x8a,
+            0xa7, 0x31, 0x71, 0xe2, 0xd3, 0x77, 0x1a, 0x61, 0xdb, 0xa2, 0x79,
+            0x0e, 0x49, 0x1d, 0x41, 0x3d, 0x93, 0xd9, 0x87, 0xe2, 0x74, 0x5a,
+            0xf2, 0x94, 0x18, 0xe4, 0x28, 0xbe, 0x34, 0x94, 0x14, 0x85, 0xc9,
+            0x34, 0x47, 0x52, 0x0f, 0xfe, 0x23, 0x1d, 0xa2, 0x30, 0x4d, 0x6a,
+            0x0f, 0xd5, 0xd0, 0x7d, 0x08, 0x37, 0x22, 0x02, 0x36, 0x96, 0x61,
+            0x59, 0xbe, 0xf3, 0xcf, 0x90, 0x4d, 0x72, 0x23, 0x24, 0xdd, 0x85,
+            0x25, 0x13, 0xdf, 0x39, 0xae, 0x03, 0x0d, 0x81, 0x73, 0x90, 0x8d,
+            0xa6, 0x36, 0x47, 0x86, 0xd3, 0xc1, 0xbf, 0xcb, 0x19, 0xea, 0x77,
+            0xa6, 0x3b, 0x25, 0xf1, 0xe7, 0xfc, 0x66, 0x1d, 0xef, 0x48, 0x0c,
+            0x5d, 0x00, 0xd4, 0x44, 0x56, 0x26, 0x9e, 0xbd, 0x84, 0xef, 0xd8,
+            0xe3, 0xa8, 0xb2, 0xc2, 0x57, 0xee, 0xc7, 0x60, 0x60, 0x68, 0x28,
+            0x48, 0xcb, 0xf5, 0x19, 0x4b, 0xc9, 0x9e, 0x49, 0xee, 0x75, 0xe4,
+            0xd0, 0xd2, 0x54, 0xba, 0xd4, 0xbf, 0xd7, 0x49, 0x70, 0xc3, 0x0e,
+            0x44, 0xb6, 0x55, 0x11, 0xd4, 0xad, 0x0e, 0x6e, 0xc7, 0x39, 0x8e,
+            0x08, 0xe0, 0x13, 0x07, 0xee, 0xee, 0xa1, 0x4e, 0x46, 0xcc, 0xd8,
+            0x7c, 0xf3, 0x6b, 0x28, 0x52, 0x21, 0x25, 0x4d, 0x8f, 0xc6, 0xa6,
+            0x76, 0x5c, 0x52, 0x4d, 0xed, 0x00, 0x85, 0xdc, 0xa5, 0xbd, 0x68,
+            0x8d, 0xdf, 0x72, 0x2e, 0x2c, 0x0f, 0xaf, 0x9d, 0x0f, 0xb2, 0xce,
+            0x7a, 0x0c, 0x3f, 0x2c, 0xee, 0x19, 0xca, 0x0f, 0xfb, 0xa4, 0x61,
+            0xca, 0x8d, 0xc5, 0xd2, 0xc8, 0x17, 0x8b, 0x07, 0x62, 0xcf, 0x67,
+            0x13, 0x55, 0x58, 0x49, 0x4d, 0x2a, 0x96, 0xf1, 0xa1, 0x39, 0xf0,
+            0xed, 0xb4, 0x2d, 0x2a, 0xf8, 0x9a, 0x9c, 0x91, 0x22, 0xb0, 0x7a,
+            0xcb, 0xc2, 0x9e, 0x5e, 0x72, 0x2d, 0xf8, 0x61, 0x5c, 0x34, 0x37,
+            0x02, 0x49, 0x10, 0x98, 0x47, 0x8a, 0x38, 0x9c, 0x98, 0x72, 0xa1,
+            0x0b, 0x0c, 0x98, 0x75, 0x12, 0x5e, 0x25, 0x7c, 0x7b, 0xfd, 0xf2,
+            0x7e, 0xef, 0x40, 0x60, 0xbd, 0x3d, 0x00, 0xf4, 0xc1, 0x4f, 0xd3,
+            0xe3, 0x49, 0x6c, 0x38, 0xd3, 0xc5, 0xd1, 0xa5, 0x66, 0x8c, 0x39,
+            0x35, 0x0e, 0xff, 0xbc, 0x2d, 0x16, 0xca, 0x17, 0xbe, 0x4c, 0xe2,
+            0x9f, 0x02, 0xed, 0x96, 0x95, 0x04, 0xdd, 0xa2, 0xa8, 0xc6, 0xb9,
+            0xff, 0x91, 0x9e, 0x69, 0x3e, 0xe7, 0x9e, 0x09, 0x08, 0x93, 0x16,
+            0xe7, 0xd1, 0xd8, 0x9e, 0xc0, 0x99, 0xdb, 0x3b, 0x2b, 0x26, 0x87,
+            0x25, 0xd8, 0x88, 0x53, 0x6a, 0x4b, 0x8b, 0xf9, 0xae, 0xe8, 0xfb,
+            0x43, 0xe8, 0x2a, 0x4d, 0x91, 0x9d, 0x48, 0x43, 0xb1, 0xca, 0x70,
+            0xa2, 0xd8, 0xd3, 0xf7, 0x25, 0xea, 0xd1, 0x39, 0x13, 0x77, 0xdc,
+            0xc0,
+        ];
+
+        let dcid = [0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08];
+
+        let frames = [
+            0x06, 0x00, 0x40, 0xc4, 0x01, 0x00, 0x00, 0xc0, 0x03, 0x03, 0x66,
+            0x60, 0x26, 0x1f, 0xf9, 0x47, 0xce, 0xa4, 0x9c, 0xce, 0x6c, 0xfa,
+            0xd6, 0x87, 0xf4, 0x57, 0xcf, 0x1b, 0x14, 0x53, 0x1b, 0xa1, 0x41,
+            0x31, 0xa0, 0xe8, 0xf3, 0x09, 0xa1, 0xd0, 0xb9, 0xc4, 0x00, 0x00,
+            0x06, 0x13, 0x01, 0x13, 0x03, 0x13, 0x02, 0x01, 0x00, 0x00, 0x91,
+            0x00, 0x00, 0x00, 0x0b, 0x00, 0x09, 0x00, 0x00, 0x06, 0x73, 0x65,
+            0x72, 0x76, 0x65, 0x72, 0xff, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0a,
+            0x00, 0x14, 0x00, 0x12, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18, 0x00,
+            0x19, 0x01, 0x00, 0x01, 0x01, 0x01, 0x02, 0x01, 0x03, 0x01, 0x04,
+            0x00, 0x23, 0x00, 0x00, 0x00, 0x33, 0x00, 0x26, 0x00, 0x24, 0x00,
+            0x1d, 0x00, 0x20, 0x4c, 0xfd, 0xfc, 0xd1, 0x78, 0xb7, 0x84, 0xbf,
+            0x32, 0x8c, 0xae, 0x79, 0x3b, 0x13, 0x6f, 0x2a, 0xed, 0xce, 0x00,
+            0x5f, 0xf1, 0x83, 0xd7, 0xbb, 0x14, 0x95, 0x20, 0x72, 0x36, 0x64,
+            0x70, 0x37, 0x00, 0x2b, 0x00, 0x03, 0x02, 0x03, 0x04, 0x00, 0x0d,
+            0x00, 0x20, 0x00, 0x1e, 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x02,
+            0x03, 0x08, 0x04, 0x08, 0x05, 0x08, 0x06, 0x04, 0x01, 0x05, 0x01,
+            0x06, 0x01, 0x02, 0x01, 0x04, 0x02, 0x05, 0x02, 0x06, 0x02, 0x02,
+            0x02, 0x00, 0x2d, 0x00, 0x02, 0x01, 0x01, 0x00, 0x1c, 0x00, 0x02,
+            0x40, 0x01,
+        ];
+
+        assert_decrypt_initial_pkt(&mut pkt, &dcid, true, &frames, 2, 4);
+    }
+
+    #[test]
+    fn decrypt_client_initial_old() {
+        let mut pkt = [
+            0xc0, 0xff, 0x00, 0x00, 0x1c, 0x08, 0x83, 0x94, 0xc8, 0xf0, 0x3e,
+            0x51, 0x57, 0x08, 0x00, 0x00, 0x44, 0x9e, 0x3b, 0x34, 0x3a, 0xa8,
+            0x53, 0x50, 0x64, 0xa4, 0x26, 0x8a, 0x0d, 0x9d, 0x7b, 0x1c, 0x9d,
+            0x25, 0x0a, 0xe3, 0x55, 0x16, 0x22, 0x76, 0xe9, 0xb1, 0xe3, 0x01,
+            0x1e, 0xf6, 0xbb, 0xc0, 0xab, 0x48, 0xad, 0x5b, 0xcc, 0x26, 0x81,
+            0xe9, 0x53, 0x85, 0x7c, 0xa6, 0x2b, 0xec, 0xd7, 0x52, 0x4d, 0xaa,
+            0xc4, 0x73, 0xe6, 0x8d, 0x74, 0x05, 0xfb, 0xba, 0x4e, 0x9e, 0xe6,
+            0x16, 0xc8, 0x70, 0x38, 0xbd, 0xbe, 0x90, 0x8c, 0x06, 0xd9, 0x60,
+            0x5d, 0x9a, 0xc4, 0x90, 0x30, 0x35, 0x9e, 0xec, 0xb1, 0xd0, 0x5a,
+            0x14, 0xe1, 0x17, 0xdb, 0x8c, 0xed, 0xe2, 0xbb, 0x09, 0xd0, 0xdb,
+            0xbf, 0xee, 0x27, 0x1c, 0xb3, 0x74, 0xd8, 0xf1, 0x0a, 0xbe, 0xc8,
+            0x2d, 0x0f, 0x59, 0xa1, 0xde, 0xe2, 0x9f, 0xe9, 0x56, 0x38, 0xed,
+            0x8d, 0xd4, 0x1d, 0xa0, 0x74, 0x87, 0x46, 0x87, 0x91, 0xb7, 0x19,
+            0xc5, 0x5c, 0x46, 0x96, 0x8e, 0xb3, 0xb5, 0x46, 0x80, 0x03, 0x71,
+            0x02, 0xa2, 0x8e, 0x53, 0xdc, 0x1d, 0x12, 0x90, 0x3d, 0xb0, 0xaf,
+            0x58, 0x21, 0x79, 0x4b, 0x41, 0xc4, 0xa9, 0x33, 0x57, 0xfa, 0x59,
+            0xce, 0x69, 0xcf, 0xe7, 0xf6, 0xbd, 0xfa, 0x62, 0x9e, 0xef, 0x78,
+            0x61, 0x64, 0x47, 0xe1, 0xd6, 0x11, 0xc4, 0xba, 0xf7, 0x1b, 0xf3,
+            0x3f, 0xeb, 0xcb, 0x03, 0x13, 0x7c, 0x2c, 0x75, 0xd2, 0x53, 0x17,
+            0xd3, 0xe1, 0x3b, 0x68, 0x43, 0x70, 0xf6, 0x68, 0x41, 0x1c, 0x0f,
+            0x00, 0x30, 0x4b, 0x50, 0x1c, 0x8f, 0xd4, 0x22, 0xbd, 0x9b, 0x9a,
+            0xd8, 0x1d, 0x64, 0x3b, 0x20, 0xda, 0x89, 0xca, 0x05, 0x25, 0xd2,
+            0x4d, 0x2b, 0x14, 0x20, 0x41, 0xca, 0xe0, 0xaf, 0x20, 0x50, 0x92,
+            0xe4, 0x30, 0x08, 0x0c, 0xd8, 0x55, 0x9e, 0xa4, 0xc5, 0xc6, 0xe4,
+            0xfa, 0x3f, 0x66, 0x08, 0x2b, 0x7d, 0x30, 0x3e, 0x52, 0xce, 0x01,
+            0x62, 0xba, 0xa9, 0x58, 0x53, 0x2b, 0x0b, 0xbc, 0x2b, 0xc7, 0x85,
+            0x68, 0x1f, 0xcf, 0x37, 0x48, 0x5d, 0xff, 0x65, 0x95, 0xe0, 0x1e,
+            0x73, 0x9c, 0x8a, 0xc9, 0xef, 0xba, 0x31, 0xb9, 0x85, 0xd5, 0xf6,
+            0x56, 0xcc, 0x09, 0x24, 0x32, 0xd7, 0x81, 0xdb, 0x95, 0x22, 0x17,
+            0x24, 0x87, 0x64, 0x1c, 0x4d, 0x3a, 0xb8, 0xec, 0xe0, 0x1e, 0x39,
+            0xbc, 0x85, 0xb1, 0x54, 0x36, 0x61, 0x47, 0x75, 0xa9, 0x8b, 0xa8,
+            0xfa, 0x12, 0xd4, 0x6f, 0x9b, 0x35, 0xe2, 0xa5, 0x5e, 0xb7, 0x2d,
+            0x7f, 0x85, 0x18, 0x1a, 0x36, 0x66, 0x63, 0x38, 0x7d, 0xdc, 0x20,
+            0x55, 0x18, 0x07, 0xe0, 0x07, 0x67, 0x3b, 0xd7, 0xe2, 0x6b, 0xf9,
+            0xb2, 0x9b, 0x5a, 0xb1, 0x0a, 0x1c, 0xa8, 0x7c, 0xbb, 0x7a, 0xd9,
+            0x7e, 0x99, 0xeb, 0x66, 0x95, 0x9c, 0x2a, 0x9b, 0xc3, 0xcb, 0xde,
+            0x47, 0x07, 0xff, 0x77, 0x20, 0xb1, 0x10, 0xfa, 0x95, 0x35, 0x46,
+            0x74, 0xe3, 0x95, 0x81, 0x2e, 0x47, 0xa0, 0xae, 0x53, 0xb4, 0x64,
+            0xdc, 0xb2, 0xd1, 0xf3, 0x45, 0xdf, 0x36, 0x0d, 0xc2, 0x27, 0x27,
+            0x0c, 0x75, 0x06, 0x76, 0xf6, 0x72, 0x4e, 0xb4, 0x79, 0xf0, 0xd2,
+            0xfb, 0xb6, 0x12, 0x44, 0x29, 0x99, 0x04, 0x57, 0xac, 0x6c, 0x91,
+            0x67, 0xf4, 0x0a, 0xab, 0x73, 0x99, 0x98, 0xf3, 0x8b, 0x9e, 0xcc,
+            0xb2, 0x4f, 0xd4, 0x7c, 0x84, 0x10, 0x13, 0x1b, 0xf6, 0x5a, 0x52,
+            0xaf, 0x84, 0x12, 0x75, 0xd5, 0xb3, 0xd1, 0x88, 0x0b, 0x19, 0x7d,
+            0xf2, 0xb5, 0xde, 0xa3, 0xe6, 0xde, 0x56, 0xeb, 0xce, 0x3f, 0xfb,
+            0x6e, 0x92, 0x77, 0xa8, 0x20, 0x82, 0xf8, 0xd9, 0x67, 0x7a, 0x67,
+            0x67, 0x08, 0x9b, 0x67, 0x1e, 0xbd, 0x24, 0x4c, 0x21, 0x4f, 0x0b,
+            0xde, 0x95, 0xc2, 0xbe, 0xb0, 0x2c, 0xd1, 0x17, 0x2d, 0x58, 0xbd,
+            0xf3, 0x9d, 0xce, 0x56, 0xff, 0x68, 0xeb, 0x35, 0xab, 0x39, 0xb4,
+            0x9b, 0x4e, 0xac, 0x7c, 0x81, 0x5e, 0xa6, 0x04, 0x51, 0xd6, 0xe6,
+            0xab, 0x82, 0x11, 0x91, 0x18, 0xdf, 0x02, 0xa5, 0x86, 0x84, 0x4a,
+            0x9f, 0xfe, 0x16, 0x2b, 0xa0, 0x06, 0xd0, 0x66, 0x9e, 0xf5, 0x76,
+            0x68, 0xca, 0xb3, 0x8b, 0x62, 0xf7, 0x1a, 0x25, 0x23, 0xa0, 0x84,
+            0x85, 0x2c, 0xd1, 0xd0, 0x79, 0xb3, 0x65, 0x8d, 0xc2, 0xf3, 0xe8,
+            0x79, 0x49, 0xb5, 0x50, 0xba, 0xb3, 0xe1, 0x77, 0xcf, 0xc4, 0x9e,
+            0xd1, 0x90, 0xdf, 0xf0, 0x63, 0x0e, 0x43, 0x07, 0x7c, 0x30, 0xde,
+            0x8f, 0x6a, 0xe0, 0x81, 0x53, 0x7f, 0x1e, 0x83, 0xda, 0x53, 0x7d,
+            0xa9, 0x80, 0xaf, 0xa6, 0x68, 0xe7, 0xb7, 0xfb, 0x25, 0x30, 0x1c,
+            0xf7, 0x41, 0x52, 0x4b, 0xe3, 0xc4, 0x98, 0x84, 0xb4, 0x28, 0x21,
+            0xf1, 0x75, 0x52, 0xfb, 0xd1, 0x93, 0x1a, 0x81, 0x30, 0x17, 0xb6,
+            0xb6, 0x59, 0x0a, 0x41, 0xea, 0x18, 0xb6, 0xba, 0x49, 0xcd, 0x48,
+            0xa4, 0x40, 0xbd, 0x9a, 0x33, 0x46, 0xa7, 0x62, 0x3f, 0xb4, 0xba,
+            0x34, 0xa3, 0xee, 0x57, 0x1e, 0x3c, 0x73, 0x1f, 0x35, 0xa7, 0xa3,
+            0xcf, 0x25, 0xb5, 0x51, 0xa6, 0x80, 0xfa, 0x68, 0x76, 0x35, 0x07,
+            0xb7, 0xfd, 0xe3, 0xaa, 0xf0, 0x23, 0xc5, 0x0b, 0x9d, 0x22, 0xda,
+            0x68, 0x76, 0xba, 0x33, 0x7e, 0xb5, 0xe9, 0xdd, 0x9e, 0xc3, 0xda,
+            0xf9, 0x70, 0x24, 0x2b, 0x6c, 0x5a, 0xab, 0x3a, 0xa4, 0xb2, 0x96,
+            0xad, 0x8b, 0x9f, 0x68, 0x32, 0xf6, 0x86, 0xef, 0x70, 0xfa, 0x93,
+            0x8b, 0x31, 0xb4, 0xe5, 0xdd, 0xd7, 0x36, 0x44, 0x42, 0xd3, 0xea,
+            0x72, 0xe7, 0x3d, 0x66, 0x8f, 0xb0, 0x93, 0x77, 0x96, 0xf4, 0x62,
+            0x92, 0x3a, 0x81, 0xa4, 0x7e, 0x1c, 0xee, 0x74, 0x26, 0xff, 0x6d,
+            0x92, 0x21, 0x26, 0x9b, 0x5a, 0x62, 0xec, 0x03, 0xd6, 0xec, 0x94,
+            0xd1, 0x26, 0x06, 0xcb, 0x48, 0x55, 0x60, 0xba, 0xb5, 0x74, 0x81,
+            0x60, 0x09, 0xe9, 0x65, 0x04, 0x24, 0x93, 0x85, 0xbb, 0x61, 0xa8,
+            0x19, 0xbe, 0x04, 0xf6, 0x2c, 0x20, 0x66, 0x21, 0x4d, 0x83, 0x60,
+            0xa2, 0x02, 0x2b, 0xeb, 0x31, 0x62, 0x40, 0xb6, 0xc7, 0xd7, 0x8b,
+            0xbe, 0x56, 0xc1, 0x30, 0x82, 0xe0, 0xca, 0x27, 0x26, 0x61, 0x21,
+            0x0a, 0xbf, 0x02, 0x0b, 0xf3, 0xb5, 0x78, 0x3f, 0x14, 0x26, 0x43,
+            0x6c, 0xf9, 0xff, 0x41, 0x84, 0x05, 0x93, 0xa5, 0xd0, 0x63, 0x8d,
+            0x32, 0xfc, 0x51, 0xc5, 0xc6, 0x5f, 0xf2, 0x91, 0xa3, 0xa7, 0xa5,
+            0x2f, 0xd6, 0x77, 0x5e, 0x62, 0x3a, 0x44, 0x39, 0xcc, 0x08, 0xdd,
+            0x25, 0x58, 0x2f, 0xeb, 0xc9, 0x44, 0xef, 0x92, 0xd8, 0xdb, 0xd3,
+            0x29, 0xc9, 0x1d, 0xe3, 0xe9, 0xc9, 0x58, 0x2e, 0x41, 0xf1, 0x7f,
+            0x3d, 0x18, 0x6f, 0x10, 0x4a, 0xd3, 0xf9, 0x09, 0x95, 0x11, 0x6c,
+            0x68, 0x2a, 0x2a, 0x14, 0xa3, 0xb4, 0xb1, 0xf5, 0x47, 0xc3, 0x35,
+            0xf0, 0xbe, 0x71, 0x0f, 0xc9, 0xfc, 0x03, 0xe0, 0xe5, 0x87, 0xb8,
+            0xcd, 0xa3, 0x1c, 0xe6, 0x5b, 0x96, 0x98, 0x78, 0xa4, 0xad, 0x42,
+            0x83, 0xe6, 0xd5, 0xb0, 0x37, 0x3f, 0x43, 0xda, 0x86, 0xe9, 0xe0,
+            0xff, 0xe1, 0xae, 0x0f, 0xdd, 0xd3, 0x51, 0x62, 0x55, 0xbd, 0x74,
+            0x56, 0x6f, 0x36, 0xa3, 0x87, 0x03, 0xd5, 0xf3, 0x42, 0x49, 0xde,
+            0xd1, 0xf6, 0x6b, 0x3d, 0x9b, 0x45, 0xb9, 0xaf, 0x2c, 0xcf, 0xef,
+            0xe9, 0x84, 0xe1, 0x33, 0x76, 0xb1, 0xb2, 0xc6, 0x40, 0x4a, 0xa4,
+            0x8c, 0x80, 0x26, 0x13, 0x23, 0x43, 0xda, 0x3f, 0x3a, 0x33, 0x65,
+            0x9e, 0xc1, 0xb3, 0xe9, 0x50, 0x80, 0x54, 0x0b, 0x28, 0xb7, 0xf3,
+            0xfc, 0xd3, 0x5f, 0xa5, 0xd8, 0x43, 0xb5, 0x79, 0xa8, 0x4c, 0x08,
+            0x91, 0x21, 0xa6, 0x0d, 0x8c, 0x17, 0x54, 0x91, 0x5c, 0x34, 0x4e,
+            0xea, 0xf4, 0x5a, 0x9b, 0xf2, 0x7d, 0xc0, 0xc1, 0xe7, 0x84, 0x16,
+            0x16, 0x91, 0x22, 0x09, 0x13, 0x13, 0xeb, 0x0e, 0x87, 0x55, 0x5a,
+            0xbd, 0x70, 0x66, 0x26, 0xe5, 0x57, 0xfc, 0x36, 0xa0, 0x4f, 0xcd,
+            0x19, 0x1a, 0x58, 0x82, 0x91, 0x04, 0xd6, 0x07, 0x5c, 0x55, 0x94,
+            0xf6, 0x27, 0xca, 0x50, 0x6b, 0xf1, 0x81, 0xda, 0xec, 0x94, 0x0f,
+            0x4a, 0x4f, 0x3a, 0xf0, 0x07, 0x4e, 0xee, 0x89, 0xda, 0xac, 0xde,
+            0x67, 0x58, 0x31, 0x26, 0x22, 0xd4, 0xfa, 0x67, 0x5b, 0x39, 0xf7,
+            0x28, 0xe0, 0x62, 0xd2, 0xbe, 0xe6, 0x80, 0xd8, 0xf4, 0x1a, 0x59,
+            0x7c, 0x26, 0x26, 0x48, 0xbb, 0x18, 0xbc, 0xfc, 0x13, 0xc8, 0xb3,
+            0xd9, 0x7b, 0x1a, 0x77, 0xb2, 0xac, 0x3a, 0xf7, 0x45, 0xd6, 0x1a,
+            0x34, 0xcc, 0x47, 0x09, 0x86, 0x5b, 0xac, 0x82, 0x4a, 0x94, 0xbb,
+            0x19, 0x05, 0x80, 0x15, 0xe4, 0xe4, 0x2d, 0xea, 0x53, 0x88, 0xb9,
+            0x11, 0xe7, 0x6d, 0x28, 0x56, 0xd6, 0x8c, 0xf6, 0xcf, 0x39, 0x41,
+            0x85,
+        ];
+
+        let dcid = [0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08];
+
+        let frames = [
+            0x06, 0x00, 0x40, 0xc4, 0x01, 0x00, 0x00, 0xc0, 0x03, 0x03, 0x66,
+            0x60, 0x26, 0x1f, 0xf9, 0x47, 0xce, 0xa4, 0x9c, 0xce, 0x6c, 0xfa,
+            0xd6, 0x87, 0xf4, 0x57, 0xcf, 0x1b, 0x14, 0x53, 0x1b, 0xa1, 0x41,
+            0x31, 0xa0, 0xe8, 0xf3, 0x09, 0xa1, 0xd0, 0xb9, 0xc4, 0x00, 0x00,
+            0x06, 0x13, 0x01, 0x13, 0x03, 0x13, 0x02, 0x01, 0x00, 0x00, 0x91,
+            0x00, 0x00, 0x00, 0x0b, 0x00, 0x09, 0x00, 0x00, 0x06, 0x73, 0x65,
+            0x72, 0x76, 0x65, 0x72, 0xff, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0a,
+            0x00, 0x14, 0x00, 0x12, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18, 0x00,
+            0x19, 0x01, 0x00, 0x01, 0x01, 0x01, 0x02, 0x01, 0x03, 0x01, 0x04,
+            0x00, 0x23, 0x00, 0x00, 0x00, 0x33, 0x00, 0x26, 0x00, 0x24, 0x00,
+            0x1d, 0x00, 0x20, 0x4c, 0xfd, 0xfc, 0xd1, 0x78, 0xb7, 0x84, 0xbf,
+            0x32, 0x8c, 0xae, 0x79, 0x3b, 0x13, 0x6f, 0x2a, 0xed, 0xce, 0x00,
+            0x5f, 0xf1, 0x83, 0xd7, 0xbb, 0x14, 0x95, 0x20, 0x72, 0x36, 0x64,
+            0x70, 0x37, 0x00, 0x2b, 0x00, 0x03, 0x02, 0x03, 0x04, 0x00, 0x0d,
+            0x00, 0x20, 0x00, 0x1e, 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x02,
+            0x03, 0x08, 0x04, 0x08, 0x05, 0x08, 0x06, 0x04, 0x01, 0x05, 0x01,
+            0x06, 0x01, 0x02, 0x01, 0x04, 0x02, 0x05, 0x02, 0x06, 0x02, 0x02,
+            0x02, 0x00, 0x2d, 0x00, 0x02, 0x01, 0x01, 0x00, 0x1c, 0x00, 0x02,
+            0x40, 0x01,
+        ];
+
+        assert_decrypt_initial_pkt(&mut pkt, &dcid, true, &frames, 2, 4);
+    }
+
+    #[test]
+    fn decrypt_server_initial() {
+        let mut pkt = [
+            0xca, 0xff, 0x00, 0x00, 0x1d, 0x00, 0x08, 0xf0, 0x67, 0xa5, 0x50,
+            0x2a, 0x42, 0x62, 0xb5, 0x00, 0x40, 0x74, 0xaa, 0xf2, 0xf0, 0x07,
+            0x82, 0x3a, 0x5d, 0x3a, 0x12, 0x07, 0xc8, 0x6e, 0xe4, 0x91, 0x32,
+            0x82, 0x4f, 0x04, 0x65, 0x24, 0x3d, 0x08, 0x2d, 0x86, 0x8b, 0x10,
+            0x7a, 0x38, 0x09, 0x2b, 0xc8, 0x05, 0x28, 0x66, 0x4c, 0xbf, 0x94,
+            0x56, 0xeb, 0xf2, 0x76, 0x73, 0xfb, 0x5f, 0xa5, 0x06, 0x1a, 0xb5,
+            0x73, 0xc9, 0xf0, 0x01, 0xb8, 0x1d, 0xa0, 0x28, 0xa0, 0x0d, 0x52,
+            0xab, 0x00, 0xb1, 0x5b, 0xeb, 0xaa, 0x70, 0x64, 0x0e, 0x10, 0x6c,
+            0xf2, 0xac, 0xd0, 0x43, 0xe9, 0xc6, 0xb4, 0x41, 0x1c, 0x0a, 0x79,
+            0x63, 0x71, 0x34, 0xd8, 0x99, 0x37, 0x01, 0xfe, 0x77, 0x9e, 0x58,
+            0xc2, 0xfe, 0x75, 0x3d, 0x14, 0xb0, 0x56, 0x40, 0x21, 0x56, 0x5e,
+            0xa9, 0x2e, 0x57, 0xbc, 0x6f, 0xaf, 0x56, 0xdf, 0xc7, 0xa4, 0x08,
+            0x70, 0xe6,
+        ];
+
+        let dcid = [0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08];
+
+        let frames = [
+            0x0d, 0x00, 0x00, 0x00, 0x00, 0x18, 0x41, 0x0a, 0x02, 0x00, 0x00,
+            0x56, 0x03, 0x03, 0xee, 0xfc, 0xe7, 0xf7, 0xb3, 0x7b, 0xa1, 0xd1,
+            0x63, 0x2e, 0x96, 0x67, 0x78, 0x25, 0xdd, 0xf7, 0x39, 0x88, 0xcf,
+            0xc7, 0x98, 0x25, 0xdf, 0x56, 0x6d, 0xc5, 0x43, 0x0b, 0x9a, 0x04,
+            0x5a, 0x12, 0x00, 0x13, 0x01, 0x00, 0x00, 0x2e, 0x00, 0x33, 0x00,
+            0x24, 0x00, 0x1d, 0x00, 0x20, 0x9d, 0x3c, 0x94, 0x0d, 0x89, 0x69,
+            0x0b, 0x84, 0xd0, 0x8a, 0x60, 0x99, 0x3c, 0x14, 0x4e, 0xca, 0x68,
+            0x4d, 0x10, 0x81, 0x28, 0x7c, 0x83, 0x4d, 0x53, 0x11, 0xbc, 0xf3,
+            0x2b, 0xb9, 0xda, 0x1a, 0x00, 0x2b, 0x00, 0x02, 0x03, 0x04,
+        ];
+
+        assert_decrypt_initial_pkt(&mut pkt, &dcid, false, &frames, 1, 2);
+    }
+
+    #[test]
+    fn decrypt_server_initial_old() {
+        let mut pkt = [
+            0xc9, 0xff, 0x00, 0x00, 0x1c, 0x00, 0x08, 0xf0, 0x67, 0xa5, 0x50,
+            0x2a, 0x42, 0x62, 0xb5, 0x00, 0x40, 0x74, 0x16, 0x8b, 0xf2, 0x2b,
+            0x70, 0x02, 0x59, 0x6f, 0x99, 0xae, 0x67, 0xab, 0xf6, 0x5a, 0x58,
+            0x52, 0xf5, 0x4f, 0x58, 0xc3, 0x7c, 0x80, 0x86, 0x82, 0xe2, 0xe4,
+            0x04, 0x92, 0xd8, 0xa3, 0x89, 0x9f, 0xb0, 0x4f, 0xc0, 0xaf, 0xe9,
+            0xaa, 0xbc, 0x87, 0x67, 0xb1, 0x8a, 0x0a, 0xa4, 0x93, 0x53, 0x74,
+            0x26, 0x37, 0x3b, 0x48, 0xd5, 0x02, 0x21, 0x4d, 0xd8, 0x56, 0xd6,
+            0x3b, 0x78, 0xce, 0xe3, 0x7b, 0xc6, 0x64, 0xb3, 0xfe, 0x86, 0xd4,
+            0x87, 0xac, 0x7a, 0x77, 0xc5, 0x30, 0x38, 0xa3, 0xcd, 0x32, 0xf0,
+            0xb5, 0x00, 0x4d, 0x9f, 0x57, 0x54, 0xc4, 0xf7, 0xf2, 0xd1, 0xf3,
+            0x5c, 0xf3, 0xf7, 0x11, 0x63, 0x51, 0xc9, 0x2b, 0xda, 0x5b, 0x23,
+            0xc8, 0x10, 0x34, 0xab, 0x74, 0xf5, 0x4c, 0xb1, 0xbd, 0x72, 0x95,
+            0x12, 0x56,
+        ];
+
+        let dcid = [0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08];
+
+        let frames = [
+            0x0d, 0x00, 0x00, 0x00, 0x00, 0x18, 0x41, 0x0a, 0x02, 0x00, 0x00,
+            0x56, 0x03, 0x03, 0xee, 0xfc, 0xe7, 0xf7, 0xb3, 0x7b, 0xa1, 0xd1,
+            0x63, 0x2e, 0x96, 0x67, 0x78, 0x25, 0xdd, 0xf7, 0x39, 0x88, 0xcf,
+            0xc7, 0x98, 0x25, 0xdf, 0x56, 0x6d, 0xc5, 0x43, 0x0b, 0x9a, 0x04,
+            0x5a, 0x12, 0x00, 0x13, 0x01, 0x00, 0x00, 0x2e, 0x00, 0x33, 0x00,
+            0x24, 0x00, 0x1d, 0x00, 0x20, 0x9d, 0x3c, 0x94, 0x0d, 0x89, 0x69,
+            0x0b, 0x84, 0xd0, 0x8a, 0x60, 0x99, 0x3c, 0x14, 0x4e, 0xca, 0x68,
+            0x4d, 0x10, 0x81, 0x28, 0x7c, 0x83, 0x4d, 0x53, 0x11, 0xbc, 0xf3,
+            0x2b, 0xb9, 0xda, 0x1a, 0x00, 0x2b, 0x00, 0x02, 0x03, 0x04,
+        ];
+
+        assert_decrypt_initial_pkt(&mut pkt, &dcid, false, &frames, 1, 2);
+    }
+
+    #[test]
+    fn decrypt_chacha20() {
+        let secret = [
+            0x9a, 0xc3, 0x12, 0xa7, 0xf8, 0x77, 0x46, 0x8e, 0xbe, 0x69, 0x42,
+            0x27, 0x48, 0xad, 0x00, 0xa1, 0x54, 0x43, 0xf1, 0x82, 0x03, 0xa0,
+            0x7d, 0x60, 0x60, 0xf6, 0x88, 0xf3, 0x0f, 0x21, 0x63, 0x2b,
+        ];
+
+        let mut pkt = [
+            0x4c, 0xfe, 0x41, 0x89, 0x65, 0x5e, 0x5c, 0xd5, 0x5c, 0x41, 0xf6,
+            0x90, 0x80, 0x57, 0x5d, 0x79, 0x99, 0xc2, 0x5a, 0x5b, 0xfb,
+        ];
+
+        let mut b = octets::OctetsMut::with_slice(&mut pkt);
+
+        let alg = crypto::Algorithm::ChaCha20_Poly1305;
+
+        let aead = crypto::Open::from_secret(alg, &secret).unwrap();
+
+        let mut hdr = Header::from_bytes(&mut b, 0).unwrap();
+        assert_eq!(hdr.ty, Type::Short);
+
+        let payload_len = b.cap();
+
+        decrypt_hdr(&mut b, &mut hdr, &aead).unwrap();
+        assert_eq!(hdr.pkt_num_len, 3);
+
+        let pn = decode_pkt_num(654_360_564, hdr.pkt_num, hdr.pkt_num_len);
+        assert_eq!(pn, 654_360_564);
+
+        let payload =
+            decrypt_pkt(&mut b, pn, hdr.pkt_num_len, payload_len, &aead).unwrap();
+
+        let payload = payload.as_ref();
+        assert_eq!(&payload, &[0x01]);
+    }
+
+    fn assert_encrypt_initial_pkt(
+        header: &mut [u8], dcid: &[u8], frames: &[u8], pn: u64, pn_len: usize,
+        is_server: bool, expected_pkt: &[u8],
+    ) {
+        let mut b = octets::OctetsMut::with_slice(header);
+
+        let hdr = Header::from_bytes(&mut b, 0).unwrap();
+        assert_eq!(hdr.ty, Type::Initial);
+
+        let mut out = vec![0; expected_pkt.len()];
+        let mut b = octets::OctetsMut::with_slice(&mut out);
+
+        b.put_bytes(header).unwrap();
+
+        let (_, aead) =
+            crypto::derive_initial_key_material(dcid, hdr.version, is_server)
+                .unwrap();
+
+        let overhead = aead.alg().tag_len();
+
+        let payload_len = frames.len() + overhead;
+
+        let payload_offset = b.off();
+
+        b.put_bytes(frames).unwrap();
+
+        let written =
+            encrypt_pkt(&mut b, pn, pn_len, payload_len, payload_offset, &aead)
+                .unwrap();
+
+        assert_eq!(written, expected_pkt.len());
+        assert_eq!(&out[..written], &expected_pkt[..]);
+    }
+
+    #[test]
+    fn encrypt_client_initial() {
+        let mut header = [
+            0xc3, 0xff, 0x00, 0x00, 0x1d, 0x08, 0x83, 0x94, 0xc8, 0xf0, 0x3e,
+            0x51, 0x57, 0x08, 0x00, 0x00, 0x44, 0x9e, 0x00, 0x00, 0x00, 0x02,
+        ];
+
+        let dcid = [0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08];
+
+        let frames = [
+            0x06, 0x00, 0x40, 0xc4, 0x01, 0x00, 0x00, 0xc0, 0x03, 0x03, 0x66,
+            0x60, 0x26, 0x1f, 0xf9, 0x47, 0xce, 0xa4, 0x9c, 0xce, 0x6c, 0xfa,
+            0xd6, 0x87, 0xf4, 0x57, 0xcf, 0x1b, 0x14, 0x53, 0x1b, 0xa1, 0x41,
+            0x31, 0xa0, 0xe8, 0xf3, 0x09, 0xa1, 0xd0, 0xb9, 0xc4, 0x00, 0x00,
+            0x06, 0x13, 0x01, 0x13, 0x03, 0x13, 0x02, 0x01, 0x00, 0x00, 0x91,
+            0x00, 0x00, 0x00, 0x0b, 0x00, 0x09, 0x00, 0x00, 0x06, 0x73, 0x65,
+            0x72, 0x76, 0x65, 0x72, 0xff, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0a,
+            0x00, 0x14, 0x00, 0x12, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18, 0x00,
+            0x19, 0x01, 0x00, 0x01, 0x01, 0x01, 0x02, 0x01, 0x03, 0x01, 0x04,
+            0x00, 0x23, 0x00, 0x00, 0x00, 0x33, 0x00, 0x26, 0x00, 0x24, 0x00,
+            0x1d, 0x00, 0x20, 0x4c, 0xfd, 0xfc, 0xd1, 0x78, 0xb7, 0x84, 0xbf,
+            0x32, 0x8c, 0xae, 0x79, 0x3b, 0x13, 0x6f, 0x2a, 0xed, 0xce, 0x00,
+            0x5f, 0xf1, 0x83, 0xd7, 0xbb, 0x14, 0x95, 0x20, 0x72, 0x36, 0x64,
+            0x70, 0x37, 0x00, 0x2b, 0x00, 0x03, 0x02, 0x03, 0x04, 0x00, 0x0d,
+            0x00, 0x20, 0x00, 0x1e, 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x02,
+            0x03, 0x08, 0x04, 0x08, 0x05, 0x08, 0x06, 0x04, 0x01, 0x05, 0x01,
+            0x06, 0x01, 0x02, 0x01, 0x04, 0x02, 0x05, 0x02, 0x06, 0x02, 0x02,
+            0x02, 0x00, 0x2d, 0x00, 0x02, 0x01, 0x01, 0x00, 0x1c, 0x00, 0x02,
+            0x40, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        ];
+
+        let pkt = [
+            0xc5, 0xff, 0x00, 0x00, 0x1d, 0x08, 0x83, 0x94, 0xc8, 0xf0, 0x3e,
+            0x51, 0x57, 0x08, 0x00, 0x00, 0x44, 0x9e, 0x4a, 0x95, 0x24, 0x5b,
+            0xfb, 0x66, 0xbc, 0x5f, 0x93, 0x03, 0x2b, 0x7d, 0xdd, 0x89, 0xfe,
+            0x0f, 0xf1, 0x5d, 0x9c, 0x4f, 0x70, 0x50, 0xfc, 0xcd, 0xb7, 0x1c,
+            0x1c, 0xd8, 0x05, 0x12, 0xd4, 0x43, 0x16, 0x43, 0xa5, 0x3a, 0xaf,
+            0xa1, 0xb0, 0xb5, 0x18, 0xb4, 0x49, 0x68, 0xb1, 0x8b, 0x8d, 0x3e,
+            0x7a, 0x4d, 0x04, 0xc3, 0x0b, 0x3e, 0xd9, 0x41, 0x03, 0x25, 0xb2,
+            0xab, 0xb2, 0xda, 0xfb, 0x1c, 0x12, 0xf8, 0xb7, 0x04, 0x79, 0xeb,
+            0x8d, 0xf9, 0x8a, 0xbc, 0xaf, 0x95, 0xdd, 0x8f, 0x3d, 0x1c, 0x78,
+            0x66, 0x0f, 0xbc, 0x71, 0x9f, 0x88, 0xb2, 0x3c, 0x8a, 0xef, 0x67,
+            0x71, 0xf3, 0xd5, 0x0e, 0x10, 0xfd, 0xfb, 0x4c, 0x9d, 0x92, 0x38,
+            0x6d, 0x44, 0x48, 0x1b, 0x6c, 0x52, 0xd5, 0x9e, 0x55, 0x38, 0xd3,
+            0xd3, 0x94, 0x2d, 0xe9, 0xf1, 0x3a, 0x7f, 0x8b, 0x70, 0x2d, 0xc3,
+            0x17, 0x24, 0x18, 0x0d, 0xa9, 0xdf, 0x22, 0x71, 0x4d, 0x01, 0x00,
+            0x3f, 0xc5, 0xe3, 0xd1, 0x65, 0xc9, 0x50, 0xe6, 0x30, 0xb8, 0x54,
+            0x0f, 0xbd, 0x81, 0xc9, 0xdf, 0x0e, 0xe6, 0x3f, 0x94, 0x99, 0x70,
+            0x26, 0xc4, 0xf2, 0xe1, 0x88, 0x7a, 0x2d, 0xef, 0x79, 0x05, 0x0a,
+            0xc2, 0xd8, 0x6b, 0xa3, 0x18, 0xe0, 0xb3, 0xad, 0xc4, 0xc5, 0xaa,
+            0x18, 0xbc, 0xf6, 0x3c, 0x7c, 0xf8, 0xe8, 0x5f, 0x56, 0x92, 0x49,
+            0x81, 0x3a, 0x22, 0x36, 0xa7, 0xe7, 0x22, 0x69, 0x44, 0x7c, 0xd1,
+            0xc7, 0x55, 0xe4, 0x51, 0xf5, 0xe7, 0x74, 0x70, 0xeb, 0x3d, 0xe6,
+            0x4c, 0x88, 0x49, 0xd2, 0x92, 0x82, 0x06, 0x98, 0x02, 0x9c, 0xfa,
+            0x18, 0xe5, 0xd6, 0x61, 0x76, 0xfe, 0x6e, 0x5b, 0xa4, 0xed, 0x18,
+            0x02, 0x6f, 0x90, 0x90, 0x0a, 0x5b, 0x49, 0x80, 0xe2, 0xf5, 0x8e,
+            0x39, 0x15, 0x1d, 0x5c, 0xd6, 0x85, 0xb1, 0x09, 0x29, 0x63, 0x6d,
+            0x4f, 0x02, 0xe7, 0xfa, 0xd2, 0xa5, 0xa4, 0x58, 0x24, 0x9f, 0x5c,
+            0x02, 0x98, 0xa6, 0xd5, 0x3a, 0xcb, 0xe4, 0x1a, 0x7f, 0xc8, 0x3f,
+            0xa7, 0xcc, 0x01, 0x97, 0x3f, 0x7a, 0x74, 0xd1, 0x23, 0x7a, 0x51,
+            0x97, 0x4e, 0x09, 0x76, 0x36, 0xb6, 0x20, 0x39, 0x97, 0xf9, 0x21,
+            0xd0, 0x7b, 0xc1, 0x94, 0x0a, 0x6f, 0x2d, 0x0d, 0xe9, 0xf5, 0xa1,
+            0x14, 0x32, 0x94, 0x61, 0x59, 0xed, 0x6c, 0xc2, 0x1d, 0xf6, 0x5c,
+            0x4d, 0xdd, 0x11, 0x15, 0xf8, 0x64, 0x27, 0x25, 0x9a, 0x19, 0x6c,
+            0x71, 0x48, 0xb2, 0x5b, 0x64, 0x78, 0xb0, 0xdc, 0x77, 0x66, 0xe1,
+            0xc4, 0xd1, 0xb1, 0xf5, 0x15, 0x9f, 0x90, 0xea, 0xbc, 0x61, 0x63,
+            0x62, 0x26, 0x24, 0x46, 0x42, 0xee, 0x14, 0x8b, 0x46, 0x4c, 0x9e,
+            0x61, 0x9e, 0xe5, 0x0a, 0x5e, 0x3d, 0xdc, 0x83, 0x62, 0x27, 0xca,
+            0xd9, 0x38, 0x98, 0x7c, 0x4e, 0xa3, 0xc1, 0xfa, 0x7c, 0x75, 0xbb,
+            0xf8, 0x8d, 0x89, 0xe9, 0xad, 0xa6, 0x42, 0xb2, 0xb8, 0x8f, 0xe8,
+            0x10, 0x7b, 0x7e, 0xa3, 0x75, 0xb1, 0xb6, 0x48, 0x89, 0xa4, 0xe9,
+            0xe5, 0xc3, 0x8a, 0x1c, 0x89, 0x6c, 0xe2, 0x75, 0xa5, 0x65, 0x8d,
+            0x25, 0x0e, 0x2d, 0x76, 0xe1, 0xed, 0x3a, 0x34, 0xce, 0x7e, 0x3a,
+            0x3f, 0x38, 0x3d, 0x0c, 0x99, 0x6d, 0x0b, 0xed, 0x10, 0x6c, 0x28,
+            0x99, 0xca, 0x6f, 0xc2, 0x63, 0xef, 0x04, 0x55, 0xe7, 0x4b, 0xb6,
+            0xac, 0x16, 0x40, 0xea, 0x7b, 0xfe, 0xdc, 0x59, 0xf0, 0x3f, 0xee,
+            0x0e, 0x17, 0x25, 0xea, 0x15, 0x0f, 0xf4, 0xd6, 0x9a, 0x76, 0x60,
+            0xc5, 0x54, 0x21, 0x19, 0xc7, 0x1d, 0xe2, 0x70, 0xae, 0x7c, 0x3e,
+            0xcf, 0xd1, 0xaf, 0x2c, 0x4c, 0xe5, 0x51, 0x98, 0x69, 0x49, 0xcc,
+            0x34, 0xa6, 0x6b, 0x3e, 0x21, 0x6b, 0xfe, 0x18, 0xb3, 0x47, 0xe6,
+            0xc0, 0x5f, 0xd0, 0x50, 0xf8, 0x59, 0x12, 0xdb, 0x30, 0x3a, 0x8f,
+            0x05, 0x4e, 0xc2, 0x3e, 0x38, 0xf4, 0x4d, 0x1c, 0x72, 0x5a, 0xb6,
+            0x41, 0xae, 0x92, 0x9f, 0xec, 0xc8, 0xe3, 0xce, 0xfa, 0x56, 0x19,
+            0xdf, 0x42, 0x31, 0xf5, 0xb4, 0xc0, 0x09, 0xfa, 0x0c, 0x0b, 0xbc,
+            0x60, 0xbc, 0x75, 0xf7, 0x6d, 0x06, 0xef, 0x15, 0x4f, 0xc8, 0x57,
+            0x70, 0x77, 0xd9, 0xd6, 0xa1, 0xd2, 0xbd, 0x9b, 0xf0, 0x81, 0xdc,
+            0x78, 0x3e, 0xce, 0x60, 0x11, 0x1b, 0xea, 0x7d, 0xa9, 0xe5, 0xa9,
+            0x74, 0x80, 0x69, 0xd0, 0x78, 0xb2, 0xbe, 0xf4, 0x8d, 0xe0, 0x4c,
+            0xab, 0xe3, 0x75, 0x5b, 0x19, 0x7d, 0x52, 0xb3, 0x20, 0x46, 0x94,
+            0x9e, 0xca, 0xa3, 0x10, 0x27, 0x4b, 0x4a, 0xac, 0x0d, 0x00, 0x8b,
+            0x19, 0x48, 0xc1, 0x08, 0x2c, 0xdf, 0xe2, 0x08, 0x3e, 0x38, 0x6d,
+            0x4f, 0xd8, 0x4c, 0x0e, 0xd0, 0x66, 0x6d, 0x3e, 0xe2, 0x6c, 0x45,
+            0x15, 0xc4, 0xfe, 0xe7, 0x34, 0x33, 0xac, 0x70, 0x3b, 0x69, 0x0a,
+            0x9f, 0x7b, 0xf2, 0x78, 0xa7, 0x74, 0x86, 0xac, 0xe4, 0x4c, 0x48,
+            0x9a, 0x0c, 0x7a, 0xc8, 0xdf, 0xe4, 0xd1, 0xa5, 0x8f, 0xb3, 0xa7,
+            0x30, 0xb9, 0x93, 0xff, 0x0f, 0x0d, 0x61, 0xb4, 0xd8, 0x95, 0x57,
+            0x83, 0x1e, 0xb4, 0xc7, 0x52, 0xff, 0xd3, 0x9c, 0x10, 0xf6, 0xb9,
+            0xf4, 0x6d, 0x8d, 0xb2, 0x78, 0xda, 0x62, 0x4f, 0xd8, 0x00, 0xe4,
+            0xaf, 0x85, 0x54, 0x8a, 0x29, 0x4c, 0x15, 0x18, 0x89, 0x3a, 0x87,
+            0x78, 0xc4, 0xf6, 0xd6, 0xd7, 0x3c, 0x93, 0xdf, 0x20, 0x09, 0x60,
+            0x10, 0x4e, 0x06, 0x2b, 0x38, 0x8e, 0xa9, 0x7d, 0xcf, 0x40, 0x16,
+            0xbc, 0xed, 0x7f, 0x62, 0xb4, 0xf0, 0x62, 0xcb, 0x6c, 0x04, 0xc2,
+            0x06, 0x93, 0xd9, 0xa0, 0xe3, 0xb7, 0x4b, 0xa8, 0xfe, 0x74, 0xcc,
+            0x01, 0x23, 0x78, 0x84, 0xf4, 0x0d, 0x76, 0x5a, 0xe5, 0x6a, 0x51,
+            0x68, 0x8d, 0x98, 0x5c, 0xf0, 0xce, 0xae, 0xf4, 0x30, 0x45, 0xed,
+            0x8c, 0x3f, 0x0c, 0x33, 0xbc, 0xed, 0x08, 0x53, 0x7f, 0x68, 0x82,
+            0x61, 0x3a, 0xcd, 0x3b, 0x08, 0xd6, 0x65, 0xfc, 0xe9, 0xdd, 0x8a,
+            0xa7, 0x31, 0x71, 0xe2, 0xd3, 0x77, 0x1a, 0x61, 0xdb, 0xa2, 0x79,
+            0x0e, 0x49, 0x1d, 0x41, 0x3d, 0x93, 0xd9, 0x87, 0xe2, 0x74, 0x5a,
+            0xf2, 0x94, 0x18, 0xe4, 0x28, 0xbe, 0x34, 0x94, 0x14, 0x85, 0xc9,
+            0x34, 0x47, 0x52, 0x0f, 0xfe, 0x23, 0x1d, 0xa2, 0x30, 0x4d, 0x6a,
+            0x0f, 0xd5, 0xd0, 0x7d, 0x08, 0x37, 0x22, 0x02, 0x36, 0x96, 0x61,
+            0x59, 0xbe, 0xf3, 0xcf, 0x90, 0x4d, 0x72, 0x23, 0x24, 0xdd, 0x85,
+            0x25, 0x13, 0xdf, 0x39, 0xae, 0x03, 0x0d, 0x81, 0x73, 0x90, 0x8d,
+            0xa6, 0x36, 0x47, 0x86, 0xd3, 0xc1, 0xbf, 0xcb, 0x19, 0xea, 0x77,
+            0xa6, 0x3b, 0x25, 0xf1, 0xe7, 0xfc, 0x66, 0x1d, 0xef, 0x48, 0x0c,
+            0x5d, 0x00, 0xd4, 0x44, 0x56, 0x26, 0x9e, 0xbd, 0x84, 0xef, 0xd8,
+            0xe3, 0xa8, 0xb2, 0xc2, 0x57, 0xee, 0xc7, 0x60, 0x60, 0x68, 0x28,
+            0x48, 0xcb, 0xf5, 0x19, 0x4b, 0xc9, 0x9e, 0x49, 0xee, 0x75, 0xe4,
+            0xd0, 0xd2, 0x54, 0xba, 0xd4, 0xbf, 0xd7, 0x49, 0x70, 0xc3, 0x0e,
+            0x44, 0xb6, 0x55, 0x11, 0xd4, 0xad, 0x0e, 0x6e, 0xc7, 0x39, 0x8e,
+            0x08, 0xe0, 0x13, 0x07, 0xee, 0xee, 0xa1, 0x4e, 0x46, 0xcc, 0xd8,
+            0x7c, 0xf3, 0x6b, 0x28, 0x52, 0x21, 0x25, 0x4d, 0x8f, 0xc6, 0xa6,
+            0x76, 0x5c, 0x52, 0x4d, 0xed, 0x00, 0x85, 0xdc, 0xa5, 0xbd, 0x68,
+            0x8d, 0xdf, 0x72, 0x2e, 0x2c, 0x0f, 0xaf, 0x9d, 0x0f, 0xb2, 0xce,
+            0x7a, 0x0c, 0x3f, 0x2c, 0xee, 0x19, 0xca, 0x0f, 0xfb, 0xa4, 0x61,
+            0xca, 0x8d, 0xc5, 0xd2, 0xc8, 0x17, 0x8b, 0x07, 0x62, 0xcf, 0x67,
+            0x13, 0x55, 0x58, 0x49, 0x4d, 0x2a, 0x96, 0xf1, 0xa1, 0x39, 0xf0,
+            0xed, 0xb4, 0x2d, 0x2a, 0xf8, 0x9a, 0x9c, 0x91, 0x22, 0xb0, 0x7a,
+            0xcb, 0xc2, 0x9e, 0x5e, 0x72, 0x2d, 0xf8, 0x61, 0x5c, 0x34, 0x37,
+            0x02, 0x49, 0x10, 0x98, 0x47, 0x8a, 0x38, 0x9c, 0x98, 0x72, 0xa1,
+            0x0b, 0x0c, 0x98, 0x75, 0x12, 0x5e, 0x25, 0x7c, 0x7b, 0xfd, 0xf2,
+            0x7e, 0xef, 0x40, 0x60, 0xbd, 0x3d, 0x00, 0xf4, 0xc1, 0x4f, 0xd3,
+            0xe3, 0x49, 0x6c, 0x38, 0xd3, 0xc5, 0xd1, 0xa5, 0x66, 0x8c, 0x39,
+            0x35, 0x0e, 0xff, 0xbc, 0x2d, 0x16, 0xca, 0x17, 0xbe, 0x4c, 0xe2,
+            0x9f, 0x02, 0xed, 0x96, 0x95, 0x04, 0xdd, 0xa2, 0xa8, 0xc6, 0xb9,
+            0xff, 0x91, 0x9e, 0x69, 0x3e, 0xe7, 0x9e, 0x09, 0x08, 0x93, 0x16,
+            0xe7, 0xd1, 0xd8, 0x9e, 0xc0, 0x99, 0xdb, 0x3b, 0x2b, 0x26, 0x87,
+            0x25, 0xd8, 0x88, 0x53, 0x6a, 0x4b, 0x8b, 0xf9, 0xae, 0xe8, 0xfb,
+            0x43, 0xe8, 0x2a, 0x4d, 0x91, 0x9d, 0x48, 0x43, 0xb1, 0xca, 0x70,
+            0xa2, 0xd8, 0xd3, 0xf7, 0x25, 0xea, 0xd1, 0x39, 0x13, 0x77, 0xdc,
+            0xc0,
+        ];
+
+        assert_encrypt_initial_pkt(
+            &mut header,
+            &dcid,
+            &frames,
+            2,
+            4,
+            false,
+            &pkt,
+        );
+    }
+
+    #[test]
+    fn encrypt_client_initial_old() {
+        let mut header = [
+            0xc3, 0xff, 0x00, 0x00, 0x1c, 0x08, 0x83, 0x94, 0xc8, 0xf0, 0x3e,
+            0x51, 0x57, 0x08, 0x00, 0x00, 0x44, 0x9e, 0x00, 0x00, 0x00, 0x02,
+        ];
+
+        let dcid = [0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08];
+
+        let frames = [
+            0x06, 0x00, 0x40, 0xc4, 0x01, 0x00, 0x00, 0xc0, 0x03, 0x03, 0x66,
+            0x60, 0x26, 0x1f, 0xf9, 0x47, 0xce, 0xa4, 0x9c, 0xce, 0x6c, 0xfa,
+            0xd6, 0x87, 0xf4, 0x57, 0xcf, 0x1b, 0x14, 0x53, 0x1b, 0xa1, 0x41,
+            0x31, 0xa0, 0xe8, 0xf3, 0x09, 0xa1, 0xd0, 0xb9, 0xc4, 0x00, 0x00,
+            0x06, 0x13, 0x01, 0x13, 0x03, 0x13, 0x02, 0x01, 0x00, 0x00, 0x91,
+            0x00, 0x00, 0x00, 0x0b, 0x00, 0x09, 0x00, 0x00, 0x06, 0x73, 0x65,
+            0x72, 0x76, 0x65, 0x72, 0xff, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0a,
+            0x00, 0x14, 0x00, 0x12, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18, 0x00,
+            0x19, 0x01, 0x00, 0x01, 0x01, 0x01, 0x02, 0x01, 0x03, 0x01, 0x04,
+            0x00, 0x23, 0x00, 0x00, 0x00, 0x33, 0x00, 0x26, 0x00, 0x24, 0x00,
+            0x1d, 0x00, 0x20, 0x4c, 0xfd, 0xfc, 0xd1, 0x78, 0xb7, 0x84, 0xbf,
+            0x32, 0x8c, 0xae, 0x79, 0x3b, 0x13, 0x6f, 0x2a, 0xed, 0xce, 0x00,
+            0x5f, 0xf1, 0x83, 0xd7, 0xbb, 0x14, 0x95, 0x20, 0x72, 0x36, 0x64,
+            0x70, 0x37, 0x00, 0x2b, 0x00, 0x03, 0x02, 0x03, 0x04, 0x00, 0x0d,
+            0x00, 0x20, 0x00, 0x1e, 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x02,
+            0x03, 0x08, 0x04, 0x08, 0x05, 0x08, 0x06, 0x04, 0x01, 0x05, 0x01,
+            0x06, 0x01, 0x02, 0x01, 0x04, 0x02, 0x05, 0x02, 0x06, 0x02, 0x02,
+            0x02, 0x00, 0x2d, 0x00, 0x02, 0x01, 0x01, 0x00, 0x1c, 0x00, 0x02,
+            0x40, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        ];
+
+        let pkt = [
+            0xc0, 0xff, 0x00, 0x00, 0x1c, 0x08, 0x83, 0x94, 0xc8, 0xf0, 0x3e,
+            0x51, 0x57, 0x08, 0x00, 0x00, 0x44, 0x9e, 0x3b, 0x34, 0x3a, 0xa8,
+            0x53, 0x50, 0x64, 0xa4, 0x26, 0x8a, 0x0d, 0x9d, 0x7b, 0x1c, 0x9d,
+            0x25, 0x0a, 0xe3, 0x55, 0x16, 0x22, 0x76, 0xe9, 0xb1, 0xe3, 0x01,
+            0x1e, 0xf6, 0xbb, 0xc0, 0xab, 0x48, 0xad, 0x5b, 0xcc, 0x26, 0x81,
+            0xe9, 0x53, 0x85, 0x7c, 0xa6, 0x2b, 0xec, 0xd7, 0x52, 0x4d, 0xaa,
+            0xc4, 0x73, 0xe6, 0x8d, 0x74, 0x05, 0xfb, 0xba, 0x4e, 0x9e, 0xe6,
+            0x16, 0xc8, 0x70, 0x38, 0xbd, 0xbe, 0x90, 0x8c, 0x06, 0xd9, 0x60,
+            0x5d, 0x9a, 0xc4, 0x90, 0x30, 0x35, 0x9e, 0xec, 0xb1, 0xd0, 0x5a,
+            0x14, 0xe1, 0x17, 0xdb, 0x8c, 0xed, 0xe2, 0xbb, 0x09, 0xd0, 0xdb,
+            0xbf, 0xee, 0x27, 0x1c, 0xb3, 0x74, 0xd8, 0xf1, 0x0a, 0xbe, 0xc8,
+            0x2d, 0x0f, 0x59, 0xa1, 0xde, 0xe2, 0x9f, 0xe9, 0x56, 0x38, 0xed,
+            0x8d, 0xd4, 0x1d, 0xa0, 0x74, 0x87, 0x46, 0x87, 0x91, 0xb7, 0x19,
+            0xc5, 0x5c, 0x46, 0x96, 0x8e, 0xb3, 0xb5, 0x46, 0x80, 0x03, 0x71,
+            0x02, 0xa2, 0x8e, 0x53, 0xdc, 0x1d, 0x12, 0x90, 0x3d, 0xb0, 0xaf,
+            0x58, 0x21, 0x79, 0x4b, 0x41, 0xc4, 0xa9, 0x33, 0x57, 0xfa, 0x59,
+            0xce, 0x69, 0xcf, 0xe7, 0xf6, 0xbd, 0xfa, 0x62, 0x9e, 0xef, 0x78,
+            0x61, 0x64, 0x47, 0xe1, 0xd6, 0x11, 0xc4, 0xba, 0xf7, 0x1b, 0xf3,
+            0x3f, 0xeb, 0xcb, 0x03, 0x13, 0x7c, 0x2c, 0x75, 0xd2, 0x53, 0x17,
+            0xd3, 0xe1, 0x3b, 0x68, 0x43, 0x70, 0xf6, 0x68, 0x41, 0x1c, 0x0f,
+            0x00, 0x30, 0x4b, 0x50, 0x1c, 0x8f, 0xd4, 0x22, 0xbd, 0x9b, 0x9a,
+            0xd8, 0x1d, 0x64, 0x3b, 0x20, 0xda, 0x89, 0xca, 0x05, 0x25, 0xd2,
+            0x4d, 0x2b, 0x14, 0x20, 0x41, 0xca, 0xe0, 0xaf, 0x20, 0x50, 0x92,
+            0xe4, 0x30, 0x08, 0x0c, 0xd8, 0x55, 0x9e, 0xa4, 0xc5, 0xc6, 0xe4,
+            0xfa, 0x3f, 0x66, 0x08, 0x2b, 0x7d, 0x30, 0x3e, 0x52, 0xce, 0x01,
+            0x62, 0xba, 0xa9, 0x58, 0x53, 0x2b, 0x0b, 0xbc, 0x2b, 0xc7, 0x85,
+            0x68, 0x1f, 0xcf, 0x37, 0x48, 0x5d, 0xff, 0x65, 0x95, 0xe0, 0x1e,
+            0x73, 0x9c, 0x8a, 0xc9, 0xef, 0xba, 0x31, 0xb9, 0x85, 0xd5, 0xf6,
+            0x56, 0xcc, 0x09, 0x24, 0x32, 0xd7, 0x81, 0xdb, 0x95, 0x22, 0x17,
+            0x24, 0x87, 0x64, 0x1c, 0x4d, 0x3a, 0xb8, 0xec, 0xe0, 0x1e, 0x39,
+            0xbc, 0x85, 0xb1, 0x54, 0x36, 0x61, 0x47, 0x75, 0xa9, 0x8b, 0xa8,
+            0xfa, 0x12, 0xd4, 0x6f, 0x9b, 0x35, 0xe2, 0xa5, 0x5e, 0xb7, 0x2d,
+            0x7f, 0x85, 0x18, 0x1a, 0x36, 0x66, 0x63, 0x38, 0x7d, 0xdc, 0x20,
+            0x55, 0x18, 0x07, 0xe0, 0x07, 0x67, 0x3b, 0xd7, 0xe2, 0x6b, 0xf9,
+            0xb2, 0x9b, 0x5a, 0xb1, 0x0a, 0x1c, 0xa8, 0x7c, 0xbb, 0x7a, 0xd9,
+            0x7e, 0x99, 0xeb, 0x66, 0x95, 0x9c, 0x2a, 0x9b, 0xc3, 0xcb, 0xde,
+            0x47, 0x07, 0xff, 0x77, 0x20, 0xb1, 0x10, 0xfa, 0x95, 0x35, 0x46,
+            0x74, 0xe3, 0x95, 0x81, 0x2e, 0x47, 0xa0, 0xae, 0x53, 0xb4, 0x64,
+            0xdc, 0xb2, 0xd1, 0xf3, 0x45, 0xdf, 0x36, 0x0d, 0xc2, 0x27, 0x27,
+            0x0c, 0x75, 0x06, 0x76, 0xf6, 0x72, 0x4e, 0xb4, 0x79, 0xf0, 0xd2,
+            0xfb, 0xb6, 0x12, 0x44, 0x29, 0x99, 0x04, 0x57, 0xac, 0x6c, 0x91,
+            0x67, 0xf4, 0x0a, 0xab, 0x73, 0x99, 0x98, 0xf3, 0x8b, 0x9e, 0xcc,
+            0xb2, 0x4f, 0xd4, 0x7c, 0x84, 0x10, 0x13, 0x1b, 0xf6, 0x5a, 0x52,
+            0xaf, 0x84, 0x12, 0x75, 0xd5, 0xb3, 0xd1, 0x88, 0x0b, 0x19, 0x7d,
+            0xf2, 0xb5, 0xde, 0xa3, 0xe6, 0xde, 0x56, 0xeb, 0xce, 0x3f, 0xfb,
+            0x6e, 0x92, 0x77, 0xa8, 0x20, 0x82, 0xf8, 0xd9, 0x67, 0x7a, 0x67,
+            0x67, 0x08, 0x9b, 0x67, 0x1e, 0xbd, 0x24, 0x4c, 0x21, 0x4f, 0x0b,
+            0xde, 0x95, 0xc2, 0xbe, 0xb0, 0x2c, 0xd1, 0x17, 0x2d, 0x58, 0xbd,
+            0xf3, 0x9d, 0xce, 0x56, 0xff, 0x68, 0xeb, 0x35, 0xab, 0x39, 0xb4,
+            0x9b, 0x4e, 0xac, 0x7c, 0x81, 0x5e, 0xa6, 0x04, 0x51, 0xd6, 0xe6,
+            0xab, 0x82, 0x11, 0x91, 0x18, 0xdf, 0x02, 0xa5, 0x86, 0x84, 0x4a,
+            0x9f, 0xfe, 0x16, 0x2b, 0xa0, 0x06, 0xd0, 0x66, 0x9e, 0xf5, 0x76,
+            0x68, 0xca, 0xb3, 0x8b, 0x62, 0xf7, 0x1a, 0x25, 0x23, 0xa0, 0x84,
+            0x85, 0x2c, 0xd1, 0xd0, 0x79, 0xb3, 0x65, 0x8d, 0xc2, 0xf3, 0xe8,
+            0x79, 0x49, 0xb5, 0x50, 0xba, 0xb3, 0xe1, 0x77, 0xcf, 0xc4, 0x9e,
+            0xd1, 0x90, 0xdf, 0xf0, 0x63, 0x0e, 0x43, 0x07, 0x7c, 0x30, 0xde,
+            0x8f, 0x6a, 0xe0, 0x81, 0x53, 0x7f, 0x1e, 0x83, 0xda, 0x53, 0x7d,
+            0xa9, 0x80, 0xaf, 0xa6, 0x68, 0xe7, 0xb7, 0xfb, 0x25, 0x30, 0x1c,
+            0xf7, 0x41, 0x52, 0x4b, 0xe3, 0xc4, 0x98, 0x84, 0xb4, 0x28, 0x21,
+            0xf1, 0x75, 0x52, 0xfb, 0xd1, 0x93, 0x1a, 0x81, 0x30, 0x17, 0xb6,
+            0xb6, 0x59, 0x0a, 0x41, 0xea, 0x18, 0xb6, 0xba, 0x49, 0xcd, 0x48,
+            0xa4, 0x40, 0xbd, 0x9a, 0x33, 0x46, 0xa7, 0x62, 0x3f, 0xb4, 0xba,
+            0x34, 0xa3, 0xee, 0x57, 0x1e, 0x3c, 0x73, 0x1f, 0x35, 0xa7, 0xa3,
+            0xcf, 0x25, 0xb5, 0x51, 0xa6, 0x80, 0xfa, 0x68, 0x76, 0x35, 0x07,
+            0xb7, 0xfd, 0xe3, 0xaa, 0xf0, 0x23, 0xc5, 0x0b, 0x9d, 0x22, 0xda,
+            0x68, 0x76, 0xba, 0x33, 0x7e, 0xb5, 0xe9, 0xdd, 0x9e, 0xc3, 0xda,
+            0xf9, 0x70, 0x24, 0x2b, 0x6c, 0x5a, 0xab, 0x3a, 0xa4, 0xb2, 0x96,
+            0xad, 0x8b, 0x9f, 0x68, 0x32, 0xf6, 0x86, 0xef, 0x70, 0xfa, 0x93,
+            0x8b, 0x31, 0xb4, 0xe5, 0xdd, 0xd7, 0x36, 0x44, 0x42, 0xd3, 0xea,
+            0x72, 0xe7, 0x3d, 0x66, 0x8f, 0xb0, 0x93, 0x77, 0x96, 0xf4, 0x62,
+            0x92, 0x3a, 0x81, 0xa4, 0x7e, 0x1c, 0xee, 0x74, 0x26, 0xff, 0x6d,
+            0x92, 0x21, 0x26, 0x9b, 0x5a, 0x62, 0xec, 0x03, 0xd6, 0xec, 0x94,
+            0xd1, 0x26, 0x06, 0xcb, 0x48, 0x55, 0x60, 0xba, 0xb5, 0x74, 0x81,
+            0x60, 0x09, 0xe9, 0x65, 0x04, 0x24, 0x93, 0x85, 0xbb, 0x61, 0xa8,
+            0x19, 0xbe, 0x04, 0xf6, 0x2c, 0x20, 0x66, 0x21, 0x4d, 0x83, 0x60,
+            0xa2, 0x02, 0x2b, 0xeb, 0x31, 0x62, 0x40, 0xb6, 0xc7, 0xd7, 0x8b,
+            0xbe, 0x56, 0xc1, 0x30, 0x82, 0xe0, 0xca, 0x27, 0x26, 0x61, 0x21,
+            0x0a, 0xbf, 0x02, 0x0b, 0xf3, 0xb5, 0x78, 0x3f, 0x14, 0x26, 0x43,
+            0x6c, 0xf9, 0xff, 0x41, 0x84, 0x05, 0x93, 0xa5, 0xd0, 0x63, 0x8d,
+            0x32, 0xfc, 0x51, 0xc5, 0xc6, 0x5f, 0xf2, 0x91, 0xa3, 0xa7, 0xa5,
+            0x2f, 0xd6, 0x77, 0x5e, 0x62, 0x3a, 0x44, 0x39, 0xcc, 0x08, 0xdd,
+            0x25, 0x58, 0x2f, 0xeb, 0xc9, 0x44, 0xef, 0x92, 0xd8, 0xdb, 0xd3,
+            0x29, 0xc9, 0x1d, 0xe3, 0xe9, 0xc9, 0x58, 0x2e, 0x41, 0xf1, 0x7f,
+            0x3d, 0x18, 0x6f, 0x10, 0x4a, 0xd3, 0xf9, 0x09, 0x95, 0x11, 0x6c,
+            0x68, 0x2a, 0x2a, 0x14, 0xa3, 0xb4, 0xb1, 0xf5, 0x47, 0xc3, 0x35,
+            0xf0, 0xbe, 0x71, 0x0f, 0xc9, 0xfc, 0x03, 0xe0, 0xe5, 0x87, 0xb8,
+            0xcd, 0xa3, 0x1c, 0xe6, 0x5b, 0x96, 0x98, 0x78, 0xa4, 0xad, 0x42,
+            0x83, 0xe6, 0xd5, 0xb0, 0x37, 0x3f, 0x43, 0xda, 0x86, 0xe9, 0xe0,
+            0xff, 0xe1, 0xae, 0x0f, 0xdd, 0xd3, 0x51, 0x62, 0x55, 0xbd, 0x74,
+            0x56, 0x6f, 0x36, 0xa3, 0x87, 0x03, 0xd5, 0xf3, 0x42, 0x49, 0xde,
+            0xd1, 0xf6, 0x6b, 0x3d, 0x9b, 0x45, 0xb9, 0xaf, 0x2c, 0xcf, 0xef,
+            0xe9, 0x84, 0xe1, 0x33, 0x76, 0xb1, 0xb2, 0xc6, 0x40, 0x4a, 0xa4,
+            0x8c, 0x80, 0x26, 0x13, 0x23, 0x43, 0xda, 0x3f, 0x3a, 0x33, 0x65,
+            0x9e, 0xc1, 0xb3, 0xe9, 0x50, 0x80, 0x54, 0x0b, 0x28, 0xb7, 0xf3,
+            0xfc, 0xd3, 0x5f, 0xa5, 0xd8, 0x43, 0xb5, 0x79, 0xa8, 0x4c, 0x08,
+            0x91, 0x21, 0xa6, 0x0d, 0x8c, 0x17, 0x54, 0x91, 0x5c, 0x34, 0x4e,
+            0xea, 0xf4, 0x5a, 0x9b, 0xf2, 0x7d, 0xc0, 0xc1, 0xe7, 0x84, 0x16,
+            0x16, 0x91, 0x22, 0x09, 0x13, 0x13, 0xeb, 0x0e, 0x87, 0x55, 0x5a,
+            0xbd, 0x70, 0x66, 0x26, 0xe5, 0x57, 0xfc, 0x36, 0xa0, 0x4f, 0xcd,
+            0x19, 0x1a, 0x58, 0x82, 0x91, 0x04, 0xd6, 0x07, 0x5c, 0x55, 0x94,
+            0xf6, 0x27, 0xca, 0x50, 0x6b, 0xf1, 0x81, 0xda, 0xec, 0x94, 0x0f,
+            0x4a, 0x4f, 0x3a, 0xf0, 0x07, 0x4e, 0xee, 0x89, 0xda, 0xac, 0xde,
+            0x67, 0x58, 0x31, 0x26, 0x22, 0xd4, 0xfa, 0x67, 0x5b, 0x39, 0xf7,
+            0x28, 0xe0, 0x62, 0xd2, 0xbe, 0xe6, 0x80, 0xd8, 0xf4, 0x1a, 0x59,
+            0x7c, 0x26, 0x26, 0x48, 0xbb, 0x18, 0xbc, 0xfc, 0x13, 0xc8, 0xb3,
+            0xd9, 0x7b, 0x1a, 0x77, 0xb2, 0xac, 0x3a, 0xf7, 0x45, 0xd6, 0x1a,
+            0x34, 0xcc, 0x47, 0x09, 0x86, 0x5b, 0xac, 0x82, 0x4a, 0x94, 0xbb,
+            0x19, 0x05, 0x80, 0x15, 0xe4, 0xe4, 0x2d, 0xea, 0x53, 0x88, 0xb9,
+            0x11, 0xe7, 0x6d, 0x28, 0x56, 0xd6, 0x8c, 0xf6, 0xcf, 0x39, 0x41,
+            0x85,
+        ];
+
+        assert_encrypt_initial_pkt(
+            &mut header,
+            &dcid,
+            &frames,
+            2,
+            4,
+            false,
+            &pkt,
+        );
+    }
+
+    #[test]
+    fn encrypt_server_initial() {
+        let mut header = [
+            0xc1, 0xff, 0x00, 0x00, 0x1d, 0x00, 0x08, 0xf0, 0x67, 0xa5, 0x50,
+            0x2a, 0x42, 0x62, 0xb5, 0x00, 0x40, 0x74, 0x00, 0x01,
+        ];
+
+        let dcid = [0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08];
+
+        let frames = [
+            0x0d, 0x00, 0x00, 0x00, 0x00, 0x18, 0x41, 0x0a, 0x02, 0x00, 0x00,
+            0x56, 0x03, 0x03, 0xee, 0xfc, 0xe7, 0xf7, 0xb3, 0x7b, 0xa1, 0xd1,
+            0x63, 0x2e, 0x96, 0x67, 0x78, 0x25, 0xdd, 0xf7, 0x39, 0x88, 0xcf,
+            0xc7, 0x98, 0x25, 0xdf, 0x56, 0x6d, 0xc5, 0x43, 0x0b, 0x9a, 0x04,
+            0x5a, 0x12, 0x00, 0x13, 0x01, 0x00, 0x00, 0x2e, 0x00, 0x33, 0x00,
+            0x24, 0x00, 0x1d, 0x00, 0x20, 0x9d, 0x3c, 0x94, 0x0d, 0x89, 0x69,
+            0x0b, 0x84, 0xd0, 0x8a, 0x60, 0x99, 0x3c, 0x14, 0x4e, 0xca, 0x68,
+            0x4d, 0x10, 0x81, 0x28, 0x7c, 0x83, 0x4d, 0x53, 0x11, 0xbc, 0xf3,
+            0x2b, 0xb9, 0xda, 0x1a, 0x00, 0x2b, 0x00, 0x02, 0x03, 0x04,
+        ];
+
+        let pkt = [
+            0xca, 0xff, 0x00, 0x00, 0x1d, 0x00, 0x08, 0xf0, 0x67, 0xa5, 0x50,
+            0x2a, 0x42, 0x62, 0xb5, 0x00, 0x40, 0x74, 0xaa, 0xf2, 0xf0, 0x07,
+            0x82, 0x3a, 0x5d, 0x3a, 0x12, 0x07, 0xc8, 0x6e, 0xe4, 0x91, 0x32,
+            0x82, 0x4f, 0x04, 0x65, 0x24, 0x3d, 0x08, 0x2d, 0x86, 0x8b, 0x10,
+            0x7a, 0x38, 0x09, 0x2b, 0xc8, 0x05, 0x28, 0x66, 0x4c, 0xbf, 0x94,
+            0x56, 0xeb, 0xf2, 0x76, 0x73, 0xfb, 0x5f, 0xa5, 0x06, 0x1a, 0xb5,
+            0x73, 0xc9, 0xf0, 0x01, 0xb8, 0x1d, 0xa0, 0x28, 0xa0, 0x0d, 0x52,
+            0xab, 0x00, 0xb1, 0x5b, 0xeb, 0xaa, 0x70, 0x64, 0x0e, 0x10, 0x6c,
+            0xf2, 0xac, 0xd0, 0x43, 0xe9, 0xc6, 0xb4, 0x41, 0x1c, 0x0a, 0x79,
+            0x63, 0x71, 0x34, 0xd8, 0x99, 0x37, 0x01, 0xfe, 0x77, 0x9e, 0x58,
+            0xc2, 0xfe, 0x75, 0x3d, 0x14, 0xb0, 0x56, 0x40, 0x21, 0x56, 0x5e,
+            0xa9, 0x2e, 0x57, 0xbc, 0x6f, 0xaf, 0x56, 0xdf, 0xc7, 0xa4, 0x08,
+            0x70, 0xe6,
+        ];
+
+        assert_encrypt_initial_pkt(&mut header, &dcid, &frames, 1, 2, true, &pkt);
+    }
+
+    #[test]
+    fn encrypt_server_initial_old() {
+        let mut header = [
+            0xc1, 0xff, 0x00, 0x00, 0x1c, 0x00, 0x08, 0xf0, 0x67, 0xa5, 0x50,
+            0x2a, 0x42, 0x62, 0xb5, 0x00, 0x40, 0x74, 0x00, 0x01,
+        ];
+
+        let dcid = [0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08];
+
+        let frames = [
+            0x0d, 0x00, 0x00, 0x00, 0x00, 0x18, 0x41, 0x0a, 0x02, 0x00, 0x00,
+            0x56, 0x03, 0x03, 0xee, 0xfc, 0xe7, 0xf7, 0xb3, 0x7b, 0xa1, 0xd1,
+            0x63, 0x2e, 0x96, 0x67, 0x78, 0x25, 0xdd, 0xf7, 0x39, 0x88, 0xcf,
+            0xc7, 0x98, 0x25, 0xdf, 0x56, 0x6d, 0xc5, 0x43, 0x0b, 0x9a, 0x04,
+            0x5a, 0x12, 0x00, 0x13, 0x01, 0x00, 0x00, 0x2e, 0x00, 0x33, 0x00,
+            0x24, 0x00, 0x1d, 0x00, 0x20, 0x9d, 0x3c, 0x94, 0x0d, 0x89, 0x69,
+            0x0b, 0x84, 0xd0, 0x8a, 0x60, 0x99, 0x3c, 0x14, 0x4e, 0xca, 0x68,
+            0x4d, 0x10, 0x81, 0x28, 0x7c, 0x83, 0x4d, 0x53, 0x11, 0xbc, 0xf3,
+            0x2b, 0xb9, 0xda, 0x1a, 0x00, 0x2b, 0x00, 0x02, 0x03, 0x04,
+        ];
+
+        let pkt = [
+            0xc9, 0xff, 0x00, 0x00, 0x1c, 0x00, 0x08, 0xf0, 0x67, 0xa5, 0x50,
+            0x2a, 0x42, 0x62, 0xb5, 0x00, 0x40, 0x74, 0x16, 0x8b, 0xf2, 0x2b,
+            0x70, 0x02, 0x59, 0x6f, 0x99, 0xae, 0x67, 0xab, 0xf6, 0x5a, 0x58,
+            0x52, 0xf5, 0x4f, 0x58, 0xc3, 0x7c, 0x80, 0x86, 0x82, 0xe2, 0xe4,
+            0x04, 0x92, 0xd8, 0xa3, 0x89, 0x9f, 0xb0, 0x4f, 0xc0, 0xaf, 0xe9,
+            0xaa, 0xbc, 0x87, 0x67, 0xb1, 0x8a, 0x0a, 0xa4, 0x93, 0x53, 0x74,
+            0x26, 0x37, 0x3b, 0x48, 0xd5, 0x02, 0x21, 0x4d, 0xd8, 0x56, 0xd6,
+            0x3b, 0x78, 0xce, 0xe3, 0x7b, 0xc6, 0x64, 0xb3, 0xfe, 0x86, 0xd4,
+            0x87, 0xac, 0x7a, 0x77, 0xc5, 0x30, 0x38, 0xa3, 0xcd, 0x32, 0xf0,
+            0xb5, 0x00, 0x4d, 0x9f, 0x57, 0x54, 0xc4, 0xf7, 0xf2, 0xd1, 0xf3,
+            0x5c, 0xf3, 0xf7, 0x11, 0x63, 0x51, 0xc9, 0x2b, 0xda, 0x5b, 0x23,
+            0xc8, 0x10, 0x34, 0xab, 0x74, 0xf5, 0x4c, 0xb1, 0xbd, 0x72, 0x95,
+            0x12, 0x56,
+        ];
+
+        assert_encrypt_initial_pkt(&mut header, &dcid, &frames, 1, 2, true, &pkt);
+    }
+
+    #[test]
+    fn encrypt_chacha20() {
+        let secret = [
+            0x9a, 0xc3, 0x12, 0xa7, 0xf8, 0x77, 0x46, 0x8e, 0xbe, 0x69, 0x42,
+            0x27, 0x48, 0xad, 0x00, 0xa1, 0x54, 0x43, 0xf1, 0x82, 0x03, 0xa0,
+            0x7d, 0x60, 0x60, 0xf6, 0x88, 0xf3, 0x0f, 0x21, 0x63, 0x2b,
+        ];
+
+        let mut header = [0x42, 0x00, 0xbf, 0xf4];
+
+        let expected_pkt = [
+            0x4c, 0xfe, 0x41, 0x89, 0x65, 0x5e, 0x5c, 0xd5, 0x5c, 0x41, 0xf6,
+            0x90, 0x80, 0x57, 0x5d, 0x79, 0x99, 0xc2, 0x5a, 0x5b, 0xfb,
+        ];
+
+        let mut b = octets::OctetsMut::with_slice(&mut header);
+
+        let hdr = Header::from_bytes(&mut b, 0).unwrap();
+        assert_eq!(hdr.ty, Type::Short);
+
+        let mut out = vec![0; expected_pkt.len()];
+        let mut b = octets::OctetsMut::with_slice(&mut out);
+
+        b.put_bytes(&header).unwrap();
+
+        let alg = crypto::Algorithm::ChaCha20_Poly1305;
+
+        let aead = crypto::Seal::from_secret(alg, &secret).unwrap();
+
+        let pn = 654_360_564;
+        let pn_len = 3;
+
+        let frames = [01];
+
+        let overhead = aead.alg().tag_len();
+
+        let payload_len = frames.len() + overhead;
+
+        let payload_offset = b.off();
+
+        b.put_bytes(&frames).unwrap();
+
+        let written =
+            encrypt_pkt(&mut b, pn, pn_len, payload_len, payload_offset, &aead)
+                .unwrap();
+
+        assert_eq!(written, expected_pkt.len());
+        assert_eq!(&out[..written], &expected_pkt[..]);
+    }
+
+    #[test]
+    fn decrypt_pkt_underflow() {
+        let mut buf = [0; 65535];
+        let mut b = octets::OctetsMut::with_slice(&mut buf);
+
+        let hdr = Header {
+            ty: Type::Initial,
+            version: crate::PROTOCOL_VERSION,
+            dcid: Vec::new(),
+            scid: Vec::new(),
+            pkt_num: 0,
+            pkt_num_len: 0,
+            token: None,
+            versions: None,
+            key_phase: false,
+        };
+
+        hdr.to_bytes(&mut b).unwrap();
+
+        b.put_bytes(&[0; 50]).unwrap();
+
+        let payload_len = b.get_varint().unwrap() as usize;
+
+        let (aead, _) =
+            crypto::derive_initial_key_material(b"", hdr.version, true).unwrap();
+
+        assert_eq!(
+            decrypt_pkt(&mut b, 0, 1, payload_len, &aead),
+            Err(Error::InvalidPacket)
+        );
+    }
+}
diff --git a/src/rand.rs b/src/rand.rs
new file mode 100644
index 0000000..dbd0a7f
--- /dev/null
+++ b/src/rand.rs
@@ -0,0 +1,64 @@
+// Copyright (C) 2018-2019, Cloudflare, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright notice,
+//       this list of conditions and the following disclaimer.
+//
+//     * Redistributions in binary form must reproduce the above copyright
+//       notice, this list of conditions and the following disclaimer in the
+//       documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+pub fn rand_bytes(buf: &mut [u8]) {
+    unsafe {
+        RAND_bytes(buf.as_mut_ptr(), buf.len());
+    }
+}
+
+pub fn rand_u8() -> u8 {
+    let mut buf = [0; 1];
+
+    rand_bytes(&mut buf);
+
+    buf[0]
+}
+
+pub fn rand_u64() -> u64 {
+    let mut buf = [0; 8];
+
+    rand_bytes(&mut buf);
+
+    u64::from_ne_bytes(buf)
+}
+
+pub fn rand_u64_uniform(max: u64) -> u64 {
+    let chunk_size = u64::max_value() / max;
+    let end_of_last_chunk = chunk_size * max;
+
+    let mut r = rand_u64();
+
+    while r >= end_of_last_chunk {
+        r = rand_u64();
+    }
+
+    r / chunk_size
+}
+
+extern {
+    fn RAND_bytes(buf: *mut u8, len: libc::size_t) -> libc::c_int;
+}
diff --git a/src/ranges.rs b/src/ranges.rs
new file mode 100644
index 0000000..c390873
--- /dev/null
+++ b/src/ranges.rs
@@ -0,0 +1,614 @@
+// Copyright (C) 2018-2019, Cloudflare, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright notice,
+//       this list of conditions and the following disclaimer.
+//
+//     * Redistributions in binary form must reproduce the above copyright
+//       notice, this list of conditions and the following disclaimer in the
+//       documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// 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::ops::Range;
+
+use std::collections::btree_map;
+use std::collections::BTreeMap;
+use std::collections::Bound;
+
+#[derive(Clone, PartialEq, PartialOrd)]
+pub struct RangeSet {
+    inner: BTreeMap<u64, u64>,
+
+    capacity: usize,
+}
+
+impl RangeSet {
+    pub fn new(capacity: usize) -> Self {
+        RangeSet {
+            inner: BTreeMap::default(),
+            capacity,
+        }
+    }
+
+    // TODO: use RangeInclusive
+    pub fn insert(&mut self, item: Range<u64>) {
+        let mut start = item.start;
+        let mut end = item.end;
+
+        // Check if preceding existing range overlaps with the new one.
+        if let Some(r) = self.prev_to(start) {
+            // New range overlaps with existing range in the set, merge them.
+            if range_overlaps(&r, &item) {
+                self.inner.remove(&r.start);
+
+                start = std::cmp::min(start, r.start);
+                end = std::cmp::max(end, r.end);
+            }
+        }
+
+        // Check if following existing ranges overlap with the new one.
+        while let Some(r) = self.next_to(start) {
+            // Existing range is fully contained in the new range, remove it.
+            if item.contains(&r.start) && item.contains(&r.end) {
+                self.inner.remove(&r.start);
+                continue;
+            }
+
+            // New range doesn't overlap anymore, we are done.
+            if !range_overlaps(&r, &item) {
+                break;
+            }
+
+            // New range overlaps with existing range in the set, merge them.
+            self.inner.remove(&r.start);
+
+            start = std::cmp::min(start, r.start);
+            end = std::cmp::max(end, r.end);
+        }
+
+        if self.inner.len() >= self.capacity {
+            if let Some(first) = self.inner.keys().next().copied() {
+                self.inner.remove(&first);
+            }
+        }
+
+        self.inner.insert(start, end);
+    }
+
+    pub fn remove_until(&mut self, largest: u64) {
+        let ranges: Vec<Range<u64>> = self
+            .inner
+            .range((Bound::Unbounded, Bound::Included(&largest)))
+            .map(|(&s, &e)| (s..e))
+            .collect();
+
+        for r in ranges {
+            self.inner.remove(&r.start);
+
+            if r.end > largest + 1 {
+                let start = largest + 1;
+                self.insert(start..r.end);
+            }
+        }
+    }
+
+    pub fn push_item(&mut self, item: u64) {
+        #[allow(clippy::range_plus_one)]
+        self.insert(item..item + 1);
+    }
+
+    pub fn first(&self) -> Option<u64> {
+        self.flatten().next()
+    }
+
+    pub fn last(&self) -> Option<u64> {
+        self.flatten().next_back()
+    }
+
+    pub fn len(&self) -> usize {
+        self.inner.len()
+    }
+
+    pub fn iter(&self) -> Iter {
+        Iter {
+            inner: self.inner.iter(),
+        }
+    }
+
+    pub fn flatten(&self) -> Flatten {
+        Flatten {
+            inner: self.inner.iter(),
+            next: 0,
+            end: 0,
+        }
+    }
+
+    fn prev_to(&self, item: u64) -> Option<Range<u64>> {
+        self.inner
+            .range((Bound::Unbounded, Bound::Included(item)))
+            .map(|(&s, &e)| (s..e))
+            .next_back()
+    }
+
+    fn next_to(&self, item: u64) -> Option<Range<u64>> {
+        self.inner
+            .range((Bound::Included(item), Bound::Unbounded))
+            .map(|(&s, &e)| (s..e))
+            .next()
+    }
+}
+
+impl Default for RangeSet {
+    fn default() -> Self {
+        Self::new(std::usize::MAX)
+    }
+}
+
+// This implements comparison between `RangeSet` and standard `Range`. The idea
+// is that a `RangeSet` with no gaps (i.e. that only contains a single range)
+// is basically equvalent to a normal `Range` so they should be comparable.
+impl PartialEq<Range<u64>> for RangeSet {
+    fn eq(&self, other: &Range<u64>) -> bool {
+        // If there is more than one range it means that the range set is not
+        // contiguous, so can't be equal to a single range.
+        if self.inner.len() != 1 {
+            return false;
+        }
+
+        // Get the first and only range in the set.
+        let (first_start, first_end) = self.inner.iter().next().unwrap();
+
+        if (*first_start..*first_end) != *other {
+            return false;
+        }
+
+        true
+    }
+}
+
+impl std::fmt::Debug for RangeSet {
+    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+        let ranges: Vec<Range<u64>> = self
+            .iter()
+            .map(|mut r| {
+                r.end -= 1;
+                r
+            })
+            .collect();
+
+        write!(f, "{:?}", ranges)
+    }
+}
+
+pub struct Iter<'a> {
+    inner: btree_map::Iter<'a, u64, u64>,
+}
+
+impl<'a> Iterator for Iter<'a> {
+    type Item = Range<u64>;
+
+    fn next(&mut self) -> Option<Range<u64>> {
+        let (&start, &end) = self.inner.next()?;
+        Some(start..end)
+    }
+}
+
+impl<'a> DoubleEndedIterator for Iter<'a> {
+    fn next_back(&mut self) -> Option<Range<u64>> {
+        let (&start, &end) = self.inner.next_back()?;
+        Some(start..end)
+    }
+}
+
+impl<'a> ExactSizeIterator for Iter<'a> {
+    fn len(&self) -> usize {
+        self.inner.len()
+    }
+}
+
+pub struct Flatten<'a> {
+    inner: btree_map::Iter<'a, u64, u64>,
+    next: u64,
+    end: u64,
+}
+
+impl<'a> Iterator for Flatten<'a> {
+    type Item = u64;
+
+    fn next(&mut self) -> Option<u64> {
+        if self.next == self.end {
+            let (&start, &end) = self.inner.next()?;
+
+            self.next = start;
+            self.end = end;
+        }
+
+        let next = self.next;
+        self.next += 1;
+
+        Some(next)
+    }
+}
+
+impl<'a> DoubleEndedIterator for Flatten<'a> {
+    fn next_back(&mut self) -> Option<u64> {
+        if self.next == self.end {
+            let (&start, &end) = self.inner.next_back()?;
+
+            self.next = start;
+            self.end = end;
+        }
+
+        self.end -= 1;
+
+        Some(self.end)
+    }
+}
+
+fn range_overlaps(r: &Range<u64>, other: &Range<u64>) -> bool {
+    other.start >= r.start && other.start <= r.end ||
+        other.end >= r.start && other.end <= r.end
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn insert_non_overlapping() {
+        let mut r = RangeSet::default();
+        assert_eq!(r.inner.len(), 0);
+        let empty: &[u64] = &[];
+        assert_eq!(&r.flatten().collect::<Vec<u64>>(), &empty);
+
+        r.insert(4..7);
+        assert_eq!(r.inner.len(), 1);
+        assert_eq!(&r.flatten().collect::<Vec<u64>>(), &[4, 5, 6]);
+
+        r.insert(9..12);
+        assert_eq!(r.inner.len(), 2);
+        assert_eq!(&r.flatten().collect::<Vec<u64>>(), &[4, 5, 6, 9, 10, 11]);
+    }
+
+    #[test]
+    fn insert_contained() {
+        let mut r = RangeSet::default();
+
+        r.insert(4..7);
+        r.insert(9..12);
+        assert_eq!(&r.flatten().collect::<Vec<u64>>(), &[4, 5, 6, 9, 10, 11]);
+
+        r.insert(4..7);
+        assert_eq!(r.inner.len(), 2);
+        assert_eq!(&r.flatten().collect::<Vec<u64>>(), &[4, 5, 6, 9, 10, 11]);
+
+        r.insert(4..6);
+        assert_eq!(r.inner.len(), 2);
+        assert_eq!(&r.flatten().collect::<Vec<u64>>(), &[4, 5, 6, 9, 10, 11]);
+
+        r.insert(5..6);
+        assert_eq!(r.inner.len(), 2);
+        assert_eq!(&r.flatten().collect::<Vec<u64>>(), &[4, 5, 6, 9, 10, 11]);
+
+        r.insert(10..11);
+        assert_eq!(r.inner.len(), 2);
+        assert_eq!(&r.flatten().collect::<Vec<u64>>(), &[4, 5, 6, 9, 10, 11]);
+
+        r.insert(9..11);
+        assert_eq!(r.inner.len(), 2);
+        assert_eq!(&r.flatten().collect::<Vec<u64>>(), &[4, 5, 6, 9, 10, 11]);
+    }
+
+    #[test]
+    fn insert_overlapping() {
+        let mut r = RangeSet::default();
+
+        r.insert(3..6);
+        r.insert(9..12);
+        assert_eq!(&r.flatten().collect::<Vec<u64>>(), &[3, 4, 5, 9, 10, 11]);
+
+        r.insert(5..7);
+        assert_eq!(r.inner.len(), 2);
+        assert_eq!(&r.flatten().collect::<Vec<u64>>(), &[3, 4, 5, 6, 9, 10, 11]);
+
+        r.insert(10..15);
+        assert_eq!(r.inner.len(), 2);
+        assert_eq!(&r.flatten().collect::<Vec<u64>>(), &[
+            3, 4, 5, 6, 9, 10, 11, 12, 13, 14
+        ]);
+
+        r.insert(2..5);
+        assert_eq!(r.inner.len(), 2);
+        assert_eq!(&r.flatten().collect::<Vec<u64>>(), &[
+            2, 3, 4, 5, 6, 9, 10, 11, 12, 13, 14
+        ]);
+
+        r.insert(8..10);
+        assert_eq!(r.inner.len(), 2);
+        assert_eq!(&r.flatten().collect::<Vec<u64>>(), &[
+            2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14
+        ]);
+
+        r.insert(6..10);
+        assert_eq!(r.inner.len(), 1);
+        assert_eq!(&r.flatten().collect::<Vec<u64>>(), &[
+            2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14
+        ]);
+    }
+
+    #[test]
+    fn insert_overlapping_multi() {
+        let mut r = RangeSet::default();
+
+        r.insert(3..6);
+        r.insert(16..20);
+        assert_eq!(&r.flatten().collect::<Vec<u64>>(), &[
+            3, 4, 5, 16, 17, 18, 19
+        ]);
+
+        r.insert(10..11);
+        assert_eq!(r.inner.len(), 3);
+        assert_eq!(&r.flatten().collect::<Vec<u64>>(), &[
+            3, 4, 5, 10, 16, 17, 18, 19
+        ]);
+
+        r.insert(13..14);
+        assert_eq!(r.inner.len(), 4);
+        assert_eq!(&r.flatten().collect::<Vec<u64>>(), &[
+            3, 4, 5, 10, 13, 16, 17, 18, 19
+        ]);
+
+        r.insert(4..17);
+        assert_eq!(r.inner.len(), 1);
+        assert_eq!(&r.flatten().collect::<Vec<u64>>(), &[
+            3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19
+        ]);
+    }
+
+    #[test]
+    fn prev_to() {
+        let mut r = RangeSet::default();
+
+        r.insert(4..7);
+        r.insert(9..12);
+
+        assert_eq!(r.prev_to(2), None);
+        assert_eq!(r.prev_to(4), Some(4..7));
+        assert_eq!(r.prev_to(15), Some(9..12));
+        assert_eq!(r.prev_to(5), Some(4..7));
+        assert_eq!(r.prev_to(8), Some(4..7));
+    }
+
+    #[test]
+    fn next_to() {
+        let mut r = RangeSet::default();
+
+        r.insert(4..7);
+        r.insert(9..12);
+
+        assert_eq!(r.next_to(2), Some(4..7));
+        assert_eq!(r.next_to(12), None);
+        assert_eq!(r.next_to(15), None);
+        assert_eq!(r.next_to(5), Some(9..12));
+        assert_eq!(r.next_to(8), Some(9..12));
+    }
+
+    #[test]
+    fn push_item() {
+        let mut r = RangeSet::default();
+
+        r.insert(4..7);
+        r.insert(9..12);
+        assert_eq!(r.inner.len(), 2);
+        assert_eq!(&r.flatten().collect::<Vec<u64>>(), &[4, 5, 6, 9, 10, 11]);
+
+        r.push_item(15);
+        assert_eq!(r.inner.len(), 3);
+        assert_eq!(&r.flatten().collect::<Vec<u64>>(), &[
+            4, 5, 6, 9, 10, 11, 15
+        ]);
+
+        r.push_item(15);
+        assert_eq!(r.inner.len(), 3);
+        assert_eq!(&r.flatten().collect::<Vec<u64>>(), &[
+            4, 5, 6, 9, 10, 11, 15
+        ]);
+
+        r.push_item(1);
+        assert_eq!(r.inner.len(), 4);
+        assert_eq!(&r.flatten().collect::<Vec<u64>>(), &[
+            1, 4, 5, 6, 9, 10, 11, 15
+        ]);
+
+        r.push_item(12);
+        r.push_item(13);
+        r.push_item(14);
+        assert_eq!(r.inner.len(), 3);
+        assert_eq!(&r.flatten().collect::<Vec<u64>>(), &[
+            1, 4, 5, 6, 9, 10, 11, 12, 13, 14, 15
+        ]);
+
+        r.push_item(2);
+        r.push_item(3);
+        assert_eq!(r.inner.len(), 2);
+        assert_eq!(&r.flatten().collect::<Vec<u64>>(), &[
+            1, 2, 3, 4, 5, 6, 9, 10, 11, 12, 13, 14, 15
+        ]);
+
+        r.push_item(8);
+        r.push_item(7);
+        assert_eq!(r.inner.len(), 1);
+        assert_eq!(&r.flatten().collect::<Vec<u64>>(), &[
+            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
+        ]);
+    }
+
+    #[test]
+    fn flatten_rev() {
+        let mut r = RangeSet::default();
+        assert_eq!(r.inner.len(), 0);
+
+        let empty: &[u64] = &[];
+        assert_eq!(&r.flatten().collect::<Vec<u64>>(), &empty);
+
+        r.insert(4..7);
+        assert_eq!(r.inner.len(), 1);
+        assert_eq!(&r.flatten().collect::<Vec<u64>>(), &[4, 5, 6]);
+        assert_eq!(&r.flatten().rev().collect::<Vec<u64>>(), &[6, 5, 4]);
+
+        r.insert(9..12);
+        assert_eq!(r.inner.len(), 2);
+        assert_eq!(&r.flatten().collect::<Vec<u64>>(), &[4, 5, 6, 9, 10, 11]);
+        assert_eq!(&r.flatten().rev().collect::<Vec<u64>>(), &[
+            11, 10, 9, 6, 5, 4
+        ]);
+    }
+
+    #[test]
+    fn flatten_one() {
+        let mut r = RangeSet::default();
+        assert_eq!(r.inner.len(), 0);
+
+        let empty: &[u64] = &[];
+        assert_eq!(&r.flatten().collect::<Vec<u64>>(), &empty);
+
+        r.insert(0..1);
+        assert_eq!(r.inner.len(), 1);
+        assert_eq!(&r.flatten().collect::<Vec<u64>>(), &[0]);
+        assert_eq!(&r.flatten().rev().collect::<Vec<u64>>(), &[0]);
+    }
+
+    #[test]
+    fn remove_largest() {
+        let mut r = RangeSet::default();
+
+        r.insert(3..6);
+        r.insert(9..11);
+        r.insert(13..14);
+        r.insert(16..20);
+        assert_eq!(&r.flatten().collect::<Vec<u64>>(), &[
+            3, 4, 5, 9, 10, 13, 16, 17, 18, 19
+        ]);
+
+        r.remove_until(2);
+        assert_eq!(&r.flatten().collect::<Vec<u64>>(), &[
+            3, 4, 5, 9, 10, 13, 16, 17, 18, 19
+        ]);
+
+        r.remove_until(4);
+        assert_eq!(&r.flatten().collect::<Vec<u64>>(), &[
+            5, 9, 10, 13, 16, 17, 18, 19
+        ]);
+
+        r.remove_until(6);
+        assert_eq!(&r.flatten().collect::<Vec<u64>>(), &[
+            9, 10, 13, 16, 17, 18, 19
+        ]);
+
+        r.remove_until(10);
+        assert_eq!(&r.flatten().collect::<Vec<u64>>(), &[13, 16, 17, 18, 19]);
+
+        r.remove_until(17);
+        assert_eq!(&r.flatten().collect::<Vec<u64>>(), &[18, 19]);
+
+        r.remove_until(18);
+        assert_eq!(&r.flatten().collect::<Vec<u64>>(), &[19]);
+
+        r.remove_until(20);
+
+        let empty: &[u64] = &[];
+        assert_eq!(&r.flatten().collect::<Vec<u64>>(), &empty);
+    }
+
+    #[test]
+    fn eq_range() {
+        let mut r = RangeSet::default();
+        assert_ne!(r, 0..0);
+
+        let expected = 3..20;
+
+        r.insert(3..6);
+        assert_ne!(r, expected);
+
+        r.insert(16..20);
+        assert_ne!(r, expected);
+
+        r.insert(10..11);
+        assert_ne!(r, expected);
+
+        r.insert(13..14);
+        assert_ne!(r, expected);
+
+        r.insert(4..17);
+        assert_eq!(r, expected);
+    }
+
+    #[test]
+    fn first_last() {
+        let mut r = RangeSet::default();
+        assert_eq!(r.first(), None);
+        assert_eq!(r.last(), None);
+
+        r.insert(10..11);
+        assert_eq!(r.first(), Some(10));
+        assert_eq!(r.last(), Some(10));
+
+        r.insert(13..14);
+        assert_eq!(r.first(), Some(10));
+        assert_eq!(r.last(), Some(13));
+
+        r.insert(3..6);
+        assert_eq!(r.first(), Some(3));
+        assert_eq!(r.last(), Some(13));
+
+        r.insert(16..20);
+        assert_eq!(r.first(), Some(3));
+        assert_eq!(r.last(), Some(19));
+
+        r.insert(4..17);
+        assert_eq!(r.first(), Some(3));
+        assert_eq!(r.last(), Some(19));
+    }
+
+    #[test]
+    fn capacity() {
+        let mut r = RangeSet::new(3);
+        assert_eq!(r.first(), None);
+        assert_eq!(r.last(), None);
+
+        r.insert(10..11);
+        assert_eq!(r.first(), Some(10));
+        assert_eq!(r.last(), Some(10));
+
+        r.insert(13..14);
+        assert_eq!(r.first(), Some(10));
+        assert_eq!(r.last(), Some(13));
+
+        r.insert(3..6);
+        assert_eq!(r.first(), Some(3));
+        assert_eq!(r.last(), Some(13));
+
+        r.insert(16..20);
+        assert_eq!(r.first(), Some(10));
+        assert_eq!(r.last(), Some(19));
+
+        r.insert(4..17);
+        assert_eq!(r.first(), Some(4));
+        assert_eq!(r.last(), Some(19));
+    }
+}
diff --git a/src/recovery/cubic.rs b/src/recovery/cubic.rs
new file mode 100644
index 0000000..a53c68b
--- /dev/null
+++ b/src/recovery/cubic.rs
@@ -0,0 +1,564 @@
+// Copyright (C) 2019, Cloudflare, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright notice,
+//       this list of conditions and the following disclaimer.
+//
+//     * Redistributions in binary form must reproduce the above copyright
+//       notice, this list of conditions and the following disclaimer in the
+//       documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+//! CUBIC Congestion Control
+//!
+//! This implementation is based on the following RFC:
+//! https://tools.ietf.org/html/rfc8312
+//!
+//! Note that Slow Start can use HyStart++ when enabled.
+
+use std::cmp;
+
+use std::time::Duration;
+use std::time::Instant;
+
+use crate::packet;
+use crate::recovery;
+use crate::recovery::reno;
+
+use crate::recovery::Acked;
+use crate::recovery::CongestionControlOps;
+use crate::recovery::Recovery;
+
+pub static CUBIC: CongestionControlOps = CongestionControlOps {
+    on_packet_sent,
+    on_packet_acked,
+    congestion_event,
+    collapse_cwnd,
+};
+
+/// CUBIC Constants.
+///
+/// These are recommended value in RFC8312.
+const BETA_CUBIC: f64 = 0.7;
+
+const C: f64 = 0.4;
+
+/// CUBIC State Variables.
+///
+/// We need to keep those variables across the connection.
+/// k, w_max, w_last_max is described in the RFC.
+#[derive(Debug, Default)]
+pub struct State {
+    k: f64,
+
+    w_max: f64,
+
+    w_last_max: f64,
+
+    // Used in CUBIC fix (see on_packet_sent())
+    last_sent_time: Option<Instant>,
+
+    // Store cwnd increment during congestion avoidance.
+    cwnd_inc: usize,
+}
+
+/// CUBIC Functions.
+///
+/// Note that these calculations are based on a count of cwnd as bytes,
+/// not packets.
+/// Unit of t (duration) and RTT are based on seconds (f64).
+impl State {
+    // K = cbrt(w_max * (1 - beta_cubic) / C) (Eq. 2)
+    fn cubic_k(&self) -> f64 {
+        let w_max = self.w_max / recovery::MAX_DATAGRAM_SIZE as f64;
+        libm::cbrt(w_max * (1.0 - BETA_CUBIC) / C)
+    }
+
+    // W_cubic(t) = C * (t - K)^3 - w_max (Eq. 1)
+    fn w_cubic(&self, t: Duration) -> f64 {
+        let w_max = self.w_max / recovery::MAX_DATAGRAM_SIZE as f64;
+
+        (C * (t.as_secs_f64() - self.k).powi(3) + w_max) *
+            recovery::MAX_DATAGRAM_SIZE as f64
+    }
+
+    // W_est(t) = w_max * beta_cubic + 3 * (1 - beta_cubic) / (1 + beta_cubic) *
+    // (t / RTT) (Eq. 4)
+    fn w_est(&self, t: Duration, rtt: Duration) -> f64 {
+        let w_max = self.w_max / recovery::MAX_DATAGRAM_SIZE as f64;
+        (w_max * BETA_CUBIC +
+            3.0 * (1.0 - BETA_CUBIC) / (1.0 + BETA_CUBIC) * t.as_secs_f64() /
+                rtt.as_secs_f64()) *
+            recovery::MAX_DATAGRAM_SIZE as f64
+    }
+}
+
+fn collapse_cwnd(r: &mut Recovery) {
+    let cubic = &mut r.cubic_state;
+
+    r.congestion_recovery_start_time = None;
+
+    cubic.w_last_max = r.congestion_window as f64;
+    cubic.w_max = cubic.w_last_max;
+
+    // 4.7 Timeout - reduce ssthresh based on BETA_CUBIC
+    r.ssthresh = (r.congestion_window as f64 * BETA_CUBIC) as usize;
+    r.ssthresh = cmp::max(r.ssthresh, recovery::MINIMUM_WINDOW);
+
+    cubic.cwnd_inc = 0;
+
+    reno::collapse_cwnd(r);
+}
+
+fn on_packet_sent(r: &mut Recovery, sent_bytes: usize, now: Instant) {
+    // See https://github.com/torvalds/linux/commit/30927520dbae297182990bb21d08762bcc35ce1d
+    // First transmit when no packets in flight
+    let cubic = &mut r.cubic_state;
+
+    if let Some(last_sent_time) = cubic.last_sent_time {
+        if r.bytes_in_flight == 0 {
+            let delta = now - last_sent_time;
+
+            // We were application limited (idle) for a while.
+            // Shift epoch start to keep cwnd growth to cubic curve.
+            if let Some(recovery_start_time) = r.congestion_recovery_start_time {
+                if delta.as_nanos() > 0 {
+                    r.congestion_recovery_start_time =
+                        Some(recovery_start_time + delta);
+                }
+            }
+        }
+    }
+
+    cubic.last_sent_time = Some(now);
+
+    reno::on_packet_sent(r, sent_bytes, now);
+}
+
+fn on_packet_acked(
+    r: &mut Recovery, packet: &Acked, epoch: packet::Epoch, now: Instant,
+) {
+    let in_congestion_recovery = r.in_congestion_recovery(packet.time_sent);
+
+    r.bytes_in_flight = r.bytes_in_flight.saturating_sub(packet.size);
+
+    if in_congestion_recovery {
+        return;
+    }
+
+    if r.app_limited {
+        return;
+    }
+
+    if r.congestion_window < r.ssthresh {
+        // Slow start.
+        if r.hystart.enabled() && epoch == packet::EPOCH_APPLICATION {
+            let (cwnd, ssthresh) = r.hystart_on_packet_acked(packet, now);
+
+            r.congestion_window = cwnd;
+            r.ssthresh = ssthresh;
+        } else {
+            // Reno Slow Start.
+            r.congestion_window += packet.size;
+        }
+    } else {
+        // Congestion avoidance.
+        let ca_start_time;
+
+        // In LSS, use lss_start_time instead of congestion_recovery_start_time.
+        if r.hystart.enabled() &&
+            epoch == packet::EPOCH_APPLICATION &&
+            r.hystart.lss_start_time().is_some()
+        {
+            ca_start_time = r.hystart.lss_start_time().unwrap();
+
+            // Reset w_max and k when LSS started.
+            if r.cubic_state.w_max == 0.0 {
+                r.cubic_state.w_max = r.congestion_window as f64;
+                r.cubic_state.k = 0.0;
+            }
+        } else {
+            match r.congestion_recovery_start_time {
+                Some(t) => ca_start_time = t,
+                None => {
+                    // When we come here without congestion_event() triggered,
+                    // initialize congestion_recovery_start_time, w_max and k.
+                    ca_start_time = now;
+                    r.congestion_recovery_start_time = Some(now);
+
+                    r.cubic_state.w_max = r.congestion_window as f64;
+                    r.cubic_state.k = 0.0;
+                },
+            }
+        }
+
+        let t = now - ca_start_time;
+
+        // w_cubic(t + rtt)
+        let w_cubic = r.cubic_state.w_cubic(t + r.min_rtt);
+
+        // w_est(t)
+        let w_est = r.cubic_state.w_est(t, r.min_rtt);
+
+        let mut cubic_cwnd = r.congestion_window;
+
+        if w_cubic < w_est {
+            // TCP friendly region.
+            cubic_cwnd = cmp::max(cubic_cwnd, w_est as usize);
+        } else if cubic_cwnd < w_cubic as usize {
+            // Concave region or convex region use same increment.
+            let cubic_inc = (w_cubic - cubic_cwnd as f64) / cubic_cwnd as f64 *
+                recovery::MAX_DATAGRAM_SIZE as f64;
+
+            cubic_cwnd += cubic_inc as usize;
+        }
+
+        // When in Limited Slow Start, take the max of CA cwnd and
+        // LSS cwnd.
+        if r.hystart.enabled() &&
+            epoch == packet::EPOCH_APPLICATION &&
+            r.hystart.lss_start_time().is_some()
+        {
+            let (lss_cwnd, _) = r.hystart_on_packet_acked(packet, now);
+
+            cubic_cwnd = cmp::max(cubic_cwnd, lss_cwnd);
+        }
+
+        // Update the increment and increase cwnd by MSS.
+        r.cubic_state.cwnd_inc += cubic_cwnd - r.congestion_window;
+
+        // cwnd_inc can be more than 1 MSS in the late stage of max probing.
+        // however QUIC recovery draft 7.4 (Congestion Avoidance) limits
+        // the increase of cwnd to 1 max packet size per cwnd acknowledged.
+        if r.cubic_state.cwnd_inc >= recovery::MAX_DATAGRAM_SIZE {
+            r.congestion_window += recovery::MAX_DATAGRAM_SIZE;
+            r.cubic_state.cwnd_inc -= recovery::MAX_DATAGRAM_SIZE;
+        }
+    }
+}
+
+fn congestion_event(
+    r: &mut Recovery, time_sent: Instant, epoch: packet::Epoch, now: Instant,
+) {
+    let in_congestion_recovery = r.in_congestion_recovery(time_sent);
+
+    // Start a new congestion event if packet was sent after the
+    // start of the previous congestion recovery period.
+    if !in_congestion_recovery {
+        r.congestion_recovery_start_time = Some(now);
+
+        // Fast convergence
+        if r.cubic_state.w_max < r.cubic_state.w_last_max {
+            r.cubic_state.w_last_max = r.cubic_state.w_max;
+            r.cubic_state.w_max =
+                r.cubic_state.w_max as f64 * (1.0 + BETA_CUBIC) / 2.0;
+        } else {
+            r.cubic_state.w_last_max = r.cubic_state.w_max;
+        }
+
+        r.cubic_state.w_max = r.congestion_window as f64;
+        r.ssthresh = (r.cubic_state.w_max * BETA_CUBIC) as usize;
+        r.ssthresh = cmp::max(r.ssthresh, recovery::MINIMUM_WINDOW);
+        r.congestion_window = r.ssthresh;
+        r.cubic_state.k = r.cubic_state.cubic_k();
+
+        r.cubic_state.cwnd_inc =
+            (r.cubic_state.cwnd_inc as f64 * BETA_CUBIC) as usize;
+
+        if r.hystart.enabled() && epoch == packet::EPOCH_APPLICATION {
+            r.hystart.congestion_event();
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::recovery::hystart;
+
+    #[test]
+    fn cubic_init() {
+        let mut cfg = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
+        cfg.set_cc_algorithm(recovery::CongestionControlAlgorithm::CUBIC);
+
+        let r = Recovery::new(&cfg);
+
+        assert!(r.cwnd() > 0);
+        assert_eq!(r.bytes_in_flight, 0);
+    }
+
+    #[test]
+    fn cubic_send() {
+        let mut cfg = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
+        cfg.set_cc_algorithm(recovery::CongestionControlAlgorithm::CUBIC);
+
+        let mut r = Recovery::new(&cfg);
+
+        r.on_packet_sent_cc(1000, Instant::now());
+
+        assert_eq!(r.bytes_in_flight, 1000);
+    }
+
+    #[test]
+    fn cubic_slow_start() {
+        let mut cfg = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
+        cfg.set_cc_algorithm(recovery::CongestionControlAlgorithm::CUBIC);
+
+        let mut r = Recovery::new(&cfg);
+        let now = Instant::now();
+
+        let p = recovery::Sent {
+            pkt_num: 0,
+            frames: vec![],
+            time_sent: now,
+            time_acked: None,
+            time_lost: None,
+            size: 5000,
+            ack_eliciting: true,
+            in_flight: true,
+            delivered: 0,
+            delivered_time: now,
+            recent_delivered_packet_sent_time: now,
+            is_app_limited: false,
+            has_data: false,
+        };
+
+        // Send 5k x 4 = 20k, higher than default cwnd(~15k)
+        // to become no longer app limited
+        r.on_packet_sent_cc(p.size, now);
+        r.on_packet_sent_cc(p.size, now);
+        r.on_packet_sent_cc(p.size, now);
+        r.on_packet_sent_cc(p.size, now);
+
+        let cwnd_prev = r.cwnd();
+
+        let acked = vec![Acked {
+            pkt_num: p.pkt_num,
+            time_sent: p.time_sent,
+            size: p.size,
+        }];
+
+        r.on_packets_acked(acked, packet::EPOCH_APPLICATION, now);
+
+        // Check if cwnd increased by packet size (slow start)
+        assert_eq!(r.cwnd(), cwnd_prev + p.size);
+    }
+
+    #[test]
+    fn cubic_congestion_event() {
+        let mut cfg = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
+        cfg.set_cc_algorithm(recovery::CongestionControlAlgorithm::CUBIC);
+
+        let mut r = Recovery::new(&cfg);
+        let now = Instant::now();
+        let prev_cwnd = r.cwnd();
+
+        r.congestion_event(now, packet::EPOCH_APPLICATION, now);
+
+        // In CUBIC, after congestion event, cwnd will be reduced by (1 -
+        // CUBIC_BETA)
+        assert_eq!(prev_cwnd as f64 * BETA_CUBIC, r.cwnd() as f64);
+    }
+
+    #[test]
+    fn cubic_congestion_avoidance() {
+        let mut cfg = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
+        cfg.set_cc_algorithm(recovery::CongestionControlAlgorithm::CUBIC);
+
+        let mut r = Recovery::new(&cfg);
+        let now = Instant::now();
+        let prev_cwnd = r.cwnd();
+
+        // Fill up bytes_in_flight to avoid app_limited=true
+        r.on_packet_sent_cc(20000, now);
+
+        // Trigger congestion event to update ssthresh
+        r.congestion_event(now, packet::EPOCH_APPLICATION, now);
+
+        // After congestion event, cwnd will be reduced.
+        let cur_cwnd = (prev_cwnd as f64 * BETA_CUBIC) as usize;
+        assert_eq!(r.cwnd(), cur_cwnd);
+
+        let rtt = Duration::from_millis(100);
+
+        let acked = vec![Acked {
+            pkt_num: 0,
+            // To exit from recovery
+            time_sent: now + rtt,
+            size: 8000,
+        }];
+
+        // Ack more than cwnd bytes with rtt=100ms
+        r.update_rtt(rtt, Duration::from_millis(0), now);
+        r.on_packets_acked(acked, packet::EPOCH_APPLICATION, now + rtt * 3);
+
+        // After acking more than cwnd, expect cwnd increased by MSS
+        assert_eq!(r.cwnd(), cur_cwnd + recovery::MAX_DATAGRAM_SIZE);
+    }
+
+    #[test]
+    fn cubic_collapse_cwnd_and_restart() {
+        let mut cfg = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
+        cfg.set_cc_algorithm(recovery::CongestionControlAlgorithm::CUBIC);
+
+        let mut r = Recovery::new(&cfg);
+        let now = Instant::now();
+
+        // Fill up bytes_in_flight to avoid app_limited=true
+        r.on_packet_sent_cc(30000, now);
+
+        // Trigger congestion event to update ssthresh
+        r.congestion_event(now, packet::EPOCH_APPLICATION, now);
+
+        // After persistent congestion, cwnd should be MINIMUM_WINDOW
+        r.collapse_cwnd();
+        assert_eq!(r.cwnd(), recovery::MINIMUM_WINDOW);
+
+        let acked = vec![Acked {
+            pkt_num: 0,
+            // To exit from recovery
+            time_sent: now + Duration::from_millis(1),
+            size: 10000,
+        }];
+
+        // rtt = 100ms
+        let rtt = Duration::from_millis(100);
+        std::thread::sleep(rtt);
+
+        // Ack 10000 x 2 to exit from slow start
+        r.on_packets_acked(acked.clone(), packet::EPOCH_APPLICATION, now);
+        std::thread::sleep(rtt);
+
+        // This will make CC into congestion avoidance mode
+        r.on_packets_acked(acked, packet::EPOCH_APPLICATION, now);
+
+        assert_eq!(r.cwnd(), recovery::MINIMUM_WINDOW + 10000);
+    }
+
+    #[test]
+    fn cubic_hystart_limited_slow_start() {
+        let mut cfg = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
+        cfg.set_cc_algorithm(recovery::CongestionControlAlgorithm::CUBIC);
+        cfg.enable_hystart(true);
+
+        let mut r = Recovery::new(&cfg);
+        let now = Instant::now();
+        let pkt_num = 0;
+        let epoch = packet::EPOCH_APPLICATION;
+
+        let p = recovery::Sent {
+            pkt_num: 0,
+            frames: vec![],
+            time_sent: now,
+            time_acked: None,
+            time_lost: None,
+            size: recovery::MAX_DATAGRAM_SIZE,
+            ack_eliciting: true,
+            in_flight: true,
+            delivered: 0,
+            delivered_time: now,
+            recent_delivered_packet_sent_time: now,
+            is_app_limited: false,
+            has_data: false,
+        };
+
+        // 1st round.
+        let n_rtt_sample = hystart::N_RTT_SAMPLE;
+        let pkts_1st_round = n_rtt_sample as u64;
+        r.hystart.start_round(pkt_num);
+
+        let rtt_1st = 50;
+
+        // Send 1st round packets.
+        for _ in 0..n_rtt_sample {
+            r.on_packet_sent_cc(p.size, now);
+        }
+
+        // Receving Acks.
+        let now = now + Duration::from_millis(rtt_1st);
+        for _ in 0..n_rtt_sample {
+            r.update_rtt(
+                Duration::from_millis(rtt_1st),
+                Duration::from_millis(0),
+                now,
+            );
+
+            let acked = vec![Acked {
+                pkt_num: p.pkt_num,
+                time_sent: p.time_sent,
+                size: p.size,
+            }];
+
+            r.on_packets_acked(acked, epoch, now);
+        }
+
+        // Not in LSS yet.
+        assert_eq!(r.hystart.lss_start_time().is_some(), false);
+
+        // 2nd round.
+        r.hystart.start_round(pkts_1st_round * 2 + 1);
+
+        let mut rtt_2nd = 100;
+        let now = now + Duration::from_millis(rtt_2nd);
+
+        // Send 2nd round packets.
+        for _ in 0..n_rtt_sample + 1 {
+            r.on_packet_sent_cc(p.size, now);
+        }
+
+        // Receving Acks.
+        // Last ack will cause to exit to LSS.
+        let mut cwnd_prev = r.cwnd();
+
+        for _ in 0..n_rtt_sample + 1 {
+            cwnd_prev = r.cwnd();
+            r.update_rtt(
+                Duration::from_millis(rtt_2nd),
+                Duration::from_millis(0),
+                now,
+            );
+
+            let acked = vec![Acked {
+                pkt_num: p.pkt_num,
+                time_sent: p.time_sent,
+                size: p.size,
+            }];
+
+            r.on_packets_acked(acked, epoch, now);
+
+            // Keep increasing RTT so that hystart exits to LSS.
+            rtt_2nd += 4;
+        }
+
+        // Now we are in LSS.
+        assert_eq!(r.hystart.lss_start_time().is_some(), true);
+        assert_eq!(r.cwnd(), cwnd_prev);
+
+        // Ack'ing more packet to increase cwnd by 1 MSS
+        for _ in 0..3 {
+            let acked = vec![Acked {
+                pkt_num: p.pkt_num,
+                time_sent: p.time_sent,
+                size: p.size,
+            }];
+            r.on_packets_acked(acked, epoch, now);
+        }
+
+        assert_eq!(r.cwnd(), cwnd_prev + recovery::MAX_DATAGRAM_SIZE);
+    }
+}
diff --git a/src/recovery/delivery_rate.rs b/src/recovery/delivery_rate.rs
new file mode 100644
index 0000000..77fd248
--- /dev/null
+++ b/src/recovery/delivery_rate.rs
@@ -0,0 +1,302 @@
+// Copyright (C) 2020, Cloudflare, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright notice,
+//       this list of conditions and the following disclaimer.
+//
+//     * Redistributions in binary form must reproduce the above copyright
+//       notice, this list of conditions and the following disclaimer in the
+//       documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+//! Delivery rate estimation.
+//!
+//! This implements the algorithm for estimating delivery rate as described in
+//! https://tools.ietf.org/html/draft-cheng-iccrg-delivery-rate-estimation-00
+
+use std::cmp;
+
+use std::time::Duration;
+use std::time::Instant;
+
+use crate::recovery::Sent;
+
+#[derive(Default)]
+pub struct Rate {
+    delivered: usize,
+
+    delivered_time: Option<Instant>,
+
+    recent_delivered_packet_sent_time: Option<Instant>,
+
+    app_limited_at_pkt: usize,
+
+    rate_sample: RateSample,
+}
+
+impl Rate {
+    pub fn on_packet_sent(&mut self, pkt: &mut Sent, now: Instant) {
+        if self.delivered_time.is_none() {
+            self.delivered_time = Some(now);
+        }
+
+        if self.recent_delivered_packet_sent_time.is_none() {
+            self.recent_delivered_packet_sent_time = Some(now);
+        }
+
+        pkt.delivered = self.delivered;
+        pkt.delivered_time = self.delivered_time.unwrap();
+
+        pkt.recent_delivered_packet_sent_time =
+            self.recent_delivered_packet_sent_time.unwrap();
+
+        pkt.is_app_limited = self.app_limited_at_pkt > 0;
+    }
+
+    pub fn on_packet_acked(&mut self, pkt: &Sent, now: Instant) {
+        self.rate_sample.prior_time = Some(pkt.delivered_time);
+
+        self.delivered += pkt.size;
+        self.delivered_time = Some(now);
+
+        if pkt.delivered > self.rate_sample.prior_delivered {
+            self.rate_sample.prior_delivered = pkt.delivered;
+            self.rate_sample.is_app_limited = pkt.is_app_limited;
+
+            self.rate_sample.send_elapsed =
+                pkt.time_sent - pkt.recent_delivered_packet_sent_time;
+
+            self.rate_sample.ack_elapsed = self
+                .delivered_time
+                .unwrap()
+                .duration_since(pkt.delivered_time);
+
+            self.recent_delivered_packet_sent_time = Some(pkt.time_sent);
+        }
+    }
+
+    pub fn estimate(&mut self) {
+        if (self.app_limited_at_pkt > 0) &&
+            (self.delivered > self.app_limited_at_pkt)
+        {
+            self.app_limited_at_pkt = 0;
+        }
+
+        match self.rate_sample.prior_time {
+            Some(_) => {
+                self.rate_sample.delivered =
+                    self.delivered - self.rate_sample.prior_delivered;
+
+                self.rate_sample.interval = cmp::max(
+                    self.rate_sample.send_elapsed,
+                    self.rate_sample.ack_elapsed,
+                );
+            },
+            None => return,
+        }
+
+        if self.rate_sample.interval.as_secs_f64() > 0.0 {
+            self.rate_sample.delivery_rate = (self.rate_sample.delivered as f64 /
+                self.rate_sample.interval.as_secs_f64())
+                as u64;
+        }
+    }
+
+    pub fn check_app_limited(&mut self, bytes_in_flight: usize) {
+        let limited = self.delivered + bytes_in_flight;
+        self.app_limited_at_pkt = if limited > 0 { limited } else { 1 };
+    }
+
+    pub fn delivery_rate(&self) -> u64 {
+        self.rate_sample.delivery_rate
+    }
+}
+
+impl std::fmt::Debug for Rate {
+    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+        write!(f, "delivered={:?} ", self.delivered)?;
+
+        if let Some(t) = self.delivered_time {
+            write!(f, "delivered_time={:?} ", t.elapsed())?;
+        }
+
+        if let Some(t) = self.recent_delivered_packet_sent_time {
+            write!(f, "recent_delivered_packet_sent_time={:?} ", t.elapsed())?;
+        }
+
+        write!(f, "app_limited_at_pkt={:?} ", self.app_limited_at_pkt)?;
+
+        Ok(())
+    }
+}
+
+#[derive(Default)]
+struct RateSample {
+    delivery_rate: u64,
+
+    is_app_limited: bool,
+
+    interval: Duration,
+
+    delivered: usize,
+
+    prior_delivered: usize,
+
+    prior_time: Option<Instant>,
+
+    send_elapsed: Duration,
+
+    ack_elapsed: Duration,
+}
+
+impl std::fmt::Debug for RateSample {
+    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+        write!(f, "delivery_rate={:?} ", self.delivery_rate)?;
+        write!(f, "interval={:?} ", self.interval)?;
+        write!(f, "delivered={:?} ", self.delivered)?;
+        write!(f, "prior_delivered={:?} ", self.prior_delivered)?;
+        write!(f, "send_elapsed={:?} ", self.send_elapsed)?;
+        if let Some(t) = self.prior_time {
+            write!(f, "prior_time={:?} ", t.elapsed())?;
+        }
+        write!(f, "ack_elapsed={:?}", self.ack_elapsed)?;
+
+        Ok(())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    use crate::recovery::*;
+
+    #[test]
+    fn rate_check() {
+        let config = Config::new(0xbabababa).unwrap();
+        let mut recovery = Recovery::new(&config);
+
+        let mut pkt_1 = Sent {
+            pkt_num: 0,
+            frames: vec![],
+            time_sent: Instant::now(),
+            time_acked: None,
+            time_lost: None,
+            size: 1200,
+            ack_eliciting: true,
+            in_flight: true,
+            delivered: 0,
+            delivered_time: Instant::now(),
+            recent_delivered_packet_sent_time: Instant::now(),
+            is_app_limited: false,
+            has_data: false,
+        };
+
+        recovery
+            .delivery_rate
+            .on_packet_sent(&mut pkt_1, Instant::now());
+        std::thread::sleep(Duration::from_millis(50));
+        recovery
+            .delivery_rate
+            .on_packet_acked(&pkt_1, Instant::now());
+
+        let mut pkt_2 = Sent {
+            pkt_num: 1,
+            frames: vec![],
+            time_sent: Instant::now(),
+            time_acked: None,
+            time_lost: None,
+            size: 1200,
+            ack_eliciting: true,
+            in_flight: true,
+            delivered: 0,
+            delivered_time: Instant::now(),
+            recent_delivered_packet_sent_time: Instant::now(),
+            is_app_limited: false,
+            has_data: false,
+        };
+
+        recovery
+            .delivery_rate
+            .on_packet_sent(&mut pkt_2, Instant::now());
+        std::thread::sleep(Duration::from_millis(50));
+        recovery
+            .delivery_rate
+            .on_packet_acked(&pkt_2, Instant::now());
+        recovery.delivery_rate.estimate();
+
+        assert!(recovery.delivery_rate() > 0);
+    }
+
+    #[test]
+    fn app_limited_check() {
+        let config = Config::new(0xbabababa).unwrap();
+        let mut recvry = Recovery::new(&config);
+
+        let mut pkt_1 = Sent {
+            pkt_num: 0,
+            frames: vec![],
+            time_sent: Instant::now(),
+            time_acked: None,
+            time_lost: None,
+            size: 1200,
+            ack_eliciting: true,
+            in_flight: true,
+            delivered: 0,
+            delivered_time: Instant::now(),
+            recent_delivered_packet_sent_time: Instant::now(),
+            is_app_limited: false,
+            has_data: false,
+        };
+
+        recvry
+            .delivery_rate
+            .on_packet_sent(&mut pkt_1, Instant::now());
+        std::thread::sleep(Duration::from_millis(50));
+        recvry.delivery_rate.on_packet_acked(&pkt_1, Instant::now());
+
+        let mut pkt_2 = Sent {
+            pkt_num: 1,
+            frames: vec![],
+            time_sent: Instant::now(),
+            time_acked: None,
+            time_lost: None,
+            size: 1200,
+            ack_eliciting: true,
+            in_flight: true,
+            delivered: 0,
+            delivered_time: Instant::now(),
+            recent_delivered_packet_sent_time: Instant::now(),
+            is_app_limited: false,
+            has_data: false,
+        };
+
+        recvry.app_limited = true;
+        recvry
+            .delivery_rate
+            .check_app_limited(recvry.bytes_in_flight);
+        recvry
+            .delivery_rate
+            .on_packet_sent(&mut pkt_2, Instant::now());
+        std::thread::sleep(Duration::from_millis(50));
+        recvry.delivery_rate.on_packet_acked(&pkt_2, Instant::now());
+        recvry.delivery_rate.estimate();
+
+        assert_eq!(recvry.delivery_rate.app_limited_at_pkt, 0);
+    }
+}
diff --git a/src/recovery/hystart.rs b/src/recovery/hystart.rs
new file mode 100644
index 0000000..5d02739
--- /dev/null
+++ b/src/recovery/hystart.rs
@@ -0,0 +1,320 @@
+// Copyright (C) 2020, Cloudflare, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright notice,
+//       this list of conditions and the following disclaimer.
+//
+//     * Redistributions in binary form must reproduce the above copyright
+//       notice, this list of conditions and the following disclaimer in the
+//       documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+//! HyStart++
+//!
+//! This implementation is based on the following I-D:
+//!
+//! https://tools.ietf.org/html/draft-balasubramanian-tcpm-hystartplusplus-02
+
+use std::cmp;
+use std::time::Duration;
+use std::time::Instant;
+
+use crate::recovery;
+
+/// Constants from I-D.
+const LOW_CWND: usize = 16;
+
+const MIN_RTT_THRESH: Duration = Duration::from_millis(4);
+
+const MAX_RTT_THRESH: Duration = Duration::from_millis(16);
+
+pub const LSS_DIVISOR: f64 = 0.25;
+
+pub const N_RTT_SAMPLE: usize = 8;
+
+#[derive(Default)]
+pub struct Hystart {
+    enabled: bool,
+
+    window_end: Option<u64>,
+
+    last_round_min_rtt: Option<Duration>,
+
+    current_round_min_rtt: Option<Duration>,
+
+    rtt_sample_count: usize,
+
+    lss_start_time: Option<Instant>,
+}
+
+impl std::fmt::Debug for Hystart {
+    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+        write!(f, "window_end={:?} ", self.window_end)?;
+        write!(f, "last_round_min_rtt={:?} ", self.last_round_min_rtt)?;
+        write!(f, "current_round_min_rtt={:?} ", self.current_round_min_rtt)?;
+        write!(f, "rtt_sample_count={:?} ", self.rtt_sample_count)?;
+        write!(f, "lss_start_time={:?} ", self.lss_start_time)?;
+
+        Ok(())
+    }
+}
+
+impl Hystart {
+    pub fn new(enabled: bool) -> Self {
+        Self {
+            enabled,
+
+            ..Default::default()
+        }
+    }
+
+    pub fn enabled(&self) -> bool {
+        self.enabled
+    }
+
+    pub fn lss_start_time(&self) -> Option<Instant> {
+        self.lss_start_time
+    }
+
+    pub fn start_round(&mut self, pkt_num: u64) {
+        if self.window_end.is_none() {
+            *self = Hystart {
+                enabled: self.enabled,
+
+                window_end: Some(pkt_num),
+
+                last_round_min_rtt: self.current_round_min_rtt,
+
+                current_round_min_rtt: None,
+
+                rtt_sample_count: 0,
+
+                lss_start_time: None,
+            };
+        }
+    }
+
+    // Returns a new (ssthresh, cwnd) during slow start.
+    pub fn on_packet_acked(
+        &mut self, packet: &recovery::Acked, rtt: Duration, cwnd: usize,
+        ssthresh: usize, now: Instant,
+    ) -> (usize, usize) {
+        let mut ssthresh = ssthresh;
+        let mut cwnd = cwnd;
+
+        if self.lss_start_time().is_none() {
+            // Reno Slow Start.
+            cwnd += packet.size;
+
+            if let Some(current_round_min_rtt) = self.current_round_min_rtt {
+                self.current_round_min_rtt =
+                    Some(cmp::min(current_round_min_rtt, rtt));
+            } else {
+                self.current_round_min_rtt = Some(rtt);
+            }
+
+            self.rtt_sample_count += 1;
+
+            if cwnd >= (LOW_CWND * recovery::MAX_DATAGRAM_SIZE) &&
+                self.rtt_sample_count >= N_RTT_SAMPLE &&
+                self.current_round_min_rtt.is_some() &&
+                self.last_round_min_rtt.is_some()
+            {
+                // clamp(min_rtt_thresh, last_round_min_rtt/8,
+                // max_rtt_thresh)
+                let rtt_thresh = cmp::max(
+                    self.last_round_min_rtt.unwrap() / 8,
+                    MIN_RTT_THRESH,
+                );
+                let rtt_thresh = cmp::min(rtt_thresh, MAX_RTT_THRESH);
+
+                // Check if we can exit to LSS.
+                if self.current_round_min_rtt.unwrap() >=
+                    (self.last_round_min_rtt.unwrap() + rtt_thresh)
+                {
+                    ssthresh = cwnd;
+
+                    self.lss_start_time = Some(now);
+                }
+            }
+
+            // Check if we reached the end of the round.
+            if let Some(end_pkt_num) = self.window_end {
+                if packet.pkt_num >= end_pkt_num {
+                    // Start of a new round.
+                    self.window_end = None;
+                }
+            }
+        } else {
+            // LSS (Limited Slow Start).
+            let k = cwnd as f64 / (LSS_DIVISOR * ssthresh as f64);
+
+            cwnd += (packet.size as f64 / k) as usize;
+        }
+
+        (cwnd, ssthresh)
+    }
+
+    // Exit HyStart++ when entering congestion avoidance.
+    pub fn congestion_event(&mut self) {
+        if self.window_end.is_some() {
+            self.window_end = None;
+
+            self.lss_start_time = None;
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn start_round() {
+        let mut hspp = Hystart::default();
+        let pkt_num = 100;
+
+        hspp.start_round(pkt_num);
+
+        assert_eq!(hspp.window_end, Some(pkt_num));
+        assert_eq!(hspp.current_round_min_rtt, None);
+    }
+
+    #[test]
+    fn reno_slow_start() {
+        let mut hspp = Hystart::default();
+        let pkt_num = 100;
+        let size = 1000;
+        let now = Instant::now();
+
+        hspp.start_round(pkt_num);
+
+        assert_eq!(hspp.window_end, Some(pkt_num));
+
+        let p = recovery::Acked {
+            pkt_num,
+            time_sent: now + Duration::from_millis(10),
+            size,
+        };
+
+        let init_cwnd = 30000;
+        let init_ssthresh = 1000000;
+
+        let (cwnd, ssthresh) = hspp.on_packet_acked(
+            &p,
+            Duration::from_millis(10),
+            init_cwnd,
+            init_ssthresh,
+            now,
+        );
+
+        // Expecting Reno slow start.
+        assert_eq!(hspp.lss_start_time().is_some(), false);
+        assert_eq!((cwnd, ssthresh), (init_cwnd + size, init_ssthresh));
+    }
+
+    #[test]
+    fn limited_slow_start() {
+        let mut hspp = Hystart::default();
+        let size = 1000;
+        let now = Instant::now();
+
+        // 1st round rtt = 50ms
+        let rtt_1st = 50;
+
+        // end of 1st round
+        let pkt_1st = N_RTT_SAMPLE as u64;
+
+        hspp.start_round(pkt_1st);
+
+        assert_eq!(hspp.window_end, Some(pkt_1st));
+
+        let (mut cwnd, mut ssthresh) = (30000, 1000000);
+        let mut pkt_num = 0;
+
+        // 1st round.
+        for _ in 0..N_RTT_SAMPLE + 1 {
+            let p = recovery::Acked {
+                pkt_num,
+                time_sent: now + Duration::from_millis(pkt_num),
+                size,
+            };
+
+            // We use a fixed rtt for 1st round.
+            let rtt = Duration::from_millis(rtt_1st);
+
+            let (new_cwnd, new_ssthresh) =
+                hspp.on_packet_acked(&p, rtt, cwnd, ssthresh, now);
+
+            cwnd = new_cwnd;
+            ssthresh = new_ssthresh;
+
+            pkt_num += 1;
+        }
+
+        // 2nd round. rtt = 100ms to trigger LSS.
+        let rtt_2nd = 100;
+
+        hspp.start_round(pkt_1st * 2 + 1);
+
+        for _ in 0..N_RTT_SAMPLE + 1 {
+            let p = recovery::Acked {
+                pkt_num,
+                time_sent: now + Duration::from_millis(pkt_num),
+                size,
+            };
+
+            // Keep increasing rtt to simulate buffer queueing delay
+            // This is to exit from slow slart to LSS.
+            let rtt = Duration::from_millis(rtt_2nd + pkt_num * 4);
+
+            let (new_cwnd, new_ssthresh) =
+                hspp.on_packet_acked(&p, rtt, cwnd, ssthresh, now);
+
+            cwnd = new_cwnd;
+            ssthresh = new_ssthresh;
+
+            pkt_num += 1;
+        }
+
+        // At this point, cwnd exits to LSS mode.
+        assert_eq!(hspp.lss_start_time().is_some(), true);
+
+        // Check if current cwnd is in LSS.
+        let cur_ssthresh = 47000;
+        let k = cur_ssthresh as f64 / (LSS_DIVISOR * cur_ssthresh as f64);
+        let lss_cwnd = cur_ssthresh as f64 + size as f64 / k;
+
+        assert_eq!((cwnd, ssthresh), (lss_cwnd as usize, cur_ssthresh));
+    }
+
+    #[test]
+    fn congestion_event() {
+        let mut hspp = Hystart::default();
+        let pkt_num = 100;
+
+        hspp.start_round(pkt_num);
+
+        assert_eq!(hspp.window_end, Some(pkt_num));
+
+        // When moving into CA mode, window_end should be cleared.
+        hspp.congestion_event();
+
+        assert_eq!(hspp.window_end, None);
+    }
+}
diff --git a/src/recovery/mod.rs b/src/recovery/mod.rs
new file mode 100644
index 0000000..4621e24
--- /dev/null
+++ b/src/recovery/mod.rs
@@ -0,0 +1,1542 @@
+// Copyright (C) 2018-2019, Cloudflare, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright notice,
+//       this list of conditions and the following disclaimer.
+//
+//     * Redistributions in binary form must reproduce the above copyright
+//       notice, this list of conditions and the following disclaimer in the
+//       documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// 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::cmp;
+
+use std::str::FromStr;
+
+use std::time::Duration;
+use std::time::Instant;
+
+use std::collections::VecDeque;
+
+use crate::Config;
+use crate::Error;
+use crate::Result;
+
+use crate::frame;
+use crate::minmax;
+use crate::packet;
+use crate::ranges;
+
+// Loss Recovery
+const PACKET_THRESHOLD: u64 = 3;
+
+const TIME_THRESHOLD: f64 = 9.0 / 8.0;
+
+const GRANULARITY: Duration = Duration::from_millis(1);
+
+const INITIAL_RTT: Duration = Duration::from_millis(333);
+
+const PERSISTENT_CONGESTION_THRESHOLD: u32 = 3;
+
+const RTT_WINDOW: Duration = Duration::from_secs(300);
+
+const MAX_PTO_PROBES_COUNT: usize = 2;
+
+// Congestion Control
+const INITIAL_WINDOW_PACKETS: usize = 10;
+
+const INITIAL_WINDOW: usize = INITIAL_WINDOW_PACKETS * MAX_DATAGRAM_SIZE;
+
+const MINIMUM_WINDOW: usize = 2 * MAX_DATAGRAM_SIZE;
+
+const MAX_DATAGRAM_SIZE: usize = 1452;
+
+const LOSS_REDUCTION_FACTOR: f64 = 0.5;
+
+pub struct Recovery {
+    loss_detection_timer: Option<Instant>,
+
+    pto_count: u32,
+
+    time_of_last_sent_ack_eliciting_pkt: [Option<Instant>; packet::EPOCH_COUNT],
+
+    largest_acked_pkt: [u64; packet::EPOCH_COUNT],
+
+    largest_sent_pkt: [u64; packet::EPOCH_COUNT],
+
+    latest_rtt: Duration,
+
+    smoothed_rtt: Option<Duration>,
+
+    rttvar: Duration,
+
+    minmax_filter: minmax::Minmax<Duration>,
+
+    min_rtt: Duration,
+
+    pub max_ack_delay: Duration,
+
+    loss_time: [Option<Instant>; packet::EPOCH_COUNT],
+
+    sent: [VecDeque<Sent>; packet::EPOCH_COUNT],
+
+    pub lost: [Vec<frame::Frame>; packet::EPOCH_COUNT],
+
+    pub acked: [Vec<frame::Frame>; packet::EPOCH_COUNT],
+
+    pub lost_count: usize,
+
+    pub loss_probes: [usize; packet::EPOCH_COUNT],
+
+    in_flight_count: [usize; packet::EPOCH_COUNT],
+
+    app_limited: bool,
+
+    delivery_rate: delivery_rate::Rate,
+
+    // Congestion control.
+    cc_ops: &'static CongestionControlOps,
+
+    congestion_window: usize,
+
+    bytes_in_flight: usize,
+
+    ssthresh: usize,
+
+    bytes_acked: usize,
+
+    congestion_recovery_start_time: Option<Instant>,
+
+    cubic_state: cubic::State,
+
+    // HyStart++.
+    hystart: hystart::Hystart,
+}
+
+impl Recovery {
+    pub fn new(config: &Config) -> Self {
+        Recovery {
+            loss_detection_timer: None,
+
+            pto_count: 0,
+
+            time_of_last_sent_ack_eliciting_pkt: [None; packet::EPOCH_COUNT],
+
+            largest_acked_pkt: [std::u64::MAX; packet::EPOCH_COUNT],
+
+            largest_sent_pkt: [0; packet::EPOCH_COUNT],
+
+            latest_rtt: Duration::new(0, 0),
+
+            // This field should be initialized to `INITIAL_RTT` for the initial
+            // PTO calculation, but it also needs to be an `Option` to track
+            // whether any RTT sample was received, so the initial value is
+            // handled by the `rtt()` method instead.
+            smoothed_rtt: None,
+
+            minmax_filter: minmax::Minmax::new(Duration::new(0, 0)),
+
+            min_rtt: Duration::new(0, 0),
+
+            rttvar: INITIAL_RTT / 2,
+
+            max_ack_delay: Duration::new(0, 0),
+
+            loss_time: [None; packet::EPOCH_COUNT],
+
+            sent: [VecDeque::new(), VecDeque::new(), VecDeque::new()],
+
+            lost: [Vec::new(), Vec::new(), Vec::new()],
+
+            acked: [Vec::new(), Vec::new(), Vec::new()],
+
+            lost_count: 0,
+
+            loss_probes: [0; packet::EPOCH_COUNT],
+
+            in_flight_count: [0; packet::EPOCH_COUNT],
+
+            congestion_window: INITIAL_WINDOW,
+
+            bytes_in_flight: 0,
+
+            ssthresh: std::usize::MAX,
+
+            bytes_acked: 0,
+
+            congestion_recovery_start_time: None,
+
+            cc_ops: config.cc_algorithm.into(),
+
+            delivery_rate: delivery_rate::Rate::default(),
+
+            cubic_state: cubic::State::default(),
+
+            app_limited: false,
+
+            hystart: hystart::Hystart::new(config.hystart),
+        }
+    }
+
+    pub fn on_packet_sent(
+        &mut self, mut pkt: Sent, epoch: packet::Epoch,
+        handshake_status: HandshakeStatus, now: Instant, trace_id: &str,
+    ) {
+        let ack_eliciting = pkt.ack_eliciting;
+        let in_flight = pkt.in_flight;
+        let sent_bytes = pkt.size;
+        let pkt_num = pkt.pkt_num;
+
+        self.delivery_rate.on_packet_sent(&mut pkt, now);
+
+        self.largest_sent_pkt[epoch] =
+            cmp::max(self.largest_sent_pkt[epoch], pkt_num);
+
+        self.sent[epoch].push_back(pkt);
+
+        if in_flight {
+            if ack_eliciting {
+                self.time_of_last_sent_ack_eliciting_pkt[epoch] = Some(now);
+            }
+
+            self.in_flight_count[epoch] += 1;
+
+            self.app_limited =
+                (self.bytes_in_flight + sent_bytes) < self.congestion_window;
+
+            self.on_packet_sent_cc(sent_bytes, now);
+
+            self.set_loss_detection_timer(handshake_status, now);
+        }
+
+        // HyStart++: Start of the round in a slow start.
+        if self.hystart.enabled() &&
+            epoch == packet::EPOCH_APPLICATION &&
+            self.congestion_window < self.ssthresh
+        {
+            self.hystart.start_round(pkt_num);
+        }
+
+        trace!("{} {:?}", trace_id, self);
+    }
+
+    fn on_packet_sent_cc(&mut self, sent_bytes: usize, now: Instant) {
+        (self.cc_ops.on_packet_sent)(self, sent_bytes, now);
+    }
+
+    pub fn on_ack_received(
+        &mut self, ranges: &ranges::RangeSet, ack_delay: u64,
+        epoch: packet::Epoch, handshake_status: HandshakeStatus, now: Instant,
+        trace_id: &str,
+    ) -> Result<()> {
+        let largest_acked = ranges.last().unwrap();
+
+        // If the largest packet number acked exceeds any packet number we have
+        // sent, then the ACK is obviously invalid, so there's no need to
+        // continue further.
+        if largest_acked > self.largest_sent_pkt[epoch] {
+            if cfg!(feature = "fuzzing") {
+                return Ok(());
+            }
+
+            return Err(Error::InvalidPacket);
+        }
+
+        if self.largest_acked_pkt[epoch] == std::u64::MAX {
+            self.largest_acked_pkt[epoch] = largest_acked;
+        } else {
+            self.largest_acked_pkt[epoch] =
+                cmp::max(self.largest_acked_pkt[epoch], largest_acked);
+        }
+
+        let mut has_ack_eliciting = false;
+
+        let mut largest_newly_acked_pkt_num = 0;
+        let mut largest_newly_acked_sent_time = now;
+
+        let mut newly_acked = Vec::new();
+
+        // Detect and mark acked packets, without removing them from the sent
+        // packets list.
+        for r in ranges.iter() {
+            let lowest_acked = r.start;
+            let largest_acked = r.end - 1;
+
+            let unacked_iter = self.sent[epoch]
+                .iter_mut()
+                // Skip packets that precede the lowest acked packet in the block.
+                .skip_while(|p| p.pkt_num < lowest_acked)
+                // Skip packets that follow the largest acked packet in the block.
+                .take_while(|p| p.pkt_num <= largest_acked)
+                // Skip packets that have already been acked or lost.
+                .filter(|p| p.time_acked.is_none() && p.time_lost.is_none());
+
+            for unacked in unacked_iter {
+                unacked.time_acked = Some(now);
+
+                if unacked.ack_eliciting {
+                    has_ack_eliciting = true;
+                }
+
+                largest_newly_acked_pkt_num = unacked.pkt_num;
+                largest_newly_acked_sent_time = unacked.time_sent;
+
+                self.acked[epoch].append(&mut unacked.frames);
+
+                if unacked.in_flight {
+                    self.in_flight_count[epoch] =
+                        self.in_flight_count[epoch].saturating_sub(1);
+
+                    self.delivery_rate.on_packet_acked(&unacked, now);
+                }
+
+                newly_acked.push(Acked {
+                    pkt_num: unacked.pkt_num,
+
+                    time_sent: unacked.time_sent,
+
+                    size: unacked.size,
+                });
+
+                trace!("{} packet newly acked {}", trace_id, unacked.pkt_num);
+            }
+        }
+
+        self.delivery_rate.estimate();
+
+        if newly_acked.is_empty() {
+            return Ok(());
+        }
+
+        if largest_newly_acked_pkt_num == largest_acked && has_ack_eliciting {
+            let latest_rtt = now - largest_newly_acked_sent_time;
+
+            let ack_delay = if epoch == packet::EPOCH_APPLICATION {
+                Duration::from_micros(ack_delay)
+            } else {
+                Duration::from_micros(0)
+            };
+
+            self.update_rtt(latest_rtt, ack_delay, now);
+        }
+
+        // Detect and mark lost packets without removing them from the sent
+        // packets list.
+        self.detect_lost_packets(epoch, now, trace_id);
+
+        self.on_packets_acked(newly_acked, epoch, now);
+
+        self.pto_count = 0;
+
+        self.set_loss_detection_timer(handshake_status, now);
+
+        self.drain_packets(epoch);
+
+        trace!("{} {:?}", trace_id, self);
+
+        Ok(())
+    }
+
+    pub fn on_loss_detection_timeout(
+        &mut self, handshake_status: HandshakeStatus, now: Instant,
+        trace_id: &str,
+    ) {
+        let (earliest_loss_time, epoch) = self.loss_time_and_space();
+
+        if earliest_loss_time.is_some() {
+            // Time threshold loss detection.
+            self.detect_lost_packets(epoch, now, trace_id);
+
+            self.set_loss_detection_timer(handshake_status, now);
+
+            trace!("{} {:?}", trace_id, self);
+            return;
+        }
+
+        let epoch = if self.bytes_in_flight > 0 {
+            // Send new data if available, else retransmit old data. If neither
+            // is available, send a single PING frame.
+            let (_, e) = self.pto_time_and_space(handshake_status, now);
+
+            e
+        } else {
+            // Client sends an anti-deadlock packet: Initial is padded to earn
+            // more anti-amplification credit, a Handshake packet proves address
+            // ownership.
+            if handshake_status.has_handshake_keys {
+                packet::EPOCH_HANDSHAKE
+            } else {
+                packet::EPOCH_INITIAL
+            }
+        };
+
+        self.pto_count += 1;
+
+        self.loss_probes[epoch] =
+            cmp::min(self.pto_count as usize, MAX_PTO_PROBES_COUNT);
+
+        let unacked_iter = self.sent[epoch]
+            .iter_mut()
+            // Skip packets that have already been acked or lost, and packets
+            // that don't contain either CRYPTO or STREAM frames.
+            .filter(|p| p.has_data && p.time_acked.is_none() && p.time_lost.is_none())
+            // Only return as many packets as the number of probe packets that
+            // will be sent.
+            .take(self.loss_probes[epoch]);
+
+        // Retransmit the frames from the oldest sent packets on PTO. However
+        // the packets are not actually declared lost (so there is no effect to
+        // congestion control), we just reschedule the data they carried.
+        //
+        // This will also trigger sending an ACK and retransmitting frames like
+        // HANDSHAKE_DONE and MAX_DATA / MAX_STREAM_DATA as well, in addition
+        // to CRYPTO and STREAM, if the original packet carried them.
+        for unacked in unacked_iter {
+            self.lost[epoch].extend_from_slice(&unacked.frames);
+        }
+
+        self.set_loss_detection_timer(handshake_status, now);
+
+        trace!("{} {:?}", trace_id, self);
+    }
+
+    pub fn on_pkt_num_space_discarded(
+        &mut self, epoch: packet::Epoch, handshake_status: HandshakeStatus,
+        now: Instant,
+    ) {
+        let unacked_bytes = self.sent[epoch]
+            .iter()
+            .filter(|p| {
+                p.in_flight && p.time_acked.is_none() && p.time_lost.is_none()
+            })
+            .fold(0, |acc, p| acc + p.size);
+
+        self.bytes_in_flight = self.bytes_in_flight.saturating_sub(unacked_bytes);
+
+        self.sent[epoch].clear();
+        self.lost[epoch].clear();
+        self.acked[epoch].clear();
+
+        self.time_of_last_sent_ack_eliciting_pkt[epoch] = None;
+        self.loss_time[epoch] = None;
+        self.loss_probes[epoch] = 0;
+        self.in_flight_count[epoch] = 0;
+
+        self.set_loss_detection_timer(handshake_status, now);
+    }
+
+    pub fn loss_detection_timer(&self) -> Option<Instant> {
+        self.loss_detection_timer
+    }
+
+    pub fn cwnd(&self) -> usize {
+        self.congestion_window
+    }
+
+    pub fn cwnd_available(&self) -> usize {
+        // Ignore cwnd when sending probe packets.
+        if self.loss_probes.iter().any(|&x| x > 0) {
+            return std::usize::MAX;
+        }
+
+        self.congestion_window.saturating_sub(self.bytes_in_flight)
+    }
+
+    pub fn rtt(&self) -> Duration {
+        self.smoothed_rtt.unwrap_or(INITIAL_RTT)
+    }
+
+    pub fn pto(&self) -> Duration {
+        self.rtt() + cmp::max(self.rttvar * 4, GRANULARITY)
+    }
+
+    pub fn delivery_rate(&self) -> u64 {
+        self.delivery_rate.delivery_rate()
+    }
+
+    fn update_rtt(
+        &mut self, latest_rtt: Duration, ack_delay: Duration, now: Instant,
+    ) {
+        self.latest_rtt = latest_rtt;
+
+        match self.smoothed_rtt {
+            // First RTT sample.
+            None => {
+                self.min_rtt = self.minmax_filter.reset(now, latest_rtt);
+
+                self.smoothed_rtt = Some(latest_rtt);
+
+                self.rttvar = latest_rtt / 2;
+            },
+
+            Some(srtt) => {
+                self.min_rtt =
+                    self.minmax_filter.running_min(RTT_WINDOW, now, latest_rtt);
+
+                let ack_delay = cmp::min(self.max_ack_delay, ack_delay);
+
+                // Adjust for ack delay if plausible.
+                let adjusted_rtt = if latest_rtt > self.min_rtt + ack_delay {
+                    latest_rtt - ack_delay
+                } else {
+                    latest_rtt
+                };
+
+                self.rttvar = self.rttvar.mul_f64(3.0 / 4.0) +
+                    sub_abs(srtt, adjusted_rtt).mul_f64(1.0 / 4.0);
+
+                self.smoothed_rtt = Some(
+                    srtt.mul_f64(7.0 / 8.0) + adjusted_rtt.mul_f64(1.0 / 8.0),
+                );
+            },
+        }
+    }
+
+    fn loss_time_and_space(&self) -> (Option<Instant>, packet::Epoch) {
+        let mut epoch = packet::EPOCH_INITIAL;
+        let mut time = self.loss_time[epoch];
+
+        // Iterate over all packet number spaces starting from Handshake.
+        #[allow(clippy::needless_range_loop)]
+        for e in packet::EPOCH_HANDSHAKE..packet::EPOCH_COUNT {
+            let new_time = self.loss_time[e];
+
+            if time.is_none() || new_time < time {
+                time = new_time;
+                epoch = e;
+            }
+        }
+
+        (time, epoch)
+    }
+
+    fn pto_time_and_space(
+        &self, handshake_status: HandshakeStatus, now: Instant,
+    ) -> (Option<Instant>, packet::Epoch) {
+        let mut duration = self.pto() * 2_u32.pow(self.pto_count);
+
+        // Arm PTO from now when there are no inflight packets.
+        if self.bytes_in_flight == 0 {
+            if handshake_status.has_handshake_keys {
+                return (Some(now + duration), packet::EPOCH_HANDSHAKE);
+            } else {
+                return (Some(now + duration), packet::EPOCH_INITIAL);
+            }
+        }
+
+        let mut pto_timeout = None;
+        let mut pto_space = packet::EPOCH_INITIAL;
+
+        // Iterate over all packet number spaces.
+        for e in packet::EPOCH_INITIAL..packet::EPOCH_COUNT {
+            if self.in_flight_count[e] == 0 {
+                continue;
+            }
+
+            if e == packet::EPOCH_APPLICATION {
+                // Skip Application Data until handshake completes.
+                if !handshake_status.completed {
+                    return (pto_timeout, pto_space);
+                }
+
+                // Include max_ack_delay and backoff for Application Data.
+                duration += self.max_ack_delay * 2_u32.pow(self.pto_count);
+            }
+
+            let new_time =
+                self.time_of_last_sent_ack_eliciting_pkt[e].map(|t| t + duration);
+
+            if pto_timeout.is_none() || new_time < pto_timeout {
+                pto_timeout = new_time;
+                pto_space = e;
+            }
+        }
+
+        (pto_timeout, pto_space)
+    }
+
+    fn set_loss_detection_timer(
+        &mut self, handshake_status: HandshakeStatus, now: Instant,
+    ) {
+        let (earliest_loss_time, _) = self.loss_time_and_space();
+
+        if earliest_loss_time.is_some() {
+            // Time threshold loss detection.
+            self.loss_detection_timer = earliest_loss_time;
+            return;
+        }
+
+        if self.bytes_in_flight == 0 && handshake_status.peer_verified_address {
+            self.loss_detection_timer = None;
+            return;
+        }
+
+        // PTO timer.
+        let (timeout, _) = self.pto_time_and_space(handshake_status, now);
+        self.loss_detection_timer = timeout;
+    }
+
+    fn detect_lost_packets(
+        &mut self, epoch: packet::Epoch, now: Instant, trace_id: &str,
+    ) {
+        let largest_acked = self.largest_acked_pkt[epoch];
+
+        self.loss_time[epoch] = None;
+
+        let loss_delay =
+            cmp::max(self.latest_rtt, self.rtt()).mul_f64(TIME_THRESHOLD);
+
+        // Minimum time of kGranularity before packets are deemed lost.
+        let loss_delay = cmp::max(loss_delay, GRANULARITY);
+
+        // Packets sent before this time are deemed lost.
+        let lost_send_time = now - loss_delay;
+
+        let mut lost_bytes = 0;
+
+        let mut largest_lost_pkt = None;
+
+        let unacked_iter = self.sent[epoch]
+            .iter_mut()
+            // Skip packets that follow the largest acked packet.
+            .take_while(|p| p.pkt_num <= largest_acked)
+            // Skip packets that have already been acked or lost.
+            .filter(|p| p.time_acked.is_none() && p.time_lost.is_none());
+
+        for unacked in unacked_iter {
+            // Mark packet as lost, or set time when it should be marked.
+            if unacked.time_sent <= lost_send_time ||
+                largest_acked >= unacked.pkt_num + PACKET_THRESHOLD
+            {
+                self.lost[epoch].append(&mut unacked.frames);
+
+                unacked.time_lost = Some(now);
+
+                if unacked.in_flight {
+                    lost_bytes += unacked.size;
+
+                    // Frames have already been removed from the packet, so
+                    // cloning the whole packet should be relatively cheap.
+                    largest_lost_pkt = Some(unacked.clone());
+
+                    self.in_flight_count[epoch] =
+                        self.in_flight_count[epoch].saturating_sub(1);
+
+                    trace!(
+                        "{} packet {} lost on epoch {}",
+                        trace_id,
+                        unacked.pkt_num,
+                        epoch
+                    );
+                }
+
+                self.lost_count += 1;
+            } else {
+                let loss_time = match self.loss_time[epoch] {
+                    None => unacked.time_sent + loss_delay,
+
+                    Some(loss_time) =>
+                        cmp::min(loss_time, unacked.time_sent + loss_delay),
+                };
+
+                self.loss_time[epoch] = Some(loss_time);
+            }
+        }
+
+        if let Some(pkt) = largest_lost_pkt {
+            self.on_packets_lost(lost_bytes, &pkt, epoch, now);
+        }
+
+        self.drain_packets(epoch);
+    }
+
+    fn drain_packets(&mut self, epoch: packet::Epoch) {
+        let mut lowest_non_expired_pkt_index = self.sent[epoch].len();
+
+        // In order to avoid removing elements from the middle of the list
+        // (which would require copying other elements to compact the list),
+        // we only remove a contiguous range of elements from the start of the
+        // list.
+        //
+        // This means that acked or lost elements coming after this will not
+        // be removed at this point, but their removal is delayed for a later
+        // time, once the gaps have been filled.
+
+        // First, find the first element that is neither acked nor lost.
+        for (i, pkt) in self.sent[epoch].iter().enumerate() {
+            if pkt.time_acked.is_none() && pkt.time_lost.is_none() {
+                lowest_non_expired_pkt_index = i;
+                break;
+            }
+        }
+
+        // Then remove elements up to the previously found index.
+        self.sent[epoch].drain(..lowest_non_expired_pkt_index);
+    }
+
+    fn on_packets_acked(
+        &mut self, acked: Vec<Acked>, epoch: packet::Epoch, now: Instant,
+    ) {
+        for pkt in acked {
+            (self.cc_ops.on_packet_acked)(self, &pkt, epoch, now);
+        }
+    }
+
+    fn in_congestion_recovery(&self, sent_time: Instant) -> bool {
+        match self.congestion_recovery_start_time {
+            Some(congestion_recovery_start_time) =>
+                sent_time <= congestion_recovery_start_time,
+
+            None => false,
+        }
+    }
+
+    fn in_persistent_congestion(&mut self, _largest_lost_pkt_num: u64) -> bool {
+        let _congestion_period = self.pto() * PERSISTENT_CONGESTION_THRESHOLD;
+
+        // TODO: properly detect persistent congestion
+        false
+    }
+
+    fn on_packets_lost(
+        &mut self, lost_bytes: usize, largest_lost_pkt: &Sent,
+        epoch: packet::Epoch, now: Instant,
+    ) {
+        self.bytes_in_flight = self.bytes_in_flight.saturating_sub(lost_bytes);
+
+        self.congestion_event(largest_lost_pkt.time_sent, epoch, now);
+
+        if self.in_persistent_congestion(largest_lost_pkt.pkt_num) {
+            self.collapse_cwnd();
+        }
+    }
+
+    fn congestion_event(
+        &mut self, time_sent: Instant, epoch: packet::Epoch, now: Instant,
+    ) {
+        (self.cc_ops.congestion_event)(self, time_sent, epoch, now);
+    }
+
+    fn collapse_cwnd(&mut self) {
+        (self.cc_ops.collapse_cwnd)(self);
+    }
+
+    pub fn rate_check_app_limited(&mut self) {
+        if self.app_limited {
+            self.delivery_rate.check_app_limited(self.bytes_in_flight)
+        }
+    }
+
+    fn hystart_on_packet_acked(
+        &mut self, packet: &Acked, now: Instant,
+    ) -> (usize, usize) {
+        self.hystart.on_packet_acked(
+            packet,
+            self.latest_rtt,
+            self.congestion_window,
+            self.ssthresh,
+            now,
+        )
+    }
+
+    pub fn update_app_limited(&mut self, v: bool) {
+        self.app_limited = v;
+    }
+
+    pub fn app_limited(&mut self) -> bool {
+        self.app_limited
+    }
+
+    #[cfg(feature = "qlog")]
+    pub fn to_qlog(&self) -> qlog::event::Event {
+        // QVis can't use all these fields and they can be large.
+        qlog::event::Event::metrics_updated(
+            Some(self.min_rtt.as_millis() as u64),
+            Some(self.rtt().as_millis() as u64),
+            Some(self.latest_rtt.as_millis() as u64),
+            Some(self.rttvar.as_millis() as u64),
+            None, // delay
+            None, // probe_count
+            Some(self.cwnd() as u64),
+            Some(self.bytes_in_flight as u64),
+            None, // ssthresh
+            None, // packets_in_flight
+            None, // in_recovery
+            None, // pacing_rate
+        )
+    }
+}
+
+/// Available congestion control algorithms.
+///
+/// This enum provides currently available list of congestion control
+/// algorithms.
+#[derive(Debug, Copy, Clone, PartialEq)]
+#[repr(C)]
+pub enum CongestionControlAlgorithm {
+    /// Reno congestion control algorithm. `reno` in a string form.
+    Reno  = 0,
+    /// CUBIC congestion control algorithm (default). `cubic` in a string form.
+    CUBIC = 1,
+}
+
+impl FromStr for CongestionControlAlgorithm {
+    type Err = crate::Error;
+
+    /// Converts a string to `CongestionControlAlgorithm`.
+    ///
+    /// If `name` is not valid, `Error::CongestionControl` is returned.
+    fn from_str(name: &str) -> std::result::Result<Self, Self::Err> {
+        match name {
+            "reno" => Ok(CongestionControlAlgorithm::Reno),
+            "cubic" => Ok(CongestionControlAlgorithm::CUBIC),
+
+            _ => Err(crate::Error::CongestionControl),
+        }
+    }
+}
+
+pub struct CongestionControlOps {
+    pub on_packet_sent: fn(r: &mut Recovery, sent_bytes: usize, now: Instant),
+
+    pub on_packet_acked:
+        fn(r: &mut Recovery, packet: &Acked, epoch: packet::Epoch, now: Instant),
+
+    pub congestion_event: fn(
+        r: &mut Recovery,
+        time_sent: Instant,
+        epoch: packet::Epoch,
+        now: Instant,
+    ),
+
+    pub collapse_cwnd: fn(r: &mut Recovery),
+}
+
+impl From<CongestionControlAlgorithm> for &'static CongestionControlOps {
+    fn from(algo: CongestionControlAlgorithm) -> Self {
+        match algo {
+            CongestionControlAlgorithm::Reno => &reno::RENO,
+            CongestionControlAlgorithm::CUBIC => &cubic::CUBIC,
+        }
+    }
+}
+
+impl std::fmt::Debug for Recovery {
+    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+        match self.loss_detection_timer {
+            Some(v) => {
+                let now = Instant::now();
+
+                if v > now {
+                    let d = v.duration_since(now);
+                    write!(f, "timer={:?} ", d)?;
+                } else {
+                    write!(f, "timer=exp ")?;
+                }
+            },
+
+            None => {
+                write!(f, "timer=none ")?;
+            },
+        };
+
+        write!(f, "latest_rtt={:?} ", self.latest_rtt)?;
+        write!(f, "srtt={:?} ", self.smoothed_rtt)?;
+        write!(f, "min_rtt={:?} ", self.min_rtt)?;
+        write!(f, "rttvar={:?} ", self.rttvar)?;
+        write!(f, "loss_time={:?} ", self.loss_time)?;
+        write!(f, "loss_probes={:?} ", self.loss_probes)?;
+        write!(f, "cwnd={} ", self.congestion_window)?;
+        write!(f, "ssthresh={} ", self.ssthresh)?;
+        write!(f, "bytes_in_flight={} ", self.bytes_in_flight)?;
+        write!(f, "app_limited={} ", self.app_limited)?;
+        write!(
+            f,
+            "congestion_recovery_start_time={:?} ",
+            self.congestion_recovery_start_time
+        )?;
+        write!(f, "{:?} ", self.delivery_rate)?;
+
+        if self.hystart.enabled() {
+            write!(f, "hystart={:?} ", self.hystart)?;
+        }
+
+        Ok(())
+    }
+}
+
+#[derive(Clone)]
+pub struct Sent {
+    pub pkt_num: u64,
+
+    pub frames: Vec<frame::Frame>,
+
+    pub time_sent: Instant,
+
+    pub time_acked: Option<Instant>,
+
+    pub time_lost: Option<Instant>,
+
+    pub size: usize,
+
+    pub ack_eliciting: bool,
+
+    pub in_flight: bool,
+
+    pub delivered: usize,
+
+    pub delivered_time: Instant,
+
+    pub recent_delivered_packet_sent_time: Instant,
+
+    pub is_app_limited: bool,
+
+    pub has_data: bool,
+}
+
+impl std::fmt::Debug for Sent {
+    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+        write!(f, "pkt_num={:?} ", self.pkt_num)?;
+        write!(f, "pkt_sent_time={:?} ", self.time_sent.elapsed())?;
+        write!(f, "pkt_size={:?} ", self.size)?;
+        write!(f, "delivered={:?} ", self.delivered)?;
+        write!(f, "delivered_time={:?} ", self.delivered_time.elapsed())?;
+        write!(
+            f,
+            "recent_delivered_packet_sent_time={:?} ",
+            self.recent_delivered_packet_sent_time.elapsed()
+        )?;
+        write!(f, "is_app_limited={} ", self.is_app_limited)?;
+        write!(f, "has_data={} ", self.has_data)?;
+
+        Ok(())
+    }
+}
+
+#[derive(Clone)]
+pub struct Acked {
+    pub pkt_num: u64,
+
+    pub time_sent: Instant,
+
+    pub size: usize,
+}
+
+#[derive(Clone, Copy, Debug)]
+pub struct HandshakeStatus {
+    pub has_handshake_keys: bool,
+
+    pub peer_verified_address: bool,
+
+    pub completed: bool,
+}
+
+#[cfg(test)]
+impl Default for HandshakeStatus {
+    fn default() -> HandshakeStatus {
+        HandshakeStatus {
+            has_handshake_keys: true,
+
+            peer_verified_address: true,
+
+            completed: true,
+        }
+    }
+}
+
+fn sub_abs(lhs: Duration, rhs: Duration) -> Duration {
+    if lhs > rhs {
+        lhs - rhs
+    } else {
+        rhs - lhs
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn lookup_cc_algo_ok() {
+        let algo = CongestionControlAlgorithm::from_str("reno").unwrap();
+        assert_eq!(algo, CongestionControlAlgorithm::Reno);
+    }
+
+    #[test]
+    fn lookup_cc_algo_bad() {
+        assert_eq!(
+            CongestionControlAlgorithm::from_str("???"),
+            Err(Error::CongestionControl)
+        );
+    }
+
+    #[test]
+    fn collapse_cwnd() {
+        let mut cfg = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
+        cfg.set_cc_algorithm(CongestionControlAlgorithm::Reno);
+
+        let mut r = Recovery::new(&cfg);
+
+        // cwnd will be reset.
+        r.collapse_cwnd();
+        assert_eq!(r.cwnd(), MINIMUM_WINDOW);
+    }
+
+    #[test]
+    fn loss_on_pto() {
+        let mut cfg = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
+        cfg.set_cc_algorithm(CongestionControlAlgorithm::Reno);
+
+        let mut r = Recovery::new(&cfg);
+
+        let mut now = Instant::now();
+
+        assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 0);
+
+        // Start by sending a few packets.
+        let p = Sent {
+            pkt_num: 0,
+            frames: vec![],
+            time_sent: now,
+            time_acked: None,
+            time_lost: None,
+            size: 1000,
+            ack_eliciting: true,
+            in_flight: true,
+            delivered: 0,
+            delivered_time: now,
+            recent_delivered_packet_sent_time: now,
+            is_app_limited: false,
+            has_data: false,
+        };
+
+        r.on_packet_sent(
+            p,
+            packet::EPOCH_APPLICATION,
+            HandshakeStatus::default(),
+            now,
+            "",
+        );
+        assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 1);
+        assert_eq!(r.bytes_in_flight, 1000);
+
+        let p = Sent {
+            pkt_num: 1,
+            frames: vec![],
+            time_sent: now,
+            time_acked: None,
+            time_lost: None,
+            size: 1000,
+            ack_eliciting: true,
+            in_flight: true,
+            delivered: 0,
+            delivered_time: now,
+            recent_delivered_packet_sent_time: now,
+            is_app_limited: false,
+            has_data: false,
+        };
+
+        r.on_packet_sent(
+            p,
+            packet::EPOCH_APPLICATION,
+            HandshakeStatus::default(),
+            now,
+            "",
+        );
+        assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 2);
+        assert_eq!(r.bytes_in_flight, 2000);
+
+        let p = Sent {
+            pkt_num: 2,
+            frames: vec![],
+            time_sent: now,
+            time_acked: None,
+            time_lost: None,
+            size: 1000,
+            ack_eliciting: true,
+            in_flight: true,
+            delivered: 0,
+            delivered_time: now,
+            recent_delivered_packet_sent_time: now,
+            is_app_limited: false,
+            has_data: false,
+        };
+
+        r.on_packet_sent(
+            p,
+            packet::EPOCH_APPLICATION,
+            HandshakeStatus::default(),
+            now,
+            "",
+        );
+        assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 3);
+        assert_eq!(r.bytes_in_flight, 3000);
+
+        let p = Sent {
+            pkt_num: 3,
+            frames: vec![],
+            time_sent: now,
+            time_acked: None,
+            time_lost: None,
+            size: 1000,
+            ack_eliciting: true,
+            in_flight: true,
+            delivered: 0,
+            delivered_time: now,
+            recent_delivered_packet_sent_time: now,
+            is_app_limited: false,
+            has_data: false,
+        };
+
+        r.on_packet_sent(
+            p,
+            packet::EPOCH_APPLICATION,
+            HandshakeStatus::default(),
+            now,
+            "",
+        );
+        assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 4);
+        assert_eq!(r.bytes_in_flight, 4000);
+
+        // Wait for 10ms.
+        now += Duration::from_millis(10);
+
+        // Only the first 2 packets are acked.
+        let mut acked = ranges::RangeSet::default();
+        acked.insert(0..2);
+
+        assert_eq!(
+            r.on_ack_received(
+                &acked,
+                25,
+                packet::EPOCH_APPLICATION,
+                HandshakeStatus::default(),
+                now,
+                ""
+            ),
+            Ok(())
+        );
+
+        assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 2);
+        assert_eq!(r.bytes_in_flight, 2000);
+        assert_eq!(r.lost_count, 0);
+
+        // Wait until loss detection timer expires.
+        now = r.loss_detection_timer().unwrap();
+
+        // PTO.
+        r.on_loss_detection_timeout(HandshakeStatus::default(), now, "");
+        assert_eq!(r.loss_probes[packet::EPOCH_APPLICATION], 1);
+        assert_eq!(r.lost_count, 0);
+        assert_eq!(r.pto_count, 1);
+
+        let p = Sent {
+            pkt_num: 4,
+            frames: vec![],
+            time_sent: now,
+            time_acked: None,
+            time_lost: None,
+            size: 1000,
+            ack_eliciting: true,
+            in_flight: true,
+            delivered: 0,
+            delivered_time: now,
+            recent_delivered_packet_sent_time: now,
+            is_app_limited: false,
+            has_data: false,
+        };
+
+        r.on_packet_sent(
+            p,
+            packet::EPOCH_APPLICATION,
+            HandshakeStatus::default(),
+            now,
+            "",
+        );
+        assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 3);
+        assert_eq!(r.bytes_in_flight, 3000);
+
+        let p = Sent {
+            pkt_num: 5,
+            frames: vec![],
+            time_sent: now,
+            time_acked: None,
+            time_lost: None,
+            size: 1000,
+            ack_eliciting: true,
+            in_flight: true,
+            delivered: 0,
+            delivered_time: now,
+            recent_delivered_packet_sent_time: now,
+            is_app_limited: false,
+            has_data: false,
+        };
+
+        r.on_packet_sent(
+            p,
+            packet::EPOCH_APPLICATION,
+            HandshakeStatus::default(),
+            now,
+            "",
+        );
+        assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 4);
+        assert_eq!(r.bytes_in_flight, 4000);
+        assert_eq!(r.lost_count, 0);
+
+        // Wait for 10ms.
+        now += Duration::from_millis(10);
+
+        // PTO packets are acked.
+        let mut acked = ranges::RangeSet::default();
+        acked.insert(4..6);
+
+        assert_eq!(
+            r.on_ack_received(
+                &acked,
+                25,
+                packet::EPOCH_APPLICATION,
+                HandshakeStatus::default(),
+                now,
+                ""
+            ),
+            Ok(())
+        );
+
+        assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 0);
+        assert_eq!(r.bytes_in_flight, 0);
+
+        assert_eq!(r.lost_count, 2);
+    }
+
+    #[test]
+    fn loss_on_timer() {
+        let mut cfg = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
+        cfg.set_cc_algorithm(CongestionControlAlgorithm::Reno);
+
+        let mut r = Recovery::new(&cfg);
+
+        let mut now = Instant::now();
+
+        assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 0);
+
+        // Start by sending a few packets.
+        let p = Sent {
+            pkt_num: 0,
+            frames: vec![],
+            time_sent: now,
+            time_acked: None,
+            time_lost: None,
+            size: 1000,
+            ack_eliciting: true,
+            in_flight: true,
+            delivered: 0,
+            delivered_time: now,
+            recent_delivered_packet_sent_time: now,
+            is_app_limited: false,
+            has_data: false,
+        };
+
+        r.on_packet_sent(
+            p,
+            packet::EPOCH_APPLICATION,
+            HandshakeStatus::default(),
+            now,
+            "",
+        );
+        assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 1);
+        assert_eq!(r.bytes_in_flight, 1000);
+
+        let p = Sent {
+            pkt_num: 1,
+            frames: vec![],
+            time_sent: now,
+            time_acked: None,
+            time_lost: None,
+            size: 1000,
+            ack_eliciting: true,
+            in_flight: true,
+            delivered: 0,
+            delivered_time: now,
+            recent_delivered_packet_sent_time: now,
+            is_app_limited: false,
+            has_data: false,
+        };
+
+        r.on_packet_sent(
+            p,
+            packet::EPOCH_APPLICATION,
+            HandshakeStatus::default(),
+            now,
+            "",
+        );
+        assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 2);
+        assert_eq!(r.bytes_in_flight, 2000);
+
+        let p = Sent {
+            pkt_num: 2,
+            frames: vec![],
+            time_sent: now,
+            time_acked: None,
+            time_lost: None,
+            size: 1000,
+            ack_eliciting: true,
+            in_flight: true,
+            delivered: 0,
+            delivered_time: now,
+            recent_delivered_packet_sent_time: now,
+            is_app_limited: false,
+            has_data: false,
+        };
+
+        r.on_packet_sent(
+            p,
+            packet::EPOCH_APPLICATION,
+            HandshakeStatus::default(),
+            now,
+            "",
+        );
+        assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 3);
+        assert_eq!(r.bytes_in_flight, 3000);
+
+        let p = Sent {
+            pkt_num: 3,
+            frames: vec![],
+            time_sent: now,
+            time_acked: None,
+            time_lost: None,
+            size: 1000,
+            ack_eliciting: true,
+            in_flight: true,
+            delivered: 0,
+            delivered_time: now,
+            recent_delivered_packet_sent_time: now,
+            is_app_limited: false,
+            has_data: false,
+        };
+
+        r.on_packet_sent(
+            p,
+            packet::EPOCH_APPLICATION,
+            HandshakeStatus::default(),
+            now,
+            "",
+        );
+        assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 4);
+        assert_eq!(r.bytes_in_flight, 4000);
+
+        // Wait for 10ms.
+        now += Duration::from_millis(10);
+
+        // Only the first 2 packets and the last one are acked.
+        let mut acked = ranges::RangeSet::default();
+        acked.insert(0..2);
+        acked.insert(3..4);
+
+        assert_eq!(
+            r.on_ack_received(
+                &acked,
+                25,
+                packet::EPOCH_APPLICATION,
+                HandshakeStatus::default(),
+                now,
+                ""
+            ),
+            Ok(())
+        );
+
+        assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 2);
+        assert_eq!(r.bytes_in_flight, 1000);
+        assert_eq!(r.lost_count, 0);
+
+        // Wait until loss detection timer expires.
+        now = r.loss_detection_timer().unwrap();
+
+        // Packet is declared lost.
+        r.on_loss_detection_timeout(HandshakeStatus::default(), now, "");
+        assert_eq!(r.loss_probes[packet::EPOCH_APPLICATION], 0);
+
+        assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 0);
+        assert_eq!(r.bytes_in_flight, 0);
+
+        assert_eq!(r.lost_count, 1);
+    }
+
+    #[test]
+    fn loss_on_reordering() {
+        let mut cfg = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
+        cfg.set_cc_algorithm(CongestionControlAlgorithm::Reno);
+
+        let mut r = Recovery::new(&cfg);
+
+        let mut now = Instant::now();
+
+        assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 0);
+
+        // Start by sending a few packets.
+        let p = Sent {
+            pkt_num: 0,
+            frames: vec![],
+            time_sent: now,
+            time_acked: None,
+            time_lost: None,
+            size: 1000,
+            ack_eliciting: true,
+            in_flight: true,
+            delivered: 0,
+            delivered_time: now,
+            recent_delivered_packet_sent_time: now,
+            is_app_limited: false,
+            has_data: false,
+        };
+
+        r.on_packet_sent(
+            p,
+            packet::EPOCH_APPLICATION,
+            HandshakeStatus::default(),
+            now,
+            "",
+        );
+        assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 1);
+        assert_eq!(r.bytes_in_flight, 1000);
+
+        let p = Sent {
+            pkt_num: 1,
+            frames: vec![],
+            time_sent: now,
+            time_acked: None,
+            time_lost: None,
+            size: 1000,
+            ack_eliciting: true,
+            in_flight: true,
+            delivered: 0,
+            delivered_time: now,
+            recent_delivered_packet_sent_time: now,
+            is_app_limited: false,
+            has_data: false,
+        };
+
+        r.on_packet_sent(
+            p,
+            packet::EPOCH_APPLICATION,
+            HandshakeStatus::default(),
+            now,
+            "",
+        );
+        assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 2);
+        assert_eq!(r.bytes_in_flight, 2000);
+
+        let p = Sent {
+            pkt_num: 2,
+            frames: vec![],
+            time_sent: now,
+            time_acked: None,
+            time_lost: None,
+            size: 1000,
+            ack_eliciting: true,
+            in_flight: true,
+            delivered: 0,
+            delivered_time: now,
+            recent_delivered_packet_sent_time: now,
+            is_app_limited: false,
+            has_data: false,
+        };
+
+        r.on_packet_sent(
+            p,
+            packet::EPOCH_APPLICATION,
+            HandshakeStatus::default(),
+            now,
+            "",
+        );
+        assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 3);
+        assert_eq!(r.bytes_in_flight, 3000);
+
+        let p = Sent {
+            pkt_num: 3,
+            frames: vec![],
+            time_sent: now,
+            time_acked: None,
+            time_lost: None,
+            size: 1000,
+            ack_eliciting: true,
+            in_flight: true,
+            delivered: 0,
+            delivered_time: now,
+            recent_delivered_packet_sent_time: now,
+            is_app_limited: false,
+            has_data: false,
+        };
+
+        r.on_packet_sent(
+            p,
+            packet::EPOCH_APPLICATION,
+            HandshakeStatus::default(),
+            now,
+            "",
+        );
+        assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 4);
+        assert_eq!(r.bytes_in_flight, 4000);
+
+        // Wait for 10ms.
+        now += Duration::from_millis(10);
+
+        // ACKs are reordered.
+        let mut acked = ranges::RangeSet::default();
+        acked.insert(2..4);
+
+        assert_eq!(
+            r.on_ack_received(
+                &acked,
+                25,
+                packet::EPOCH_APPLICATION,
+                HandshakeStatus::default(),
+                now,
+                ""
+            ),
+            Ok(())
+        );
+
+        now += Duration::from_millis(10);
+
+        let mut acked = ranges::RangeSet::default();
+        acked.insert(0..2);
+
+        assert_eq!(
+            r.on_ack_received(
+                &acked,
+                25,
+                packet::EPOCH_APPLICATION,
+                HandshakeStatus::default(),
+                now,
+                ""
+            ),
+            Ok(())
+        );
+
+        assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 0);
+        assert_eq!(r.bytes_in_flight, 0);
+
+        // Spurious loss.
+        assert_eq!(r.lost_count, 1);
+    }
+}
+
+mod cubic;
+mod delivery_rate;
+mod hystart;
+mod reno;
diff --git a/src/recovery/reno.rs b/src/recovery/reno.rs
new file mode 100644
index 0000000..b5019f7
--- /dev/null
+++ b/src/recovery/reno.rs
@@ -0,0 +1,263 @@
+// Copyright (C) 2019, Cloudflare, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright notice,
+//       this list of conditions and the following disclaimer.
+//
+//     * Redistributions in binary form must reproduce the above copyright
+//       notice, this list of conditions and the following disclaimer in the
+//       documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+//! Reno Congestion Control
+//!
+//! Note that Slow Start can use HyStart++ when enabled.
+
+use std::cmp;
+use std::time::Instant;
+
+use crate::packet;
+use crate::recovery;
+
+use crate::recovery::Acked;
+use crate::recovery::CongestionControlOps;
+use crate::recovery::Recovery;
+
+pub static RENO: CongestionControlOps = CongestionControlOps {
+    on_packet_sent,
+    on_packet_acked,
+    congestion_event,
+    collapse_cwnd,
+};
+
+pub fn on_packet_sent(r: &mut Recovery, sent_bytes: usize, _now: Instant) {
+    r.bytes_in_flight += sent_bytes;
+}
+
+fn on_packet_acked(
+    r: &mut Recovery, packet: &Acked, epoch: packet::Epoch, now: Instant,
+) {
+    r.bytes_in_flight = r.bytes_in_flight.saturating_sub(packet.size);
+
+    if r.in_congestion_recovery(packet.time_sent) {
+        return;
+    }
+
+    if r.app_limited {
+        return;
+    }
+
+    if r.congestion_window < r.ssthresh {
+        // Slow start.
+        if r.hystart.enabled() && epoch == packet::EPOCH_APPLICATION {
+            let (cwnd, ssthresh) = r.hystart_on_packet_acked(packet, now);
+
+            r.congestion_window = cwnd;
+            r.ssthresh = ssthresh;
+        } else {
+            r.congestion_window += packet.size;
+        }
+    } else {
+        // Congestion avoidance.
+        let mut reno_cwnd = r.congestion_window;
+
+        r.bytes_acked += packet.size;
+
+        if r.bytes_acked >= r.congestion_window {
+            r.bytes_acked -= r.congestion_window;
+            reno_cwnd += recovery::MAX_DATAGRAM_SIZE;
+        }
+
+        // When in Limited Slow Start, take the max of CA cwnd and
+        // LSS cwnd.
+        if r.hystart.enabled() &&
+            epoch == packet::EPOCH_APPLICATION &&
+            r.hystart.lss_start_time().is_some()
+        {
+            let (lss_cwnd, _) = r.hystart_on_packet_acked(packet, now);
+
+            reno_cwnd = cmp::max(reno_cwnd, lss_cwnd);
+        }
+
+        r.congestion_window = reno_cwnd;
+    }
+}
+
+fn congestion_event(
+    r: &mut Recovery, time_sent: Instant, epoch: packet::Epoch, now: Instant,
+) {
+    // Start a new congestion event if packet was sent after the
+    // start of the previous congestion recovery period.
+    if !r.in_congestion_recovery(time_sent) {
+        r.congestion_recovery_start_time = Some(now);
+
+        r.congestion_window = (r.congestion_window as f64 *
+            recovery::LOSS_REDUCTION_FACTOR)
+            as usize;
+
+        r.congestion_window =
+            cmp::max(r.congestion_window, recovery::MINIMUM_WINDOW);
+
+        r.bytes_acked = (r.congestion_window as f64 *
+            recovery::LOSS_REDUCTION_FACTOR) as usize;
+
+        r.ssthresh = r.congestion_window;
+
+        if r.hystart.enabled() && epoch == packet::EPOCH_APPLICATION {
+            r.hystart.congestion_event();
+        }
+    }
+}
+
+pub fn collapse_cwnd(r: &mut Recovery) {
+    r.congestion_window = recovery::MINIMUM_WINDOW;
+    r.bytes_acked = 0;
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    use std::time::Duration;
+
+    #[test]
+    fn reno_init() {
+        let mut cfg = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
+        cfg.set_cc_algorithm(recovery::CongestionControlAlgorithm::Reno);
+
+        let r = Recovery::new(&cfg);
+
+        assert!(r.cwnd() > 0);
+        assert_eq!(r.bytes_in_flight, 0);
+    }
+
+    #[test]
+    fn reno_send() {
+        let mut cfg = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
+        cfg.set_cc_algorithm(recovery::CongestionControlAlgorithm::Reno);
+
+        let mut r = Recovery::new(&cfg);
+
+        let now = Instant::now();
+
+        r.on_packet_sent_cc(1000, now);
+
+        assert_eq!(r.bytes_in_flight, 1000);
+    }
+
+    #[test]
+    fn reno_slow_start() {
+        let mut cfg = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
+        cfg.set_cc_algorithm(recovery::CongestionControlAlgorithm::Reno);
+
+        let mut r = Recovery::new(&cfg);
+
+        let now = Instant::now();
+
+        let p = recovery::Sent {
+            pkt_num: 0,
+            frames: vec![],
+            time_sent: now,
+            time_acked: None,
+            time_lost: None,
+            size: 5000,
+            ack_eliciting: true,
+            in_flight: true,
+            delivered: 0,
+            delivered_time: std::time::Instant::now(),
+            recent_delivered_packet_sent_time: std::time::Instant::now(),
+            is_app_limited: false,
+            has_data: false,
+        };
+
+        // Send 5k x 4 = 20k, higher than default cwnd(~15k)
+        // to become no longer app limited.
+        r.on_packet_sent_cc(p.size, now);
+        r.on_packet_sent_cc(p.size, now);
+        r.on_packet_sent_cc(p.size, now);
+        r.on_packet_sent_cc(p.size, now);
+
+        let cwnd_prev = r.cwnd();
+
+        let acked = vec![Acked {
+            pkt_num: p.pkt_num,
+            time_sent: p.time_sent,
+            size: p.size,
+        }];
+
+        r.on_packets_acked(acked, packet::EPOCH_APPLICATION, now);
+
+        // Check if cwnd increased by packet size (slow start).
+        assert_eq!(r.cwnd(), cwnd_prev + p.size);
+    }
+
+    #[test]
+    fn reno_congestion_event() {
+        let mut cfg = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
+        cfg.set_cc_algorithm(recovery::CongestionControlAlgorithm::Reno);
+
+        let mut r = Recovery::new(&cfg);
+
+        let prev_cwnd = r.cwnd();
+
+        let now = Instant::now();
+
+        r.congestion_event(now, packet::EPOCH_APPLICATION, now);
+
+        // In Reno, after congestion event, cwnd will be cut in half.
+        assert_eq!(prev_cwnd / 2, r.cwnd());
+    }
+
+    #[test]
+    fn reno_congestion_avoidance() {
+        let mut cfg = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
+        cfg.set_cc_algorithm(recovery::CongestionControlAlgorithm::Reno);
+
+        let mut r = Recovery::new(&cfg);
+        let now = Instant::now();
+        let prev_cwnd = r.cwnd();
+
+        // Fill up bytes_in_flight to avoid app_limited=true
+        r.on_packet_sent_cc(20000, now);
+
+        // Trigger congestion event to update ssthresh
+        r.congestion_event(now, packet::EPOCH_APPLICATION, now);
+
+        // After congestion event, cwnd will be reduced.
+        let cur_cwnd =
+            (prev_cwnd as f64 * recovery::LOSS_REDUCTION_FACTOR) as usize;
+        assert_eq!(r.cwnd(), cur_cwnd);
+
+        let rtt = Duration::from_millis(100);
+
+        let acked = vec![Acked {
+            pkt_num: 0,
+            // To exit from recovery
+            time_sent: now + rtt,
+            // More than cur_cwnd to increase cwnd
+            size: 8000,
+        }];
+
+        // Ack more than cwnd bytes with rtt=100ms
+        r.update_rtt(rtt, Duration::from_millis(0), now);
+        r.on_packets_acked(acked, packet::EPOCH_APPLICATION, now + rtt * 2);
+
+        // After acking more than cwnd, expect cwnd increased by MSS
+        assert_eq!(r.cwnd(), cur_cwnd + recovery::MAX_DATAGRAM_SIZE);
+    }
+}
diff --git a/src/stream.rs b/src/stream.rs
new file mode 100644
index 0000000..c2fbf36
--- /dev/null
+++ b/src/stream.rs
@@ -0,0 +1,2391 @@
+// Copyright (C) 2018-2019, Cloudflare, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright notice,
+//       this list of conditions and the following disclaimer.
+//
+//     * Redistributions in binary form must reproduce the above copyright
+//       notice, this list of conditions and the following disclaimer in the
+//       documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// 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::cmp;
+
+use std::collections::hash_map;
+use std::collections::BTreeMap;
+use std::collections::BinaryHeap;
+use std::collections::HashMap;
+use std::collections::HashSet;
+use std::collections::VecDeque;
+
+use crate::Error;
+use crate::Result;
+
+use crate::ranges;
+
+const DEFAULT_URGENCY: u8 = 127;
+
+/// Keeps track of QUIC streams and enforces stream limits.
+#[derive(Default)]
+pub struct StreamMap {
+    /// Map of streams indexed by stream ID.
+    streams: HashMap<u64, Stream>,
+
+    /// Set of streams that were completed and garbage collected.
+    ///
+    /// Instead of keeping the full stream state forever, we collect completed
+    /// streams to save memory, but we still need to keep track of previously
+    /// created streams, to prevent peers from re-creating them.
+    collected: HashSet<u64>,
+
+    /// Peer's maximum bidirectional stream count limit.
+    peer_max_streams_bidi: u64,
+
+    /// Peer's maximum unidirectional stream count limit.
+    peer_max_streams_uni: u64,
+
+    /// The total number of bidirectional streams opened by the peer.
+    peer_opened_streams_bidi: u64,
+
+    /// The total number of unidirectional streams opened by the peer.
+    peer_opened_streams_uni: u64,
+
+    /// Local maximum bidirectional stream count limit.
+    local_max_streams_bidi: u64,
+    local_max_streams_bidi_next: u64,
+
+    /// Local maximum unidirectional stream count limit.
+    local_max_streams_uni: u64,
+    local_max_streams_uni_next: u64,
+
+    /// The total number of bidirectional streams opened by the local endpoint.
+    local_opened_streams_bidi: u64,
+
+    /// The total number of unidirectional streams opened by the local endpoint.
+    local_opened_streams_uni: u64,
+
+    /// Queue of stream IDs corresponding to streams that have buffered data
+    /// ready to be sent to the peer. This also implies that the stream has
+    /// enough flow control credits to send at least some of that data.
+    ///
+    /// Streams are grouped by their priority, where each urgency level has two
+    /// queues, one for non-incremental streams and one for incremental ones.
+    ///
+    /// Streams with lower urgency level are scheduled first, and within the
+    /// same urgency level Non-incremental streams are scheduled first, in the
+    /// order of their stream IDs, and incremental streams are scheduled in a
+    /// round-robin fashion after all non-incremental streams have been flushed.
+    flushable: BTreeMap<u8, (BinaryHeap<std::cmp::Reverse<u64>>, VecDeque<u64>)>,
+
+    /// Set of stream IDs corresponding to streams that have outstanding data
+    /// to read. This is used to generate a `StreamIter` of streams without
+    /// having to iterate over the full list of streams.
+    readable: HashSet<u64>,
+
+    /// Set of stream IDs corresponding to streams that have enough flow control
+    /// capacity to be written to, and is not finished. This is used to generate
+    /// a `StreamIter` of streams without having to iterate over the full list
+    /// of streams.
+    writable: HashSet<u64>,
+
+    /// Set of stream IDs corresponding to streams that are almost out of flow
+    /// control credit and need to send MAX_STREAM_DATA. This is used to
+    /// generate a `StreamIter` of streams without having to iterate over the
+    /// full list of streams.
+    almost_full: HashSet<u64>,
+
+    /// Set of stream IDs corresponding to streams that are blocked. The value
+    /// of the map elements represents the offset of the stream at which the
+    /// blocking occurred.
+    blocked: HashMap<u64, u64>,
+}
+
+impl StreamMap {
+    pub fn new(max_streams_bidi: u64, max_streams_uni: u64) -> StreamMap {
+        StreamMap {
+            local_max_streams_bidi: max_streams_bidi,
+            local_max_streams_bidi_next: max_streams_bidi,
+
+            local_max_streams_uni: max_streams_uni,
+            local_max_streams_uni_next: max_streams_uni,
+
+            ..StreamMap::default()
+        }
+    }
+
+    /// Returns the stream with the given ID if it exists.
+    pub fn get(&self, id: u64) -> Option<&Stream> {
+        self.streams.get(&id)
+    }
+
+    /// Returns the mutable stream with the given ID if it exists.
+    pub fn get_mut(&mut self, id: u64) -> Option<&mut Stream> {
+        self.streams.get_mut(&id)
+    }
+
+    /// Returns the mutable stream with the given ID if it exists, or creates
+    /// a new one otherwise.
+    ///
+    /// The `local` parameter indicates whether the stream's creation was
+    /// requested by the local application rather than the peer, and is
+    /// used to validate the requested stream ID, and to select the initial
+    /// flow control values from the local and remote transport parameters
+    /// (also passed as arguments).
+    ///
+    /// This also takes care of enforcing both local and the peer's stream
+    /// count limits. If one of these limits is violated, the `StreamLimit`
+    /// error is returned.
+    pub(crate) fn get_or_create(
+        &mut self, id: u64, local_params: &crate::TransportParams,
+        peer_params: &crate::TransportParams, local: bool, is_server: bool,
+    ) -> Result<&mut Stream> {
+        let stream = match self.streams.entry(id) {
+            hash_map::Entry::Vacant(v) => {
+                // Stream has already been closed and garbage collected.
+                if self.collected.contains(&id) {
+                    return Err(Error::Done);
+                }
+
+                if local != is_local(id, is_server) {
+                    return Err(Error::InvalidStreamState);
+                }
+
+                let (max_rx_data, max_tx_data) = match (local, is_bidi(id)) {
+                    // Locally-initiated bidirectional stream.
+                    (true, true) => (
+                        local_params.initial_max_stream_data_bidi_local,
+                        peer_params.initial_max_stream_data_bidi_remote,
+                    ),
+
+                    // Locally-initiated unidirectional stream.
+                    (true, false) => (0, peer_params.initial_max_stream_data_uni),
+
+                    // Remotely-initiated bidirectional stream.
+                    (false, true) => (
+                        local_params.initial_max_stream_data_bidi_remote,
+                        peer_params.initial_max_stream_data_bidi_local,
+                    ),
+
+                    // Remotely-initiated unidirectional stream.
+                    (false, false) =>
+                        (local_params.initial_max_stream_data_uni, 0),
+                };
+
+                // Enforce stream count limits.
+                match (is_local(id, is_server), is_bidi(id)) {
+                    (true, true) => {
+                        if self.local_opened_streams_bidi >=
+                            self.peer_max_streams_bidi
+                        {
+                            return Err(Error::StreamLimit);
+                        }
+
+                        self.local_opened_streams_bidi += 1;
+                    },
+
+                    (true, false) => {
+                        if self.local_opened_streams_uni >=
+                            self.peer_max_streams_uni
+                        {
+                            return Err(Error::StreamLimit);
+                        }
+
+                        self.local_opened_streams_uni += 1;
+                    },
+
+                    (false, true) => {
+                        if self.peer_opened_streams_bidi >=
+                            self.local_max_streams_bidi
+                        {
+                            return Err(Error::StreamLimit);
+                        }
+
+                        self.peer_opened_streams_bidi += 1;
+                    },
+
+                    (false, false) => {
+                        if self.peer_opened_streams_uni >=
+                            self.local_max_streams_uni
+                        {
+                            return Err(Error::StreamLimit);
+                        }
+
+                        self.peer_opened_streams_uni += 1;
+                    },
+                };
+
+                let s = Stream::new(max_rx_data, max_tx_data, is_bidi(id), local);
+                v.insert(s)
+            },
+
+            hash_map::Entry::Occupied(v) => v.into_mut(),
+        };
+
+        // Stream might already be writable due to initial flow control limits.
+        if stream.is_writable() {
+            self.writable.insert(id);
+        }
+
+        Ok(stream)
+    }
+
+    /// Pushes the stream ID to the back of the flushable streams queue with
+    /// the specified urgency.
+    ///
+    /// Note that the caller is responsible for checking that the specified
+    /// stream ID was not in the queue already before calling this.
+    ///
+    /// Queueing a stream multiple times simultaneously means that it might be
+    /// unfairly scheduled more often than other streams, and might also cause
+    /// spurious cycles through the queue, so it should be avoided.
+    pub fn push_flushable(&mut self, stream_id: u64, urgency: u8, incr: bool) {
+        // Push the element to the back of the queue corresponding to the given
+        // urgency. If the queue doesn't exist yet, create it first.
+        let queues = self
+            .flushable
+            .entry(urgency)
+            .or_insert_with(|| (BinaryHeap::new(), VecDeque::new()));
+
+        if !incr {
+            // Non-incremental streams are scheduled in order of their stream ID.
+            queues.0.push(std::cmp::Reverse(stream_id))
+        } else {
+            // Incremental streams are scheduled in a round-robin fashion.
+            queues.1.push_back(stream_id)
+        };
+    }
+
+    /// Removes and returns the first stream ID from the flushable streams
+    /// queue with the specified urgency.
+    ///
+    /// Note that if the stream is still flushable after sending some of its
+    /// outstanding data, it needs to be added back to the queue.
+    pub fn pop_flushable(&mut self) -> Option<u64> {
+        // Remove the first element from the queue corresponding to the lowest
+        // urgency that has elements.
+        let (node, clear) =
+            if let Some((urgency, queues)) = self.flushable.iter_mut().next() {
+                let node = if !queues.0.is_empty() {
+                    queues.0.pop().map(|x| x.0)
+                } else {
+                    queues.1.pop_front()
+                };
+
+                let clear = if queues.0.is_empty() && queues.1.is_empty() {
+                    Some(*urgency)
+                } else {
+                    None
+                };
+
+                (node, clear)
+            } else {
+                (None, None)
+            };
+
+        // Remove the queue from the list of queues if it is now empty, so that
+        // the next time `pop_flushable()` is called the next queue with elements
+        // is used.
+        if let Some(urgency) = &clear {
+            self.flushable.remove(urgency);
+        }
+
+        node
+    }
+
+    /// Adds or removes the stream ID to/from the readable streams set.
+    ///
+    /// If the stream was already in the list, this does nothing.
+    pub fn mark_readable(&mut self, stream_id: u64, readable: bool) {
+        if readable {
+            self.readable.insert(stream_id);
+        } else {
+            self.readable.remove(&stream_id);
+        }
+    }
+
+    /// Adds or removes the stream ID to/from the writable streams set.
+    ///
+    /// This should also be called anytime a new stream is created, in addition
+    /// to when an existing stream becomes writable (or stops being writable).
+    ///
+    /// If the stream was already in the list, this does nothing.
+    pub fn mark_writable(&mut self, stream_id: u64, writable: bool) {
+        if writable {
+            self.writable.insert(stream_id);
+        } else {
+            self.writable.remove(&stream_id);
+        }
+    }
+
+    /// Adds or removes the stream ID to/from the almost full streams set.
+    ///
+    /// If the stream was already in the list, this does nothing.
+    pub fn mark_almost_full(&mut self, stream_id: u64, almost_full: bool) {
+        if almost_full {
+            self.almost_full.insert(stream_id);
+        } else {
+            self.almost_full.remove(&stream_id);
+        }
+    }
+
+    /// Adds or removes the stream ID to/from the blocked streams set with the
+    /// given offset value.
+    ///
+    /// If the stream was already in the list, this does nothing.
+    pub fn mark_blocked(&mut self, stream_id: u64, blocked: bool, off: u64) {
+        if blocked {
+            self.blocked.insert(stream_id, off);
+        } else {
+            self.blocked.remove(&stream_id);
+        }
+    }
+
+    /// Updates the peer's maximum bidirectional stream count limit.
+    pub fn update_peer_max_streams_bidi(&mut self, v: u64) {
+        self.peer_max_streams_bidi = cmp::max(self.peer_max_streams_bidi, v);
+    }
+
+    /// Updates the peer's maximum unidirectional stream count limit.
+    pub fn update_peer_max_streams_uni(&mut self, v: u64) {
+        self.peer_max_streams_uni = cmp::max(self.peer_max_streams_uni, v);
+    }
+
+    /// Commits the new max_streams_bidi limit.
+    pub fn update_max_streams_bidi(&mut self) {
+        self.local_max_streams_bidi = self.local_max_streams_bidi_next;
+    }
+
+    /// Returns the new max_streams_bidi limit.
+    pub fn max_streams_bidi_next(&mut self) -> u64 {
+        self.local_max_streams_bidi_next
+    }
+
+    /// Commits the new max_streams_uni limit.
+    pub fn update_max_streams_uni(&mut self) {
+        self.local_max_streams_uni = self.local_max_streams_uni_next;
+    }
+
+    /// Returns the new max_streams_uni limit.
+    pub fn max_streams_uni_next(&mut self) -> u64 {
+        self.local_max_streams_uni_next
+    }
+
+    /// Drops completed stream.
+    ///
+    /// This should only be called when Stream::is_complete() returns true for
+    /// the given stream.
+    pub fn collect(&mut self, stream_id: u64, local: bool) {
+        if !local {
+            // If the stream was created by the peer, give back a max streams
+            // credit.
+            if is_bidi(stream_id) {
+                self.local_max_streams_bidi_next =
+                    self.local_max_streams_bidi_next.saturating_add(1);
+            } else {
+                self.local_max_streams_uni_next =
+                    self.local_max_streams_uni_next.saturating_add(1);
+            }
+        }
+
+        self.streams.remove(&stream_id);
+        self.collected.insert(stream_id);
+    }
+
+    /// Creates an iterator over streams that have outstanding data to read.
+    pub fn readable(&self) -> StreamIter {
+        StreamIter::from(&self.readable)
+    }
+
+    /// Creates an iterator over streams that can be written to.
+    pub fn writable(&self) -> StreamIter {
+        StreamIter::from(&self.writable)
+    }
+
+    /// Creates an iterator over streams that need to send MAX_STREAM_DATA.
+    pub fn almost_full(&self) -> StreamIter {
+        StreamIter::from(&self.almost_full)
+    }
+
+    /// Creates an iterator over streams that need to send STREAM_DATA_BLOCKED.
+    pub fn blocked(&self) -> hash_map::Iter<u64, u64> {
+        self.blocked.iter()
+    }
+
+    /// Returns true if there are any streams that have data to write.
+    pub fn has_flushable(&self) -> bool {
+        !self.flushable.is_empty()
+    }
+
+    /// Returns true if there are any streams that need to update the local
+    /// flow control limit.
+    pub fn has_almost_full(&self) -> bool {
+        !self.almost_full.is_empty()
+    }
+
+    /// Returns true if there are any streams that are blocked.
+    pub fn has_blocked(&self) -> bool {
+        !self.blocked.is_empty()
+    }
+
+    /// Returns true if the max bidirectional streams count needs to be updated
+    /// by sending a MAX_STREAMS frame to the peer.
+    pub fn should_update_max_streams_bidi(&self) -> bool {
+        self.local_max_streams_bidi_next != self.local_max_streams_bidi &&
+            self.local_max_streams_bidi_next / 2 >
+                self.local_max_streams_bidi - self.peer_opened_streams_bidi
+    }
+
+    /// Returns true if the max unidirectional streams count needs to be updated
+    /// by sending a MAX_STREAMS frame to the peer.
+    pub fn should_update_max_streams_uni(&self) -> bool {
+        self.local_max_streams_uni_next != self.local_max_streams_uni &&
+            self.local_max_streams_uni_next / 2 >
+                self.local_max_streams_uni - self.peer_opened_streams_uni
+    }
+
+    /// Returns the number of active streams in the map.
+    #[cfg(test)]
+    pub fn len(&self) -> usize {
+        self.streams.len()
+    }
+}
+
+/// A QUIC stream.
+#[derive(Default)]
+pub struct Stream {
+    /// Receive-side stream buffer.
+    pub recv: RecvBuf,
+
+    /// Send-side stream buffer.
+    pub send: SendBuf,
+
+    /// Whether the stream is bidirectional.
+    pub bidi: bool,
+
+    /// Whether the stream was created by the local endpoint.
+    pub local: bool,
+
+    /// Application data.
+    pub data: Option<Box<dyn Send + std::any::Any>>,
+
+    /// The stream's urgency (lower is better). Default is `DEFAULT_URGENCY`.
+    pub urgency: u8,
+
+    /// Whether the stream can be flushed incrementally. Default is `true`.
+    pub incremental: bool,
+}
+
+impl Stream {
+    /// Creates a new stream with the given flow control limits.
+    pub fn new(
+        max_rx_data: u64, max_tx_data: u64, bidi: bool, local: bool,
+    ) -> Stream {
+        Stream {
+            recv: RecvBuf::new(max_rx_data),
+            send: SendBuf::new(max_tx_data),
+            bidi,
+            local,
+            data: None,
+            urgency: DEFAULT_URGENCY,
+            incremental: true,
+        }
+    }
+
+    /// Returns true if the stream has data to read.
+    pub fn is_readable(&self) -> bool {
+        self.recv.ready()
+    }
+
+    /// Returns true if the stream has enough flow control capacity to be
+    /// written to, and is not finished.
+    pub fn is_writable(&self) -> bool {
+        !self.send.shutdown &&
+            !self.send.is_fin() &&
+            self.send.off < self.send.max_data
+    }
+
+    /// Returns true if the stream has data to send and is allowed to send at
+    /// least some of it.
+    pub fn is_flushable(&self) -> bool {
+        self.send.ready() && self.send.off_front() < self.send.max_data
+    }
+
+    /// Returns true if the stream is complete.
+    ///
+    /// For bidirectional streams this happens when both the receive and send
+    /// sides are complete. That is when all incoming data has been read by the
+    /// application, and when all outgoing data has been acked by the peer.
+    ///
+    /// For unidirectional streams this happens when either the receive or send
+    /// side is complete, depending on whether the stream was created locally
+    /// or not.
+    pub fn is_complete(&self) -> bool {
+        match (self.bidi, self.local) {
+            // For bidirectional streams we need to check both receive and send
+            // sides for completion.
+            (true, _) => self.recv.is_fin() && self.send.is_complete(),
+
+            // For unidirectional streams generated locally, we only need to
+            // check the send side for completion.
+            (false, true) => self.send.is_complete(),
+
+            // For unidirectional streams generated by the peer, we only need
+            // to check the receive side for completion.
+            (false, false) => self.recv.is_fin(),
+        }
+    }
+}
+
+/// Returns true if the stream was created locally.
+pub fn is_local(stream_id: u64, is_server: bool) -> bool {
+    (stream_id & 0x1) == (is_server as u64)
+}
+
+/// Returns true if the stream is bidirectional.
+pub fn is_bidi(stream_id: u64) -> bool {
+    (stream_id & 0x2) == 0
+}
+
+/// An iterator over QUIC streams.
+#[derive(Default)]
+pub struct StreamIter {
+    streams: Vec<u64>,
+}
+
+impl StreamIter {
+    fn from(streams: &HashSet<u64>) -> Self {
+        StreamIter {
+            streams: streams.iter().copied().collect(),
+        }
+    }
+}
+
+impl Iterator for StreamIter {
+    type Item = u64;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        self.streams.pop()
+    }
+}
+
+impl ExactSizeIterator for StreamIter {
+    fn len(&self) -> usize {
+        self.streams.len()
+    }
+}
+
+/// Receive-side stream buffer.
+///
+/// Stream data received by the peer is buffered in a list of data chunks
+/// ordered by offset in ascending order. Contiguous data can then be read
+/// into a slice.
+#[derive(Debug, Default)]
+pub struct RecvBuf {
+    /// Chunks of data received from the peer that have not yet been read by
+    /// the application, ordered by offset.
+    data: BinaryHeap<RangeBuf>,
+
+    /// The lowest data offset that has yet to be read by the application.
+    off: u64,
+
+    /// The total length of data received on this stream.
+    len: u64,
+
+    /// The maximum offset the peer is allowed to send us.
+    max_data: u64,
+
+    /// The updated maximum offset the peer is allowed to send us.
+    max_data_next: u64,
+
+    /// The final stream offset received from the peer, if any.
+    fin_off: Option<u64>,
+
+    /// Whether incoming data is validated but not buffered.
+    drain: bool,
+}
+
+impl RecvBuf {
+    /// Creates a new receive buffer.
+    fn new(max_data: u64) -> RecvBuf {
+        RecvBuf {
+            max_data,
+            max_data_next: max_data,
+            ..RecvBuf::default()
+        }
+    }
+
+    /// Inserts the given chunk of data in the buffer.
+    ///
+    /// This also takes care of enforcing stream flow control limits, as well
+    /// as handling incoming data that overlaps data that is already in the
+    /// buffer.
+    pub fn push(&mut self, buf: RangeBuf) -> Result<()> {
+        if buf.max_off() > self.max_data {
+            return Err(Error::FlowControl);
+        }
+
+        if let Some(fin_off) = self.fin_off {
+            // Stream's size is known, forbid data beyond that point.
+            if buf.max_off() > fin_off {
+                return Err(Error::FinalSize);
+            }
+
+            // Stream's size is already known, forbid changing it.
+            if buf.fin() && fin_off != buf.max_off() {
+                return Err(Error::FinalSize);
+            }
+        }
+
+        // Stream's known size is lower than data already received.
+        if buf.fin() && buf.max_off() < self.len {
+            return Err(Error::FinalSize);
+        }
+
+        // We already saved the final offset, so there's nothing else we
+        // need to keep from the RangeBuf if it's empty.
+        if self.fin_off.is_some() && buf.is_empty() {
+            return Ok(());
+        }
+
+        // No need to process an empty buffer with the fin flag, if we already
+        // know the final size.
+        if buf.fin() && buf.is_empty() && self.fin_off.is_some() {
+            return Ok(());
+        }
+
+        if buf.fin() {
+            self.fin_off = Some(buf.max_off());
+        }
+
+        // No need to store empty buffer that doesn't carry the fin flag.
+        if !buf.fin() && buf.is_empty() {
+            return Ok(());
+        }
+
+        // Check if data is fully duplicate, that is the buffer's max offset is
+        // lower or equal to the offset already stored in the recv buffer.
+        if self.off >= buf.max_off() {
+            // An exception is applied to empty range buffers, because an empty
+            // buffer's max offset matches the max offset of the recv buffer.
+            //
+            // By this point all spurious empty buffers should have already been
+            // discarded, so allowing empty buffers here should be safe.
+            if !buf.is_empty() {
+                return Ok(());
+            }
+        }
+
+        if self.drain {
+            return Ok(());
+        }
+
+        let mut tmp_buf = Some(buf);
+
+        while let Some(mut buf) = tmp_buf {
+            tmp_buf = None;
+
+            // Discard incoming data below current stream offset. Bytes up to
+            // `self.off` have already been received so we should not buffer
+            // them again. This is also important to make sure `ready()` doesn't
+            // get stuck when a buffer with lower offset than the stream's is
+            // buffered.
+            if self.off > buf.off() {
+                buf = buf.split_off((self.off - buf.off()) as usize);
+            }
+
+            for b in &self.data {
+                // New buffer is fully contained in existing buffer.
+                if buf.off() >= b.off() && buf.max_off() <= b.max_off() {
+                    return Ok(());
+                }
+
+                // New buffer's start overlaps existing buffer.
+                if buf.off() >= b.off() && buf.off() < b.max_off() {
+                    buf = buf.split_off((b.max_off() - buf.off()) as usize);
+                }
+
+                // New buffer's end overlaps existing buffer.
+                if buf.off() < b.off() && buf.max_off() > b.off() {
+                    tmp_buf = Some(buf.split_off((b.off() - buf.off()) as usize));
+                }
+            }
+
+            self.len = cmp::max(self.len, buf.max_off());
+
+            self.data.push(buf);
+        }
+
+        Ok(())
+    }
+
+    /// Writes data from the receive buffer into the given output buffer.
+    ///
+    /// Only contiguous data is written to the output buffer, starting from
+    /// offset 0. The offset is incremented as data is read out of the receive
+    /// buffer into the application buffer. If there is no data at the expected
+    /// read offset, the `Done` error is returned.
+    ///
+    /// On success the amount of data read, and a flag indicating if there is
+    /// no more data in the buffer, are returned as a tuple.
+    pub fn pop(&mut self, out: &mut [u8]) -> Result<(usize, bool)> {
+        let mut len = 0;
+        let mut cap = out.len();
+
+        if !self.ready() {
+            return Err(Error::Done);
+        }
+
+        while cap > 0 && self.ready() {
+            let mut buf = match self.data.peek_mut() {
+                Some(v) => v,
+
+                None => break,
+            };
+
+            let buf_len = cmp::min(buf.len(), cap);
+
+            out[len..len + buf_len].copy_from_slice(&buf[..buf_len]);
+
+            self.off += buf_len as u64;
+
+            len += buf_len;
+            cap -= buf_len;
+
+            if buf_len < buf.len() {
+                buf.consume(buf_len);
+
+                // We reached the maximum capacity, so end here.
+                break;
+            }
+
+            std::collections::binary_heap::PeekMut::pop(buf);
+        }
+
+        self.max_data_next = self.max_data_next.saturating_add(len as u64);
+
+        Ok((len, self.is_fin()))
+    }
+
+    /// Resets the stream at the given offset.
+    pub fn reset(&mut self, final_size: u64) -> Result<usize> {
+        // Stream's size is already known, forbid changing it.
+        if let Some(fin_off) = self.fin_off {
+            if fin_off != final_size {
+                return Err(Error::FinalSize);
+            }
+        }
+
+        // Stream's known size is lower than data already received.
+        if final_size < self.len {
+            return Err(Error::FinalSize);
+        }
+
+        self.fin_off = Some(final_size);
+
+        // Return how many bytes need to be removed from the connection flow
+        // control.
+        Ok((final_size - self.len) as usize)
+    }
+
+    /// Commits the new max_data limit.
+    pub fn update_max_data(&mut self) {
+        self.max_data = self.max_data_next;
+    }
+
+    /// Return the new max_data limit.
+    pub fn max_data_next(&mut self) -> u64 {
+        self.max_data_next
+    }
+
+    /// Shuts down receiving data.
+    pub fn shutdown(&mut self) -> Result<()> {
+        if self.drain {
+            return Err(Error::Done);
+        }
+
+        self.drain = true;
+
+        self.data.clear();
+
+        Ok(())
+    }
+
+    /// Returns the lowest offset of data buffered.
+    #[allow(dead_code)]
+    pub fn off_front(&self) -> u64 {
+        self.off
+    }
+
+    /// Returns true if we need to update the local flow control limit.
+    pub fn almost_full(&self) -> bool {
+        // Send MAX_STREAM_DATA when the new limit is at least double the
+        // amount of data that can be received before blocking.
+        self.fin_off.is_none() &&
+            self.max_data_next != self.max_data &&
+            self.max_data_next / 2 > self.max_data - self.len
+    }
+
+    /// Returns the largest offset ever received.
+    pub fn max_off(&self) -> u64 {
+        self.len
+    }
+
+    /// Returns true if the receive-side of the stream is complete.
+    ///
+    /// This happens when the stream's receive final size is known, and the
+    /// application has read all data from the stream.
+    pub fn is_fin(&self) -> bool {
+        if self.fin_off == Some(self.off) {
+            return true;
+        }
+
+        false
+    }
+
+    /// Returns true if the stream has data to be read.
+    fn ready(&self) -> bool {
+        let buf = match self.data.peek() {
+            Some(v) => v,
+
+            None => return false,
+        };
+
+        buf.off() == self.off
+    }
+}
+
+/// Send-side stream buffer.
+///
+/// Stream data scheduled to be sent to the peer is buffered in a list of data
+/// chunks ordered by offset in ascending order. Contiguous data can then be
+/// read into a slice.
+///
+/// By default, new data is appended at the end of the stream, but data can be
+/// inserted at the start of the buffer (this is to allow data that needs to be
+/// retransmitted to be re-buffered).
+#[derive(Debug, Default)]
+pub struct SendBuf {
+    /// Chunks of data to be sent, ordered by offset.
+    data: BinaryHeap<RangeBuf>,
+
+    /// The maximum offset of data buffered in the stream.
+    off: u64,
+
+    /// The amount of data that was ever written to this stream.
+    len: u64,
+
+    /// The maximum offset we are allowed to send to the peer.
+    max_data: u64,
+
+    /// The final stream offset written to the stream, if any.
+    fin_off: Option<u64>,
+
+    /// Whether the stream's send-side has been shut down.
+    shutdown: bool,
+
+    /// Ranges of data offsets that have been acked.
+    acked: ranges::RangeSet,
+}
+
+impl SendBuf {
+    /// Creates a new send buffer.
+    fn new(max_data: u64) -> SendBuf {
+        SendBuf {
+            max_data,
+            ..SendBuf::default()
+        }
+    }
+
+    /// Inserts the given slice of data at the end of the buffer.
+    ///
+    /// The number of bytes that were actually stored in the buffer is returned
+    /// (this may be lower than the size of the input buffer, in case of partial
+    /// writes).
+    pub fn push_slice(
+        &mut self, mut data: &[u8], mut fin: bool,
+    ) -> Result<usize> {
+        if self.shutdown {
+            // Since we won't write any more data anyway, pretend that we sent
+            // all data that was passed in.
+            return Ok(data.len());
+        }
+
+        if data.is_empty() {
+            // Create a dummy range buffer, in order to propagate the `fin` flag
+            // into `RangeBuf::push()`. This will be discarded later on.
+            let buf = RangeBuf::from(&[], self.off, fin);
+
+            return self.push(buf).map(|_| 0);
+        }
+
+        if data.len() > self.cap() {
+            // Truncate the input buffer according to the stream's capacity.
+            let len = self.cap();
+            data = &data[..len];
+
+            // We are not buffering the full input, so clear the fin flag.
+            fin = false;
+        }
+
+        let buf = RangeBuf::from(data, self.off, fin);
+        self.push(buf)?;
+
+        self.off += data.len() as u64;
+
+        Ok(data.len())
+    }
+
+    /// Inserts the given chunk of data in the buffer.
+    pub fn push(&mut self, buf: RangeBuf) -> Result<()> {
+        if let Some(fin_off) = self.fin_off {
+            // Can't write past final offset.
+            if buf.max_off() > fin_off {
+                return Err(Error::FinalSize);
+            }
+
+            // Can't "undo" final offset.
+            if buf.max_off() == fin_off && !buf.fin() {
+                return Err(Error::FinalSize);
+            }
+        }
+
+        if self.shutdown {
+            return Ok(());
+        }
+
+        if buf.fin() {
+            self.fin_off = Some(buf.max_off());
+        }
+
+        // Don't queue data that was already fully acked.
+        if self.ack_off() >= buf.max_off() {
+            return Ok(());
+        }
+
+        self.len += buf.len() as u64;
+
+        // We already recorded the final offset, so we can just discard the
+        // empty buffer now.
+        if buf.is_empty() {
+            return Ok(());
+        }
+
+        self.data.push(buf);
+
+        Ok(())
+    }
+
+    /// Returns contiguous data from the send buffer as a single `RangeBuf`.
+    pub fn pop(&mut self, max_data: usize) -> Result<RangeBuf> {
+        let mut out = RangeBuf::default();
+        out.data =
+            Vec::with_capacity(cmp::min(max_data as u64, self.len) as usize);
+        out.off = self.off;
+
+        let mut out_len = max_data;
+        let mut out_off = self.data.peek().map_or_else(|| out.off, RangeBuf::off);
+
+        while out_len > 0 &&
+            self.ready() &&
+            self.off_front() == out_off &&
+            self.off_front() < self.max_data
+        {
+            let mut buf = match self.data.peek_mut() {
+                Some(v) => v,
+
+                None => break,
+            };
+
+            let buf_len = cmp::min(buf.len(), out_len);
+
+            if out.is_empty() {
+                out.off = buf.off();
+            }
+
+            self.len -= buf_len as u64;
+
+            out_len -= buf_len;
+            out_off = buf.off() + buf_len as u64;
+
+            out.data.extend_from_slice(&buf[..buf_len]);
+
+            if buf_len < buf.len() {
+                buf.consume(buf_len);
+
+                // We reached the maximum capacity, so end here.
+                break;
+            }
+
+            std::collections::binary_heap::PeekMut::pop(buf);
+        }
+
+        // Override the `fin` flag set for the output buffer by matching the
+        // buffer's maximum offset against the stream's final offset (if known).
+        //
+        // This is more efficient than tracking `fin` using the range buffers
+        // themselves, and lets us avoid queueing empty buffers just so we can
+        // propagate the final size.
+        out.fin = self.fin_off == Some(out.max_off());
+
+        Ok(out)
+    }
+
+    /// Updates the max_data limit to the given value.
+    pub fn update_max_data(&mut self, max_data: u64) {
+        self.max_data = cmp::max(self.max_data, max_data);
+    }
+
+    /// Increments the acked data offset.
+    pub fn ack(&mut self, off: u64, len: usize) {
+        self.acked.insert(off..off + len as u64);
+    }
+
+    /// Shuts down sending data.
+    pub fn shutdown(&mut self) -> Result<()> {
+        if self.shutdown {
+            return Err(Error::Done);
+        }
+
+        self.shutdown = true;
+
+        self.data.clear();
+
+        Ok(())
+    }
+
+    /// Returns the largest offset of data buffered.
+    #[allow(dead_code)]
+    pub fn off_back(&self) -> u64 {
+        self.off
+    }
+
+    /// Returns the lowest offset of data buffered.
+    pub fn off_front(&self) -> u64 {
+        match self.data.peek() {
+            Some(v) => v.off(),
+
+            None => self.off,
+        }
+    }
+
+    /// The maximum offset we are allowed to send to the peer.
+    pub fn max_off(&self) -> u64 {
+        self.max_data
+    }
+
+    /// Returns true if all data in the stream has been sent.
+    ///
+    /// This happens when the stream's send final size is knwon, and the
+    /// application has already written data up to that point.
+    pub fn is_fin(&self) -> bool {
+        if self.fin_off == Some(self.off) {
+            return true;
+        }
+
+        false
+    }
+
+    /// Returns true if the send-side of the stream is complete.
+    ///
+    /// This happens when the stream's send final size is known, and the peer
+    /// has already acked all stream data up to that point.
+    pub fn is_complete(&self) -> bool {
+        if let Some(fin_off) = self.fin_off {
+            if self.acked == (0..fin_off) {
+                return true;
+            }
+        }
+
+        false
+    }
+
+    /// Returns true if there is data to be written.
+    fn ready(&self) -> bool {
+        !self.data.is_empty()
+    }
+
+    /// Returns the highest contiguously acked offset.
+    fn ack_off(&self) -> u64 {
+        match self.acked.iter().next() {
+            // Only consider the initial range if it contiguously covers the
+            // start of the stream (i.e. from offset 0).
+            Some(std::ops::Range { start: 0, end }) => end,
+
+            Some(_) | None => 0,
+        }
+    }
+
+    /// Returns the outgoing flow control capacity.
+    pub fn cap(&self) -> usize {
+        (self.max_data - self.off) as usize
+    }
+}
+
+/// Buffer holding data at a specific offset.
+#[derive(Clone, Debug, Default, Eq)]
+pub struct RangeBuf {
+    /// The internal buffer holding the data.
+    data: Vec<u8>,
+
+    /// The starting offset within `data`. This allows partially consuming a
+    /// buffer without duplicating the data.
+    pos: usize,
+
+    /// The starting offset within a stream.
+    off: u64,
+
+    /// Whether this contains the final byte in the stream.
+    fin: bool,
+}
+
+impl RangeBuf {
+    /// Creates a new `RangeBuf` from the given slice.
+    pub(crate) fn from(buf: &[u8], off: u64, fin: bool) -> RangeBuf {
+        RangeBuf {
+            data: Vec::from(buf),
+            pos: 0,
+            off,
+            fin,
+        }
+    }
+
+    /// Returns whether `self` holds the final offset in the stream.
+    pub fn fin(&self) -> bool {
+        self.fin
+    }
+
+    /// Returns the starting offset of `self`.
+    pub fn off(&self) -> u64 {
+        self.off + self.pos as u64
+    }
+
+    /// Returns the final offset of `self`.
+    pub fn max_off(&self) -> u64 {
+        self.off() + self.len() as u64
+    }
+
+    /// Returns the length of `self`.
+    pub fn len(&self) -> usize {
+        self.data.len() - self.pos
+    }
+
+    /// Returns true if `self` has a length of zero bytes.
+    pub fn is_empty(&self) -> bool {
+        self.len() == 0
+    }
+
+    /// Consumes the starting `count` bytes of `self`.
+    pub fn consume(&mut self, count: usize) {
+        self.pos += count;
+    }
+
+    /// Splits the buffer into two at the given index.
+    pub fn split_off(&mut self, at: usize) -> RangeBuf {
+        let buf = RangeBuf {
+            data: self.data.split_off(at),
+            pos: 0,
+            off: self.off + at as u64,
+            fin: self.fin,
+        };
+
+        self.fin = false;
+
+        buf
+    }
+}
+
+impl std::ops::Deref for RangeBuf {
+    type Target = [u8];
+
+    fn deref(&self) -> &[u8] {
+        &self.data[self.pos..]
+    }
+}
+
+impl std::ops::DerefMut for RangeBuf {
+    fn deref_mut(&mut self) -> &mut [u8] {
+        &mut self.data[self.pos..]
+    }
+}
+
+impl Ord for RangeBuf {
+    fn cmp(&self, other: &RangeBuf) -> cmp::Ordering {
+        // Invert ordering to implement min-heap.
+        self.off.cmp(&other.off).reverse()
+    }
+}
+
+impl PartialOrd for RangeBuf {
+    fn partial_cmp(&self, other: &RangeBuf) -> Option<cmp::Ordering> {
+        Some(self.cmp(other))
+    }
+}
+
+impl PartialEq for RangeBuf {
+    fn eq(&self, other: &RangeBuf) -> bool {
+        self.off == other.off
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn empty_read() {
+        let mut recv = RecvBuf::new(std::u64::MAX);
+        assert_eq!(recv.len, 0);
+
+        let mut buf = [0; 32];
+
+        assert_eq!(recv.pop(&mut buf), Err(Error::Done));
+    }
+
+    #[test]
+    fn empty_stream_frame() {
+        let mut recv = RecvBuf::new(15);
+        assert_eq!(recv.len, 0);
+
+        let buf = RangeBuf::from(b"hello", 0, false);
+        assert!(recv.push(buf).is_ok());
+        assert_eq!(recv.len, 5);
+        assert_eq!(recv.off, 0);
+        assert_eq!(recv.data.len(), 1);
+
+        let mut buf = [0; 32];
+        assert_eq!(recv.pop(&mut buf), Ok((5, false)));
+
+        // Don't store non-fin empty buffer.
+        let buf = RangeBuf::from(b"", 10, false);
+        assert!(recv.push(buf).is_ok());
+        assert_eq!(recv.len, 5);
+        assert_eq!(recv.off, 5);
+        assert_eq!(recv.data.len(), 0);
+
+        // Check flow control for empty buffer.
+        let buf = RangeBuf::from(b"", 16, false);
+        assert_eq!(recv.push(buf), Err(Error::FlowControl));
+
+        // Store fin empty buffer.
+        let buf = RangeBuf::from(b"", 5, true);
+        assert!(recv.push(buf).is_ok());
+        assert_eq!(recv.len, 5);
+        assert_eq!(recv.off, 5);
+        assert_eq!(recv.data.len(), 1);
+
+        // Don't store additional fin empty buffers.
+        let buf = RangeBuf::from(b"", 5, true);
+        assert!(recv.push(buf).is_ok());
+        assert_eq!(recv.len, 5);
+        assert_eq!(recv.off, 5);
+        assert_eq!(recv.data.len(), 1);
+
+        // Don't store additional fin non-empty buffers.
+        let buf = RangeBuf::from(b"aa", 3, true);
+        assert!(recv.push(buf).is_ok());
+        assert_eq!(recv.len, 5);
+        assert_eq!(recv.off, 5);
+        assert_eq!(recv.data.len(), 1);
+
+        // Validate final size with fin empty buffers.
+        let buf = RangeBuf::from(b"", 6, true);
+        assert_eq!(recv.push(buf), Err(Error::FinalSize));
+        let buf = RangeBuf::from(b"", 4, true);
+        assert_eq!(recv.push(buf), Err(Error::FinalSize));
+
+        let mut buf = [0; 32];
+        assert_eq!(recv.pop(&mut buf), Ok((0, true)));
+    }
+
+    #[test]
+    fn ordered_read() {
+        let mut recv = RecvBuf::new(std::u64::MAX);
+        assert_eq!(recv.len, 0);
+
+        let mut buf = [0; 32];
+
+        let first = RangeBuf::from(b"hello", 0, false);
+        let second = RangeBuf::from(b"world", 5, false);
+        let third = RangeBuf::from(b"something", 10, true);
+
+        assert!(recv.push(second).is_ok());
+        assert_eq!(recv.len, 10);
+        assert_eq!(recv.off, 0);
+
+        assert_eq!(recv.pop(&mut buf), Err(Error::Done));
+
+        assert!(recv.push(third).is_ok());
+        assert_eq!(recv.len, 19);
+        assert_eq!(recv.off, 0);
+
+        assert_eq!(recv.pop(&mut buf), Err(Error::Done));
+
+        assert!(recv.push(first).is_ok());
+        assert_eq!(recv.len, 19);
+        assert_eq!(recv.off, 0);
+
+        let (len, fin) = recv.pop(&mut buf).unwrap();
+        assert_eq!(len, 19);
+        assert_eq!(fin, true);
+        assert_eq!(&buf[..len], b"helloworldsomething");
+        assert_eq!(recv.len, 19);
+        assert_eq!(recv.off, 19);
+
+        assert_eq!(recv.pop(&mut buf), Err(Error::Done));
+    }
+
+    #[test]
+    fn split_read() {
+        let mut recv = RecvBuf::new(std::u64::MAX);
+        assert_eq!(recv.len, 0);
+
+        let mut buf = [0; 32];
+
+        let first = RangeBuf::from(b"something", 0, false);
+        let second = RangeBuf::from(b"helloworld", 9, true);
+
+        assert!(recv.push(first).is_ok());
+        assert_eq!(recv.len, 9);
+        assert_eq!(recv.off, 0);
+
+        assert!(recv.push(second).is_ok());
+        assert_eq!(recv.len, 19);
+        assert_eq!(recv.off, 0);
+
+        let (len, fin) = recv.pop(&mut buf[..10]).unwrap();
+        assert_eq!(len, 10);
+        assert_eq!(fin, false);
+        assert_eq!(&buf[..len], b"somethingh");
+        assert_eq!(recv.len, 19);
+        assert_eq!(recv.off, 10);
+
+        let (len, fin) = recv.pop(&mut buf[..5]).unwrap();
+        assert_eq!(len, 5);
+        assert_eq!(fin, false);
+        assert_eq!(&buf[..len], b"ellow");
+        assert_eq!(recv.len, 19);
+        assert_eq!(recv.off, 15);
+
+        let (len, fin) = recv.pop(&mut buf[..10]).unwrap();
+        assert_eq!(len, 4);
+        assert_eq!(fin, true);
+        assert_eq!(&buf[..len], b"orld");
+        assert_eq!(recv.len, 19);
+        assert_eq!(recv.off, 19);
+    }
+
+    #[test]
+    fn incomplete_read() {
+        let mut recv = RecvBuf::new(std::u64::MAX);
+        assert_eq!(recv.len, 0);
+
+        let mut buf = [0; 32];
+
+        let first = RangeBuf::from(b"something", 0, false);
+        let second = RangeBuf::from(b"helloworld", 9, true);
+
+        assert!(recv.push(second).is_ok());
+        assert_eq!(recv.len, 19);
+        assert_eq!(recv.off, 0);
+
+        assert_eq!(recv.pop(&mut buf), Err(Error::Done));
+
+        assert!(recv.push(first).is_ok());
+        assert_eq!(recv.len, 19);
+        assert_eq!(recv.off, 0);
+
+        let (len, fin) = recv.pop(&mut buf).unwrap();
+        assert_eq!(len, 19);
+        assert_eq!(fin, true);
+        assert_eq!(&buf[..len], b"somethinghelloworld");
+        assert_eq!(recv.len, 19);
+        assert_eq!(recv.off, 19);
+    }
+
+    #[test]
+    fn zero_len_read() {
+        let mut recv = RecvBuf::new(std::u64::MAX);
+        assert_eq!(recv.len, 0);
+
+        let mut buf = [0; 32];
+
+        let first = RangeBuf::from(b"something", 0, false);
+        let second = RangeBuf::from(b"", 9, true);
+
+        assert!(recv.push(first).is_ok());
+        assert_eq!(recv.len, 9);
+        assert_eq!(recv.off, 0);
+        assert_eq!(recv.data.len(), 1);
+
+        assert!(recv.push(second).is_ok());
+        assert_eq!(recv.len, 9);
+        assert_eq!(recv.off, 0);
+        assert_eq!(recv.data.len(), 1);
+
+        let (len, fin) = recv.pop(&mut buf).unwrap();
+        assert_eq!(len, 9);
+        assert_eq!(fin, true);
+        assert_eq!(&buf[..len], b"something");
+        assert_eq!(recv.len, 9);
+        assert_eq!(recv.off, 9);
+    }
+
+    #[test]
+    fn past_read() {
+        let mut recv = RecvBuf::new(std::u64::MAX);
+        assert_eq!(recv.len, 0);
+
+        let mut buf = [0; 32];
+
+        let first = RangeBuf::from(b"something", 0, false);
+        let second = RangeBuf::from(b"hello", 3, false);
+        let third = RangeBuf::from(b"ello", 4, true);
+        let fourth = RangeBuf::from(b"ello", 5, true);
+
+        assert!(recv.push(first).is_ok());
+        assert_eq!(recv.len, 9);
+        assert_eq!(recv.off, 0);
+        assert_eq!(recv.data.len(), 1);
+
+        let (len, fin) = recv.pop(&mut buf).unwrap();
+        assert_eq!(len, 9);
+        assert_eq!(fin, false);
+        assert_eq!(&buf[..len], b"something");
+        assert_eq!(recv.len, 9);
+        assert_eq!(recv.off, 9);
+
+        assert!(recv.push(second).is_ok());
+        assert_eq!(recv.len, 9);
+        assert_eq!(recv.off, 9);
+        assert_eq!(recv.data.len(), 0);
+
+        assert_eq!(recv.push(third), Err(Error::FinalSize));
+
+        assert!(recv.push(fourth).is_ok());
+        assert_eq!(recv.len, 9);
+        assert_eq!(recv.off, 9);
+        assert_eq!(recv.data.len(), 0);
+
+        assert_eq!(recv.pop(&mut buf), Err(Error::Done));
+    }
+
+    #[test]
+    fn fully_overlapping_read() {
+        let mut recv = RecvBuf::new(std::u64::MAX);
+        assert_eq!(recv.len, 0);
+
+        let mut buf = [0; 32];
+
+        let first = RangeBuf::from(b"something", 0, false);
+        let second = RangeBuf::from(b"hello", 4, false);
+
+        assert!(recv.push(first).is_ok());
+        assert_eq!(recv.len, 9);
+        assert_eq!(recv.off, 0);
+        assert_eq!(recv.data.len(), 1);
+
+        assert!(recv.push(second).is_ok());
+        assert_eq!(recv.len, 9);
+        assert_eq!(recv.off, 0);
+        assert_eq!(recv.data.len(), 1);
+
+        let (len, fin) = recv.pop(&mut buf).unwrap();
+        assert_eq!(len, 9);
+        assert_eq!(fin, false);
+        assert_eq!(&buf[..len], b"something");
+        assert_eq!(recv.len, 9);
+        assert_eq!(recv.off, 9);
+        assert_eq!(recv.data.len(), 0);
+
+        assert_eq!(recv.pop(&mut buf), Err(Error::Done));
+    }
+
+    #[test]
+    fn fully_overlapping_read2() {
+        let mut recv = RecvBuf::new(std::u64::MAX);
+        assert_eq!(recv.len, 0);
+
+        let mut buf = [0; 32];
+
+        let first = RangeBuf::from(b"something", 0, false);
+        let second = RangeBuf::from(b"hello", 4, false);
+
+        assert!(recv.push(second).is_ok());
+        assert_eq!(recv.len, 9);
+        assert_eq!(recv.off, 0);
+        assert_eq!(recv.data.len(), 1);
+
+        assert!(recv.push(first).is_ok());
+        assert_eq!(recv.len, 9);
+        assert_eq!(recv.off, 0);
+        assert_eq!(recv.data.len(), 2);
+
+        let (len, fin) = recv.pop(&mut buf).unwrap();
+        assert_eq!(len, 9);
+        assert_eq!(fin, false);
+        assert_eq!(&buf[..len], b"somehello");
+        assert_eq!(recv.len, 9);
+        assert_eq!(recv.off, 9);
+        assert_eq!(recv.data.len(), 0);
+
+        assert_eq!(recv.pop(&mut buf), Err(Error::Done));
+    }
+
+    #[test]
+    fn fully_overlapping_read3() {
+        let mut recv = RecvBuf::new(std::u64::MAX);
+        assert_eq!(recv.len, 0);
+
+        let mut buf = [0; 32];
+
+        let first = RangeBuf::from(b"something", 0, false);
+        let second = RangeBuf::from(b"hello", 3, false);
+
+        assert!(recv.push(second).is_ok());
+        assert_eq!(recv.len, 8);
+        assert_eq!(recv.off, 0);
+        assert_eq!(recv.data.len(), 1);
+
+        assert!(recv.push(first).is_ok());
+        assert_eq!(recv.len, 9);
+        assert_eq!(recv.off, 0);
+        assert_eq!(recv.data.len(), 3);
+
+        let (len, fin) = recv.pop(&mut buf).unwrap();
+        assert_eq!(len, 9);
+        assert_eq!(fin, false);
+        assert_eq!(&buf[..len], b"somhellog");
+        assert_eq!(recv.len, 9);
+        assert_eq!(recv.off, 9);
+        assert_eq!(recv.data.len(), 0);
+
+        assert_eq!(recv.pop(&mut buf), Err(Error::Done));
+    }
+
+    #[test]
+    fn fully_overlapping_read_multi() {
+        let mut recv = RecvBuf::new(std::u64::MAX);
+        assert_eq!(recv.len, 0);
+
+        let mut buf = [0; 32];
+
+        let first = RangeBuf::from(b"somethingsomething", 0, false);
+        let second = RangeBuf::from(b"hello", 3, false);
+        let third = RangeBuf::from(b"hello", 12, false);
+
+        assert!(recv.push(second).is_ok());
+        assert_eq!(recv.len, 8);
+        assert_eq!(recv.off, 0);
+        assert_eq!(recv.data.len(), 1);
+
+        assert!(recv.push(third).is_ok());
+        assert_eq!(recv.len, 17);
+        assert_eq!(recv.off, 0);
+        assert_eq!(recv.data.len(), 2);
+
+        assert!(recv.push(first).is_ok());
+        assert_eq!(recv.len, 18);
+        assert_eq!(recv.off, 0);
+        assert_eq!(recv.data.len(), 5);
+
+        let (len, fin) = recv.pop(&mut buf).unwrap();
+        assert_eq!(len, 18);
+        assert_eq!(fin, false);
+        assert_eq!(&buf[..len], b"somhellogsomhellog");
+        assert_eq!(recv.len, 18);
+        assert_eq!(recv.off, 18);
+        assert_eq!(recv.data.len(), 0);
+
+        assert_eq!(recv.pop(&mut buf), Err(Error::Done));
+    }
+
+    #[test]
+    fn overlapping_start_read() {
+        let mut recv = RecvBuf::new(std::u64::MAX);
+        assert_eq!(recv.len, 0);
+
+        let mut buf = [0; 32];
+
+        let first = RangeBuf::from(b"something", 0, false);
+        let second = RangeBuf::from(b"hello", 8, true);
+
+        assert!(recv.push(first).is_ok());
+        assert_eq!(recv.len, 9);
+        assert_eq!(recv.off, 0);
+        assert_eq!(recv.data.len(), 1);
+
+        assert!(recv.push(second).is_ok());
+        assert_eq!(recv.len, 13);
+        assert_eq!(recv.off, 0);
+        assert_eq!(recv.data.len(), 2);
+
+        let (len, fin) = recv.pop(&mut buf).unwrap();
+        assert_eq!(len, 13);
+        assert_eq!(fin, true);
+        assert_eq!(&buf[..len], b"somethingello");
+        assert_eq!(recv.len, 13);
+        assert_eq!(recv.off, 13);
+
+        assert_eq!(recv.pop(&mut buf), Err(Error::Done));
+    }
+
+    #[test]
+    fn overlapping_end_read() {
+        let mut recv = RecvBuf::new(std::u64::MAX);
+        assert_eq!(recv.len, 0);
+
+        let mut buf = [0; 32];
+
+        let first = RangeBuf::from(b"hello", 0, false);
+        let second = RangeBuf::from(b"something", 3, true);
+
+        assert!(recv.push(second).is_ok());
+        assert_eq!(recv.len, 12);
+        assert_eq!(recv.off, 0);
+        assert_eq!(recv.data.len(), 1);
+
+        assert!(recv.push(first).is_ok());
+        assert_eq!(recv.len, 12);
+        assert_eq!(recv.off, 0);
+        assert_eq!(recv.data.len(), 2);
+
+        let (len, fin) = recv.pop(&mut buf).unwrap();
+        assert_eq!(len, 12);
+        assert_eq!(fin, true);
+        assert_eq!(&buf[..len], b"helsomething");
+        assert_eq!(recv.len, 12);
+        assert_eq!(recv.off, 12);
+
+        assert_eq!(recv.pop(&mut buf), Err(Error::Done));
+    }
+
+    #[test]
+    fn partially_multi_overlapping_reordered_read() {
+        let mut recv = RecvBuf::new(std::u64::MAX);
+        assert_eq!(recv.len, 0);
+
+        let mut buf = [0; 32];
+
+        let first = RangeBuf::from(b"hello", 8, false);
+        let second = RangeBuf::from(b"something", 0, false);
+        let third = RangeBuf::from(b"moar", 11, true);
+
+        assert!(recv.push(first).is_ok());
+        assert_eq!(recv.len, 13);
+        assert_eq!(recv.off, 0);
+        assert_eq!(recv.data.len(), 1);
+
+        assert!(recv.push(second).is_ok());
+        assert_eq!(recv.len, 13);
+        assert_eq!(recv.off, 0);
+        assert_eq!(recv.data.len(), 2);
+
+        assert!(recv.push(third).is_ok());
+        assert_eq!(recv.len, 15);
+        assert_eq!(recv.off, 0);
+        assert_eq!(recv.data.len(), 3);
+
+        let (len, fin) = recv.pop(&mut buf).unwrap();
+        assert_eq!(len, 15);
+        assert_eq!(fin, true);
+        assert_eq!(&buf[..len], b"somethinhelloar");
+        assert_eq!(recv.len, 15);
+        assert_eq!(recv.off, 15);
+        assert_eq!(recv.data.len(), 0);
+
+        assert_eq!(recv.pop(&mut buf), Err(Error::Done));
+    }
+
+    #[test]
+    fn partially_multi_overlapping_reordered_read2() {
+        let mut recv = RecvBuf::new(std::u64::MAX);
+        assert_eq!(recv.len, 0);
+
+        let mut buf = [0; 32];
+
+        let first = RangeBuf::from(b"aaa", 0, false);
+        let second = RangeBuf::from(b"bbb", 2, false);
+        let third = RangeBuf::from(b"ccc", 4, false);
+        let fourth = RangeBuf::from(b"ddd", 6, false);
+        let fifth = RangeBuf::from(b"eee", 9, false);
+        let sixth = RangeBuf::from(b"fff", 11, false);
+
+        assert!(recv.push(second).is_ok());
+        assert_eq!(recv.len, 5);
+        assert_eq!(recv.off, 0);
+        assert_eq!(recv.data.len(), 1);
+
+        assert!(recv.push(fourth).is_ok());
+        assert_eq!(recv.len, 9);
+        assert_eq!(recv.off, 0);
+        assert_eq!(recv.data.len(), 2);
+
+        assert!(recv.push(third).is_ok());
+        assert_eq!(recv.len, 9);
+        assert_eq!(recv.off, 0);
+        assert_eq!(recv.data.len(), 3);
+
+        assert!(recv.push(first).is_ok());
+        assert_eq!(recv.len, 9);
+        assert_eq!(recv.off, 0);
+        assert_eq!(recv.data.len(), 4);
+
+        assert!(recv.push(sixth).is_ok());
+        assert_eq!(recv.len, 14);
+        assert_eq!(recv.off, 0);
+        assert_eq!(recv.data.len(), 5);
+
+        assert!(recv.push(fifth).is_ok());
+        assert_eq!(recv.len, 14);
+        assert_eq!(recv.off, 0);
+        assert_eq!(recv.data.len(), 6);
+
+        let (len, fin) = recv.pop(&mut buf).unwrap();
+        assert_eq!(len, 14);
+        assert_eq!(fin, false);
+        assert_eq!(&buf[..len], b"aabbbcdddeefff");
+        assert_eq!(recv.len, 14);
+        assert_eq!(recv.off, 14);
+        assert_eq!(recv.data.len(), 0);
+
+        assert_eq!(recv.pop(&mut buf), Err(Error::Done));
+    }
+
+    #[test]
+    fn empty_write() {
+        let mut send = SendBuf::new(std::u64::MAX);
+        assert_eq!(send.len, 0);
+
+        let write = send.pop(std::usize::MAX).unwrap();
+        assert_eq!(write.len(), 0);
+        assert_eq!(write.fin(), false);
+    }
+
+    #[test]
+    fn multi_write() {
+        let mut send = SendBuf::new(std::u64::MAX);
+        assert_eq!(send.len, 0);
+
+        let first = b"something";
+        let second = b"helloworld";
+
+        assert!(send.push_slice(first, false).is_ok());
+        assert_eq!(send.len, 9);
+
+        assert!(send.push_slice(second, true).is_ok());
+        assert_eq!(send.len, 19);
+
+        let write = send.pop(128).unwrap();
+        assert_eq!(write.len(), 19);
+        assert_eq!(write.fin(), true);
+        assert_eq!(&write[..], b"somethinghelloworld");
+        assert_eq!(send.len, 0);
+    }
+
+    #[test]
+    fn split_write() {
+        let mut send = SendBuf::new(std::u64::MAX);
+        assert_eq!(send.len, 0);
+
+        let first = b"something";
+        let second = b"helloworld";
+
+        assert!(send.push_slice(first, false).is_ok());
+        assert_eq!(send.len, 9);
+
+        assert!(send.push_slice(second, true).is_ok());
+        assert_eq!(send.len, 19);
+
+        let write = send.pop(10).unwrap();
+        assert_eq!(write.off(), 0);
+        assert_eq!(write.len(), 10);
+        assert_eq!(write.fin(), false);
+        assert_eq!(&write[..], b"somethingh");
+        assert_eq!(send.len, 9);
+
+        let write = send.pop(5).unwrap();
+        assert_eq!(write.off(), 10);
+        assert_eq!(write.len(), 5);
+        assert_eq!(write.fin(), false);
+        assert_eq!(&write[..], b"ellow");
+        assert_eq!(send.len, 4);
+
+        let write = send.pop(10).unwrap();
+        assert_eq!(write.off(), 15);
+        assert_eq!(write.len(), 4);
+        assert_eq!(write.fin(), true);
+        assert_eq!(&write[..], b"orld");
+        assert_eq!(send.len, 0);
+    }
+
+    #[test]
+    fn resend() {
+        let mut send = SendBuf::new(std::u64::MAX);
+        assert_eq!(send.len, 0);
+        assert_eq!(send.off_front(), 0);
+
+        let first = b"something";
+        let second = b"helloworld";
+
+        assert!(send.push_slice(first, false).is_ok());
+        assert_eq!(send.off_front(), 0);
+
+        assert!(send.push_slice(second, true).is_ok());
+        assert_eq!(send.off_front(), 0);
+
+        let write1 = send.pop(4).unwrap();
+        assert_eq!(write1.off(), 0);
+        assert_eq!(write1.len(), 4);
+        assert_eq!(write1.fin(), false);
+        assert_eq!(&write1[..], b"some");
+        assert_eq!(send.len, 15);
+        assert_eq!(send.off_front(), 4);
+
+        let write2 = send.pop(5).unwrap();
+        assert_eq!(write2.off(), 4);
+        assert_eq!(write2.len(), 5);
+        assert_eq!(write2.fin(), false);
+        assert_eq!(&write2[..], b"thing");
+        assert_eq!(send.len, 10);
+        assert_eq!(send.off_front(), 9);
+
+        let write3 = send.pop(5).unwrap();
+        assert_eq!(write3.off(), 9);
+        assert_eq!(write3.len(), 5);
+        assert_eq!(write3.fin(), false);
+        assert_eq!(&write3[..], b"hello");
+        assert_eq!(send.len, 5);
+        assert_eq!(send.off_front(), 14);
+
+        send.push(write2).unwrap();
+        assert_eq!(send.len, 10);
+        assert_eq!(send.off_front(), 4);
+
+        send.push(write1).unwrap();
+        assert_eq!(send.len, 14);
+        assert_eq!(send.off_front(), 0);
+
+        let write4 = send.pop(11).unwrap();
+        assert_eq!(write4.off(), 0);
+        assert_eq!(write4.len(), 9);
+        assert_eq!(write4.fin(), false);
+        assert_eq!(&write4[..], b"something");
+        assert_eq!(send.len, 5);
+        assert_eq!(send.off_front(), 14);
+
+        let write5 = send.pop(11).unwrap();
+        assert_eq!(write5.off(), 14);
+        assert_eq!(write5.len(), 5);
+        assert_eq!(write5.fin(), true);
+        assert_eq!(&write5[..], b"world");
+        assert_eq!(send.len, 0);
+        assert_eq!(send.off_front(), 19);
+    }
+
+    #[test]
+    fn write_blocked_by_off() {
+        let mut send = SendBuf::default();
+        assert_eq!(send.len, 0);
+
+        let first = b"something";
+        let second = b"helloworld";
+
+        assert_eq!(send.push_slice(first, false), Ok(0));
+        assert_eq!(send.len, 0);
+
+        assert_eq!(send.push_slice(second, true), Ok(0));
+        assert_eq!(send.len, 0);
+
+        send.update_max_data(5);
+
+        assert_eq!(send.push_slice(first, false), Ok(5));
+        assert_eq!(send.len, 5);
+
+        assert_eq!(send.push_slice(second, true), Ok(0));
+        assert_eq!(send.len, 5);
+
+        let write = send.pop(10).unwrap();
+        assert_eq!(write.off(), 0);
+        assert_eq!(write.len(), 5);
+        assert_eq!(write.fin(), false);
+        assert_eq!(&write[..], b"somet");
+        assert_eq!(send.len, 0);
+
+        let write = send.pop(10).unwrap();
+        assert_eq!(write.off(), 5);
+        assert_eq!(write.len(), 0);
+        assert_eq!(write.fin(), false);
+        assert_eq!(&write[..], b"");
+        assert_eq!(send.len, 0);
+
+        send.update_max_data(15);
+
+        assert_eq!(send.push_slice(&first[5..], false), Ok(4));
+        assert_eq!(send.len, 4);
+
+        assert_eq!(send.push_slice(second, true), Ok(6));
+        assert_eq!(send.len, 10);
+
+        let write = send.pop(10).unwrap();
+        assert_eq!(write.off(), 5);
+        assert_eq!(write.len(), 10);
+        assert_eq!(write.fin(), false);
+        assert_eq!(&write[..], b"hinghellow");
+        assert_eq!(send.len, 0);
+
+        send.update_max_data(25);
+
+        assert_eq!(send.push_slice(&second[6..], true), Ok(4));
+        assert_eq!(send.len, 4);
+
+        let write = send.pop(10).unwrap();
+        assert_eq!(write.off(), 15);
+        assert_eq!(write.len(), 4);
+        assert_eq!(write.fin(), true);
+        assert_eq!(&write[..], b"orld");
+        assert_eq!(send.len, 0);
+    }
+
+    #[test]
+    fn zero_len_write() {
+        let mut send = SendBuf::new(std::u64::MAX);
+        assert_eq!(send.len, 0);
+
+        let first = b"something";
+
+        assert!(send.push_slice(first, false).is_ok());
+        assert_eq!(send.len, 9);
+
+        assert!(send.push_slice(&[], true).is_ok());
+        assert_eq!(send.len, 9);
+
+        let write = send.pop(10).unwrap();
+        assert_eq!(write.off(), 0);
+        assert_eq!(write.len(), 9);
+        assert_eq!(write.fin(), true);
+        assert_eq!(&write[..], b"something");
+        assert_eq!(send.len, 0);
+    }
+
+    #[test]
+    fn recv_flow_control() {
+        let mut stream = Stream::new(15, 0, true, true);
+        assert!(!stream.recv.almost_full());
+
+        let mut buf = [0; 32];
+
+        let first = RangeBuf::from(b"hello", 0, false);
+        let second = RangeBuf::from(b"world", 5, false);
+        let third = RangeBuf::from(b"something", 10, false);
+
+        assert_eq!(stream.recv.push(second), Ok(()));
+        assert_eq!(stream.recv.push(first), Ok(()));
+        assert!(!stream.recv.almost_full());
+
+        assert_eq!(stream.recv.push(third), Err(Error::FlowControl));
+
+        let (len, fin) = stream.recv.pop(&mut buf).unwrap();
+        assert_eq!(&buf[..len], b"helloworld");
+        assert_eq!(fin, false);
+
+        assert!(stream.recv.almost_full());
+
+        stream.recv.update_max_data();
+        assert_eq!(stream.recv.max_data_next(), 25);
+        assert!(!stream.recv.almost_full());
+
+        let third = RangeBuf::from(b"something", 10, false);
+        assert_eq!(stream.recv.push(third), Ok(()));
+    }
+
+    #[test]
+    fn recv_past_fin() {
+        let mut stream = Stream::new(15, 0, true, true);
+        assert!(!stream.recv.almost_full());
+
+        let first = RangeBuf::from(b"hello", 0, true);
+        let second = RangeBuf::from(b"world", 5, false);
+
+        assert_eq!(stream.recv.push(first), Ok(()));
+        assert_eq!(stream.recv.push(second), Err(Error::FinalSize));
+    }
+
+    #[test]
+    fn recv_fin_dup() {
+        let mut stream = Stream::new(15, 0, true, true);
+        assert!(!stream.recv.almost_full());
+
+        let first = RangeBuf::from(b"hello", 0, true);
+        let second = RangeBuf::from(b"hello", 0, true);
+
+        assert_eq!(stream.recv.push(first), Ok(()));
+        assert_eq!(stream.recv.push(second), Ok(()));
+
+        let mut buf = [0; 32];
+
+        let (len, fin) = stream.recv.pop(&mut buf).unwrap();
+        assert_eq!(&buf[..len], b"hello");
+        assert_eq!(fin, true);
+    }
+
+    #[test]
+    fn recv_fin_change() {
+        let mut stream = Stream::new(15, 0, true, true);
+        assert!(!stream.recv.almost_full());
+
+        let first = RangeBuf::from(b"hello", 0, true);
+        let second = RangeBuf::from(b"world", 5, true);
+
+        assert_eq!(stream.recv.push(second), Ok(()));
+        assert_eq!(stream.recv.push(first), Err(Error::FinalSize));
+    }
+
+    #[test]
+    fn recv_fin_lower_than_received() {
+        let mut stream = Stream::new(15, 0, true, true);
+        assert!(!stream.recv.almost_full());
+
+        let first = RangeBuf::from(b"hello", 0, true);
+        let second = RangeBuf::from(b"world", 5, false);
+
+        assert_eq!(stream.recv.push(second), Ok(()));
+        assert_eq!(stream.recv.push(first), Err(Error::FinalSize));
+    }
+
+    #[test]
+    fn recv_fin_flow_control() {
+        let mut stream = Stream::new(15, 0, true, true);
+        assert!(!stream.recv.almost_full());
+
+        let mut buf = [0; 32];
+
+        let first = RangeBuf::from(b"hello", 0, false);
+        let second = RangeBuf::from(b"world", 5, true);
+
+        assert_eq!(stream.recv.push(first), Ok(()));
+        assert_eq!(stream.recv.push(second), Ok(()));
+
+        let (len, fin) = stream.recv.pop(&mut buf).unwrap();
+        assert_eq!(&buf[..len], b"helloworld");
+        assert_eq!(fin, true);
+
+        assert!(!stream.recv.almost_full());
+    }
+
+    #[test]
+    fn recv_fin_reset_mismatch() {
+        let mut stream = Stream::new(15, 0, true, true);
+        assert!(!stream.recv.almost_full());
+
+        let first = RangeBuf::from(b"hello", 0, true);
+
+        assert_eq!(stream.recv.push(first), Ok(()));
+        assert_eq!(stream.recv.reset(10), Err(Error::FinalSize));
+    }
+
+    #[test]
+    fn recv_reset_dup() {
+        let mut stream = Stream::new(15, 0, true, true);
+        assert!(!stream.recv.almost_full());
+
+        let first = RangeBuf::from(b"hello", 0, false);
+
+        assert_eq!(stream.recv.push(first), Ok(()));
+        assert_eq!(stream.recv.reset(5), Ok(0));
+        assert_eq!(stream.recv.reset(5), Ok(0));
+    }
+
+    #[test]
+    fn recv_reset_change() {
+        let mut stream = Stream::new(15, 0, true, true);
+        assert!(!stream.recv.almost_full());
+
+        let first = RangeBuf::from(b"hello", 0, false);
+
+        assert_eq!(stream.recv.push(first), Ok(()));
+        assert_eq!(stream.recv.reset(5), Ok(0));
+        assert_eq!(stream.recv.reset(10), Err(Error::FinalSize));
+    }
+
+    #[test]
+    fn recv_reset_lower_than_received() {
+        let mut stream = Stream::new(15, 0, true, true);
+        assert!(!stream.recv.almost_full());
+
+        let first = RangeBuf::from(b"hello", 0, false);
+
+        assert_eq!(stream.recv.push(first), Ok(()));
+        assert_eq!(stream.recv.reset(4), Err(Error::FinalSize));
+    }
+
+    #[test]
+    fn send_flow_control() {
+        let mut stream = Stream::new(0, 15, true, true);
+
+        let first = b"hello";
+        let second = b"world";
+        let third = b"something";
+
+        assert!(stream.send.push_slice(first, false).is_ok());
+        assert!(stream.send.push_slice(second, false).is_ok());
+        assert!(stream.send.push_slice(third, false).is_ok());
+
+        let write = stream.send.pop(25).unwrap();
+        assert_eq!(write.off(), 0);
+        assert_eq!(write.len(), 15);
+        assert_eq!(write.fin(), false);
+        assert_eq!(write.data, b"helloworldsomet");
+
+        let write = stream.send.pop(25).unwrap();
+        assert_eq!(write.off(), 15);
+        assert_eq!(write.len(), 0);
+        assert_eq!(write.fin(), false);
+        assert_eq!(write.data, b"");
+
+        let first = RangeBuf::from(b"helloworldsomet", 0, false);
+        assert_eq!(stream.send.push(first), Ok(()));
+
+        let write = stream.send.pop(10).unwrap();
+        assert_eq!(write.off(), 0);
+        assert_eq!(write.len(), 10);
+        assert_eq!(write.fin(), false);
+        assert_eq!(write.data, b"helloworld");
+
+        let write = stream.send.pop(10).unwrap();
+        assert_eq!(write.off(), 10);
+        assert_eq!(write.len(), 5);
+        assert_eq!(write.fin(), false);
+        assert_eq!(write.data, b"somet");
+    }
+
+    #[test]
+    fn send_past_fin() {
+        let mut stream = Stream::new(0, 15, true, true);
+
+        let first = b"hello";
+        let second = b"world";
+        let third = b"third";
+
+        assert_eq!(stream.send.push_slice(first, false), Ok(5));
+
+        assert_eq!(stream.send.push_slice(second, true), Ok(5));
+        assert!(stream.send.is_fin());
+
+        assert_eq!(stream.send.push_slice(third, false), Err(Error::FinalSize));
+    }
+
+    #[test]
+    fn send_fin_dup() {
+        let mut stream = Stream::new(0, 15, true, true);
+
+        let first = RangeBuf::from(b"hello", 0, true);
+        let second = RangeBuf::from(b"hello", 0, true);
+
+        assert_eq!(stream.send.push(first), Ok(()));
+        assert_eq!(stream.send.push(second), Ok(()));
+    }
+
+    #[test]
+    fn send_undo_fin() {
+        let mut stream = Stream::new(0, 15, true, true);
+
+        let first = b"hello";
+        let second = RangeBuf::from(b"hello", 0, false);
+
+        assert_eq!(stream.send.push_slice(first, true), Ok(5));
+        assert!(stream.send.is_fin());
+
+        assert_eq!(stream.send.push(second), Err(Error::FinalSize));
+    }
+
+    #[test]
+    fn send_fin_max_data_match() {
+        let mut stream = Stream::new(0, 15, true, true);
+
+        let slice = b"hellohellohello";
+
+        assert!(stream.send.push_slice(slice, true).is_ok());
+
+        let write = stream.send.pop(15).unwrap();
+        assert_eq!(write.off(), 0);
+        assert_eq!(write.len(), 15);
+        assert_eq!(write.fin(), true);
+        assert_eq!(write.data, slice);
+    }
+
+    #[test]
+    fn send_fin_zero_length() {
+        let mut stream = Stream::new(0, 15, true, true);
+
+        assert_eq!(stream.send.push_slice(b"hello", false), Ok(5));
+        assert_eq!(stream.send.push_slice(b"", true), Ok(0));
+        assert!(stream.send.is_fin());
+
+        let write = stream.send.pop(5).unwrap();
+        assert_eq!(write.off(), 0);
+        assert_eq!(write.len(), 5);
+        assert_eq!(write.fin(), true);
+        assert_eq!(write.data, b"hello");
+    }
+
+    #[test]
+    fn send_ack() {
+        let mut stream = Stream::new(0, 15, true, true);
+
+        assert_eq!(stream.send.push_slice(b"hello", false), Ok(5));
+        assert_eq!(stream.send.push_slice(b"world", false), Ok(5));
+        assert_eq!(stream.send.push_slice(b"", true), Ok(0));
+        assert!(stream.send.is_fin());
+
+        let write = stream.send.pop(5).unwrap();
+        assert_eq!(write.off(), 0);
+        assert_eq!(write.len(), 5);
+        assert_eq!(write.fin(), false);
+        assert_eq!(write.data, b"hello");
+
+        stream.send.ack(write.off(), write.len());
+
+        assert_eq!(stream.send.push(write), Ok(()));
+
+        let write = stream.send.pop(5).unwrap();
+        assert_eq!(write.off(), 5);
+        assert_eq!(write.len(), 5);
+        assert_eq!(write.fin(), true);
+        assert_eq!(write.data, b"world");
+    }
+
+    #[test]
+    fn send_ack_reordering() {
+        let mut stream = Stream::new(0, 15, true, true);
+
+        assert_eq!(stream.send.push_slice(b"hello", false), Ok(5));
+        assert_eq!(stream.send.push_slice(b"world", false), Ok(5));
+        assert_eq!(stream.send.push_slice(b"", true), Ok(0));
+        assert!(stream.send.is_fin());
+
+        let write1 = stream.send.pop(5).unwrap();
+        assert_eq!(write1.off(), 0);
+        assert_eq!(write1.len(), 5);
+        assert_eq!(write1.fin(), false);
+        assert_eq!(write1.data, b"hello");
+
+        let write2 = stream.send.pop(1).unwrap();
+        assert_eq!(write2.off(), 5);
+        assert_eq!(write2.len(), 1);
+        assert_eq!(write2.fin(), false);
+        assert_eq!(write2.data, b"w");
+
+        stream.send.ack(write2.off(), write2.len());
+        stream.send.ack(write1.off(), write1.len());
+
+        assert_eq!(stream.send.push(write1), Ok(()));
+        assert_eq!(stream.send.push(write2), Ok(()));
+
+        let write = stream.send.pop(5).unwrap();
+        assert_eq!(write.off(), 6);
+        assert_eq!(write.len(), 4);
+        assert_eq!(write.fin(), true);
+        assert_eq!(write.data, b"orld");
+    }
+
+    #[test]
+    fn recv_data_below_off() {
+        let mut stream = Stream::new(15, 0, true, true);
+
+        let first = RangeBuf::from(b"hello", 0, false);
+
+        assert_eq!(stream.recv.push(first), Ok(()));
+
+        let mut buf = [0; 10];
+
+        let (len, fin) = stream.recv.pop(&mut buf).unwrap();
+        assert_eq!(&buf[..len], b"hello");
+        assert_eq!(fin, false);
+
+        let first = RangeBuf::from(b"elloworld", 1, true);
+        assert_eq!(stream.recv.push(first), Ok(()));
+
+        let (len, fin) = stream.recv.pop(&mut buf).unwrap();
+        assert_eq!(&buf[..len], b"world");
+        assert_eq!(fin, true);
+    }
+
+    #[test]
+    fn stream_complete() {
+        let mut stream = Stream::new(30, 30, true, true);
+
+        assert_eq!(stream.send.push_slice(b"hello", false), Ok(5));
+        assert_eq!(stream.send.push_slice(b"world", false), Ok(5));
+
+        assert!(!stream.send.is_complete());
+        assert!(!stream.send.is_fin());
+
+        assert_eq!(stream.send.push_slice(b"", true), Ok(0));
+
+        assert!(!stream.send.is_complete());
+        assert!(stream.send.is_fin());
+
+        let buf = RangeBuf::from(b"hello", 0, true);
+        assert!(stream.recv.push(buf).is_ok());
+        assert!(!stream.recv.is_fin());
+
+        stream.send.ack(6, 4);
+        assert!(!stream.send.is_complete());
+
+        let mut buf = [0; 2];
+        assert_eq!(stream.recv.pop(&mut buf), Ok((2, false)));
+        assert!(!stream.recv.is_fin());
+
+        stream.send.ack(1, 5);
+        assert!(!stream.send.is_complete());
+
+        stream.send.ack(0, 1);
+        assert!(stream.send.is_complete());
+
+        assert!(!stream.is_complete());
+
+        let mut buf = [0; 3];
+        assert_eq!(stream.recv.pop(&mut buf), Ok((3, true)));
+        assert!(stream.recv.is_fin());
+
+        assert!(stream.is_complete());
+    }
+
+    #[test]
+    fn send_fin_zero_length_output() {
+        let mut stream = Stream::new(0, 15, true, true);
+
+        assert_eq!(stream.send.push_slice(b"hello", false), Ok(5));
+        assert!(!stream.send.is_fin());
+
+        let write = stream.send.pop(5).unwrap();
+        assert_eq!(write.off(), 0);
+        assert_eq!(write.len(), 5);
+        assert_eq!(write.fin(), false);
+        assert_eq!(write.data, b"hello");
+
+        assert_eq!(stream.send.push_slice(b"", true), Ok(0));
+        assert!(stream.send.is_fin());
+
+        let write = stream.send.pop(5).unwrap();
+        assert_eq!(write.off(), 5);
+        assert_eq!(write.len(), 0);
+        assert_eq!(write.fin(), true);
+        assert_eq!(write.data, b"");
+    }
+}
diff --git a/src/tls.rs b/src/tls.rs
new file mode 100644
index 0000000..8044bb7
--- /dev/null
+++ b/src/tls.rs
@@ -0,0 +1,1008 @@
+// Copyright (C) 2018-2019, Cloudflare, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright notice,
+//       this list of conditions and the following disclaimer.
+//
+//     * Redistributions in binary form must reproduce the above copyright
+//       notice, this list of conditions and the following disclaimer in the
+//       documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// 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::ffi;
+use std::ptr;
+use std::slice;
+
+use libc::c_char;
+use libc::c_int;
+use libc::c_long;
+use libc::c_uint;
+use libc::c_void;
+
+use crate::Error;
+use crate::Result;
+
+use crate::Connection;
+
+use crate::crypto;
+use crate::octets;
+use crate::packet;
+
+const TLS1_3_VERSION: u16 = 0x0304;
+const TLS_ALERT_ERROR: u64 = 0x100;
+
+#[allow(non_camel_case_types)]
+#[repr(transparent)]
+struct SSL_METHOD(c_void);
+
+#[allow(non_camel_case_types)]
+#[repr(transparent)]
+struct SSL_CTX(c_void);
+
+#[allow(non_camel_case_types)]
+#[repr(transparent)]
+struct SSL(c_void);
+
+#[allow(non_camel_case_types)]
+#[repr(transparent)]
+struct SSL_CIPHER(c_void);
+
+#[allow(non_camel_case_types)]
+#[repr(transparent)]
+struct X509_VERIFY_PARAM(c_void);
+
+#[allow(non_camel_case_types)]
+#[repr(transparent)]
+#[cfg(windows)]
+struct X509_STORE(c_void);
+
+#[allow(non_camel_case_types)]
+#[repr(transparent)]
+struct X509(c_void);
+
+#[repr(C)]
+#[allow(non_camel_case_types)]
+struct SSL_QUIC_METHOD {
+    set_read_secret: extern fn(
+        ssl: *mut SSL,
+        level: crypto::Level,
+        cipher: *const SSL_CIPHER,
+        secret: *const u8,
+        secret_len: usize,
+    ) -> c_int,
+
+    set_write_secret: extern fn(
+        ssl: *mut SSL,
+        level: crypto::Level,
+        cipher: *const SSL_CIPHER,
+        secret: *const u8,
+        secret_len: usize,
+    ) -> c_int,
+
+    add_handshake_data: extern fn(
+        ssl: *mut SSL,
+        level: crypto::Level,
+        data: *const u8,
+        len: usize,
+    ) -> c_int,
+
+    flush_flight: extern fn(ssl: *mut SSL) -> c_int,
+
+    send_alert:
+        extern fn(ssl: *mut SSL, level: crypto::Level, alert: u8) -> c_int,
+}
+
+lazy_static::lazy_static! {
+    static ref QUICHE_EX_DATA_INDEX: c_int = unsafe {
+        SSL_get_ex_new_index(0, ptr::null(), ptr::null(), ptr::null(), ptr::null())
+    };
+}
+
+static QUICHE_STREAM_METHOD: SSL_QUIC_METHOD = SSL_QUIC_METHOD {
+    set_read_secret,
+    set_write_secret,
+    add_handshake_data,
+    flush_flight,
+    send_alert,
+};
+
+pub struct Context(*mut SSL_CTX);
+
+impl Context {
+    pub fn new() -> Result<Context> {
+        unsafe {
+            let ctx_raw = SSL_CTX_new(TLS_method());
+
+            let mut ctx = Context(ctx_raw);
+
+            ctx.load_ca_certs()?;
+
+            Ok(ctx)
+        }
+    }
+
+    pub fn new_handshake(&mut self) -> Result<Handshake> {
+        unsafe {
+            let ssl = SSL_new(self.as_ptr());
+            Ok(Handshake(ssl))
+        }
+    }
+
+    pub fn load_verify_locations_from_file(&mut self, file: &str) -> Result<()> {
+        let file = ffi::CString::new(file).map_err(|_| Error::TlsFail)?;
+        map_result(unsafe {
+            SSL_CTX_load_verify_locations(
+                self.as_ptr(),
+                file.as_ptr(),
+                std::ptr::null(),
+            )
+        })
+    }
+
+    pub fn load_verify_locations_from_directory(
+        &mut self, path: &str,
+    ) -> Result<()> {
+        let path = ffi::CString::new(path).map_err(|_| Error::TlsFail)?;
+        map_result(unsafe {
+            SSL_CTX_load_verify_locations(
+                self.as_ptr(),
+                std::ptr::null(),
+                path.as_ptr(),
+            )
+        })
+    }
+
+    pub fn use_certificate_chain_file(&mut self, file: &str) -> Result<()> {
+        let cstr = ffi::CString::new(file).map_err(|_| Error::TlsFail)?;
+        map_result(unsafe {
+            SSL_CTX_use_certificate_chain_file(self.as_ptr(), cstr.as_ptr())
+        })
+    }
+
+    pub fn use_privkey_file(&mut self, file: &str) -> Result<()> {
+        let cstr = ffi::CString::new(file).map_err(|_| Error::TlsFail)?;
+        map_result(unsafe {
+            SSL_CTX_use_PrivateKey_file(self.as_ptr(), cstr.as_ptr(), 1)
+        })
+    }
+
+    #[cfg(not(windows))]
+    fn load_ca_certs(&mut self) -> Result<()> {
+        unsafe { map_result(SSL_CTX_set_default_verify_paths(self.as_ptr())) }
+    }
+
+    #[cfg(windows)]
+    fn load_ca_certs(&mut self) -> Result<()> {
+        unsafe {
+            let cstr = ffi::CString::new("Root").map_err(|_| Error::TlsFail)?;
+            let sys_store = winapi::um::wincrypt::CertOpenSystemStoreA(
+                0,
+                cstr.as_ptr() as winapi::um::winnt::LPCSTR,
+            );
+            if sys_store.is_null() {
+                return Err(Error::TlsFail);
+            }
+
+            let ctx_store = SSL_CTX_get_cert_store(self.as_ptr());
+            if ctx_store.is_null() {
+                return Err(Error::TlsFail);
+            }
+
+            let mut ctx_p = winapi::um::wincrypt::CertEnumCertificatesInStore(
+                sys_store,
+                ptr::null(),
+            );
+
+            while !ctx_p.is_null() {
+                let in_p = (*ctx_p).pbCertEncoded as *const u8;
+
+                let cert = d2i_X509(
+                    ptr::null_mut(),
+                    &in_p,
+                    (*ctx_p).cbCertEncoded as i32,
+                );
+                if !cert.is_null() {
+                    X509_STORE_add_cert(ctx_store, cert);
+                }
+
+                X509_free(cert);
+
+                ctx_p = winapi::um::wincrypt::CertEnumCertificatesInStore(
+                    sys_store, ctx_p,
+                );
+            }
+
+            // tidy up
+            winapi::um::wincrypt::CertFreeCertificateContext(ctx_p);
+            winapi::um::wincrypt::CertCloseStore(sys_store, 0);
+        }
+
+        Ok(())
+    }
+
+    pub fn set_verify(&mut self, verify: bool) {
+        let mode = if verify {
+            0x01 // SSL_VERIFY_PEER
+        } else {
+            0x00 // SSL_VERIFY_NONE
+        };
+
+        unsafe {
+            SSL_CTX_set_verify(self.as_ptr(), mode, ptr::null());
+        }
+    }
+
+    pub fn enable_keylog(&mut self) {
+        unsafe {
+            SSL_CTX_set_keylog_callback(self.as_ptr(), keylog);
+        }
+    }
+
+    pub fn set_alpn(&mut self, v: &[Vec<u8>]) -> Result<()> {
+        let mut protos: Vec<u8> = Vec::new();
+
+        for proto in v {
+            protos.push(proto.len() as u8);
+            protos.append(&mut proto.clone());
+        }
+
+        // Configure ALPN for servers.
+        unsafe {
+            SSL_CTX_set_alpn_select_cb(
+                self.as_ptr(),
+                select_alpn,
+                ptr::null_mut(),
+            );
+        }
+
+        // Configure ALPN for clients.
+        map_result_zero_is_success(unsafe {
+            SSL_CTX_set_alpn_protos(self.as_ptr(), protos.as_ptr(), protos.len())
+        })
+    }
+
+    pub fn set_early_data_enabled(&mut self, enabled: bool) {
+        let enabled = if enabled { 1 } else { 0 };
+
+        unsafe {
+            SSL_CTX_set_early_data_enabled(self.as_ptr(), enabled);
+        }
+    }
+
+    fn as_ptr(&self) -> *mut SSL_CTX {
+        self.0
+    }
+}
+
+unsafe impl std::marker::Send for Context {}
+
+impl Drop for Context {
+    fn drop(&mut self) {
+        unsafe { SSL_CTX_free(self.as_ptr()) }
+    }
+}
+
+pub struct Handshake(*mut SSL);
+
+impl Handshake {
+    pub unsafe fn from_ptr(ssl: *mut c_void) -> Handshake {
+        let ssl = ssl as *mut SSL;
+        Handshake(ssl)
+    }
+
+    pub fn get_error(&self, ret_code: c_int) -> c_int {
+        unsafe { SSL_get_error(self.as_ptr(), ret_code) }
+    }
+
+    pub fn init(&self, conn: &Connection) -> Result<()> {
+        self.set_state(conn.is_server);
+
+        self.set_ex_data(*QUICHE_EX_DATA_INDEX, conn)?;
+
+        self.set_min_proto_version(TLS1_3_VERSION);
+        self.set_max_proto_version(TLS1_3_VERSION);
+
+        self.set_quic_method()?;
+
+        // TODO: the early data context should include transport parameters and
+        // HTTP/3 SETTINGS in wire format.
+        self.set_quic_early_data_context(b"quiche")?;
+
+        self.set_quiet_shutdown(true);
+
+        Ok(())
+    }
+
+    pub fn set_state(&self, is_server: bool) {
+        unsafe {
+            if is_server {
+                SSL_set_accept_state(self.as_ptr());
+            } else {
+                SSL_set_connect_state(self.as_ptr());
+            }
+        }
+    }
+
+    pub fn set_ex_data<T>(&self, idx: c_int, data: &T) -> Result<()> {
+        map_result(unsafe {
+            let ptr = data as *const T as *const c_void;
+            SSL_set_ex_data(self.as_ptr(), idx, ptr)
+        })
+    }
+
+    pub fn set_quic_method(&self) -> Result<()> {
+        map_result(unsafe {
+            SSL_set_quic_method(self.as_ptr(), &QUICHE_STREAM_METHOD)
+        })
+    }
+
+    pub fn set_quic_early_data_context(&self, context: &[u8]) -> Result<()> {
+        map_result(unsafe {
+            SSL_set_quic_early_data_context(
+                self.as_ptr(),
+                context.as_ptr(),
+                context.len(),
+            )
+        })
+    }
+
+    pub fn set_min_proto_version(&self, version: u16) {
+        unsafe { SSL_set_min_proto_version(self.as_ptr(), version) }
+    }
+
+    pub fn set_max_proto_version(&self, version: u16) {
+        unsafe { SSL_set_max_proto_version(self.as_ptr(), version) }
+    }
+
+    pub fn set_quiet_shutdown(&self, mode: bool) {
+        unsafe { SSL_set_quiet_shutdown(self.as_ptr(), if mode { 1 } else { 0 }) }
+    }
+
+    pub fn set_host_name(&self, name: &str) -> Result<()> {
+        let cstr = ffi::CString::new(name).map_err(|_| Error::TlsFail)?;
+        map_result_ssl(self, unsafe {
+            SSL_set_tlsext_host_name(self.as_ptr(), cstr.as_ptr())
+        })?;
+
+        let param = unsafe { SSL_get0_param(self.as_ptr()) };
+
+        map_result(unsafe {
+            X509_VERIFY_PARAM_set1_host(param, cstr.as_ptr(), name.len())
+        })
+    }
+
+    pub fn set_quic_transport_params(&self, buf: &[u8]) -> Result<()> {
+        map_result_ssl(self, unsafe {
+            SSL_set_quic_transport_params(self.as_ptr(), buf.as_ptr(), buf.len())
+        })
+    }
+
+    pub fn quic_transport_params(&self) -> &[u8] {
+        let mut ptr: *const u8 = ptr::null();
+        let mut len: usize = 0;
+
+        unsafe {
+            SSL_get_peer_quic_transport_params(self.as_ptr(), &mut ptr, &mut len);
+        }
+
+        if len == 0 {
+            return &mut [];
+        }
+
+        unsafe { slice::from_raw_parts(ptr, len) }
+    }
+
+    pub fn alpn_protocol(&self) -> &[u8] {
+        let mut ptr: *const u8 = ptr::null();
+        let mut len: u32 = 0;
+
+        unsafe {
+            SSL_get0_alpn_selected(self.as_ptr(), &mut ptr, &mut len);
+        }
+
+        if len == 0 {
+            return &mut [];
+        }
+
+        unsafe { slice::from_raw_parts(ptr, len as usize) }
+    }
+
+    pub fn provide_data(&self, level: crypto::Level, buf: &[u8]) -> Result<()> {
+        map_result_ssl(self, unsafe {
+            SSL_provide_quic_data(self.as_ptr(), level, buf.as_ptr(), buf.len())
+        })
+    }
+
+    pub fn do_handshake(&self) -> Result<()> {
+        map_result_ssl(self, unsafe { SSL_do_handshake(self.as_ptr()) })
+    }
+
+    pub fn write_level(&self) -> crypto::Level {
+        unsafe { SSL_quic_write_level(self.as_ptr()) }
+    }
+
+    pub fn cipher(&self) -> Option<crypto::Algorithm> {
+        let cipher =
+            map_result_ptr(unsafe { SSL_get_current_cipher(self.as_ptr()) });
+
+        get_cipher_from_ptr(cipher.ok()?).ok()
+    }
+
+    pub fn curve(&self) -> Option<String> {
+        let curve = unsafe {
+            let curve_id = SSL_get_curve_id(self.as_ptr());
+            if curve_id == 0 {
+                return None;
+            }
+
+            let curve_name = SSL_get_curve_name(curve_id);
+            match std::ffi::CStr::from_ptr(curve_name).to_str() {
+                Ok(v) => v,
+
+                Err(_) => return None,
+            }
+        };
+
+        Some(curve.to_string())
+    }
+
+    pub fn sigalg(&self) -> Option<String> {
+        let sigalg = unsafe {
+            let sigalg_id = SSL_get_peer_signature_algorithm(self.as_ptr());
+            if sigalg_id == 0 {
+                return None;
+            }
+
+            let sigalg_name = SSL_get_signature_algorithm_name(sigalg_id, 1);
+            match std::ffi::CStr::from_ptr(sigalg_name).to_str() {
+                Ok(v) => v,
+
+                Err(_) => return None,
+            }
+        };
+
+        Some(sigalg.to_string())
+    }
+
+    pub fn peer_cert(&self) -> Option<Vec<u8>> {
+        let peer_cert = unsafe {
+            let mut out: *mut libc::c_uchar = ptr::null_mut();
+
+            let x509 = SSL_get_peer_certificate(self.as_ptr());
+            if x509.is_null() {
+                return None;
+            }
+
+            let out_len = i2d_X509(x509, &mut out);
+            if out_len <= 0 {
+                return None;
+            }
+
+            let der = slice::from_raw_parts(out, out_len as usize);
+            let der = der.to_vec();
+
+            OPENSSL_free(out as *mut c_void);
+
+            der
+        };
+
+        Some(peer_cert)
+    }
+
+    pub fn is_completed(&self) -> bool {
+        unsafe { SSL_in_init(self.as_ptr()) == 0 }
+    }
+
+    pub fn is_resumed(&self) -> bool {
+        unsafe { SSL_session_reused(self.as_ptr()) == 1 }
+    }
+
+    pub fn is_in_early_data(&self) -> bool {
+        unsafe { SSL_in_early_data(self.as_ptr()) == 1 }
+    }
+
+    pub fn clear(&mut self) -> Result<()> {
+        map_result_ssl(self, unsafe { SSL_clear(self.as_ptr()) })
+    }
+
+    fn as_ptr(&self) -> *mut SSL {
+        self.0
+    }
+}
+
+unsafe impl std::marker::Send for Handshake {}
+
+impl Drop for Handshake {
+    fn drop(&mut self) {
+        unsafe { SSL_free(self.as_ptr()) }
+    }
+}
+
+fn get_ex_data_from_ptr<'a, T>(ptr: *mut SSL, idx: c_int) -> Option<&'a mut T> {
+    unsafe {
+        let data = SSL_get_ex_data(ptr, idx) as *mut T;
+        data.as_mut()
+    }
+}
+
+fn get_cipher_from_ptr(cipher: *const SSL_CIPHER) -> Result<crypto::Algorithm> {
+    let cipher_id = unsafe { SSL_CIPHER_get_id(cipher) };
+
+    let alg = match cipher_id {
+        0x0300_1301 => crypto::Algorithm::AES128_GCM,
+        0x0300_1302 => crypto::Algorithm::AES256_GCM,
+        0x0300_1303 => crypto::Algorithm::ChaCha20_Poly1305,
+        _ => return Err(Error::TlsFail),
+    };
+
+    Ok(alg)
+}
+
+extern fn set_read_secret(
+    ssl: *mut SSL, level: crypto::Level, cipher: *const SSL_CIPHER,
+    secret: *const u8, secret_len: usize,
+) -> c_int {
+    let conn =
+        match get_ex_data_from_ptr::<Connection>(ssl, *QUICHE_EX_DATA_INDEX) {
+            Some(v) => v,
+
+            None => return 0,
+        };
+
+    trace!("{} set read secret lvl={:?}", conn.trace_id, level);
+
+    let space = match level {
+        crypto::Level::Initial => &mut conn.pkt_num_spaces[packet::EPOCH_INITIAL],
+        crypto::Level::ZeroRTT =>
+            &mut conn.pkt_num_spaces[packet::EPOCH_APPLICATION],
+        crypto::Level::Handshake =>
+            &mut conn.pkt_num_spaces[packet::EPOCH_HANDSHAKE],
+        crypto::Level::OneRTT =>
+            &mut conn.pkt_num_spaces[packet::EPOCH_APPLICATION],
+    };
+
+    let aead = match get_cipher_from_ptr(cipher) {
+        Ok(v) => v,
+
+        Err(_) => return 0,
+    };
+
+    // 0-RTT read secrets are present only on the server.
+    if level != crypto::Level::ZeroRTT || conn.is_server {
+        let secret = unsafe { slice::from_raw_parts(secret, secret_len) };
+
+        let open = match crypto::Open::from_secret(aead, &secret) {
+            Ok(v) => v,
+
+            Err(_) => return 0,
+        };
+
+        if level == crypto::Level::ZeroRTT {
+            space.crypto_0rtt_open = Some(open);
+            return 1;
+        }
+
+        space.crypto_open = Some(open);
+    }
+
+    1
+}
+
+extern fn set_write_secret(
+    ssl: *mut SSL, level: crypto::Level, cipher: *const SSL_CIPHER,
+    secret: *const u8, secret_len: usize,
+) -> c_int {
+    let conn =
+        match get_ex_data_from_ptr::<Connection>(ssl, *QUICHE_EX_DATA_INDEX) {
+            Some(v) => v,
+
+            None => return 0,
+        };
+
+    trace!("{} set write secret lvl={:?}", conn.trace_id, level);
+
+    let space = match level {
+        crypto::Level::Initial => &mut conn.pkt_num_spaces[packet::EPOCH_INITIAL],
+        crypto::Level::ZeroRTT =>
+            &mut conn.pkt_num_spaces[packet::EPOCH_APPLICATION],
+        crypto::Level::Handshake =>
+            &mut conn.pkt_num_spaces[packet::EPOCH_HANDSHAKE],
+        crypto::Level::OneRTT =>
+            &mut conn.pkt_num_spaces[packet::EPOCH_APPLICATION],
+    };
+
+    let aead = match get_cipher_from_ptr(cipher) {
+        Ok(v) => v,
+
+        Err(_) => return 0,
+    };
+
+    // 0-RTT write secrets are present only on the client.
+    if level != crypto::Level::ZeroRTT || !conn.is_server {
+        let secret = unsafe { slice::from_raw_parts(secret, secret_len) };
+
+        let seal = match crypto::Seal::from_secret(aead, &secret) {
+            Ok(v) => v,
+
+            Err(_) => return 0,
+        };
+
+        space.crypto_seal = Some(seal);
+    }
+
+    1
+}
+
+extern fn add_handshake_data(
+    ssl: *mut SSL, level: crypto::Level, data: *const u8, len: usize,
+) -> c_int {
+    let conn =
+        match get_ex_data_from_ptr::<Connection>(ssl, *QUICHE_EX_DATA_INDEX) {
+            Some(v) => v,
+
+            None => return 0,
+        };
+
+    trace!(
+        "{} write message lvl={:?} len={}",
+        conn.trace_id,
+        level,
+        len
+    );
+
+    let buf = unsafe { slice::from_raw_parts(data, len) };
+
+    let space = match level {
+        crypto::Level::Initial => &mut conn.pkt_num_spaces[packet::EPOCH_INITIAL],
+        crypto::Level::ZeroRTT => unreachable!(),
+        crypto::Level::Handshake =>
+            &mut conn.pkt_num_spaces[packet::EPOCH_HANDSHAKE],
+        crypto::Level::OneRTT =>
+            &mut conn.pkt_num_spaces[packet::EPOCH_APPLICATION],
+    };
+
+    if space.crypto_stream.send.push_slice(buf, false).is_err() {
+        return 0;
+    }
+
+    1
+}
+
+extern fn flush_flight(_ssl: *mut SSL) -> c_int {
+    // We don't really need to anything here since the output packets are
+    // generated separately, when conn.send() is called.
+
+    1
+}
+
+extern fn send_alert(ssl: *mut SSL, level: crypto::Level, alert: u8) -> c_int {
+    let conn =
+        match get_ex_data_from_ptr::<Connection>(ssl, *QUICHE_EX_DATA_INDEX) {
+            Some(v) => v,
+
+            None => return 0,
+        };
+
+    trace!(
+        "{} send alert lvl={:?} alert={:x}",
+        conn.trace_id,
+        level,
+        alert
+    );
+
+    let error: u64 = TLS_ALERT_ERROR + u64::from(alert);
+    conn.error = Some(error);
+
+    1
+}
+
+extern fn keylog(ssl: *mut SSL, line: *const c_char) {
+    let conn =
+        match get_ex_data_from_ptr::<Connection>(ssl, *QUICHE_EX_DATA_INDEX) {
+            Some(v) => v,
+
+            None => return,
+        };
+
+    if let Some(keylog) = &mut conn.keylog {
+        let data = unsafe { ffi::CStr::from_ptr(line).to_bytes() };
+
+        let mut full_line = Vec::with_capacity(data.len() + 1);
+        full_line.extend_from_slice(data);
+        full_line.push(b'\n');
+
+        keylog.write_all(&full_line[..]).ok();
+    }
+}
+
+extern fn select_alpn(
+    ssl: *mut SSL, out: *mut *const u8, out_len: *mut u8, inp: *mut u8,
+    in_len: c_uint, _arg: *mut c_void,
+) -> c_int {
+    let conn =
+        match get_ex_data_from_ptr::<Connection>(ssl, *QUICHE_EX_DATA_INDEX) {
+            Some(v) => v,
+
+            None => return 3, // SSL_TLSEXT_ERR_NOACK
+        };
+
+    if conn.application_protos.is_empty() {
+        return 3; // SSL_TLSEXT_ERR_NOACK
+    }
+
+    let mut protos = octets::Octets::with_slice(unsafe {
+        slice::from_raw_parts(inp, in_len as usize)
+    });
+
+    while let Ok(proto) = protos.get_bytes_with_u8_length() {
+        let found = conn.application_protos.iter().any(|expected| {
+            trace!(
+                "checking peer ALPN {:?} against {:?}",
+                std::str::from_utf8(proto.as_ref()),
+                std::str::from_utf8(expected.as_slice())
+            );
+
+            if expected.len() == proto.len() &&
+                expected.as_slice() == proto.as_ref()
+            {
+                unsafe {
+                    *out = expected.as_slice().as_ptr();
+                    *out_len = expected.len() as u8;
+                }
+
+                return true;
+            }
+
+            false
+        });
+
+        if found {
+            return 0; // SSL_TLSEXT_ERR_OK
+        }
+    }
+
+    3 // SSL_TLSEXT_ERR_NOACK
+}
+
+fn map_result(bssl_result: c_int) -> Result<()> {
+    match bssl_result {
+        1 => Ok(()),
+        _ => Err(Error::TlsFail),
+    }
+}
+
+fn map_result_zero_is_success(bssl_result: c_int) -> Result<()> {
+    match bssl_result {
+        0 => Ok(()),
+        _ => Err(Error::TlsFail),
+    }
+}
+
+fn map_result_ptr<'a, T>(bssl_result: *const T) -> Result<&'a T> {
+    match unsafe { bssl_result.as_ref() } {
+        Some(v) => Ok(v),
+        None => Err(Error::TlsFail),
+    }
+}
+
+fn map_result_ssl(ssl: &Handshake, bssl_result: c_int) -> Result<()> {
+    match bssl_result {
+        1 => Ok(()),
+
+        _ => {
+            let ssl_err = ssl.get_error(bssl_result);
+            match ssl_err {
+                // SSL_ERROR_SSL
+                1 => {
+                    log_ssl_error();
+
+                    Err(Error::TlsFail)
+                },
+
+                // SSL_ERROR_WANT_READ
+                2 => Err(Error::Done),
+
+                // SSL_ERROR_WANT_WRITE
+                3 => Err(Error::Done),
+
+                // SSL_ERROR_WANT_X509_LOOKUP
+                4 => Err(Error::Done),
+
+                // SSL_ERROR_SYSCALL
+                5 => Err(Error::TlsFail),
+
+                // SSL_ERROR_PENDING_CERTIFICATE
+                12 => Err(Error::Done),
+
+                // SSL_ERROR_WANT_PRIVATE_KEY_OPERATION
+                13 => Err(Error::Done),
+
+                // SSL_ERROR_PENDING_TICKET
+                14 => Err(Error::Done),
+
+                _ => Err(Error::TlsFail),
+            }
+        },
+    }
+}
+
+fn log_ssl_error() {
+    let err = [0; 1024];
+
+    unsafe {
+        let e = ERR_peek_error();
+        ERR_error_string_n(e, err.as_ptr(), err.len());
+    }
+
+    trace!("{}", std::str::from_utf8(&err).unwrap());
+}
+
+extern {
+    // SSL_METHOD
+    fn TLS_method() -> *const SSL_METHOD;
+
+    // SSL_CTX
+    fn SSL_CTX_new(method: *const SSL_METHOD) -> *mut SSL_CTX;
+    fn SSL_CTX_free(ctx: *mut SSL_CTX);
+
+    fn SSL_CTX_use_certificate_chain_file(
+        ctx: *mut SSL_CTX, file: *const c_char,
+    ) -> c_int;
+
+    fn SSL_CTX_use_PrivateKey_file(
+        ctx: *mut SSL_CTX, file: *const c_char, ty: c_int,
+    ) -> c_int;
+
+    fn SSL_CTX_load_verify_locations(
+        ctx: *mut SSL_CTX, file: *const c_char, path: *const c_char,
+    ) -> c_int;
+
+    #[cfg(not(windows))]
+    fn SSL_CTX_set_default_verify_paths(ctx: *mut SSL_CTX) -> c_int;
+
+    #[cfg(windows)]
+    fn SSL_CTX_get_cert_store(ctx: *mut SSL_CTX) -> *mut X509_STORE;
+
+    fn SSL_CTX_set_verify(ctx: *mut SSL_CTX, mode: c_int, cb: *const c_void);
+
+    fn SSL_CTX_set_keylog_callback(
+        ctx: *mut SSL_CTX, cb: extern fn(ssl: *mut SSL, line: *const c_char),
+    );
+
+    fn SSL_CTX_set_alpn_protos(
+        ctx: *mut SSL_CTX, protos: *const u8, protos_len: usize,
+    ) -> c_int;
+
+    fn SSL_CTX_set_alpn_select_cb(
+        ctx: *mut SSL_CTX,
+        cb: extern fn(
+            ssl: *mut SSL,
+            out: *mut *const u8,
+            out_len: *mut u8,
+            inp: *mut u8,
+            in_len: c_uint,
+            arg: *mut c_void,
+        ) -> c_int,
+        arg: *mut c_void,
+    );
+
+    fn SSL_CTX_set_early_data_enabled(ctx: *mut SSL_CTX, enabled: i32);
+
+    // SSL
+    fn SSL_get_ex_new_index(
+        argl: c_long, argp: *const c_void, unused: *const c_void,
+        dup_unused: *const c_void, free_func: *const c_void,
+    ) -> c_int;
+
+    fn SSL_new(ctx: *mut SSL_CTX) -> *mut SSL;
+
+    fn SSL_get_error(ssl: *mut SSL, ret_code: c_int) -> c_int;
+
+    fn SSL_set_accept_state(ssl: *mut SSL);
+    fn SSL_set_connect_state(ssl: *mut SSL);
+
+    fn SSL_get0_param(ssl: *mut SSL) -> *mut X509_VERIFY_PARAM;
+
+    fn SSL_set_ex_data(ssl: *mut SSL, idx: c_int, ptr: *const c_void) -> c_int;
+    fn SSL_get_ex_data(ssl: *mut SSL, idx: c_int) -> *mut c_void;
+
+    fn SSL_get_current_cipher(ssl: *mut SSL) -> *const SSL_CIPHER;
+
+    fn SSL_get_curve_id(ssl: *mut SSL) -> u16;
+    fn SSL_get_curve_name(curve: u16) -> *const c_char;
+
+    fn SSL_get_peer_signature_algorithm(ssl: *mut SSL) -> u16;
+    fn SSL_get_signature_algorithm_name(
+        sigalg: u16, include_curve: i32,
+    ) -> *const c_char;
+
+    fn SSL_get_peer_certificate(ssl: *mut SSL) -> *const X509;
+
+    fn SSL_set_min_proto_version(ssl: *mut SSL, version: u16);
+    fn SSL_set_max_proto_version(ssl: *mut SSL, version: u16);
+
+    fn SSL_set_quiet_shutdown(ssl: *mut SSL, mode: c_int);
+
+    fn SSL_set_tlsext_host_name(ssl: *mut SSL, name: *const c_char) -> c_int;
+
+    fn SSL_set_quic_transport_params(
+        ssl: *mut SSL, params: *const u8, params_len: usize,
+    ) -> c_int;
+
+    fn SSL_set_quic_method(
+        ssl: *mut SSL, quic_method: *const SSL_QUIC_METHOD,
+    ) -> c_int;
+
+    fn SSL_set_quic_early_data_context(
+        ssl: *mut SSL, context: *const u8, context_len: usize,
+    ) -> c_int;
+
+    fn SSL_get_peer_quic_transport_params(
+        ssl: *mut SSL, out_params: *mut *const u8, out_params_len: *mut usize,
+    );
+
+    fn SSL_get0_alpn_selected(
+        ssl: *mut SSL, out: *mut *const u8, out_len: *mut u32,
+    );
+
+    fn SSL_provide_quic_data(
+        ssl: *mut SSL, level: crypto::Level, data: *const u8, len: usize,
+    ) -> c_int;
+
+    fn SSL_do_handshake(ssl: *mut SSL) -> c_int;
+
+    fn SSL_quic_write_level(ssl: *mut SSL) -> crypto::Level;
+
+    fn SSL_session_reused(ssl: *mut SSL) -> c_int;
+
+    fn SSL_in_init(ssl: *mut SSL) -> c_int;
+
+    fn SSL_in_early_data(ssl: *mut SSL) -> c_int;
+
+    fn SSL_clear(ssl: *mut SSL) -> c_int;
+
+    fn SSL_free(ssl: *mut SSL);
+
+    // SSL_CIPHER
+    fn SSL_CIPHER_get_id(cipher: *const SSL_CIPHER) -> c_uint;
+
+    // X509_VERIFY_PARAM
+    fn X509_VERIFY_PARAM_set1_host(
+        param: *mut X509_VERIFY_PARAM, name: *const c_char, namelen: usize,
+    ) -> c_int;
+
+    // X509_STORE
+    #[cfg(windows)]
+    fn X509_STORE_add_cert(ctx: *mut X509_STORE, x: *mut X509) -> c_int;
+
+    // X509
+    #[cfg(windows)]
+    fn X509_free(x: *mut X509);
+    #[cfg(windows)]
+    fn d2i_X509(px: *mut X509, input: *const *const u8, len: c_int) -> *mut X509;
+
+    fn i2d_X509(px: *const X509, out: *mut *mut u8) -> c_int;
+
+    // ERR
+    fn ERR_peek_error() -> c_uint;
+
+    fn ERR_error_string_n(err: c_uint, buf: *const u8, len: usize);
+
+    // OPENSSL
+    fn OPENSSL_free(ptr: *mut c_void);
+}