Backed out changeset b6e7f733e07a (bug 1614711) for build bustages at third_party/rust/neqo-crypto/src/lib.rs ona CLOSED TREE
authorCoroiu Cristina <ccoroiu@mozilla.com>
Thu, 13 Feb 2020 20:36:29 +0200
changeset 513793 51dbdcd6e874c5d466f7a163e43973358e06284d
parent 513792 0a95e41e55df1da7b93580bbac07f42de894d69d
child 513794 baab608583fda4b939a6702a6f4a5370321f7485
push id37121
push useropoprus@mozilla.com
push dateThu, 13 Feb 2020 21:42:57 +0000
treeherdermozilla-central@51dbdcd6e874 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1614711
milestone75.0a1
backs outb6e7f733e07a233631ee0975174e642a4a61840c
first release with
nightly linux32
51dbdcd6e874 / 75.0a1 / 20200213214257 / files
nightly linux64
51dbdcd6e874 / 75.0a1 / 20200213214257 / files
nightly mac
51dbdcd6e874 / 75.0a1 / 20200213214257 / files
nightly win32
51dbdcd6e874 / 75.0a1 / 20200213214257 / files
nightly win64
51dbdcd6e874 / 75.0a1 / 20200213214257 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out changeset b6e7f733e07a (bug 1614711) for build bustages at third_party/rust/neqo-crypto/src/lib.rs ona CLOSED TREE
.cargo/config.in
Cargo.lock
netwerk/protocol/http/nsHttp.cpp
netwerk/protocol/http/nsHttp.h
netwerk/socket/neqo_glue/Cargo.toml
third_party/rust/neqo-common/.cargo-checksum.json
third_party/rust/neqo-common/Cargo.toml
third_party/rust/neqo-common/src/codec.rs
third_party/rust/neqo-common/src/datagram.rs
third_party/rust/neqo-common/tests/log.rs
third_party/rust/neqo-crypto/.cargo-checksum.json
third_party/rust/neqo-crypto/Cargo.toml
third_party/rust/neqo-crypto/bindings/bindings.toml
third_party/rust/neqo-crypto/build.rs
third_party/rust/neqo-crypto/src/aead.rs
third_party/rust/neqo-crypto/src/agent.rs
third_party/rust/neqo-crypto/src/agentio.rs
third_party/rust/neqo-crypto/src/constants.rs
third_party/rust/neqo-crypto/src/ext.rs
third_party/rust/neqo-crypto/src/hkdf.rs
third_party/rust/neqo-crypto/src/hp.rs
third_party/rust/neqo-crypto/src/lib.rs
third_party/rust/neqo-crypto/src/p11.rs
third_party/rust/neqo-crypto/src/prio.rs
third_party/rust/neqo-crypto/src/replay.rs
third_party/rust/neqo-crypto/src/result.rs
third_party/rust/neqo-crypto/src/secrets.rs
third_party/rust/neqo-crypto/src/selfencrypt.rs
third_party/rust/neqo-crypto/src/ssl.rs
third_party/rust/neqo-crypto/tests/aead.rs
third_party/rust/neqo-crypto/tests/agent.rs
third_party/rust/neqo-crypto/tests/ext.rs
third_party/rust/neqo-crypto/tests/handshake.rs
third_party/rust/neqo-crypto/tests/hkdf.rs
third_party/rust/neqo-crypto/tests/hp.rs
third_party/rust/neqo-crypto/tests/init.rs
third_party/rust/neqo-crypto/tests/selfencrypt.rs
third_party/rust/neqo-http3/.cargo-checksum.json
third_party/rust/neqo-http3/Cargo.toml
third_party/rust/neqo-http3/src/connection.rs
third_party/rust/neqo-http3/src/connection_client.rs
third_party/rust/neqo-http3/src/connection_server.rs
third_party/rust/neqo-http3/src/control_stream_remote.rs
third_party/rust/neqo-http3/src/hframe.rs
third_party/rust/neqo-http3/src/hsettings_frame.rs
third_party/rust/neqo-http3/src/lib.rs
third_party/rust/neqo-http3/src/server.rs
third_party/rust/neqo-http3/src/server_events.rs
third_party/rust/neqo-http3/src/stream_type_reader.rs
third_party/rust/neqo-http3/src/transaction_client.rs
third_party/rust/neqo-http3/src/transaction_server.rs
third_party/rust/neqo-qpack/.cargo-checksum.json
third_party/rust/neqo-qpack/Cargo.toml
third_party/rust/neqo-qpack/src/decoder.rs
third_party/rust/neqo-qpack/src/encoder.rs
third_party/rust/neqo-qpack/src/lib.rs
third_party/rust/neqo-qpack/src/table.rs
third_party/rust/neqo-transport/.cargo-checksum.json
third_party/rust/neqo-transport/Cargo.toml
third_party/rust/neqo-transport/src/cc.rs
third_party/rust/neqo-transport/src/cid.rs
third_party/rust/neqo-transport/src/connection.rs
third_party/rust/neqo-transport/src/crypto.rs
third_party/rust/neqo-transport/src/dump.rs
third_party/rust/neqo-transport/src/flow_mgr.rs
third_party/rust/neqo-transport/src/frame.rs
third_party/rust/neqo-transport/src/lib.rs
third_party/rust/neqo-transport/src/packet.rs
third_party/rust/neqo-transport/src/recovery.rs
third_party/rust/neqo-transport/src/recv_stream.rs
third_party/rust/neqo-transport/src/send_stream.rs
third_party/rust/neqo-transport/src/server.rs
third_party/rust/neqo-transport/src/stats.rs
third_party/rust/neqo-transport/src/tparams.rs
third_party/rust/neqo-transport/src/tracking.rs
third_party/rust/neqo-transport/tests/conn_vectors.rs
third_party/rust/neqo-transport/tests/connection.rs
third_party/rust/neqo-transport/tests/server.rs
--- a/.cargo/config.in
+++ b/.cargo/config.in
@@ -10,17 +10,17 @@ replace-with = "vendored-sources"
 [source."https://github.com/mozilla/rkv"]
 git = "https://github.com/mozilla/rkv"
 replace-with = "vendored-sources"
 rev = "6a866fdad2ca880df9b87fcbc9921abac1e91914"
 
 [source."https://github.com/mozilla/neqo"]
 git = "https://github.com/mozilla/neqo"
 replace-with = "vendored-sources"
-tag = "v0.1.14"
+tag = "v0.1.12"
 
 [source."https://github.com/kvark/spirv_cross"]
 branch = "wgpu"
 git = "https://github.com/kvark/spirv_cross"
 replace-with = "vendored-sources"
 
 [source."https://github.com/kvark/peek-poke"]
 git = "https://github.com/kvark/peek-poke"
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2566,72 +2566,73 @@ dependencies = [
 [[package]]
 name = "murmurhash3"
 version = "0.0.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a2983372caf4480544083767bf2d27defafe32af49ab4df3a0b7fc90793a3664"
 
 [[package]]
 name = "neqo-common"
-version = "0.1.14"
-source = "git+https://github.com/mozilla/neqo?tag=v0.1.14#562b6f9ac332c6017fd068bb8f6cbe6ca8176403"
+version = "0.1.12"
+source = "git+https://github.com/mozilla/neqo?tag=v0.1.12#ccb7c07326230c4b1dc68d98a15ce364a4718c15"
 dependencies = [
  "env_logger",
  "lazy_static",
  "log",
  "num-traits",
 ]
 
 [[package]]
 name = "neqo-crypto"
-version = "0.1.14"
-source = "git+https://github.com/mozilla/neqo?tag=v0.1.14#562b6f9ac332c6017fd068bb8f6cbe6ca8176403"
+version = "0.1.12"
+source = "git+https://github.com/mozilla/neqo?tag=v0.1.12#ccb7c07326230c4b1dc68d98a15ce364a4718c15"
 dependencies = [
  "bindgen",
  "log",
  "neqo-common",
  "serde",
  "serde_derive",
  "toml",
 ]
 
 [[package]]
 name = "neqo-http3"
-version = "0.1.14"
-source = "git+https://github.com/mozilla/neqo?tag=v0.1.14#562b6f9ac332c6017fd068bb8f6cbe6ca8176403"
+version = "0.1.12"
+source = "git+https://github.com/mozilla/neqo?tag=v0.1.12#ccb7c07326230c4b1dc68d98a15ce364a4718c15"
 dependencies = [
  "log",
  "neqo-common",
  "neqo-crypto",
  "neqo-qpack",
  "neqo-transport",
  "num-traits",
  "smallvec 1.2.0",
 ]
 
 [[package]]
 name = "neqo-qpack"
-version = "0.1.14"
-source = "git+https://github.com/mozilla/neqo?tag=v0.1.14#562b6f9ac332c6017fd068bb8f6cbe6ca8176403"
+version = "0.1.12"
+source = "git+https://github.com/mozilla/neqo?tag=v0.1.12#ccb7c07326230c4b1dc68d98a15ce364a4718c15"
 dependencies = [
  "log",
  "neqo-common",
  "neqo-crypto",
  "neqo-transport",
 ]
 
 [[package]]
 name = "neqo-transport"
-version = "0.1.14"
-source = "git+https://github.com/mozilla/neqo?tag=v0.1.14#562b6f9ac332c6017fd068bb8f6cbe6ca8176403"
+version = "0.1.12"
+source = "git+https://github.com/mozilla/neqo?tag=v0.1.12#ccb7c07326230c4b1dc68d98a15ce364a4718c15"
 dependencies = [
  "lazy_static",
  "log",
  "neqo-common",
  "neqo-crypto",
+ "rand",
  "smallvec 1.2.0",
 ]
 
 [[package]]
 name = "neqo_glue"
 version = "0.1.0"
 dependencies = [
  "neqo-common",
--- a/netwerk/protocol/http/nsHttp.cpp
+++ b/netwerk/protocol/http/nsHttp.cpp
@@ -22,17 +22,17 @@
 #include "nsIRequest.h"
 #include "nsJSUtils.h"
 #include <errno.h>
 #include <functional>
 
 namespace mozilla {
 namespace net {
 
-const nsCString kHttp3Version = NS_LITERAL_CSTRING("h3-25");
+const nsCString kHttp3Version = NS_LITERAL_CSTRING("h3-24");
 
 // define storage for all atoms
 namespace nsHttp {
 #define HTTP_ATOM(_name, _value) nsHttpAtom _name(_value);
 #include "nsHttpAtomList.h"
 #undef HTTP_ATOM
 }  // namespace nsHttp
 
--- a/netwerk/protocol/http/nsHttp.h
+++ b/netwerk/protocol/http/nsHttp.h
@@ -49,17 +49,17 @@ enum class SpdyVersion {
   // 27 was http/2-draft09, h2-10, and h2-11
   // 28 was http/2-draft12
   // 29 was http/2-draft13
   // 30 was h2-14 and h2-15
   // 31 was h2-16
 };
 
 extern const nsCString kHttp3Version;
-const char kHttp3VersionHEX[] = "ff00000019";  // this is draft 25.
+const char kHttp3VersionHEX[] = "ff00000018";  // this is draft 24.
 
 //-----------------------------------------------------------------------------
 // http connection capabilities
 //-----------------------------------------------------------------------------
 
 #define NS_HTTP_ALLOW_KEEPALIVE (1 << 0)
 #define NS_HTTP_LARGE_KEEPALIVE (1 << 1)
 
--- a/netwerk/socket/neqo_glue/Cargo.toml
+++ b/netwerk/socket/neqo_glue/Cargo.toml
@@ -3,21 +3,21 @@ name = "neqo_glue"
 version = "0.1.0"
 authors = ["Dragana Damjanovic <dd.mozilla@gmail.com>"]
 edition = "2018"
 
 [lib]
 name = "neqo_glue"
 
 [dependencies]
-neqo-http3 = { tag = "v0.1.14", git = "https://github.com/mozilla/neqo" }
-neqo-transport = { tag = "v0.1.14", git = "https://github.com/mozilla/neqo" }
-neqo-common = { tag = "v0.1.14", git = "https://github.com/mozilla/neqo" }
+neqo-http3 = { tag = "v0.1.12", git = "https://github.com/mozilla/neqo" }
+neqo-transport = { tag = "v0.1.12", git = "https://github.com/mozilla/neqo" }
+neqo-common = { tag = "v0.1.12", git = "https://github.com/mozilla/neqo" }
 nserror = { path = "../../../xpcom/rust/nserror" }
 nsstring = { path = "../../../xpcom/rust/nsstring" }
 xpcom = { path = "../../../xpcom/rust/xpcom" }
 thin-vec = { version = "0.1.0", features = ["gecko-ffi"] }
 
 [dependencies.neqo-crypto]
-tag = "v0.1.14"
+tag = "v0.1.12"
 git = "https://github.com/mozilla/neqo"
 default-features = false
 features = ["gecko"]
--- a/third_party/rust/neqo-common/.cargo-checksum.json
+++ b/third_party/rust/neqo-common/.cargo-checksum.json
@@ -1,1 +1,1 @@
-{"files":{"Cargo.toml":"34739642048f7cc148d2ba25092aa98857fc80aac1794e7f8e5b91a969338809","src/codec.rs":"00846df0051f32ec8b75b2f8e0344422e0693acbd4151aaec31e3ae02d6e696c","src/datagram.rs":"4beb13d5ea7927df6801fbe684dc231626c1856010eaef975d866ee66e894a45","src/incrdecoder.rs":"7b7b7fba57714a3baf0fe881010a9f5a9814bf26b9283a6d56d1c44010cbd822","src/lib.rs":"f6ee17bc45cafdccb562340a4d253a517c5366a74d07c38960aedc2554fe783c","src/log.rs":"943e4e332400d94805d60f965d1d0ae7aad180f6d5b50936d0bd9e085bbc1502","src/once.rs":"d8b2bf7a9e3ce83bdd7f29d8f73ce7ad0268c9618ae7255028fea9f90c9c9fd6","src/timer.rs":"56082a6ecb45bd31c7c677c4c1f0830e55821c860e70b5637b2015fa3be63743","tests/log.rs":"480b165b7907ec642c508b303d63005eee1427115d6973a349eaf6b2242ed18d"},"package":null}
\ No newline at end of file
+{"files":{"Cargo.toml":"fda2ba8d82e1c85cd700989e553a40863fe720de05b54c4c77dccc80257446c4","src/codec.rs":"6a35a0b4284b9279f1f937ba691ad7e4994a66343b92a9b69524baa71433d811","src/datagram.rs":"c0d0bfabd35f51dee6ad3edc5477e3b53f1cd24af78a756ce96cf37c78223a61","src/incrdecoder.rs":"7b7b7fba57714a3baf0fe881010a9f5a9814bf26b9283a6d56d1c44010cbd822","src/lib.rs":"f6ee17bc45cafdccb562340a4d253a517c5366a74d07c38960aedc2554fe783c","src/log.rs":"943e4e332400d94805d60f965d1d0ae7aad180f6d5b50936d0bd9e085bbc1502","src/once.rs":"d8b2bf7a9e3ce83bdd7f29d8f73ce7ad0268c9618ae7255028fea9f90c9c9fd6","src/timer.rs":"56082a6ecb45bd31c7c677c4c1f0830e55821c860e70b5637b2015fa3be63743","tests/log.rs":"23addde558449c79ac71877e1a96f24069d7e85839ec8464cb1f4e60032f29b1"},"package":null}
\ No newline at end of file
--- a/third_party/rust/neqo-common/Cargo.toml
+++ b/third_party/rust/neqo-common/Cargo.toml
@@ -1,11 +1,11 @@
 [package]
 name = "neqo-common"
-version = "0.1.14"
+version = "0.1.12"
 authors = ["Bobby Holley <bobbyholley@gmail.com>"]
 edition = "2018"
 license = "MIT/Apache-2.0"
 
 [dependencies]
 num-traits = "0.2"
 log = "0.4.0"
 env_logger = "0.6.1"
--- a/third_party/rust/neqo-common/src/codec.rs
+++ b/third_party/rust/neqo-common/src/codec.rs
@@ -24,22 +24,16 @@ impl<'a> Decoder<'a> {
     }
 
     /// Get the number of bytes remaining until the end.
     #[must_use]
     pub fn remaining(&self) -> usize {
         self.buf.len() - self.offset
     }
 
-    /// The number of bytes from the underlying slice that have been decoded.
-    #[must_use]
-    pub fn offset(&self) -> usize {
-        self.offset
-    }
-
     /// Skip n bytes.  Panics if `n` is too large.
     pub fn skip(&mut self, n: usize) {
         assert!(self.remaining() >= n);
         self.offset += n;
     }
 
     /// Skip helper that panics if `n` is `None` or not able to fit in `usize`.
     fn skip_inner(&mut self, n: Option<u64>) {
@@ -75,17 +69,17 @@ impl<'a> Decoder<'a> {
         if self.remaining() < 1 {
             None
         } else {
             Some(self.buf[self.offset])
         }
     }
 
     /// Decodes arbitrary data.
-    pub fn decode(&mut self, n: usize) -> Option<&'a [u8]> {
+    pub fn decode(&mut self, n: usize) -> Option<&[u8]> {
         if self.remaining() < n {
             return None;
         }
         let res = &self.buf[self.offset..self.offset + n];
         self.offset += n;
         Some(res)
     }
 
@@ -115,45 +109,45 @@ impl<'a> Decoder<'a> {
             1 => Some((u64::from(b1 & 0x3f) << 8) | self.decode_uint(1)?),
             2 => Some((u64::from(b1 & 0x3f) << 24) | self.decode_uint(3)?),
             3 => Some((u64::from(b1 & 0x3f) << 56) | self.decode_uint(7)?),
             _ => unreachable!(),
         }
     }
 
     /// Decodes the rest of the buffer.  Infallible.
-    pub fn decode_remainder(&mut self) -> &'a [u8] {
+    pub fn decode_remainder(&mut self) -> &[u8] {
         let res = &self.buf[self.offset..];
         self.offset = self.buf.len();
         res
     }
 
-    fn decode_checked(&mut self, n: Option<u64>) -> Option<&'a [u8]> {
+    fn decode_checked(&mut self, n: Option<u64>) -> Option<&[u8]> {
         let len = match n {
             Some(l) => l,
             _ => return None,
         };
         if let Ok(l) = usize::try_from(len) {
             self.decode(l)
         } else {
             // sizeof(usize) < sizeof(u64) and the value is greater than
             // usize can hold. Throw away the rest of the input.
             self.offset = self.buf.len();
             None
         }
     }
 
     /// Decodes a TLS-style length-prefixed buffer.
-    pub fn decode_vec(&mut self, n: usize) -> Option<&'a [u8]> {
+    pub fn decode_vec(&mut self, n: usize) -> Option<&[u8]> {
         let len = self.decode_uint(n);
         self.decode_checked(len)
     }
 
     /// Decodes a QUIC varint-length-prefixed buffer.
-    pub fn decode_vvec(&mut self) -> Option<&'a [u8]> {
+    pub fn decode_vvec(&mut self) -> Option<&[u8]> {
         let len = self.decode_varint();
         self.decode_checked(len)
     }
 }
 
 // Implement `Deref` for `Decoder` so that values can be examined without moving the cursor.
 impl<'a> Deref for Decoder<'a> {
     type Target = [u8];
@@ -334,21 +328,16 @@ impl Encoder {
             _ => panic!("Varint value too large"),
         };
         // Now, we need to encode the high bits after the main block, ...
         self.encode_uint(count, (v >> 8) | bits);
         // ..., then rotate the entire thing right by the same amount.
         self.buf[start..].rotate_right(count);
         self
     }
-
-    /// Truncate the encoder to the given size.
-    pub fn truncate(&mut self, len: usize) {
-        self.buf.truncate(len);
-    }
 }
 
 impl Debug for Encoder {
     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
         f.write_str(&hex(self))
     }
 }
 
--- a/third_party/rust/neqo-common/src/datagram.rs
+++ b/third_party/rust/neqo-common/src/datagram.rs
@@ -2,19 +2,17 @@
 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
 use std::net::SocketAddr;
 use std::ops::Deref;
 
-use crate::hex;
-
-#[derive(PartialEq, Clone)]
+#[derive(Debug, PartialEq, Clone)]
 pub struct Datagram {
     src: SocketAddr,
     dst: SocketAddr,
     d: Vec<u8>,
 }
 
 impl Datagram {
     pub fn new<V: Into<Vec<u8>>>(src: SocketAddr, dst: SocketAddr, d: V) -> Self {
@@ -38,20 +36,8 @@ impl Datagram {
 
 impl Deref for Datagram {
     type Target = Vec<u8>;
     #[must_use]
     fn deref(&self) -> &Self::Target {
         &self.d
     }
 }
-
-impl std::fmt::Debug for Datagram {
-    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
-        write!(
-            f,
-            "Datagram {:?}->{:?}: {}",
-            self.src,
-            self.dst,
-            hex(&self.d)
-        )
-    }
-}
--- a/third_party/rust/neqo-common/tests/log.rs
+++ b/third_party/rust/neqo-common/tests/log.rs
@@ -1,16 +1,15 @@
 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
 #![cfg_attr(feature = "deny-warnings", deny(warnings))]
-#![warn(clippy::use_self)]
 
 use neqo_common::{qdebug, qerror, qinfo, qtrace, qwarn};
 
 #[test]
 fn basic() {
     qerror!("error");
     qwarn!("warn");
     qinfo!("info");
--- a/third_party/rust/neqo-crypto/.cargo-checksum.json
+++ b/third_party/rust/neqo-crypto/.cargo-checksum.json
@@ -1,1 +1,1 @@
-{"files":{"Cargo.toml":"7c3b530c7717ad3e5c3da4ab280ae04d2c897c9a5ac411717392d057605404ed","TODO":"ac0f1c2ebcca03f5b3c0cc56c5aedbb030a4b511e438bc07a57361c789f91e9f","bindings/bindings.toml":"00ff7348732c956b4f8829f00df2b18b3a7211f5fa2a4cea4ae40c0f859e5f50","bindings/mozpkix.hpp":"77072c8bb0f6eb6bfe8cbadc111dcd92e0c79936d13f2e501aae1e5d289a6675","bindings/nspr_err.h":"2d5205d017b536c2d838bcf9bc4ec79f96dd50e7bb9b73892328781f1ee6629d","bindings/nspr_error.h":"e41c03c77b8c22046f8618832c9569fbcc7b26d8b9bbc35eea7168f35e346889","bindings/nspr_io.h":"085b289849ef0e77f88512a27b4d9bdc28252bd4d39c6a17303204e46ef45f72","bindings/nspr_time.h":"2e637fd338a5cf0fd3fb0070a47f474a34c2a7f4447f31b6875f5a9928d0a261","bindings/nss_ciphers.h":"95ec6344a607558b3c5ba8510f463b6295f3a2fb3f538a01410531045a5f62d1","bindings/nss_init.h":"ef49045063782fb612aff459172cc6a89340f15005808608ade5320ca9974310","bindings/nss_p11.h":"0b81e64fe6db49b2ecff94edd850be111ef99ec11220e88ceb1c67be90143a78","bindings/nss_secerr.h":"713e8368bdae5159af7893cfa517dabfe5103cede051dee9c9557c850a2defc6","bindings/nss_ssl.h":"af222fb957b989e392e762fa2125c82608a0053aff4fb97e556691646c88c335","bindings/nss_sslerr.h":"24b97f092183d8486f774cdaef5030d0249221c78343570d83a4ee5b594210ae","bindings/nss_sslopt.h":"b7807eb7abdad14db6ad7bc51048a46b065a0ea65a4508c95a12ce90e59d1eea","build.rs":"eb324a3f076f0079acc0332379bc68ba6fb1232c3f9e44ef63334fe625d569c1","src/aead.rs":"2013408fbcf9e93331ae14d9d6bdd096966f125b3cf48f83e671f537e89d4e77","src/agent.rs":"5f460010eff4a604b23c456b5cff132f995b30767f0188285fdf39d7724ecf6f","src/agentio.rs":"aeb91f3e4c4cc5b8a816307747090c5df02924801511f9523f9d767fe9dd67e9","src/auth.rs":"71ac7e297a5f872d26cf67b6bbd96e4548ea38374bdd84c1094f76a5de4ed1cb","src/cert.rs":"fd3fd2bbb38754bdcee3898549feae412943c9f719032531c1ad6e61783b5394","src/constants.rs":"e756c07525bd7c2ff271e504708f903b3ede0a3ae821446bd37701055eb11f5f","src/err.rs":"04f38831ca62d29d8aadfe9daf95fd29e68ece184e6d3e00bfb9ee1d12744033","src/exp.rs":"61586662407359c1ecb8ed4987bc3c702f26ba2e203a091a51b6d6363cbd510f","src/ext.rs":"bf7b5f23caf26ab14fba3baf0823dd093e4194f759779e4cfd608478312ed58c","src/hkdf.rs":"1bb57806bbf67af74966bb2bb724de9d6b0094c6f5cddbe12d46292d58ba1f16","src/hp.rs":"0384bc676d8cc66a2cfec7be9df176f04557e4f1424c6d19d03ba5687920ac86","src/lib.rs":"49e0ad22fb5aec2e0864b907cb6d419389d53014e33c147f53198b440ec8929f","src/p11.rs":"6e94cbb594b709c3081449bf50d9961d36648b5db95fb824779bff4f45125ad2","src/prio.rs":"bc4e97049563b136cb7b39f5171e7909d56a77ed46690aaacb781eeb4a4743e0","src/replay.rs":"9bc5826cc8be6afe787f0d403b3958245efce9bfbc7b3100734e5aec3f8b9753","src/result.rs":"cef34dfcb907723e195b56501132e4560e250b327783cb5e41201da5b63e9b5c","src/secrets.rs":"531ec0de048f55108f2612d8f330bee18ffd58b3b26124ca290cc14cec8671dc","src/selfencrypt.rs":"02e963e8b9ea0802f7ee64384e5ccef3e31420e75bc1aacd02270dd504ffbdb1","src/ssl.rs":"ee0e638bd0a6ce2f01ecb6a1c1a203ac7a7ae8145b889a0d6f2015f98d65c4b4","src/time.rs":"d77f0f276385603633b2078f05ff9b4dddc8cfb84c595697689876b6996f69d2","tests/aead.rs":"cccac271087fe37d0a890e5da04984bbfacb4bc12331473dfc189e4d6ebff5f2","tests/agent.rs":"4fa8fa803266b985e9b6329e6a218fe7bd779200b8e0cfa94f5813e0ccc10995","tests/ext.rs":"f5edc1f229703f786ec31a8035465c00275223f14a3c4abe52f3c7cf2686cc03","tests/handshake.rs":"bcc687c0e1b485658847faf28a9f5dbfdb297812bed1bd2e80593d5f9e1fee36","tests/hkdf.rs":"0e4853f629050ba4d8069be52b7a441b670d1abaf6b8cd670a8215e0b88beb37","tests/hp.rs":"e6dd3cb4bceebc6fca8f270d8302ef34e14bda6c91fc4f9342ba1681be57ee03","tests/init.rs":"55df7cb95deb629f8701b55a8bcb91e797f30fb10e847a36a0a5a4e80488b002","tests/selfencrypt.rs":"60bfe8a0729cdaa6c2171146083266fa0e625a1d98b5f8735cd22b725d32398b"},"package":null}
\ No newline at end of file
+{"files":{"Cargo.toml":"7636458c97a2cffc541b679cb0fc53a5aa4ea5c9dc462e1a383f7c471f112d35","TODO":"ac0f1c2ebcca03f5b3c0cc56c5aedbb030a4b511e438bc07a57361c789f91e9f","bindings/bindings.toml":"0f305bda9513e7fb4b521df79912ad5ba21784377b84f4b531895619e561f356","bindings/mozpkix.hpp":"77072c8bb0f6eb6bfe8cbadc111dcd92e0c79936d13f2e501aae1e5d289a6675","bindings/nspr_err.h":"2d5205d017b536c2d838bcf9bc4ec79f96dd50e7bb9b73892328781f1ee6629d","bindings/nspr_error.h":"e41c03c77b8c22046f8618832c9569fbcc7b26d8b9bbc35eea7168f35e346889","bindings/nspr_io.h":"085b289849ef0e77f88512a27b4d9bdc28252bd4d39c6a17303204e46ef45f72","bindings/nspr_time.h":"2e637fd338a5cf0fd3fb0070a47f474a34c2a7f4447f31b6875f5a9928d0a261","bindings/nss_ciphers.h":"95ec6344a607558b3c5ba8510f463b6295f3a2fb3f538a01410531045a5f62d1","bindings/nss_init.h":"ef49045063782fb612aff459172cc6a89340f15005808608ade5320ca9974310","bindings/nss_p11.h":"0b81e64fe6db49b2ecff94edd850be111ef99ec11220e88ceb1c67be90143a78","bindings/nss_secerr.h":"713e8368bdae5159af7893cfa517dabfe5103cede051dee9c9557c850a2defc6","bindings/nss_ssl.h":"af222fb957b989e392e762fa2125c82608a0053aff4fb97e556691646c88c335","bindings/nss_sslerr.h":"24b97f092183d8486f774cdaef5030d0249221c78343570d83a4ee5b594210ae","bindings/nss_sslopt.h":"b7807eb7abdad14db6ad7bc51048a46b065a0ea65a4508c95a12ce90e59d1eea","build.rs":"363243f6fb484c081dc73ad456ec2f7577525f94113930c49f3c466784405a70","src/aead.rs":"b598dc13a6fd1e97848571ef130202abb3ad05eab95334668c06b2480387ef5b","src/agent.rs":"1a0af9b1354023c976120c1ec92f2b5e25f9427cbc61dfa9772a267a47882731","src/agentio.rs":"712ff073e1fd9a55169481502bc7d2b78e0f3b498cfa55635c8061867d511cd1","src/auth.rs":"71ac7e297a5f872d26cf67b6bbd96e4548ea38374bdd84c1094f76a5de4ed1cb","src/cert.rs":"fd3fd2bbb38754bdcee3898549feae412943c9f719032531c1ad6e61783b5394","src/constants.rs":"75dec8e3c74326f492a115a0e7a487daba32eba30bcbd64d2223333b3caa4008","src/err.rs":"04f38831ca62d29d8aadfe9daf95fd29e68ece184e6d3e00bfb9ee1d12744033","src/exp.rs":"61586662407359c1ecb8ed4987bc3c702f26ba2e203a091a51b6d6363cbd510f","src/ext.rs":"e9b251fd156b49eff221c079ce3c4095cd7d13cd52e711a6a08f9682764073a5","src/hkdf.rs":"6d44f63493f0c558a23339f88fe766f8afdb0bda3dc11a79e8a99d3c8d0b6acb","src/hp.rs":"854ce7b9d44892fbb01ac4078b84266771a9254cebfea5b94e7f4b4a7fb1b946","src/lib.rs":"9ada53450e66cdcf944a72e4b23feb06a3c3c92813e79a2ac122dea6319a6d81","src/p11.rs":"7a755e56372b4b037359cf9de9ba831bf986599e371a47d56630a2cc3dcc82da","src/prio.rs":"0e213056f6bf0c797c2cfe13c6d14dbb64a64b1218fff21cbf36fb3309b852f9","src/replay.rs":"01eae2accfefbc26719fcccd4bcb8c1ea6400ab96fbb696ecdb8f32164f931a2","src/result.rs":"d76c7bc5e99c80a5a124909ab586cdb91d894856e52f5146430da43584a6d6c1","src/secrets.rs":"09b26118995b3b2301646f5e9fa9ce25ad813e4780184eda53ab7eff8d1da5c5","src/selfencrypt.rs":"3a642f95073e329f9211468304605dd3953d9fbeef24cf64f00dd541ef6a30ee","src/ssl.rs":"d8bf4aa4869e7d161a2e862d7a628484f8736273823320fc6eb693ba86b8aef0","src/time.rs":"d77f0f276385603633b2078f05ff9b4dddc8cfb84c595697689876b6996f69d2","tests/aead.rs":"4472c50dff9e70533bfa9bc0c964011b832335066d3e3e7fe4d3240f40291b7f","tests/agent.rs":"451cf24b3f211f7b31fffea58f2a0d9d760c9b1af8dc7c47be663c099f4cfd65","tests/ext.rs":"5249e866a5a0b57fee733f99a7d9cba16ef63358de5657c9b69488d8e6e680a4","tests/handshake.rs":"a9dab4781c63b58fecfe9434275fcea53d08981ce99980113f47c10416f5ba63","tests/hkdf.rs":"afc6a7654c6222ff17f68dbba7ca16b0e13044e1107e19e9b9ca6fa2bd473bce","tests/hp.rs":"77c4998ee25ebd8ffc3e00f9bf79d03a42df3a291d0fb371b4e8ea680876cf4b","tests/init.rs":"0243ec4b6052a8ce83494b815fca3a19aed3fcb44d87e0206faeb21529d63445","tests/selfencrypt.rs":"88eec5c3421d5a9efe7fc4ddac749bed8afc1789c6cc1a7701ebf5d5f23c58ec"},"package":null}
\ No newline at end of file
--- a/third_party/rust/neqo-crypto/Cargo.toml
+++ b/third_party/rust/neqo-crypto/Cargo.toml
@@ -1,11 +1,11 @@
 [package]
 name = "neqo-crypto"
-version = "0.1.14"
+version = "0.1.12"
 authors = ["Martin Thomson <mt@lowentropy.net>"]
 edition = "2018"
 build = "build.rs"
 license = "MIT/Apache-2.0"
 
 [dependencies]
 neqo-common = { path = "../neqo-common" }
 log = "0.4.0"
--- a/third_party/rust/neqo-crypto/bindings/bindings.toml
+++ b/third_party/rust/neqo-crypto/bindings/bindings.toml
@@ -34,17 +34,16 @@ functions = [
     "SSL_GetImplementedCiphers",
     "SSL_GetNextProto",
     "SSL_GetNumImplementedCiphers",
     "SSL_GetPreliminaryChannelInfo",
     "SSL_ForceHandshake",
     "SSL_ImportFD",
     "SSL_NamedGroupConfig",
     "SSL_OptionSet",
-    "SSL_OptionGetDefault",
     "SSL_PeerCertificate",
     "SSL_PeerCertificateChain",
     "SSL_PeerSignedCertTimestamps",
     "SSL_PeerStapledOCSPResponses",
     "SSL_ResetHandshake",
     "SSL_SetNextProtoNego",
     "SSL_SetURL",
     "SSL_VersionRangeSet",
--- a/third_party/rust/neqo-crypto/build.rs
+++ b/third_party/rust/neqo-crypto/build.rs
@@ -124,20 +124,17 @@ fn get_bash() -> PathBuf {
     // another instance of bash that might be sitting around (like WSL).
     match env::var("MOZILLABUILD") {
         Ok(d) => PathBuf::from(d).join("msys").join("bin").join("bash.exe"),
         _ => PathBuf::from("bash"),
     }
 }
 
 fn build_nss(dir: PathBuf) {
-    let mut build_nss = vec![
-        String::from("./build.sh"),
-        String::from("-Ddisable_tests=1"),
-    ];
+    let mut build_nss = vec![String::from("./build.sh")];
     if is_debug() {
         build_nss.push(String::from("--static"));
     } else {
         build_nss.push(String::from("-o"));
     }
     if let Ok(d) = env::var("NSS_JOBS") {
         build_nss.push(String::from("-j"));
         build_nss.push(d);
@@ -165,17 +162,22 @@ fn dynamic_link_both(extra_libs: &[&str]
     } else {
         &["plds4", "plc4", "nspr4"]
     };
     for lib in nspr_libs.iter().chain(extra_libs) {
         println!("cargo:rustc-link-lib=dylib={}", lib);
     }
 }
 
-fn static_link() {
+fn static_link(nsstarget: &PathBuf) {
+    let lib_dir = nsstarget.join("lib");
+    println!(
+        "cargo:rustc-link-search=native={}",
+        lib_dir.to_str().unwrap()
+    );
     let mut static_libs = vec![
         "certdb",
         "certhi",
         "cryptohi",
         "freebl",
         "nss_static",
         "nssb",
         "nssdev",
@@ -292,18 +294,19 @@ fn setup_standalone() -> Vec<String> {
 
     let includes = get_includes(&nsstarget, &nssdist);
 
     let nsslibdir = nsstarget.join("lib");
     println!(
         "cargo:rustc-link-search=native={}",
         nsslibdir.to_str().unwrap()
     );
+
     if is_debug() {
-        static_link();
+        static_link(&nsstarget);
     } else {
         dynamic_link();
     }
 
     let mut flags: Vec<String> = Vec::new();
     for i in includes {
         flags.push(String::from("-I") + i.to_str().unwrap());
     }
--- a/third_party/rust/neqo-crypto/src/aead.rs
+++ b/third_party/rust/neqo-crypto/src/aead.rs
@@ -48,23 +48,17 @@ experimental_api!(SSL_AeadDecrypt(
 ));
 experimental_api!(SSL_DestroyAead(ctx: *mut SSLAeadContext));
 scoped_ptr!(AeadContext, SSLAeadContext, SSL_DestroyAead);
 
 pub struct Aead {
     ctx: AeadContext,
 }
 
-// TODO(mt) move unused_self once https://github.com/rust-lang/rust-clippy/issues/5053 is fixed
-#[allow(clippy::unused_self)]
 impl Aead {
-    /// Create a new AEAD based on the indicated TLS version and cipher suite.
-    ///
-    /// # Errors
-    /// Returns `Error` when the supporting NSS functions fail.
     pub fn new(version: Version, cipher: Cipher, secret: &SymKey, prefix: &str) -> Res<Self> {
         let s: *mut PK11SymKey = **secret;
         unsafe { Self::from_raw(version, cipher, s, prefix) }
     }
 
     #[must_use]
     pub fn expansion(&self) -> usize {
         16
@@ -93,19 +87,16 @@ impl Aead {
             None => Err(Error::InternalError),
         }
     }
 
     /// Decrypt a plaintext.
     ///
     /// The space provided in `output` needs to be larger than `input` by
     /// the value provided in `Aead::expansion`.
-    ///
-    /// # Errors
-    /// If the input can't be protected or any input is too large for NSS.
     pub fn encrypt<'a>(
         &self,
         count: u64,
         aad: &[u8],
         input: &[u8],
         output: &'a mut [u8],
     ) -> Res<&'a [u8]> {
         let mut l: c_uint = 0;
@@ -125,19 +116,16 @@ impl Aead {
         Ok(&output[0..(l.try_into()?)])
     }
 
     /// Decrypt a ciphertext.
     ///
     /// Note that NSS insists upon having extra space available for decryption, so
     /// the buffer for `output` should be the same length as `input`, even though
     /// the final result will be shorter.
-    ///
-    /// # Errors
-    /// If the input isn't authenticated or any input is too large for NSS.
     pub fn decrypt<'a>(
         &self,
         count: u64,
         aad: &[u8],
         input: &[u8],
         output: &'a mut [u8],
     ) -> Res<&'a [u8]> {
         let mut l: c_uint = 0;
--- a/third_party/rust/neqo-crypto/src/agent.rs
+++ b/third_party/rust/neqo-crypto/src/agent.rs
@@ -1,30 +1,30 @@
 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-pub use crate::agentio::{as_c_void, Record, RecordList};
 use crate::agentio::{AgentIo, METHODS};
+pub use crate::agentio::{Record, RecordList};
 use crate::assert_initialized;
 use crate::auth::AuthenticationStatus;
 pub use crate::cert::CertificateInfo;
 use crate::constants::*;
 use crate::err::{is_blocked, secstatus_to_res, Error, PRErrorCode, Res};
 use crate::ext::{ExtensionHandler, ExtensionTracker};
 use crate::p11;
 use crate::prio;
 use crate::replay::AntiReplay;
 use crate::secrets::SecretHolder;
 use crate::ssl::{self, PRBool};
 use crate::time::{PRTime, Time};
 
-use neqo_common::{matches, qdebug, qinfo, qtrace, qwarn};
+use neqo_common::{qdebug, qinfo, qwarn};
 use std::cell::RefCell;
 use std::convert::{TryFrom, TryInto};
 use std::ffi::CString;
 use std::mem::{self, MaybeUninit};
 use std::ops::{Deref, DerefMut};
 use std::os::raw::{c_uint, c_void};
 use std::pin::Pin;
 use std::ptr::{null, null_mut, NonNull};
@@ -38,23 +38,21 @@ pub enum HandshakeState {
     AuthenticationPending,
     Authenticated(PRErrorCode),
     Complete(SecretAgentInfo),
     Failed(Error),
 }
 
 impl HandshakeState {
     #[must_use]
-    pub fn is_connected(&self) -> bool {
-        matches!(self, Self::Complete(_))
-    }
-
-    #[must_use]
-    pub fn is_final(&self) -> bool {
-        matches!(self, Self::Complete(_) | Self::Failed(_))
+    pub fn connected(&self) -> bool {
+        match self {
+            Self::Complete(_) => true,
+            _ => false,
+        }
     }
 }
 
 fn get_alpn(fd: *mut ssl::PRFileDesc, pre: bool) -> Res<Option<String>> {
     let mut alpn_state = ssl::SSLNextProtoState::SSL_NEXT_PROTO_NO_SUPPORT;
     let mut chosen = vec![0_u8; 255];
     let mut chosen_len: c_uint = 0;
     secstatus_to_res(unsafe {
@@ -74,17 +72,17 @@ fn get_alpn(fd: *mut ssl::PRFileDesc, pr
             chosen.truncate(chosen_len as usize);
             Some(match String::from_utf8(chosen) {
                 Ok(a) => a,
                 _ => return Err(Error::InternalError),
             })
         }
         _ => None,
     };
-    qtrace!([format!("{:p}", fd)], "got ALPN {:?}", alpn);
+    qinfo!([format!("{:p}", fd)], "got ALPN {:?}", alpn);
     Ok(alpn)
 }
 
 pub struct SecretAgentPreInfo {
     info: ssl::SSLPreliminaryChannelInfo,
     alpn: Option<String>,
 }
 
@@ -120,17 +118,17 @@ impl SecretAgentPreInfo {
     preinfo_arg!(version, ssl_preinfo_version, protocolVersion: Version);
     preinfo_arg!(cipher_suite, ssl_preinfo_cipher_suite, cipherSuite: Cipher);
     #[must_use]
     pub fn early_data(&self) -> bool {
         self.info.canSendEarlyData != 0
     }
     #[must_use]
     pub fn max_early_data(&self) -> usize {
-        usize::try_from(self.info.maxEarlyDataSize).unwrap()
+        self.info.maxEarlyDataSize as usize
     }
     #[must_use]
     pub fn alpn(&self) -> Option<&String> {
         self.alpn.as_ref()
     }
 
     preinfo_arg!(
         early_data_cipher,
@@ -222,61 +220,62 @@ pub struct SecretAgent {
     inf: Option<SecretAgentInfo>,
 
     /// Whether or not EndOfEarlyData should be suppressed.
     no_eoed: bool,
 }
 
 impl SecretAgent {
     fn new() -> Res<Self> {
-        let mut io = Box::pin(AgentIo::new());
-        let fd = Self::create_fd(&mut io)?;
-        Ok(Self {
-            fd,
+        let mut agent = Self {
+            fd: null_mut(),
             secrets: SecretHolder::default(),
             raw: None,
-            io,
+            io: Pin::new(Box::new(AgentIo::new())),
             state: HandshakeState::New,
 
-            auth_required: Box::pin(false),
-            alert: Box::pin(None),
-            now: Box::pin(0),
+            auth_required: Pin::new(Box::new(false)),
+            alert: Pin::new(Box::new(None)),
+            now: Pin::new(Box::new(0)),
 
             extension_handlers: Vec::new(),
             inf: None,
 
             no_eoed: false,
-        })
+        };
+        agent.create_fd()?;
+        Ok(agent)
     }
 
     // Create a new SSL file descriptor.
     //
     // Note that we create separate bindings for PRFileDesc as both
     // ssl::PRFileDesc and prio::PRFileDesc.  This keeps the bindings
     // minimal, but it means that the two forms need casts to translate
     // between them.  ssl::PRFileDesc is left as an opaque type, as the
     // ssl::SSL_* APIs only need an opaque type.
-    fn create_fd(io: &mut Pin<Box<AgentIo>>) -> Res<*mut ssl::PRFileDesc> {
+    fn create_fd(&mut self) -> Res<()> {
         assert_initialized();
         let label = CString::new("sslwrapper")?;
         let id = unsafe { prio::PR_GetUniqueIdentity(label.as_ptr()) };
 
         let base_fd = unsafe { prio::PR_CreateIOLayerStub(id, METHODS) };
         if base_fd.is_null() {
             return Err(Error::CreateSslSocket);
         }
         let fd = unsafe {
-            (*base_fd).secret = as_c_void(io) as *mut _;
+            (*base_fd).secret = &mut *self.io as *mut AgentIo as *mut _;
             ssl::SSL_ImportFD(null_mut(), base_fd as *mut ssl::PRFileDesc)
         };
         if fd.is_null() {
             unsafe { prio::PR_Close(base_fd) };
             return Err(Error::CreateSslSocket);
         }
-        Ok(fd)
+        self.fd = fd;
+        Ok(())
     }
 
     unsafe extern "C" fn auth_complete_hook(
         arg: *mut c_void,
         _fd: *mut ssl::PRFileDesc,
         _check_sig: ssl::PRBool,
         _is_server: ssl::PRBool,
     ) -> ssl::SECStatus {
@@ -316,63 +315,58 @@ impl SecretAgent {
     }
 
     // Ready this for connecting.
     fn ready(&mut self, is_server: bool) -> Res<()> {
         secstatus_to_res(unsafe {
             ssl::SSL_AuthCertificateHook(
                 self.fd,
                 Some(Self::auth_complete_hook),
-                as_c_void(&mut self.auth_required),
+                &mut *self.auth_required as *mut bool as *mut c_void,
             )
         })?;
 
         secstatus_to_res(unsafe {
             ssl::SSL_AlertSentCallback(
                 self.fd,
                 Some(Self::alert_sent_cb),
-                as_c_void(&mut self.alert),
+                &mut *self.alert as *mut Option<Alert> as *mut c_void,
             )
         })?;
 
         // TODO(mt) move to time.rs so we can remove PRTime definition from nss_ssl bindings.
-        unsafe { ssl::SSL_SetTimeFunc(self.fd, Some(Self::time_func), as_c_void(&mut self.now)) }?;
+        unsafe {
+            ssl::SSL_SetTimeFunc(
+                self.fd,
+                Some(Self::time_func),
+                &mut *self.now as *mut PRTime as *mut c_void,
+            )
+        }?;
 
         self.configure()?;
         secstatus_to_res(unsafe { ssl::SSL_ResetHandshake(self.fd, is_server as ssl::PRBool) })
     }
 
     /// Default configuration.
-    ///
-    /// # Errors
-    /// If `set_version_range` fails.
     fn configure(&mut self) -> Res<()> {
         self.set_version_range(TLS_VERSION_1_3, TLS_VERSION_1_3)?;
         self.set_option(ssl::Opt::Locking, false)?;
         self.set_option(ssl::Opt::Tickets, false)?;
         self.set_option(ssl::Opt::OcspStapling, true)?;
         Ok(())
     }
 
-    /// Set the versions that are supported.
-    ///
-    /// # Errors
-    /// If the range of versions isn't supported.
     pub fn set_version_range(&mut self, min: Version, max: Version) -> Res<()> {
         let range = ssl::SSLVersionRange {
             min: min as ssl::PRUint16,
             max: max as ssl::PRUint16,
         };
         secstatus_to_res(unsafe { ssl::SSL_VersionRangeSet(self.fd, &range) })
     }
 
-    /// Enable a set of ciphers.  Note that the order of these is not respected.
-    ///
-    /// # Errors
-    /// If NSS can't enable or disable ciphers.
     pub fn enable_ciphers(&mut self, ciphers: &[Cipher]) -> Res<()> {
         let all_ciphers = unsafe { ssl::SSL_GetImplementedCiphers() };
         let cipher_count = unsafe { ssl::SSL_GetNumImplementedCiphers() } as usize;
         for i in 0..cipher_count {
             let p = all_ciphers.wrapping_add(i);
             secstatus_to_res(unsafe {
                 ssl::SSL_CipherPrefSet(self.fd, i32::from(*p), false as ssl::PRBool)
             })?;
@@ -381,65 +375,52 @@ impl SecretAgent {
         for c in ciphers {
             secstatus_to_res(unsafe {
                 ssl::SSL_CipherPrefSet(self.fd, i32::from(*c), true as ssl::PRBool)
             })?;
         }
         Ok(())
     }
 
-    /// Set key exchange groups.
-    ///
-    /// # Errors
-    /// If the underlying API fails (which shouldn't happen).
     pub fn set_groups(&mut self, groups: &[Group]) -> Res<()> {
         // SSLNamedGroup is a different size to Group, so copy one by one.
         let group_vec: Vec<_> = groups
             .iter()
             .map(|&g| ssl::SSLNamedGroup::Type::from(g))
             .collect();
 
         let ptr = group_vec.as_slice().as_ptr();
         secstatus_to_res(unsafe {
             ssl::SSL_NamedGroupConfig(self.fd, ptr, c_uint::try_from(group_vec.len())?)
         })
     }
 
     /// Set TLS options.
-    ///
-    /// # Errors
-    /// Returns an error if the option or option value is invalid; i.e., never.
     pub fn set_option(&mut self, opt: ssl::Opt, value: bool) -> Res<()> {
         secstatus_to_res(unsafe {
             ssl::SSL_OptionSet(self.fd, opt.as_int(), opt.map_enabled(value))
         })
     }
 
     /// Enable 0-RTT.
-    ///
-    /// # Errors
-    /// See `set_option`.
     pub fn enable_0rtt(&mut self) -> Res<()> {
         self.set_option(ssl::Opt::EarlyData, true)
     }
 
     /// Disable the `EndOfEarlyData` message.
     pub fn disable_end_of_early_data(&mut self) {
         self.no_eoed = true;
     }
 
     /// `set_alpn` sets a list of preferred protocols, starting with the most preferred.
     /// Though ALPN [RFC7301] permits octet sequences, this only allows for UTF-8-encoded
     /// strings.
     ///
     /// This asserts if no items are provided, or if any individual item is longer than
     /// 255 octets in length.
-    ///
-    /// # Errors
-    /// This should always panic rather than return an error.
     pub fn set_alpn(&mut self, protocols: &[impl AsRef<str>]) -> Res<()> {
         // Validate and set length.
         let mut encoded_len = protocols.len();
         for v in protocols {
             assert!(v.as_ref().len() < 256);
             encoded_len += v.as_ref().len();
         }
 
@@ -473,19 +454,16 @@ impl SecretAgent {
         })
     }
 
     /// Install an extension handler.
     ///
     /// This can be called multiple times with different values for `ext`.  The handler is provided as
     /// Rc<RefCell<>> so that the caller is able to hold a reference to the handler and later access any
     /// state that it accumulates.
-    ///
-    /// # Errors
-    /// When the extension handler can't be successfully installed.
     pub fn extension_handler(
         &mut self,
         ext: Extension,
         handler: Rc<RefCell<dyn ExtensionHandler>>,
     ) -> Res<()> {
         let tracker = unsafe { ExtensionTracker::new(self.fd, ext, handler) }?;
         self.extension_handlers.push(tracker);
         Ok(())
@@ -516,19 +494,16 @@ impl SecretAgent {
             _ => None,
         }
     }
 
     /// Get any preliminary information about the status of the connection.
     ///
     /// This includes whether 0-RTT was accepted and any information related to that.
     /// Calling this function collects all the relevant information.
-    ///
-    /// # Errors
-    /// When the underlying socket functions fail.
     pub fn preinfo(&self) -> Res<SecretAgentPreInfo> {
         SecretAgentPreInfo::new(self.fd)
     }
 
     /// Get the peer's certificate chain.
     #[must_use]
     pub fn peer_certificate(&self) -> Option<CertificateInfo> {
         CertificateInfo::new(self.fd)
@@ -568,26 +543,23 @@ impl SecretAgent {
             self.capture_error(res)?;
             let info = self.capture_error(SecretAgentInfo::new(self.fd))?;
             HandshakeState::Complete(info)
         };
         qinfo!([self], "state -> {:?}", self.state);
         Ok(())
     }
 
-    /// Drive the TLS handshake, taking bytes from `input` and putting
-    /// any bytes necessary into `output`.
-    /// This takes the current time as `now`.
-    /// On success a tuple of a `HandshakeState` and usize indicate whether the handshake
-    /// is complete and how many bytes were written to `output`, respectively.
-    /// If the state is `HandshakeState::AuthenticationPending`, then ONLY call this
-    /// function if you want to proceed, because this will mark the certificate as OK.
-    ///
-    /// # Errors
-    /// When the handshake fails this returns an error.
+    // Drive the TLS handshake, taking bytes from @input and putting
+    // any bytes necessary into @output.
+    // This takes the current time as @now.
+    // On success a tuple of a HandshakeState and usize indicate whether the handshake
+    // is complete and how many bytes were written to @output, respectively.
+    // If the state is HandshakeState::AuthenticationPending, then ONLY call this
+    // function if you want to proceed, because this will mark the certificate as OK.
     pub fn handshake(&mut self, now: Instant, input: &[u8]) -> Res<Vec<u8>> {
         *self.now = Time::from(now).try_into()?;
         self.set_raw(false)?;
 
         let rv = {
             // Within this scope, _h maintains a mutable reference to self.io.
             let _h = self.io.wrap(input);
             match self.state {
@@ -627,25 +599,22 @@ impl SecretAgent {
                 let eoed = Record::new(1, 22, END_OF_EARLY_DATA);
                 self.capture_error(eoed.write(self.fd))?;
                 self.no_eoed = false;
             }
         }
         Ok(())
     }
 
-    /// Drive the TLS handshake, but get the raw content of records, not
-    /// protected records as bytes. This function is incompatible with
-    /// `handshake()`; use either this or `handshake()` exclusively.
-    ///
-    /// Ideally, this only includes records from the current epoch.
-    /// If you send data from multiple epochs, you might end up being sad.
-    ///
-    /// # Errors
-    /// When the handshake fails this returns an error.
+    // Drive the TLS handshake, but get the raw content of records, not
+    // protected records as bytes. This function is incompatible with
+    // handshake(); use either this or handshake() exclusively.
+    //
+    // Ideally, this only includes records from the current epoch.
+    // If you send data from multiple epochs, you might end up being sad.
     pub fn handshake_raw(&mut self, now: Instant, input: Option<Record>) -> Res<RecordList> {
         *self.now = Time::from(now).try_into()?;
         let mut records = self.setup_raw()?;
 
         // Fire off any authentication we might need to complete.
         if let HandshakeState::Authenticated(ref err) = self.state {
             let result =
                 secstatus_to_res(unsafe { ssl::SSL_AuthCertificateComplete(self.fd, *err) });
@@ -668,54 +637,28 @@ impl SecretAgent {
 
         if self.no_eoed {
             records.remove_eoed();
         }
 
         Ok(*Pin::into_inner(records))
     }
 
-    pub fn close(&mut self) {
-        // It should be safe to close multiple times.
-        if self.fd.is_null() {
-            return;
-        }
-        if let Some(true) = self.raw {
-            // Need to hold the record list in scope until the close is done.
-            let _records = self.setup_raw().expect("Can only close");
-            unsafe { prio::PR_Close(self.fd as *mut prio::PRFileDesc) };
-        } else {
-            // Need to hold the IO wrapper in scope until the close is done.
-            let _io = self.io.wrap(&[]);
-            unsafe { prio::PR_Close(self.fd as *mut prio::PRFileDesc) };
-        };
-        let _output = self.io.take_output();
-        self.fd = null_mut();
-    }
-
-    /// State returns the status of the handshake.
+    // State returns the status of the handshake.
     #[must_use]
     pub fn state(&self) -> &HandshakeState {
         &self.state
     }
-    /// Take a read secret.  This will only return a non-`None` value once.
-    #[must_use]
-    pub fn read_secret(&mut self, epoch: Epoch) -> Option<p11::SymKey> {
-        self.secrets.take_read(epoch)
-    }
-    /// Take a write secret.
     #[must_use]
-    pub fn write_secret(&mut self, epoch: Epoch) -> Option<p11::SymKey> {
-        self.secrets.take_write(epoch)
+    pub fn read_secret(&self, epoch: Epoch) -> Option<&p11::SymKey> {
+        self.secrets.read().get(epoch)
     }
-}
-
-impl Drop for SecretAgent {
-    fn drop(&mut self) {
-        self.close();
+    #[must_use]
+    pub fn write_secret(&self, epoch: Epoch) -> Option<&p11::SymKey> {
+        self.secrets.write().get(epoch)
     }
 }
 
 impl ::std::fmt::Display for SecretAgent {
     fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
         write!(f, "Agent {:p}", self.fd)
     }
 }
@@ -725,28 +668,24 @@ impl ::std::fmt::Display for SecretAgent
 pub struct Client {
     agent: SecretAgent,
 
     /// Records the last resumption token.
     resumption: Pin<Box<Option<Vec<u8>>>>,
 }
 
 impl Client {
-    /// Create a new client agent.
-    ///
-    /// # Errors
-    /// Errors returned if the socket can't be created or configured.
     pub fn new(server_name: &str) -> Res<Self> {
         let mut agent = SecretAgent::new()?;
         let url = CString::new(server_name)?;
         secstatus_to_res(unsafe { ssl::SSL_SetURL(agent.fd, url.as_ptr()) })?;
         agent.ready(false)?;
         let mut client = Self {
             agent,
-            resumption: Box::pin(None),
+            resumption: Pin::new(Box::new(None)),
         };
         client.ready()?;
         Ok(client)
     }
 
     unsafe extern "C" fn resumption_token_cb(
         fd: *mut ssl::PRFileDesc,
         token: *const u8,
@@ -758,37 +697,32 @@ impl Client {
         let mut v = Vec::with_capacity(len as usize);
         v.extend_from_slice(std::slice::from_raw_parts(token, len as usize));
         qdebug!([format!("{:p}", fd)], "Got resumption token");
         *resumption = Some(v);
         ssl::SECSuccess
     }
 
     fn ready(&mut self) -> Res<()> {
-        let fd = self.fd;
         unsafe {
             ssl::SSL_SetResumptionTokenCallback(
-                fd,
+                self.fd,
                 Some(Self::resumption_token_cb),
-                as_c_void(&mut self.resumption),
+                &mut *self.resumption as *mut Option<Vec<u8>> as *mut c_void,
             )
         }
     }
 
     /// Return the resumption token.
     #[must_use]
     pub fn resumption_token(&self) -> Option<&Vec<u8>> {
         (*self.resumption).as_ref()
     }
 
     /// Enable resumption, using a token previously provided.
-    ///
-    /// # Errors
-    /// Error returned when the resumption token is invalid or
-    /// the socket is not able to use the value.
     pub fn set_resumption_token(&mut self, token: &[u8]) -> Res<()> {
         unsafe {
             ssl::SSL_SetResumptionToken(
                 self.agent.fd,
                 token.as_ptr(),
                 c_uint::try_from(token.len())?,
             )
         }
@@ -845,20 +779,16 @@ impl ZeroRttCheckState {
 #[derive(Debug)]
 pub struct Server {
     agent: SecretAgent,
     /// This holds the HRR callback context.
     zero_rtt_check: Option<Pin<Box<ZeroRttCheckState>>>,
 }
 
 impl Server {
-    /// Create a new server agent.
-    ///
-    /// # Errors
-    /// Errors returned when NSS fails.
     pub fn new(certificates: &[impl AsRef<str>]) -> Res<Self> {
         let mut agent = SecretAgent::new()?;
 
         for n in certificates {
             let c = CString::new(n.as_ref())?;
             let cert = match NonNull::new(unsafe {
                 p11::PK11_FindCertFromNickname(c.as_ptr(), null_mut())
             }) {
@@ -918,46 +848,37 @@ impl Server {
                 *retry_token_len = c_uint::try_from(tok.len()).expect("token was way too big");
                 ssl::SSLHelloRetryRequestAction::ssl_hello_retry_request
             }
         }
     }
 
     /// Enable 0-RTT.  This shadows the function of the same name that can be accessed
     /// via the Deref implementation on Server.
-    ///
-    /// # Errors
-    /// Returns an error if the underlying NSS functions fail.
     pub fn enable_0rtt(
         &mut self,
         anti_replay: &AntiReplay,
         max_early_data: u32,
         checker: Box<dyn ZeroRttChecker>,
     ) -> Res<()> {
-        let mut check_state = Box::pin(ZeroRttCheckState::new(self.agent.fd, checker));
+        let mut check_state = Pin::new(Box::new(ZeroRttCheckState::new(self.agent.fd, checker)));
+        let arg = &mut *check_state as *mut ZeroRttCheckState as *mut c_void;
         unsafe {
-            ssl::SSL_HelloRetryRequestCallback(
-                self.agent.fd,
-                Some(Self::hello_retry_cb),
-                as_c_void(&mut check_state),
-            )
+            ssl::SSL_HelloRetryRequestCallback(self.agent.fd, Some(Self::hello_retry_cb), arg)
         }?;
         unsafe { ssl::SSL_SetMaxEarlyDataSize(self.agent.fd, max_early_data) }?;
         self.zero_rtt_check = Some(check_state);
         self.agent.enable_0rtt()?;
         anti_replay.config_socket(self.fd)?;
         Ok(())
     }
 
     /// Send a session ticket to the client.
     /// This adds |extra| application-specific content into that ticket.
     /// The records that are sent are captured and returned.
-    ///
-    /// # Errors
-    /// If NSS is unable to send a ticket, or if this agent is incorrectly configured.
     pub fn send_ticket(&mut self, now: Instant, extra: &[u8]) -> Res<RecordList> {
         *self.agent.now = Time::from(now).try_into()?;
         let records = self.setup_raw()?;
 
         unsafe {
             ssl::SSL_SendSessionTicket(self.fd, extra.as_ptr(), c_uint::try_from(extra.len())?)
         }?;
 
--- a/third_party/rust/neqo-crypto/src/agentio.rs
+++ b/third_party/rust/neqo-crypto/src/agentio.rs
@@ -21,21 +21,16 @@ use std::ptr::{null, null_mut};
 use std::vec::Vec;
 
 // Alias common types.
 type PrFd = *mut prio::PRFileDesc;
 type PrStatus = prio::PRStatus::Type;
 const PR_SUCCESS: PrStatus = prio::PRStatus::PR_SUCCESS;
 const PR_FAILURE: PrStatus = prio::PRStatus::PR_FAILURE;
 
-/// Convert a pinned, boxed object into a void pointer.
-pub fn as_c_void<T: Unpin>(pin: &mut Pin<Box<T>>) -> *mut c_void {
-    Pin::into_inner(pin.as_mut()) as *mut T as *mut c_void
-}
-
 // This holds the length of the slice, not the slice itself.
 #[derive(Default, Debug)]
 struct RecordLength {
     epoch: Epoch,
     ct: ssl::SSLContentType::Type,
     len: usize,
 }
 
@@ -112,20 +107,19 @@ impl RecordList {
 
         let slice = std::slice::from_raw_parts(data, len as usize);
         records.append(epoch, ct, slice);
         ssl::SECSuccess
     }
 
     /// Create a new record list.
     pub(crate) fn setup(fd: *mut ssl::PRFileDesc) -> Res<Pin<Box<Self>>> {
-        let mut records = Box::pin(Self::default());
-        unsafe {
-            ssl::SSL_RecordLayerWriteCallback(fd, Some(Self::ingest), as_c_void(&mut records))
-        }?;
+        let mut records = Pin::new(Box::new(Self::default()));
+        let records_ptr = &mut *records as *mut Self as *mut c_void;
+        unsafe { ssl::SSL_RecordLayerWriteCallback(fd, Some(Self::ingest), records_ptr) }?;
         Ok(records)
     }
 }
 
 impl Deref for RecordList {
     type Target = Vec<Record>;
     #[must_use]
     fn deref(&self) -> &Vec<Record> {
--- a/third_party/rust/neqo-crypto/src/constants.rs
+++ b/third_party/rust/neqo-crypto/src/constants.rs
@@ -7,25 +7,17 @@
 #![allow(dead_code)]
 
 use crate::ssl;
 
 // Ideally all of these would be enums, but size matters and we need to allow
 // for values outside of those that are defined here.
 
 pub type Alert = u8;
-
 pub type Epoch = u16;
-// TLS doesn't really have an "initial" concept that maps to QUIC so directly,
-// but this should be clear enough.
-pub const TLS_EPOCH_INITIAL: Epoch = 0 as Epoch;
-pub const TLS_EPOCH_ZERO_RTT: Epoch = 1 as Epoch;
-pub const TLS_EPOCH_HANDSHAKE: Epoch = 2 as Epoch;
-// Also, we don't use TLS epochs > 3.
-pub const TLS_EPOCH_APPLICATION_DATA: Epoch = 3 as Epoch;
 
 /// Rather than defining a type alias and a bunch of constants, which leads to a ton of repetition,
 /// use this macro.
 macro_rules! remap_enum {
     { $t:ident: $s:ty { $( $n:ident = $v:path ),+ $(,)? } } => {
         pub type $t = $s;
         $( pub const $n: $t = $v as $t; )+
     };
--- a/third_party/rust/neqo-crypto/src/ext.rs
+++ b/third_party/rust/neqo-crypto/src/ext.rs
@@ -1,15 +1,14 @@
 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use crate::agentio::as_c_void;
 use crate::constants::*;
 use crate::err::Res;
 use crate::ssl::{
     PRBool, PRFileDesc, SECFailure, SECStatus, SECSuccess, SSLAlertDescription,
     SSLExtensionHandler, SSLExtensionWriter, SSLHandshakeType,
 };
 
 use std::cell::RefCell;
@@ -113,24 +112,21 @@ impl ExtensionTracker {
                     *alert = a;
                     SECFailure
                 }
             }
         })
     }
 
     /// Use the provided handler to manage an extension.  This is quite unsafe.
+    /// # Safety
     ///
-    /// # Safety
     /// The holder of this `ExtensionTracker` needs to ensure that it lives at
     /// least as long as the file descriptor, as NSS provides no way to remove
     /// an extension handler once it is configured.
-    ///
-    /// # Errors
-    /// If the underlying NSS API fails to register a handler.
     pub unsafe fn new(
         fd: *mut PRFileDesc,
         extension: Extension,
         handler: Rc<RefCell<dyn ExtensionHandler>>,
     ) -> Res<Self> {
         // The ergonomics here aren't great for users of this API, but it's
         // horrific here. The pinned outer box gives us a stable pointer to the inner
         // box.  This is the pointer that is passed to NSS.
@@ -139,25 +135,26 @@ impl ExtensionTracker {
         // what we end up with a reference to in callbacks.  That extra wrapper around
         // the Rc avoid any touching of reference counts in callbacks, which would
         // inevitably lead to leaks as we don't control how many times the callback
         // is invoked.
         //
         // This way, only this "outer" code deals with the reference count.
         let mut tracker = Self {
             extension,
-            handler: Box::pin(Box::new(handler)),
+            handler: Pin::new(Box::new(Box::new(handler))),
         };
+        let p = &mut *tracker.handler as *mut BoxedExtensionHandler as *mut c_void;
         SSL_InstallExtensionHooks(
             fd,
             extension,
             Some(Self::extension_writer),
-            as_c_void(&mut tracker.handler),
+            p,
             Some(Self::extension_handler),
-            as_c_void(&mut tracker.handler),
+            p,
         )?;
         Ok(tracker)
     }
 }
 
 impl std::fmt::Debug for ExtensionTracker {
     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
         write!(f, "ExtensionTracker: {:?}", self.extension)
--- a/third_party/rust/neqo-crypto/src/hkdf.rs
+++ b/third_party/rust/neqo-crypto/src/hkdf.rs
@@ -29,39 +29,32 @@ experimental_api!(SSL_HkdfExpandLabel(
     prk: *mut PK11SymKey,
     handshake_hash: *const u8,
     handshake_hash_len: c_uint,
     label: *const c_char,
     label_len: c_uint,
     secret: *mut *mut PK11SymKey,
 ));
 
-fn key_size(version: Version, cipher: Cipher) -> Res<usize> {
+pub fn key_size(version: Version, cipher: Cipher) -> Res<usize> {
     if version != TLS_VERSION_1_3 {
         return Err(Error::UnsupportedVersion);
     }
     Ok(match cipher {
         TLS_AES_128_GCM_SHA256 | TLS_CHACHA20_POLY1305_SHA256 => 32,
         TLS_AES_256_GCM_SHA384 => 48,
         _ => return Err(Error::UnsupportedCipher),
     })
 }
 
-/// Generate a random key of the right size for the given suite.
-///
-/// # Errors
-/// Only if NSS fails.
-pub fn generate_key(version: Version, cipher: Cipher) -> Res<SymKey> {
-    import_key(version, cipher, &random(key_size(version, cipher)?))
+pub fn generate_key(version: Version, cipher: Cipher, size: usize) -> Res<SymKey> {
+    import_key(version, cipher, &random(size)?)
 }
 
 /// Import a symmetric key for use with HKDF.
-///
-/// # Errors
-/// Errors returned if the key buffer is an incompatible size or the NSS functions fail.
 pub fn import_key(version: Version, cipher: Cipher, buf: &[u8]) -> Res<SymKey> {
     if version != TLS_VERSION_1_3 {
         return Err(Error::UnsupportedVersion);
     }
     let mech = match cipher {
         TLS_AES_128_GCM_SHA256 | TLS_CHACHA20_POLY1305_SHA256 => CKM_NSS_HKDF_SHA256,
         TLS_AES_256_GCM_SHA384 => CKM_NSS_HKDF_SHA384,
         _ => return Err(Error::UnsupportedCipher),
@@ -88,19 +81,16 @@ pub fn import_key(version: Version, ciph
     };
     match NonNull::new(key_ptr) {
         Some(p) => Ok(SymKey::new(p)),
         None => Err(Error::InternalError),
     }
 }
 
 /// Extract a PRK from the given salt and IKM using the algorithm defined in RFC 5869.
-///
-/// # Errors
-/// Errors returned if inputs are too large or the NSS functions fail.
 pub fn extract(
     version: Version,
     cipher: Cipher,
     salt: Option<&SymKey>,
     ikm: &SymKey,
 ) -> Res<SymKey> {
     let mut prk: *mut PK11SymKey = null_mut();
     let salt_ptr: *mut PK11SymKey = match salt {
@@ -110,19 +100,16 @@ pub fn extract(
     unsafe { SSL_HkdfExtract(version, cipher, salt_ptr, **ikm, &mut prk) }?;
     match NonNull::new(prk) {
         Some(p) => Ok(SymKey::new(p)),
         None => Err(Error::InternalError),
     }
 }
 
 /// Expand a PRK using the HKDF-Expand-Label function defined in RFC 8446.
-///
-/// # Errors
-/// Errors returned if inputs are too large or the NSS functions fail.
 pub fn expand_label(
     version: Version,
     cipher: Cipher,
     prk: &SymKey,
     handshake_hash: &[u8],
     label: &str,
 ) -> Res<SymKey> {
     let l = label.as_bytes();
--- a/third_party/rust/neqo-crypto/src/hp.rs
+++ b/third_party/rust/neqo-crypto/src/hp.rs
@@ -24,30 +24,26 @@ experimental_api!(SSL_HkdfExpandLabelWit
     handshake_hash_len: c_uint,
     label: *const c_char,
     label_len: c_uint,
     mech: CK_MECHANISM_TYPE,
     key_size: c_uint,
     secret: *mut *mut PK11SymKey,
 ));
 
-#[derive(Clone)]
 pub struct HpKey(SymKey);
 
 impl Debug for HpKey {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "HP-{:?}", self.0)
+        f.write_str("HP Key")
     }
 }
 
 impl HpKey {
     /// QUIC-specific API for extracting a header-protection key.
-    ///
-    /// # Errors
-    /// Errors if HKDF fails or if the label is too long to fit in a `c_uint`.
     pub fn extract(version: Version, cipher: Cipher, prk: &SymKey, label: &str) -> Res<Self> {
         let l = label.as_bytes();
         let mut secret: *mut PK11SymKey = null_mut();
 
         let (mech, key_size) = match cipher {
             TLS_AES_128_GCM_SHA256 => (CK_MECHANISM_TYPE::from(CKM_AES_ECB), 16),
             TLS_AES_256_GCM_SHA384 => (CK_MECHANISM_TYPE::from(CKM_AES_ECB), 32),
             TLS_CHACHA20_POLY1305_SHA256 => (CK_MECHANISM_TYPE::from(CKM_NSS_CHACHA20_CTR), 32),
@@ -72,20 +68,16 @@ impl HpKey {
         }?;
         match NonNull::new(secret) {
             None => Err(Error::HkdfError),
             Some(p) => Ok(Self(SymKey::new(p))),
         }
     }
 
     /// Generate a header protection mask for QUIC.
-    ///
-    /// # Errors
-    /// An error is returned if the NSS functions fail; a sample of the
-    /// wrong size is the obvious cause.
     #[allow(clippy::cast_sign_loss)]
     pub fn mask(&self, sample: &[u8]) -> Res<Vec<u8>> {
         let k: *mut PK11SymKey = *self.0;
         let mech = unsafe { PK11_GetMechanism(k) };
         // Cast is safe because block size is always greater than or equal to 0
         let block_size = unsafe { PK11_GetBlockSize(mech, null_mut()) } as usize;
 
         let mut output = vec![0_u8; block_size];
--- a/third_party/rust/neqo-crypto/src/lib.rs
+++ b/third_party/rust/neqo-crypto/src/lib.rs
@@ -36,17 +36,17 @@ mod time;
 
 pub use self::agent::{
     Agent, Client, HandshakeState, Record, RecordList, SecretAgent, SecretAgentInfo,
     SecretAgentPreInfo, Server, ZeroRttCheckResult, ZeroRttChecker,
 };
 pub use self::constants::*;
 pub use self::err::{Error, PRErrorCode, Res};
 pub use self::ext::{ExtensionHandler, ExtensionHandlerResult, ExtensionWriterResult};
-pub use self::p11::{random, SymKey};
+pub use self::p11::SymKey;
 pub use self::replay::AntiReplay;
 pub use self::secrets::SecretDirection;
 pub use auth::AuthenticationStatus;
 
 use neqo_common::once::OnceResult;
 
 use std::ffi::CString;
 use std::os::raw::c_char;
@@ -99,28 +99,16 @@ pub fn init() {
             secstatus_to_res(nss::NSS_NoDB_Init(null())).expect("NSS_NoDB_Init failed");
             secstatus_to_res(nss::NSS_SetDomesticPolicy()).expect("NSS_SetDomesticPolicy failed");
 
             NssLoaded::NoDb
         });
     }
 }
 
-/// This enables SSLTRACE by calling a simple, harmless function to trigger its
-/// side effects.  SSLTRACE is not enabled in NSS until a socket is made or
-/// global options are accessed.  Reading an option is the least impact approach.
-/// This allows us to use SSLTRACE in all of our unit tests and programs.
-#[cfg(debug_assertions)]
-fn enable_ssl_trace() {
-    let opt = ssl::Opt::Locking.as_int();
-    let mut _v: ::std::os::raw::c_int = 0;
-    secstatus_to_res(unsafe { ssl::SSL_OptionGetDefault(opt, &mut _v) })
-        .expect("SSL_OptionGetDefault failed");
-}
-
 pub fn init_db<P: Into<PathBuf>>(dir: P) {
     time::init();
     unsafe {
         INITIALIZED.call_once(|| {
             if already_initialized() {
                 return NssLoaded::External;
             }
 
@@ -142,19 +130,16 @@ pub fn init_db<P: Into<PathBuf>>(dir: P)
             secstatus_to_res(ssl::SSL_ConfigServerSessionIDCache(
                 1024,
                 0,
                 0,
                 dircstr.as_ptr(),
             ))
             .expect("SSL_ConfigServerSessionIDCache failed");
 
-            #[cfg(debug_assertions)]
-            enable_ssl_trace();
-
             NssLoaded::Db(path.into_boxed_path())
         });
     }
 }
 
 /// Panic if NSS isn't initialized.
 pub fn assert_initialized() {
     unsafe {
--- a/third_party/rust/neqo-crypto/src/p11.rs
+++ b/third_party/rust/neqo-crypto/src/p11.rs
@@ -62,59 +62,43 @@ macro_rules! scoped_ptr {
 scoped_ptr!(Certificate, CERTCertificate, CERT_DestroyCertificate);
 scoped_ptr!(CertList, CERTCertList, CERT_DestroyCertList);
 scoped_ptr!(PrivateKey, SECKEYPrivateKey, SECKEY_DestroyPrivateKey);
 scoped_ptr!(SymKey, PK11SymKey, PK11_FreeSymKey);
 scoped_ptr!(Slot, PK11SlotInfo, PK11_FreeSlot);
 
 impl SymKey {
     /// You really don't want to use this.
-    ///
-    /// # Errors
-    /// Internal errors in case of failures in NSS.
     pub fn as_bytes<'a>(&'a self) -> Res<&'a [u8]> {
         secstatus_to_res(unsafe { PK11_ExtractKeyValue(self.ptr) })?;
 
         let key_item = unsafe { PK11_GetKeyData(self.ptr) };
         // This is accessing a value attached to the key, so we can treat this as a borrow.
         match unsafe { key_item.as_mut() } {
             None => Err(Error::InternalError),
             Some(key) => Ok(unsafe { std::slice::from_raw_parts(key.data, key.len as usize) }),
         }
     }
 }
 
-impl Clone for SymKey {
-    #[must_use]
-    fn clone(&self) -> Self {
-        let ptr = unsafe { PK11_ReferenceSymKey(self.ptr) };
-        assert!(!ptr.is_null());
-        Self { ptr }
-    }
-}
-
 impl std::fmt::Debug for SymKey {
     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
         if let Ok(b) = self.as_bytes() {
             write!(f, "SymKey {}", hex(b))
         } else {
             write!(f, "Opaque SymKey")
         }
     }
 }
 
 /// Generate a randomized buffer.
-#[must_use]
-pub fn random(size: usize) -> Vec<u8> {
+pub fn random(size: usize) -> Res<Vec<u8>> {
     let mut buf = vec![0; size];
-    secstatus_to_res(unsafe {
-        PK11_GenerateRandom(buf.as_mut_ptr(), buf.len().try_into().unwrap())
-    })
-    .unwrap();
-    buf
+    secstatus_to_res(unsafe { PK11_GenerateRandom(buf.as_mut_ptr(), buf.len().try_into()?) })?;
+    Ok(buf)
 }
 
 #[cfg(test)]
 mod test {
     use super::random;
     use test_fixture::fixture_init;
 
     #[test]
--- a/third_party/rust/neqo-crypto/src/prio.rs
+++ b/third_party/rust/neqo-crypto/src/prio.rs
@@ -1,16 +1,19 @@
 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![allow(dead_code, non_upper_case_globals, non_snake_case)]
-#![allow(clippy::cognitive_complexity, clippy::empty_enum, clippy::too_many_lines)]
+#![allow(dead_code)]
+#![allow(non_upper_case_globals)]
+#![allow(non_snake_case)]
+#![allow(clippy::cognitive_complexity)]
+#![allow(clippy::empty_enum)]
 
 include!(concat!(env!("OUT_DIR"), "/nspr_io.rs"));
 
 pub enum PRFileInfo {}
 pub enum PRFileInfo64 {}
 pub enum PRFilePrivate {}
 pub enum PRIOVec {}
 pub enum PRSendFileData {}
--- a/third_party/rust/neqo-crypto/src/replay.rs
+++ b/third_party/rust/neqo-crypto/src/replay.rs
@@ -44,20 +44,16 @@ scoped_ptr!(
 #[allow(clippy::module_name_repetitions)]
 pub struct AntiReplay {
     ctx: AntiReplayContext,
 }
 
 impl AntiReplay {
     /// Make a new anti-replay context.
     /// See the documentation in NSS for advice on how to set these values.
-    ///
-    /// # Errors
-    /// Returns an error if `now` is in the past relative to our baseline or
-    /// NSS is unable to generate an anti-replay context.
     pub fn new(now: Instant, window: Duration, k: usize, bits: usize) -> Res<Self> {
         let mut ctx: *mut SSLAntiReplayContext = null_mut();
         unsafe {
             SSL_CreateAntiReplayContext(
                 Time::from(now).try_into()?,
                 Interval::from(window).try_into()?,
                 c_uint::try_from(k)?,
                 c_uint::try_from(bits)?,
--- a/third_party/rust/neqo-crypto/src/result.rs
+++ b/third_party/rust/neqo-crypto/src/result.rs
@@ -85,17 +85,17 @@ mod tests {
                 );
             }
             _ => unreachable!(),
         }
     }
 
     #[test]
     fn is_err_zero_code() {
-    // This code doesn't work without initializing NSS first.
+        // This code doesn't work without initializing NSS first.
         fixture_init();
 
         set_error_code(0);
         let r = result(ssl::SECFailure);
         assert!(r.is_err());
         match r.unwrap_err() {
             Error::NssError { name, code, .. } => {
                 assert_eq!(name, "UNKNOWN_ERROR");
--- a/third_party/rust/neqo-crypto/src/secrets.rs
+++ b/third_party/rust/neqo-crypto/src/secrets.rs
@@ -1,23 +1,22 @@
 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use crate::agentio::as_c_void;
 use crate::constants::*;
 use crate::err::Res;
 use crate::p11::{PK11SymKey, PK11_ReferenceSymKey, SymKey};
 use crate::ssl::{PRFileDesc, SSLSecretCallback, SSLSecretDirection};
 
 use neqo_common::qdebug;
+use std::ops::Deref;
 use std::os::raw::c_void;
-use std::pin::Pin;
 use std::ptr::NonNull;
 
 experimental_api!(SSL_SecretCallback(
     fd: *mut PRFileDesc,
     cb: SSLSecretCallback,
     arg: *mut c_void,
 ));
 
@@ -41,29 +40,29 @@ impl From<SSLSecretDirection::Type> for 
 #[derive(Debug, Default)]
 #[allow(clippy::module_name_repetitions)]
 pub struct DirectionalSecrets {
     // We only need to maintain 3 secrets for the epochs used during the handshake.
     secrets: [Option<SymKey>; 3],
 }
 
 impl DirectionalSecrets {
-    fn put(&mut self, epoch: Epoch, key: SymKey) {
+    pub fn put(&mut self, epoch: Epoch, key: SymKey) {
         assert!(epoch > 0);
         let i = (epoch - 1) as usize;
         assert!(i < self.secrets.len());
         // assert!(self.secrets[i].is_none());
         self.secrets[i] = Some(key);
     }
 
-    pub fn take(&mut self, epoch: Epoch) -> Option<SymKey> {
+    pub fn get(&self, epoch: Epoch) -> Option<&SymKey> {
         assert!(epoch > 0);
         let i = (epoch - 1) as usize;
         assert!(i < self.secrets.len());
-        self.secrets[i].take()
+        self.secrets[i].as_ref()
     }
 }
 
 #[derive(Debug, Default)]
 pub struct Secrets {
     r: DirectionalSecrets,
     w: DirectionalSecrets,
 }
@@ -82,50 +81,49 @@ impl Secrets {
     }
 
     fn put_raw(&mut self, epoch: Epoch, dir: SSLSecretDirection::Type, key_ptr: *mut PK11SymKey) {
         let key_ptr = unsafe { PK11_ReferenceSymKey(key_ptr) };
         let key = match NonNull::new(key_ptr) {
             None => panic!("NSS shouldn't be passing out NULL secrets"),
             Some(p) => SymKey::new(p),
         };
-        self.put(SecretDirection::from(dir), epoch, key);
+        self.put(dir.into(), epoch, key);
     }
 
-    fn put(&mut self, dir: SecretDirection, epoch: Epoch, key: SymKey) {
+    pub fn put(&mut self, dir: SecretDirection, epoch: Epoch, key: SymKey) {
         qdebug!("{:?} secret available for {:?}", dir, epoch);
         let keys = match dir {
             SecretDirection::Read => &mut self.r,
             SecretDirection::Write => &mut self.w,
         };
         keys.put(epoch, key);
     }
+
+    pub fn read(&self) -> &DirectionalSecrets {
+        &self.r
+    }
+
+    pub fn write(&self) -> &DirectionalSecrets {
+        &self.w
+    }
 }
 
-#[derive(Debug)]
+#[derive(Debug, Default)]
 pub struct SecretHolder {
-    secrets: Pin<Box<Secrets>>,
+    secrets: Box<Secrets>,
 }
 
 impl SecretHolder {
     /// This registers with NSS.  The lifetime of this object needs to match the lifetime
     /// of the connection, or bad things might happen.
     pub fn register(&mut self, fd: *mut PRFileDesc) -> Res<()> {
-        let p = as_c_void(&mut self.secrets);
-        unsafe { SSL_SecretCallback(fd, Some(Secrets::secret_available), p) }
-    }
-
-    pub fn take_read(&mut self, epoch: Epoch) -> Option<SymKey> {
-        self.secrets.r.take(epoch)
-    }
-
-    pub fn take_write(&mut self, epoch: Epoch) -> Option<SymKey> {
-        self.secrets.w.take(epoch)
+        let p = &*self.secrets as *const Secrets as *const c_void;
+        unsafe { SSL_SecretCallback(fd, Some(Secrets::secret_available), p as *mut c_void) }
     }
 }
 
-impl Default for SecretHolder {
-    fn default() -> Self {
-        Self {
-            secrets: Box::pin(Secrets::default()),
-        }
+impl Deref for SecretHolder {
+    type Target = Secrets;
+    fn deref(&self) -> &Self::Target {
+        self.secrets.as_ref()
     }
 }
--- a/third_party/rust/neqo-crypto/src/selfencrypt.rs
+++ b/third_party/rust/neqo-crypto/src/selfencrypt.rs
@@ -22,20 +22,19 @@ pub struct SelfEncrypt {
     key: SymKey,
     old_key: Option<SymKey>,
 }
 
 impl SelfEncrypt {
     const VERSION: u8 = 1;
     const SALT_LENGTH: usize = 16;
 
-    /// # Errors
-    /// Failure to generate a new HKDF key using NSS results in an error.
     pub fn new(version: Version, cipher: Cipher) -> Res<Self> {
-        let key = hkdf::generate_key(version, cipher)?;
+        let sz = hkdf::key_size(version, cipher)?;
+        let key = hkdf::generate_key(version, cipher, sz)?;
         Ok(Self {
             version,
             cipher,
             key_id: 0,
             key,
             old_key: None,
         })
     }
@@ -43,46 +42,41 @@ impl SelfEncrypt {
     fn make_aead(&self, k: &SymKey, salt: &[u8]) -> Res<Aead> {
         debug_assert_eq!(salt.len(), Self::SALT_LENGTH);
         let salt = hkdf::import_key(self.version, self.cipher, salt)?;
         let secret = hkdf::extract(self.version, self.cipher, Some(&salt), k)?;
         Aead::new(self.version, self.cipher, &secret, "neqo self")
     }
 
     /// Rotate keys.  This causes any previous key that is being held to be replaced by the current key.
-    ///
-    /// # Errors
-    /// Failure to generate a new HKDF key using NSS results in an error.
     pub fn rotate(&mut self) -> Res<()> {
-        let new_key = hkdf::generate_key(self.version, self.cipher)?;
+        let sz = hkdf::key_size(self.version, self.cipher)?;
+        let new_key = hkdf::generate_key(self.version, self.cipher, sz)?;
         self.old_key = Some(mem::replace(&mut self.key, new_key));
         let (kid, _) = self.key_id.overflowing_add(1);
         self.key_id = kid;
         qinfo!(["SelfEncrypt"], "Rotated keys to {}", self.key_id);
         Ok(())
     }
 
     /// Seal an item using the underlying key.  This produces a single buffer that contains
     /// the encrypted `plaintext`, plus a version number and salt.
     /// `aad` is only used as input to the AEAD, it is not included in the output; the
     /// caller is responsible for carrying the AAD as appropriate.
-    ///
-    /// # Errors
-    /// Failure to protect using NSS AEAD APIs produces an error.
     #[allow(clippy::similar_names)] // aad is similar to aead
     pub fn seal(&self, aad: &[u8], plaintext: &[u8]) -> Res<Vec<u8>> {
         // Format is:
         // struct {
         //   uint8 version;
         //   uint8 key_id;
         //   uint8 salt[16];
         //   opaque aead_encrypted(plaintext)[length as expanded];
         // };
         // AAD covers the entire header, plus the value of the AAD parameter that is provided.
-        let salt = random(Self::SALT_LENGTH);
+        let salt = random(Self::SALT_LENGTH)?;
         let aead = self.make_aead(&self.key, &salt)?;
         let encoded_len = 2 + salt.len() + plaintext.len() + aead.expansion();
 
         let mut enc = Encoder::with_capacity(encoded_len);
         enc.encode_byte(Self::VERSION);
         enc.encode_byte(self.key_id);
         enc.encode(&salt);
 
@@ -112,20 +106,16 @@ impl SelfEncrypt {
                 self.old_key.as_ref()
             } else {
                 None
             }
         }
     }
 
     /// Open the protected `ciphertext`.
-    ///
-    /// # Errors
-    /// Returns an error when the self-encrypted object is invalid;
-    /// when the keys have been rotated; or when NSS fails.
     #[allow(clippy::similar_names)] // aad is similar to aead
     pub fn open(&self, aad: &[u8], ciphertext: &[u8]) -> Res<Vec<u8>> {
         if ciphertext[0] != Self::VERSION {
             return Err(Error::SelfEncryptFailure);
         }
         let key = if let Some(k) = self.select_key(ciphertext[1]) {
             k
         } else {
--- a/third_party/rust/neqo-crypto/src/ssl.rs
+++ b/third_party/rust/neqo-crypto/src/ssl.rs
@@ -1,16 +1,18 @@
 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![allow(dead_code, non_upper_case_globals, non_snake_case)]
-#![allow(clippy::cognitive_complexity, clippy::too_many_lines)]
+#![allow(dead_code)]
+#![allow(non_upper_case_globals)]
+#![allow(non_snake_case)]
+#![allow(clippy::cognitive_complexity)]
 
 use crate::constants::*;
 
 use std::os::raw::{c_uint, c_void};
 
 include!(concat!(env!("OUT_DIR"), "/nss_ssl.rs"));
 mod SSLOption {
     include!(concat!(env!("OUT_DIR"), "/nss_sslopt.rs"));
--- a/third_party/rust/neqo-crypto/tests/aead.rs
+++ b/third_party/rust/neqo-crypto/tests/aead.rs
@@ -1,10 +1,9 @@
 #![cfg_attr(feature = "deny-warnings", deny(warnings))]
-#![warn(clippy::pedantic)]
 
 use neqo_crypto::aead::Aead;
 use neqo_crypto::constants::*;
 use neqo_crypto::hkdf;
 use test_fixture::fixture_init;
 
 const AAD: &[u8] = &[
     0xc1, 0xff, 0x00, 0x00, 0x12, 0x05, 0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, 0xb5, 0x00, 0x40,
--- a/third_party/rust/neqo-crypto/tests/agent.rs
+++ b/third_party/rust/neqo-crypto/tests/agent.rs
@@ -1,10 +1,9 @@
 #![cfg_attr(feature = "deny-warnings", deny(warnings))]
-#![warn(clippy::pedantic)]
 
 use neqo_crypto::*;
 
 use std::boxed::Box;
 
 mod handshake;
 use crate::handshake::*;
 use test_fixture::{fixture_init, now};
@@ -44,41 +43,41 @@ fn basic() {
     assert_eq!(*client.state(), HandshakeState::AuthenticationPending);
 
     client.authenticated(AuthenticationStatus::Ok);
     assert_eq!(*client.state(), HandshakeState::Authenticated(0));
 
     // Calling handshake() again indicates that we're happy with the cert.
     let bytes = client.handshake(now(), &[]).expect("send CF");
     assert!(!bytes.is_empty());
-    assert!(client.state().is_connected());
+    assert!(client.state().connected());
 
     let client_info = client.info().expect("got info");
     assert_eq!(TLS_VERSION_1_3, client_info.version());
     assert_eq!(TLS_AES_128_GCM_SHA256, client_info.cipher_suite());
 
     let bytes = server.handshake(now(), &bytes[..]).expect("finish");
     assert!(bytes.is_empty());
-    assert!(server.state().is_connected());
+    assert!(server.state().connected());
 
     let server_info = server.info().expect("got info");
     assert_eq!(TLS_VERSION_1_3, server_info.version());
     assert_eq!(TLS_AES_128_GCM_SHA256, server_info.cipher_suite());
 }
 
-fn check_client_preinfo(client_preinfo: &SecretAgentPreInfo) {
+fn check_client_preinfo(client_preinfo: SecretAgentPreInfo) {
     assert_eq!(client_preinfo.version(), None);
     assert_eq!(client_preinfo.cipher_suite(), None);
     assert_eq!(client_preinfo.early_data(), false);
     assert_eq!(client_preinfo.early_data_cipher(), None);
     assert_eq!(client_preinfo.max_early_data(), 0);
     assert_eq!(client_preinfo.alpn(), None);
 }
 
-fn check_server_preinfo(server_preinfo: &SecretAgentPreInfo) {
+fn check_server_preinfo(server_preinfo: SecretAgentPreInfo) {
     assert_eq!(server_preinfo.version(), Some(TLS_VERSION_1_3));
     assert_eq!(server_preinfo.cipher_suite(), Some(TLS_AES_128_GCM_SHA256));
     assert_eq!(server_preinfo.early_data(), false);
     assert_eq!(server_preinfo.early_data_cipher(), None);
     assert_eq!(server_preinfo.max_early_data(), 0);
     assert_eq!(server_preinfo.alpn(), None);
 }
 
@@ -89,40 +88,40 @@ fn raw() {
     println!("client {:?}", client);
     let mut server = Server::new(&["key"]).expect("should create server");
     println!("server {:?}", server);
 
     let client_records = client.handshake_raw(now(), None).expect("send CH");
     assert!(!client_records.is_empty());
     assert_eq!(*client.state(), HandshakeState::InProgress);
 
-    check_client_preinfo(&client.preinfo().expect("get preinfo"));
+    check_client_preinfo(client.preinfo().expect("get preinfo"));
 
     let server_records =
         forward_records(now(), &mut server, client_records).expect("read CH, send SH");
     assert!(!server_records.is_empty());
     assert_eq!(*server.state(), HandshakeState::InProgress);
 
-    check_server_preinfo(&server.preinfo().expect("get preinfo"));
+    check_server_preinfo(server.preinfo().expect("get preinfo"));
 
     let client_records = forward_records(now(), &mut client, server_records).expect("send CF");
     assert!(client_records.is_empty());
     assert_eq!(*client.state(), HandshakeState::AuthenticationPending);
 
     client.authenticated(AuthenticationStatus::Ok);
     assert_eq!(*client.state(), HandshakeState::Authenticated(0));
 
     // Calling handshake() again indicates that we're happy with the cert.
     let client_records = client.handshake_raw(now(), None).expect("send CF");
     assert!(!client_records.is_empty());
-    assert!(client.state().is_connected());
+    assert!(client.state().connected());
 
     let server_records = forward_records(now(), &mut server, client_records).expect("finish");
     assert!(server_records.is_empty());
-    assert!(server.state().is_connected());
+    assert!(server.state().connected());
 
     // The client should have one certificate for the server.
     let mut certs = client.peer_certificate().unwrap();
     let cert_vec: Vec<&[u8]> = certs.collect();
     assert_eq!(1, cert_vec.len());
 
     // The server shouldn't have a client certificate.
     assert!(server.peer_certificate().is_none());
@@ -349,26 +348,8 @@ fn reject_zero_rtt() {
             Box::new(RejectZeroRtt {}),
         )
         .expect("should enable 0-RTT");
 
     connect(&mut client, &mut server);
     assert!(!client.info().unwrap().early_data_accepted());
     assert!(!server.info().unwrap().early_data_accepted());
 }
-
-#[test]
-fn close() {
-    let mut client = Client::new("server.example").expect("should create client");
-    let mut server = Server::new(&["key"]).expect("should create server");
-    connect(&mut client, &mut server);
-    client.close();
-    server.close();
-}
-
-#[test]
-fn close_client_twice() {
-    let mut client = Client::new("server.example").expect("should create client");
-    let mut server = Server::new(&["key"]).expect("should create server");
-    connect(&mut client, &mut server);
-    client.close();
-    client.close(); // Should be a noop.
-}
--- a/third_party/rust/neqo-crypto/tests/ext.rs
+++ b/third_party/rust/neqo-crypto/tests/ext.rs
@@ -1,10 +1,9 @@
 #![cfg_attr(feature = "deny-warnings", deny(warnings))]
-#![warn(clippy::pedantic)]
 
 use neqo_crypto::*;
 use std::cell::RefCell;
 use std::rc::Rc;
 use test_fixture::fixture_init;
 
 mod handshake;
 use crate::handshake::*;
@@ -55,20 +54,20 @@ impl ExtensionHandler for SimpleExtensio
     }
 
     fn handle(&mut self, msg: HandshakeMessage, d: &[u8]) -> ExtensionHandlerResult {
         match msg {
             TLS_HS_CLIENT_HELLO | TLS_HS_ENCRYPTED_EXTENSIONS => {
                 self.handled = true;
                 if d.len() != 1 {
                     ExtensionHandlerResult::Alert(50) // decode_error
-                } else if d[0] == 77 {
+                } else if d[0] != 77 {
+                    ExtensionHandlerResult::Alert(47) // illegal_parameter
+                } else {
                     ExtensionHandlerResult::Ok
-                } else {
-                    ExtensionHandlerResult::Alert(47) // illegal_parameter
                 }
             }
             _ => ExtensionHandlerResult::Alert(110), // unsupported_extension
         }
     }
 }
 
 #[test]
--- a/third_party/rust/neqo-crypto/tests/handshake.rs
+++ b/third_party/rust/neqo-crypto/tests/handshake.rs
@@ -11,64 +11,68 @@ pub fn forward_records(
     agent: &mut SecretAgent,
     records_in: RecordList,
 ) -> Res<RecordList> {
     let mut expected_state = match agent.state() {
         HandshakeState::New => HandshakeState::New,
         _ => HandshakeState::InProgress,
     };
     let mut records_out = RecordList::default();
-    for record in records_in {
+    for record in records_in.into_iter() {
         assert_eq!(records_out.len(), 0);
         assert_eq!(*agent.state(), expected_state);
 
         records_out = agent.handshake_raw(now, Some(record))?;
         expected_state = HandshakeState::InProgress;
     }
     Ok(records_out)
 }
 
 fn handshake(now: Instant, client: &mut SecretAgent, server: &mut SecretAgent) {
     let mut a = client;
     let mut b = server;
     let mut records = a.handshake_raw(now, None).unwrap();
-    let is_done = |agent: &mut SecretAgent| agent.state().is_final();
+    let is_done = |agent: &mut SecretAgent| match *agent.state() {
+        HandshakeState::Complete(_) | HandshakeState::Failed(_) => true,
+        _ => false,
+    };
     while !is_done(b) {
-        records = if let Ok(r) = forward_records(now, &mut b, records) {
-            r
-        } else {
-            // TODO(mt) take the alert generated by the failed handshake
-            // and allow it to be sent to the peer.
-            return;
+        records = match forward_records(now, &mut b, records) {
+            Ok(r) => r,
+            _ => {
+                // TODO(mt) take the alert generated by the failed handshake
+                // and allow it to be sent to the peer.
+                return;
+            }
         };
 
         if *b.state() == HandshakeState::AuthenticationPending {
             b.authenticated(AuthenticationStatus::Ok);
             records = b.handshake_raw(now, None).unwrap();
         }
         mem::swap(&mut a, &mut b);
     }
 }
 
 pub fn connect_at(now: Instant, client: &mut SecretAgent, server: &mut SecretAgent) {
     handshake(now, client, server);
     qinfo!("client: {:?}", client.state());
     qinfo!("server: {:?}", server.state());
-    assert!(client.state().is_connected());
-    assert!(server.state().is_connected());
+    assert!(client.state().connected());
+    assert!(server.state().connected());
 }
 
 pub fn connect(client: &mut SecretAgent, server: &mut SecretAgent) {
     connect_at(now(), client, server);
 }
 
 pub fn connect_fail(client: &mut SecretAgent, server: &mut SecretAgent) {
     handshake(now(), client, server);
-    assert!(!client.state().is_connected());
-    assert!(!server.state().is_connected());
+    assert!(!client.state().connected());
+    assert!(!server.state().connected());
 }
 
 #[derive(Clone, Copy, Debug)]
 pub enum Resumption {
     WithoutZeroRtt,
     WithZeroRtt,
 }
 
@@ -76,17 +80,17 @@ pub const ZERO_RTT_TOKEN_DATA: &[u8] = b
 
 #[derive(Debug)]
 pub struct PermissiveZeroRttChecker {
     resuming: bool,
 }
 
 impl Default for PermissiveZeroRttChecker {
     fn default() -> Self {
-        Self { resuming: true }
+        PermissiveZeroRttChecker { resuming: true }
     }
 }
 
 impl ZeroRttChecker for PermissiveZeroRttChecker {
     fn check(&self, token: &[u8]) -> ZeroRttCheckResult {
         if self.resuming {
             assert_eq!(ZERO_RTT_TOKEN_DATA, token);
         } else {
--- a/third_party/rust/neqo-crypto/tests/hkdf.rs
+++ b/third_party/rust/neqo-crypto/tests/hkdf.rs
@@ -1,10 +1,9 @@
 #![cfg_attr(feature = "deny-warnings", deny(warnings))]
-#![warn(clippy::pedantic)]
 
 use neqo_crypto::constants::*;
 use neqo_crypto::{hkdf, SymKey};
 use test_fixture::fixture_init;
 
 const SALT: &[u8] = &[
     0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
     0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
--- a/third_party/rust/neqo-crypto/tests/hp.rs
+++ b/third_party/rust/neqo-crypto/tests/hp.rs
@@ -1,59 +1,55 @@
 #![cfg_attr(feature = "deny-warnings", deny(warnings))]
-#![warn(clippy::pedantic)]
 
 use neqo_crypto::constants::*;
 use neqo_crypto::hkdf;
 use neqo_crypto::hp::HpKey;
 use test_fixture::fixture_init;
 
 fn make_hp(cipher: Cipher) -> HpKey {
     let ikm = hkdf::import_key(TLS_VERSION_1_3, cipher, &[0; 16]).expect("import IKM");
     let prk = hkdf::extract(TLS_VERSION_1_3, cipher, None, &ikm).expect("extract works");
     HpKey::extract(TLS_VERSION_1_3, cipher, &prk, "hp").expect("extract label works")
 }
 
 #[test]
 fn aes128() {
+    fixture_init();
+    let mask = make_hp(TLS_AES_128_GCM_SHA256)
+        .mask(&[0; 16])
+        .expect("should produce a mask");
     const EXPECTED: &[u8] = &[
         0x04, 0x7b, 0xda, 0x65, 0xc3, 0x41, 0xcf, 0xbc, 0x5d, 0xe1, 0x75, 0x2b, 0x9d, 0x7d, 0xc3,
         0x14,
     ];
-
-    fixture_init();
-    let mask = make_hp(TLS_AES_128_GCM_SHA256)
-        .mask(&[0; 16])
-        .expect("should produce a mask");
     assert_eq!(mask, EXPECTED);
 }
 
 #[test]
 fn aes256() {
+    fixture_init();
+    let mask = make_hp(TLS_AES_256_GCM_SHA384)
+        .mask(&[0; 16])
+        .expect("should produce a mask");
     const EXPECTED: &[u8] = &[
         0xb5, 0xea, 0xa2, 0x1c, 0x25, 0x77, 0x48, 0x18, 0xbf, 0x25, 0xea, 0xfa, 0xbd, 0x8d, 0x80,
         0x2b,
     ];
-
-    fixture_init();
-    let mask = make_hp(TLS_AES_256_GCM_SHA384)
-        .mask(&[0; 16])
-        .expect("should produce a mask");
     assert_eq!(mask, EXPECTED);
 }
 
 #[cfg(feature = "chacha")]
 #[test]
 fn chacha20_ctr() {
+    fixture_init();
+    let mask = make_hp(TLS_CHACHA20_POLY1305_SHA256)
+        .mask(&[0; 16])
+        .expect("should produce a mask");
     const EXPECTED: &[u8] = &[
         0x34, 0x11, 0xb3, 0x53, 0x02, 0x0b, 0x16, 0xda, 0x0a, 0x85, 0x5a, 0x52, 0x0d, 0x06, 0x07,
         0x1f, 0x4a, 0xb1, 0xaf, 0xf7, 0x83, 0xa8, 0xf0, 0x29, 0xc3, 0x19, 0xef, 0x57, 0x48, 0xe7,
         0x8e, 0x3e, 0x11, 0x91, 0xe1, 0xd5, 0x92, 0x8f, 0x61, 0x6d, 0x3f, 0x3d, 0x76, 0xb5, 0x29,
         0xf1, 0x62, 0x2f, 0x1e, 0xad, 0xdd, 0x23, 0x59, 0x45, 0xac, 0xd2, 0x19, 0x8a, 0xb4, 0x1f,
         0x2f, 0x52, 0x46, 0x89,
     ];
-
-    fixture_init();
-    let mask = make_hp(TLS_CHACHA20_POLY1305_SHA256)
-        .mask(&[0; 16])
-        .expect("should produce a mask");
     assert_eq!(mask, EXPECTED);
 }
--- a/third_party/rust/neqo-crypto/tests/init.rs
+++ b/third_party/rust/neqo-crypto/tests/init.rs
@@ -1,25 +1,23 @@
 #![cfg_attr(feature = "deny-warnings", deny(warnings))]
-#![warn(clippy::pedantic)]
 
 // This uses external interfaces to neqo_crypto rather than being a module
 // inside of lib.rs. Because all other code uses the test_fixture module,
 // they will be calling into the public version of init_db().  Calling into
 // the version exposed to an inner module in lib.rs would result in calling
 // a different version of init_db.  That causes explosions as they get
 // different versions of the Once instance they use and they initialize NSS
 // twice, probably likely in parallel.  That doesn't work out well.
 use neqo_crypto::*;
 
 // Pull in the NSS internals so that we can ask NSS if it thinks that
 // it is properly initialized.
-#[allow(dead_code, non_upper_case_globals)]
-#[allow(clippy::redundant_static_lifetimes, clippy::unseparated_literal_suffix)]
 mod nss {
+    #![allow(clippy::redundant_static_lifetimes, dead_code, non_upper_case_globals)]
     include!(concat!(env!("OUT_DIR"), "/nss_init.rs"));
 }
 
 #[cfg(nss_nodb)]
 #[test]
 fn init_nodb() {
     init();
     assert_initialized();
--- a/third_party/rust/neqo-crypto/tests/selfencrypt.rs
+++ b/third_party/rust/neqo-crypto/tests/selfencrypt.rs
@@ -1,10 +1,9 @@
 #![cfg_attr(feature = "deny-warnings", deny(warnings))]
-#![warn(clippy::pedantic)]
 
 use neqo_crypto::constants::*;
 use neqo_crypto::{init, selfencrypt::SelfEncrypt, Error};
 
 #[test]
 fn se_create() {
     init();
     SelfEncrypt::new(TLS_VERSION_1_3, TLS_AES_128_GCM_SHA256).expect("constructor works");
--- a/third_party/rust/neqo-http3/.cargo-checksum.json
+++ b/third_party/rust/neqo-http3/.cargo-checksum.json
@@ -1,1 +1,1 @@
-{"files":{"Cargo.toml":"78607779e3698b42d8f05f088fd53cb549d8c30c09f7d3d2ab6dfe2b272ecedf","src/client_events.rs":"8e77e6e92c3d5933621f2baee3baacab230486ad8b6df1eca321ea74ed7cdcbd","src/connection.rs":"8499ea115fc061eb5d2eedb0a5cac6069a255ad756e6e89ce2f6e6a8dc5772fc","src/connection_client.rs":"2c8e7ffc5b67defef0e2a43b0053d1044b2ce4e83cadf1d3ee4d1e313cec35ff","src/connection_server.rs":"a32edf220f4664dccfc80141128a211d9997d86e8e988726c1380836015f1d0e","src/control_stream_local.rs":"319f8277fc4765b31a4a094bfd663e681d71831532925b0043bae5da96202e64","src/control_stream_remote.rs":"c205633af8539cd55f289071c6845b5bb2b0a9778f15976829c5d4a492360e19","src/hframe.rs":"8733c3af83da5ddbc6aa238710662fdba7f790bf266d242b40d727f68315d1cc","src/hsettings_frame.rs":"349a4413ce13f03e05264e6c4b22d231276a1c96e3983aada4478b038ec89dbc","src/lib.rs":"f47849cc5f47945c95aa58a9ed830ff512572512f0b3a7ddb6b545e9e16e08bf","src/server.rs":"212b98c363c0160304eaf02bb73dad6138236f52390ab7664ce4984657fdcca3","src/server_connection_events.rs":"d2b973a095f29cb0ac6fb84705165b034960d09b2dde7693bab96e6b802c6fba","src/server_events.rs":"f997bd329d45115f6a527eba8f0f1ecf21c0dd9a3184f08fc5002e34f4cfe2f0","src/stream_type_reader.rs":"da2b7b0358cb4829493cb964cae67c85e9efdf4127958aade7a56733ddc4f12e","src/transaction_client.rs":"65f0cea42843ad9057f587d6ef0a1751f46fe13db468904434ebaeb27f763a84","src/transaction_server.rs":"8603c3f835f680e2c63c1ed1b5962b208acd476a12e4bb7221d68b36c57c505a","tests/httpconn.rs":"7955f6ac4406b5d770e0fb10258aff529a1c01020374dfc5f85d8608abb68f6f"},"package":null}
\ No newline at end of file
+{"files":{"Cargo.toml":"a6bb6d8f914fc1199d18189cd7d83a1f2e54b9bca7ef40408fe7297827f81ea6","src/client_events.rs":"8e77e6e92c3d5933621f2baee3baacab230486ad8b6df1eca321ea74ed7cdcbd","src/connection.rs":"b5a34c2e519b9cfd931fa6305c9e85a13c5a10981a8b98a6c81b56efe8da7221","src/connection_client.rs":"c056e209d9ec306695152c31d43beeaf6c407511db89180a5c828c454339ce51","src/connection_server.rs":"eb5a11935a1d8ce04f29907c3146aca1dae1a2a0d44025d4a82d21bdddce72b7","src/control_stream_local.rs":"319f8277fc4765b31a4a094bfd663e681d71831532925b0043bae5da96202e64","src/control_stream_remote.rs":"1b96316d6eecc582436382517fcffdb2bb4f9d73194301bc3e2e253d3322e95e","src/hframe.rs":"5f6508d473b6cb5a6539be7db5e5f9f5ee8ee72bf166a351781a3990b67afda5","src/hsettings_frame.rs":"61bc427ece16ed7aa9d7040b0fb8f67d596c40c1c47f3711ed317bc973bfbc6e","src/lib.rs":"2c5b25a8aa7f1476b7ca5223d926a45afe1f8ca7d956221bdd924849d7c8e564","src/server.rs":"c4004faf8968c43fcb83387ea305b7c1a06ba5236f6f5b3cfdfd6e2f5542da04","src/server_connection_events.rs":"d2b973a095f29cb0ac6fb84705165b034960d09b2dde7693bab96e6b802c6fba","src/server_events.rs":"0ff69865aadede05aa6d9917b0ccbe2e44558a829a708d7cd58014887a099ad8","src/stream_type_reader.rs":"be1ea1f553292b5447f0d6d89bdfa73732189db236ce34b4067cda0276229540","src/transaction_client.rs":"9f11285df4f480b5ecbf1d5ab60d6aff5b62ee608f079f06ea3bd43e8f144d45","src/transaction_server.rs":"f4daf0389b437e9081bc30db8e25c56e8b9e2e2423465b7986cc3629f5cdc7b3","tests/httpconn.rs":"7955f6ac4406b5d770e0fb10258aff529a1c01020374dfc5f85d8608abb68f6f"},"package":null}
\ No newline at end of file
--- a/third_party/rust/neqo-http3/Cargo.toml
+++ b/third_party/rust/neqo-http3/Cargo.toml
@@ -1,11 +1,11 @@
 [package]
 name = "neqo-http3"
-version = "0.1.14"
+version = "0.1.12"
 authors = ["Dragana Damjanovic <dragana.damjano@gmail.com>"]
 edition = "2018"
 license = "MIT/Apache-2.0"
 
 [dependencies]
 neqo-common = { path = "./../neqo-common" }
 neqo-crypto = { path = "./../neqo-crypto" }
 neqo-transport = { path = "./../neqo-transport" }
--- a/third_party/rust/neqo-http3/src/connection.rs
+++ b/third_party/rust/neqo-http3/src/connection.rs
@@ -80,17 +80,17 @@ impl<T: Http3Transaction> ::std::fmt::Di
     }
 }
 
 impl<T: Http3Transaction> Http3Connection<T> {
     pub fn new(max_table_size: u32, max_blocked_streams: u16) -> Self {
         if max_table_size > (1 << 30) - 1 {
             panic!("Wrong max_table_size");
         }
-        Self {
+        Http3Connection {
             state: Http3State::Initializing,
             local_settings: LocalSettings {
                 max_table_size,
                 max_blocked_streams,
             },
             control_stream_local: ControlStreamLocal::default(),
             control_stream_remote: ControlStreamRemote::new(),
             new_streams: HashMap::new(),
@@ -336,17 +336,16 @@ impl<T: Http3Transaction> Http3Connectio
             self.transactions.remove(&stream_id);
             Ok(true)
         } else {
             Ok(false)
         }
     }
 
     pub fn handle_state_change(&mut self, conn: &mut Connection, state: &State) -> Res<bool> {
-        qdebug!([self], "Handle state change {:?}", state);
         match state {
             State::Connected => {
                 debug_assert!(matches!(
                     self.state,
                     Http3State::Initializing | Http3State::ZeroRtt
                 ));
                 if self.state == Http3State::Initializing {
                     self.initialize_http3_connection(conn)?;
@@ -359,17 +358,17 @@ impl<T: Http3Transaction> Http3Connectio
                     self.state = Http3State::Closing(error.clone().into());
                     Ok(true)
                 } else {
                     Ok(false)
                 }
             }
             State::Closed(error) => {
                 if !matches!(self.state, Http3State::Closed(_)) {
-                    self.state = Http3State::Closed(error.clone().into());
+                    self.state = Http3State::Closing(error.clone().into());
                     Ok(true)
                 } else {
                     Ok(false)
                 }
             }
             _ => Ok(false),
         }
     }
--- a/third_party/rust/neqo-http3/src/connection_client.rs
+++ b/third_party/rust/neqo-http3/src/connection_client.rs
@@ -39,25 +39,25 @@ impl Http3Client {
         server_name: &str,
         protocols: &[impl AsRef<str>],
         cid_manager: Rc<RefCell<dyn ConnectionIdManager>>,
         local_addr: SocketAddr,
         remote_addr: SocketAddr,
         max_table_size: u32,
         max_blocked_streams: u16,
     ) -> Res<Self> {
-        Ok(Self::new_with_conn(
+        Ok(Http3Client::new_with_conn(
             Connection::new_client(server_name, protocols, cid_manager, local_addr, remote_addr)?,
             max_table_size,
             max_blocked_streams,
         ))
     }
 
     pub fn new_with_conn(c: Connection, max_table_size: u32, max_blocked_streams: u16) -> Self {
-        Self {
+        Http3Client {
             conn: c,
             base_handler: Http3Connection::new(max_table_size, max_blocked_streams),
             events: Http3ClientEvents::default(),
         }
     }
 
     pub fn role(&self) -> Role {
         self.conn.role()
@@ -179,17 +179,16 @@ impl Http3Client {
         let transaction = self
             .base_handler
             .transactions
             .get_mut(&stream_id)
             .ok_or(Error::InvalidStreamId)?;
         match transaction.read_response_headers() {
             Ok((headers, fin)) => {
                 if transaction.done() {
-                    qinfo!([self], "read_response_headers transaction done");
                     self.base_handler.transactions.remove(&stream_id);
                 }
                 Ok((headers, fin))
             }
             Err(e) => Err(e),
         }
     }
 
@@ -563,17 +562,17 @@ mod tests {
         client.authenticated(AuthenticationStatus::Ok, now());
 
         let out = client.process(out.dgram(), now());
         let connected = |e| matches!(e, Http3ClientEvent::StateChange(Http3State::Connected));
         assert!(client.events().any(connected));
 
         assert_eq!(client.state(), Http3State::Connected);
         let _ = server.conn.process(out.dgram(), now());
-        assert!(server.conn.state().connected());
+        assert_eq!(*server.conn.state(), State::Connected);
     }
 
     // Perform only Quic transport handshake.
     fn connect_only_transport() -> (Http3Client, TestServer) {
         let mut client = default_http3_client();
         let mut server = make_default_server();
         connect_only_transport_with(&mut client, &mut server);
         (client, server)
@@ -2478,17 +2477,17 @@ mod tests {
             false,
         );
 
         assert_eq!(*server.conn.state(), State::Handshaking);
         let out = client.process(out.dgram(), now());
         assert_eq!(client.state(), Http3State::Connected);
 
         let _out = server.conn.process(out.dgram(), now());
-        assert!(server.conn.state().connected());
+        assert_eq!(*server.conn.state(), State::Connected);
 
         assert!(client.tls_info().unwrap().resumed());
         assert!(server.conn.tls_info().unwrap().resumed());
     }
 
     #[test]
     fn zero_rtt_send_request() {
         let (mut client, mut server) = start_with_0rtt();
@@ -2511,17 +2510,17 @@ mod tests {
             ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION,
             true,
         );
 
         assert_eq!(*server.conn.state(), State::Handshaking);
         let out = client.process(out.dgram(), now());
         assert_eq!(client.state(), Http3State::Connected);
         let out = server.conn.process(out.dgram(), now());
-        assert!(server.conn.state().connected());
+        assert_eq!(*server.conn.state(), State::Connected);
         let out = client.process(out.dgram(), now());
         assert!(out.as_dgram_ref().is_none());
 
         // After the server has been connected, send a response.
         let res = server.conn.stream_send(request_stream_id, HTTP_RESPONSE_2);
         assert_eq!(res, Ok(HTTP_RESPONSE_2.len()));
         server.conn.stream_close_send(request_stream_id).unwrap();
 
@@ -2603,16 +2602,17 @@ mod tests {
         assert_eq!(request_stream_id, 0);
     }
 
     // Connect to a server, get token and reconnect using 0-rtt. Seerver sends new Settings.
     fn zero_rtt_change_settings(
         original_settings: &[HSetting],
         resumption_settings: &[HSetting],
         expected_client_state: Http3State,
+        expected_server_state: State,
         expected_encoder_stream_data: &[u8],
     ) {
         let mut client = default_http3_client();
         let mut server = make_server(original_settings);
         // Connect and get a token
         connect_with(&mut client, &mut server);
         let token = exchange_token(&mut client, &mut server.conn);
 
@@ -2638,17 +2638,17 @@ mod tests {
             false,
         );
 
         assert_eq!(*server.conn.state(), State::Handshaking);
         let out = client.process(out.dgram(), now());
         assert_eq!(client.state(), Http3State::Connected);
 
         let _out = server.conn.process(out.dgram(), now());
-        assert!(server.conn.state().connected());
+        assert_eq!(*server.conn.state(), State::Connected);
 
         assert!(client.tls_info().unwrap().resumed());
         assert!(server.conn.tls_info().unwrap().resumed());
 
         // Send new settings.
         let control_stream = server.conn.stream_create(StreamType::UniDi).unwrap();
         let mut enc = Encoder::default();
         server.settings.encode(&mut enc);
@@ -2656,17 +2656,17 @@ mod tests {
         assert_eq!(sent.unwrap(), CONTROL_STREAM_TYPE.len());
         sent = server.conn.stream_send(control_stream, &enc);
         assert_eq!(sent.unwrap(), enc.len());
 
         let out = server.conn.process(None, now());
         client.process(out.dgram(), now());
 
         assert_eq!(client.state(), expected_client_state);
-        assert!(server.conn.state().connected());
+        assert_eq!(*server.conn.state(), expected_server_state);
     }
 
     #[test]
     fn zero_rtt_new_server_setting_are_the_same() {
         // Send a new server settings that are the same as the old one.
         zero_rtt_change_settings(
             &[
                 HSetting::new(HSettingType::MaxTableCapacity, 100),
@@ -2674,16 +2674,17 @@ mod tests {
                 HSetting::new(HSettingType::MaxHeaderListSize, 10000),
             ],
             &[
                 HSetting::new(HSettingType::MaxTableCapacity, 100),
                 HSetting::new(HSettingType::BlockedStreams, 100),
                 HSetting::new(HSettingType::MaxHeaderListSize, 10000),
             ],
             Http3State::Connected,
+            State::Connected,
             ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION,
         );
     }
 
     #[test]
     fn zero_rtt_new_server_setting_omit_max_table() {
         // Send a new server settings without MaxTableCapacity
         zero_rtt_change_settings(
@@ -2692,16 +2693,17 @@ mod tests {
                 HSetting::new(HSettingType::BlockedStreams, 100),
                 HSetting::new(HSettingType::MaxHeaderListSize, 10000),
             ],
             &[
                 HSetting::new(HSettingType::BlockedStreams, 100),
                 HSetting::new(HSettingType::MaxHeaderListSize, 10000),
             ],
             Http3State::Closing(CloseError::Application(265)),
+            State::Connected,
             ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION,
         );
     }
 
     #[test]
     fn zero_rtt_new_server_setting_omit_blocked_streams() {
         // Send a new server settings without BlockedStreams
         zero_rtt_change_settings(
@@ -2710,16 +2712,17 @@ mod tests {
                 HSetting::new(HSettingType::BlockedStreams, 100),
                 HSetting::new(HSettingType::MaxHeaderListSize, 10000),
             ],
             &[
                 HSetting::new(HSettingType::MaxTableCapacity, 100),
                 HSetting::new(HSettingType::MaxHeaderListSize, 10000),
             ],
             Http3State::Closing(CloseError::Application(265)),
+            State::Connected,
             ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION,
         );
     }
 
     #[test]
     fn zero_rtt_new_server_setting_omit_header_list_size() {
         // Send a new server settings without MaxHeaderListSize
         zero_rtt_change_settings(
@@ -2728,16 +2731,17 @@ mod tests {
                 HSetting::new(HSettingType::BlockedStreams, 100),
                 HSetting::new(HSettingType::MaxHeaderListSize, 10000),
             ],
             &[
                 HSetting::new(HSettingType::MaxTableCapacity, 100),
                 HSetting::new(HSettingType::BlockedStreams, 100),
             ],
             Http3State::Connected,
+            State::Connected,
             ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION,
         );
     }
 
     #[test]
     fn zero_rtt_new_server_setting_max_table_size_bigger() {
         // Send a new server settings MaxTableCapacity=200
         zero_rtt_change_settings(
@@ -2747,16 +2751,17 @@ mod tests {
                 HSetting::new(HSettingType::MaxHeaderListSize, 10000),
             ],
             &[
                 HSetting::new(HSettingType::MaxTableCapacity, 200),
                 HSetting::new(HSettingType::BlockedStreams, 100),
                 HSetting::new(HSettingType::MaxHeaderListSize, 10000),
             ],
             Http3State::Closing(CloseError::Application(265)),
+            State::Connected,
             ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION,
         );
     }
 
     #[test]
     fn zero_rtt_new_server_setting_max_table_size_smaller() {
         // Send a new server settings MaxTableCapacity=50
         zero_rtt_change_settings(
@@ -2766,16 +2771,17 @@ mod tests {
                 HSetting::new(HSettingType::MaxHeaderListSize, 10000),
             ],
             &[
                 HSetting::new(HSettingType::MaxTableCapacity, 50),
                 HSetting::new(HSettingType::BlockedStreams, 100),
                 HSetting::new(HSettingType::MaxHeaderListSize, 10000),
             ],
             Http3State::Closing(CloseError::Application(265)),
+            State::Connected,
             ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION,
         );
     }
 
     #[test]
     fn zero_rtt_new_server_setting_blocked_streams_bigger() {
         // Send a new server settings withBlockedStreams=200
         zero_rtt_change_settings(
@@ -2785,16 +2791,17 @@ mod tests {
                 HSetting::new(HSettingType::MaxHeaderListSize, 10000),
             ],
             &[
                 HSetting::new(HSettingType::MaxTableCapacity, 100),
                 HSetting::new(HSettingType::BlockedStreams, 200),
                 HSetting::new(HSettingType::MaxHeaderListSize, 10000),
             ],
             Http3State::Connected,
+            State::Connected,
             ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION,
         );
     }
 
     #[test]
     fn zero_rtt_new_server_setting_blocked_streams_smaller() {
         // Send a new server settings withBlockedStreams=50
         zero_rtt_change_settings(
@@ -2804,16 +2811,17 @@ mod tests {
                 HSetting::new(HSettingType::MaxHeaderListSize, 10000),
             ],
             &[
                 HSetting::new(HSettingType::MaxTableCapacity, 100),
                 HSetting::new(HSettingType::BlockedStreams, 50),
                 HSetting::new(HSettingType::MaxHeaderListSize, 10000),
             ],
             Http3State::Closing(CloseError::Application(265)),
+            State::Connected,
             ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION,
         );
     }
 
     #[test]
     fn zero_rtt_new_server_setting_max_header_size_bigger() {
         // Send a new server settings with MaxHeaderListSize=20000
         zero_rtt_change_settings(
@@ -2823,16 +2831,17 @@ mod tests {
                 HSetting::new(HSettingType::MaxHeaderListSize, 10000),
             ],
             &[
                 HSetting::new(HSettingType::MaxTableCapacity, 100),
                 HSetting::new(HSettingType::BlockedStreams, 100),
                 HSetting::new(HSettingType::MaxHeaderListSize, 20000),
             ],
             Http3State::Connected,
+            State::Connected,
             ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION,
         );
     }
 
     #[test]
     fn zero_rtt_new_server_setting_max_headers_size_smaller() {
         // Send the new server settings with MaxHeaderListSize=5000
         zero_rtt_change_settings(
@@ -2842,16 +2851,17 @@ mod tests {
                 HSetting::new(HSettingType::MaxHeaderListSize, 10000),
             ],
             &[
                 HSetting::new(HSettingType::MaxTableCapacity, 100),
                 HSetting::new(HSettingType::BlockedStreams, 100),
                 HSetting::new(HSettingType::MaxHeaderListSize, 5000),
             ],
             Http3State::Closing(CloseError::Application(265)),
+            State::Connected,
             ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION,
         );
     }
 
     #[test]
     fn zero_rtt_max_table_size_first_omitted() {
         // send server original settings without MaxTableCapacity
         // send new server setting with MaxTableCapacity
@@ -2861,16 +2871,17 @@ mod tests {
                 HSetting::new(HSettingType::MaxHeaderListSize, 10000),
             ],
             &[
                 HSetting::new(HSettingType::MaxTableCapacity, 100),
                 HSetting::new(HSettingType::BlockedStreams, 100),
                 HSetting::new(HSettingType::MaxHeaderListSize, 10000),
             ],
             Http3State::Connected,
+            State::Connected,
             ENCODER_STREAM_DATA,
         );
     }
 
     #[test]
     fn zero_rtt_blocked_streams_first_omitted() {
         // Send server original settings without BlockedStreams
         // Send the new server settings with BlockedStreams
@@ -2880,16 +2891,17 @@ mod tests {
                 HSetting::new(HSettingType::MaxHeaderListSize, 10000),
             ],
             &[
                 HSetting::new(HSettingType::MaxTableCapacity, 100),
                 HSetting::new(HSettingType::BlockedStreams, 100),
                 HSetting::new(HSettingType::MaxHeaderListSize, 10000),
             ],
             Http3State::Connected,
+            State::Connected,
             ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION,
         );
     }
 
     #[test]
     fn zero_rtt_max_header_size_first_omitted() {
         // Send server settings without MaxHeaderListSize
         // Send new settings with MaxHeaderListSize.
@@ -2899,12 +2911,13 @@ mod tests {
                 HSetting::new(HSettingType::BlockedStreams, 10000),
             ],
             &[
                 HSetting::new(HSettingType::MaxTableCapacity, 100),
                 HSetting::new(HSettingType::BlockedStreams, 100),
                 HSetting::new(HSettingType::MaxHeaderListSize, 10000),
             ],
             Http3State::Closing(CloseError::Application(265)),
+            State::Connected,
             ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION,
         );
     }
 }
--- a/third_party/rust/neqo-http3/src/connection_server.rs
+++ b/third_party/rust/neqo-http3/src/connection_server.rs
@@ -22,17 +22,17 @@ pub struct Http3ServerHandler {
 impl ::std::fmt::Display for Http3ServerHandler {
     fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
         write!(f, "Http3 server connection")
     }
 }
 
 impl Http3ServerHandler {
     pub fn new(max_table_size: u32, max_blocked_streams: u16) -> Self {
-        Self {
+        Http3ServerHandler {
             base_handler: Http3Connection::new(max_table_size, max_blocked_streams),
             events: Http3ServerConnEvents::default(),
         }
     }
     pub fn set_response(&mut self, stream_id: u64, headers: &[Header], data: Vec<u8>) -> Res<()> {
         self.base_handler
             .transactions
             .get_mut(&stream_id)
--- a/third_party/rust/neqo-http3/src/control_stream_remote.rs
+++ b/third_party/rust/neqo-http3/src/control_stream_remote.rs
@@ -19,18 +19,18 @@ pub struct ControlStreamRemote {
 
 impl ::std::fmt::Display for ControlStreamRemote {
     fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
         write!(f, "Http3 remote control stream {:?}", self.stream_id)
     }
 }
 
 impl ControlStreamRemote {
-    pub fn new() -> Self {
-        Self {
+    pub fn new() -> ControlStreamRemote {
+        ControlStreamRemote {
             stream_id: None,
             frame_reader: HFrameReader::new(),
             fin: false,
         }
     }
 
     pub fn add_remote_stream(&mut self, stream_id: u64) -> Res<()> {
         qinfo!([self], "A new control stream {}.", stream_id);
--- a/third_party/rust/neqo-http3/src/hframe.rs
+++ b/third_party/rust/neqo-http3/src/hframe.rs
@@ -60,79 +60,79 @@ pub enum HFrame {
     DuplicatePush {
         push_id: u64,
     },
 }
 
 impl HFrame {
     fn get_type(&self) -> HFrameType {
         match self {
-            Self::Data { .. } => H3_FRAME_TYPE_DATA,
-            Self::Headers { .. } => H3_FRAME_TYPE_HEADERS,
-            Self::CancelPush { .. } => H3_FRAME_TYPE_CANCEL_PUSH,
-            Self::Settings { .. } => H3_FRAME_TYPE_SETTINGS,
-            Self::PushPromise { .. } => H3_FRAME_TYPE_PUSH_PROMISE,
-            Self::Goaway { .. } => H3_FRAME_TYPE_GOAWAY,
-            Self::MaxPushId { .. } => H3_FRAME_TYPE_MAX_PUSH_ID,
-            Self::DuplicatePush { .. } => H3_FRAME_TYPE_DUPLICATE_PUSH,
+            HFrame::Data { .. } => H3_FRAME_TYPE_DATA,
+            HFrame::Headers { .. } => H3_FRAME_TYPE_HEADERS,
+            HFrame::CancelPush { .. } => H3_FRAME_TYPE_CANCEL_PUSH,
+            HFrame::Settings { .. } => H3_FRAME_TYPE_SETTINGS,
+            HFrame::PushPromise { .. } => H3_FRAME_TYPE_PUSH_PROMISE,
+            HFrame::Goaway { .. } => H3_FRAME_TYPE_GOAWAY,
+            HFrame::MaxPushId { .. } => H3_FRAME_TYPE_MAX_PUSH_ID,
+            HFrame::DuplicatePush { .. } => H3_FRAME_TYPE_DUPLICATE_PUSH,
         }
     }
 
     pub fn encode(&self, enc: &mut Encoder) {
         enc.encode_varint(self.get_type());
 
         match self {
-            Self::Data { len } | Self::Headers { len } => {
+            HFrame::Data { len } | HFrame::Headers { len } => {
                 // DATA and HEADERS frames only encode the length here.
                 enc.encode_varint(*len);
             }
-            Self::CancelPush { push_id } => {
+            HFrame::CancelPush { push_id } => {
                 enc.encode_vvec_with(|enc_inner| {
                     enc_inner.encode_varint(*push_id);
                 });
             }
-            Self::Settings { settings } => {
+            HFrame::Settings { settings } => {
                 settings.encode_frame_contents(enc);
             }
-            Self::PushPromise {
+            HFrame::PushPromise {
                 push_id,
                 header_block,
             } => {
                 enc.encode_varint((header_block.len() + (Encoder::varint_len(*push_id))) as u64);
                 enc.encode_varint(*push_id);
                 enc.encode(header_block);
             }
-            Self::Goaway { stream_id } => {
+            HFrame::Goaway { stream_id } => {
                 enc.encode_vvec_with(|enc_inner| {
                     enc_inner.encode_varint(*stream_id);
                 });
             }
-            Self::MaxPushId { push_id } => {
+            HFrame::MaxPushId { push_id } => {
                 enc.encode_vvec_with(|enc_inner| {
                     enc_inner.encode_varint(*push_id);
                 });
             }
-            Self::DuplicatePush { push_id } => {
+            HFrame::DuplicatePush { push_id } => {
                 enc.encode_vvec_with(|enc_inner| {
                     enc_inner.encode_varint(*push_id);
                 });
             }
         }
     }
 
     pub fn is_allowed(&self, s: HStreamType) -> bool {
         match self {
-            Self::Data { .. } => !(s == HStreamType::Control),
-            Self::Headers { .. } => !(s == HStreamType::Control),
-            Self::CancelPush { .. } => (s == HStreamType::Control),
-            Self::Settings { .. } => (s == HStreamType::Control),
-            Self::PushPromise { .. } => (s == HStreamType::Request),
-            Self::Goaway { .. } => (s == HStreamType::Control),
-            Self::MaxPushId { .. } => (s == HStreamType::Control),
-            Self::DuplicatePush { .. } => (s == HStreamType::Request),
+            HFrame::Data { .. } => !(s == HStreamType::Control),
+            HFrame::Headers { .. } => !(s == HStreamType::Control),
+            HFrame::CancelPush { .. } => (s == HStreamType::Control),
+            HFrame::Settings { .. } => (s == HStreamType::Control),
+            HFrame::PushPromise { .. } => (s == HStreamType::Request),
+            HFrame::Goaway { .. } => (s == HStreamType::Control),
+            HFrame::MaxPushId { .. } => (s == HStreamType::Control),
+            HFrame::DuplicatePush { .. } => (s == HStreamType::Request),
         }
     }
 }
 
 #[derive(Copy, Clone, PartialEq, Debug)]
 enum HFrameReaderState {
     BeforeFrame,
     GetType,
@@ -153,18 +153,18 @@ pub struct HFrameReader {
 
 impl Default for HFrameReader {
     fn default() -> Self {
         Self::new()
     }
 }
 
 impl HFrameReader {
-    pub fn new() -> Self {
-        Self {
+    pub fn new() -> HFrameReader {
+        HFrameReader {
             state: HFrameReaderState::BeforeFrame,
             hframe_type: 0,
             hframe_len: 0,
             decoder: IncrementalDecoder::decode_varint(),
             payload: Vec::new(),
         }
     }
 
--- a/third_party/rust/neqo-http3/src/hsettings_frame.rs
+++ b/third_party/rust/neqo-http3/src/hsettings_frame.rs
@@ -32,31 +32,31 @@ fn hsetting_default(setting_type: HSetti
 #[derive(Debug, Clone, PartialEq)]
 pub struct HSetting {
     pub setting_type: HSettingType,
     pub value: u64,
 }
 
 impl HSetting {
     pub fn new(setting_type: HSettingType, value: u64) -> Self {
-        Self {
+        HSetting {
             setting_type,
             value,
         }
     }
 }
 
 #[derive(Clone, Debug, Default, PartialEq)]
 pub struct HSettings {
     settings: Vec<HSetting>,
 }
 
 impl HSettings {
     pub fn new(settings: &[HSetting]) -> Self {
-        Self {
+        HSettings {
             settings: settings.to_vec(),
         }
     }
 
     pub fn get(&self, setting: HSettingType) -> u64 {
         match self.settings.iter().find(|s| s.setting_type == setting) {
             Some(v) => v.value,
             None => hsetting_default(setting),
--- a/third_party/rust/neqo-http3/src/lib.rs
+++ b/third_party/rust/neqo-http3/src/lib.rs
@@ -1,16 +1,15 @@
 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
 #![cfg_attr(feature = "deny-warnings", deny(warnings))]
-#![warn(clippy::use_self)]
 
 mod client_events;
 mod connection;
 pub mod connection_client;
 mod connection_server;
 mod control_stream_local;
 mod control_stream_remote;
 pub mod hframe;
@@ -18,19 +17,19 @@ mod hsettings_frame;
 pub mod server;
 mod server_connection_events;
 mod server_events;
 mod stream_type_reader;
 mod transaction_client;
 pub mod transaction_server;
 //pub mod server;
 
-use neqo_qpack::Error as QpackError;
+use neqo_qpack;
+use neqo_transport;
 pub use neqo_transport::Output;
-use neqo_transport::{AppError, Error as TransportError};
 
 pub use client_events::Http3ClientEvent;
 pub use connection::Http3State;
 pub use connection_client::Http3Client;
 pub use neqo_qpack::Header;
 pub use server::Http3Server;
 pub use server_events::Http3ServerEvent;
 pub use transaction_server::TransactionServer;
@@ -59,94 +58,92 @@ pub enum Error {
     QpackError(neqo_qpack::Error),
 
     // Internal errors from here.
     AlreadyClosed,
     DecodingFrame,
     InvalidStreamId,
     NoMoreData,
     NotEnoughData,
-    TransportError(TransportError),
+    TransportError(neqo_transport::Error),
     Unavailable,
     Unexpected,
     InvalidResumptionToken,
 }
 
 impl Error {
-    pub fn code(&self) -> AppError {
+    pub fn code(&self) -> neqo_transport::AppError {
         match self {
-            Self::HttpNoError => 0x100,
-            Self::HttpGeneralProtocolError => 0x101,
-            Self::HttpInternalError => 0x102,
-            Self::HttpStreamCreationError => 0x103,
-            Self::HttpClosedCriticalStream => 0x104,
-            Self::HttpFrameUnexpected => 0x105,
-            Self::HttpFrameError => 0x106,
-            Self::HttpExcessiveLoad => 0x107,
-            Self::HttpIdError => 0x108,
-            Self::HttpSettingsError => 0x109,
-            Self::HttpMissingSettings => 0x10a,
-            Self::HttpRequestRejected => 0x10b,
-            Self::HttpRequestCancelled => 0x10c,
-            Self::HttpRequestIncomplete => 0x10d,
-            Self::HttpEarlyResponse => 0x10e,
-            Self::HttpConnectError => 0x10f,
-            Self::HttpVersionFallback => 0x110,
-            Self::QpackError(e) => e.code(),
+            Error::HttpNoError => 0x100,
+            Error::HttpGeneralProtocolError => 0x101,
+            Error::HttpInternalError => 0x102,
+            Error::HttpStreamCreationError => 0x103,
+            Error::HttpClosedCriticalStream => 0x104,
+            Error::HttpFrameUnexpected => 0x105,
+            Error::HttpFrameError => 0x106,
+            Error::HttpExcessiveLoad => 0x107,
+            Error::HttpIdError => 0x108,
+            Error::HttpSettingsError => 0x109,
+            Error::HttpMissingSettings => 0x10a,
+            Error::HttpRequestRejected => 0x10b,
+            Error::HttpRequestCancelled => 0x10c,
+            Error::HttpRequestIncomplete => 0x10d,
+            Error::HttpEarlyResponse => 0x10e,
+            Error::HttpConnectError => 0x10f,
+            Error::HttpVersionFallback => 0x110,
+            Error::QpackError(e) => e.code(),
             // These are all internal errors.
             _ => 3,
         }
     }
+
+    pub fn from_code(error: neqo_transport::AppError) -> Error {
+        match error {
+            0x100 => Error::HttpNoError,
+            0x101 => Error::HttpGeneralProtocolError,
+            0x102 => Error::HttpInternalError,
+            0x103 => Error::HttpStreamCreationError,
+            0x104 => Error::HttpClosedCriticalStream,
+            0x105 => Error::HttpFrameUnexpected,
+            0x106 => Error::HttpFrameError,
+            0x107 => Error::HttpExcessiveLoad,
+            0x108 => Error::HttpIdError,
+            0x109 => Error::HttpSettingsError,
+            0x10a => Error::HttpMissingSettings,
+            0x10b => Error::HttpRequestRejected,
+            0x10c => Error::HttpRequestCancelled,
+            0x10d => Error::HttpRequestIncomplete,
+            0x10e => Error::HttpEarlyResponse,
+            0x10f => Error::HttpConnectError,
+            0x110 => Error::HttpVersionFallback,
+            0x200 => Error::QpackError(neqo_qpack::Error::DecompressionFailed),
+            0x201 => Error::QpackError(neqo_qpack::Error::EncoderStreamError),
+            0x202 => Error::QpackError(neqo_qpack::Error::DecoderStreamError),
+            _ => Error::HttpInternalError,
+        }
+    }
 }
 
-impl From<TransportError> for Error {
-    fn from(err: TransportError) -> Self {
-        Self::TransportError(err)
-    }
-}
-
-impl From<QpackError> for Error {
-    fn from(err: QpackError) -> Self {
-        Self::QpackError(err)
+impl From<neqo_transport::Error> for Error {
+    fn from(err: neqo_transport::Error) -> Self {
+        Error::TransportError(err)
     }
 }
 
-impl From<AppError> for Error {
-    fn from(error: AppError) -> Self {
-        match error {
-            0x100 => Self::HttpNoError,
-            0x101 => Self::HttpGeneralProtocolError,
-            0x102 => Self::HttpInternalError,
-            0x103 => Self::HttpStreamCreationError,
-            0x104 => Self::HttpClosedCriticalStream,
-            0x105 => Self::HttpFrameUnexpected,
-            0x106 => Self::HttpFrameError,
-            0x107 => Self::HttpExcessiveLoad,
-            0x108 => Self::HttpIdError,
-            0x109 => Self::HttpSettingsError,
-            0x10a => Self::HttpMissingSettings,
-            0x10b => Self::HttpRequestRejected,
-            0x10c => Self::HttpRequestCancelled,
-            0x10d => Self::HttpRequestIncomplete,
-            0x10e => Self::HttpEarlyResponse,
-            0x10f => Self::HttpConnectError,
-            0x110 => Self::HttpVersionFallback,
-            0x200 => Self::QpackError(QpackError::DecompressionFailed),
-            0x201 => Self::QpackError(QpackError::EncoderStreamError),
-            0x202 => Self::QpackError(QpackError::DecoderStreamError),
-            _ => Self::HttpInternalError,
-        }
+impl From<neqo_qpack::Error> for Error {
+    fn from(err: neqo_qpack::Error) -> Self {
+        Error::QpackError(err)
     }
 }
 
 impl ::std::error::Error for Error {
     fn source(&self) -> Option<&(dyn ::std::error::Error + 'static)> {
         match self {
-            Self::TransportError(e) => Some(e),
-            Self::QpackError(e) => Some(e),
+            Error::TransportError(e) => Some(e),
+            Error::QpackError(e) => Some(e),
             _ => None,
         }
     }
 }
 
 impl ::std::fmt::Display for Error {
     fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
         write!(f, "HTTP/3 error: {:?}", self)
--- a/third_party/rust/neqo-http3/src/server.rs
+++ b/third_party/rust/neqo-http3/src/server.rs
@@ -38,18 +38,18 @@ impl Http3Server {
     pub fn new(
         now: Instant,
         certs: &[impl AsRef<str>],
         protocols: &[impl AsRef<str>],
         anti_replay: AntiReplay,
         cid_manager: Rc<RefCell<dyn ConnectionIdManager>>,
         max_table_size: u32,
         max_blocked_streams: u16,
-    ) -> Res<Self> {
-        Ok(Self {
+    ) -> Res<Http3Server> {
+        Ok(Http3Server {
             server: Server::new(now, certs, protocols, anti_replay, cid_manager)?,
             max_table_size,
             max_blocked_streams,
             http3_handlers: HashMap::new(),
             events: Http3ServerEvents::default(),
         })
     }
 
--- a/third_party/rust/neqo-http3/src/server_events.rs
+++ b/third_party/rust/neqo-http3/src/server_events.rs
@@ -34,17 +34,17 @@ impl ::std::fmt::Display for ClientReque
 }
 
 impl ClientRequestStream {
     pub fn new(
         conn: ActiveConnectionRef,
         handler: Rc<RefCell<Http3ServerHandler>>,
         stream_id: u64,
     ) -> Self {
-        Self {
+        ClientRequestStream {
             conn,
             handler,
             stream_id,
         }
     }
     pub fn set_response(&mut self, headers: &[Header], data: Vec<u8>) -> Res<()> {
         qinfo!([self], "Set new response.");
         self.handler
--- a/third_party/rust/neqo-http3/src/stream_type_reader.rs
+++ b/third_party/rust/neqo-http3/src/stream_type_reader.rs
@@ -9,18 +9,18 @@ use neqo_transport::Connection;
 
 #[derive(Debug)]
 pub struct NewStreamTypeReader {
     reader: IncrementalDecoder,
     fin: bool,
 }
 
 impl NewStreamTypeReader {
-    pub fn new() -> Self {
-        Self {
+    pub fn new() -> NewStreamTypeReader {
+        NewStreamTypeReader {
             reader: IncrementalDecoder::decode_varint(),
             fin: false,
         }
     }
     pub fn get_type(&mut self, conn: &mut Connection, stream_id: u64) -> Option<u64> {
         // On any error we will only close this stream!
         loop {
             let to_read = self.reader.min_remaining();
--- a/third_party/rust/neqo-http3/src/transaction_client.rs
+++ b/third_party/rust/neqo-http3/src/transaction_client.rs
@@ -4,17 +4,17 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
 use crate::hframe::{HFrame, HFrameReader};
 
 use crate::client_events::Http3ClientEvents;
 use crate::connection::Http3Transaction;
 use crate::Header;
-use neqo_common::{qdebug, qinfo, qtrace, qwarn, Encoder};
+use neqo_common::{qdebug, qinfo, qtrace, Encoder};
 use neqo_qpack::decoder::QPackDecoder;
 use neqo_qpack::encoder::QPackEncoder;
 use neqo_transport::Connection;
 
 use crate::{Error, Res};
 use std::cmp::min;
 use std::mem;
 
@@ -31,18 +31,18 @@ struct Request {
     scheme: String,
     host: String,
     path: String,
     headers: Vec<Header>,
     buf: Option<Vec<u8>>,
 }
 
 impl Request {
-    pub fn new(method: &str, scheme: &str, host: &str, path: &str, headers: &[Header]) -> Self {
-        let mut r = Self {
+    pub fn new(method: &str, scheme: &str, host: &str, path: &str, headers: &[Header]) -> Request {
+        let mut r = Request {
             method: method.to_owned(),
             scheme: scheme.to_owned(),
             host: host.to_owned(),
             path: path.to_owned(),
             headers: Vec::new(),
             buf: None,
         };
         r.headers.push((":method".into(), method.to_owned()));
@@ -175,19 +175,19 @@ impl TransactionClient {
     pub fn new(
         stream_id: u64,
         method: &str,
         scheme: &str,
         host: &str,
         path: &str,
         headers: &[Header],
         conn_events: Http3ClientEvents,
-    ) -> Self {
+    ) -> TransactionClient {
         qinfo!("Create a request stream_id={}", stream_id);
-        Self {
+        TransactionClient {
             send_state: TransactionSendState::SendingHeaders {
                 request: Request::new(method, scheme, host, path, headers),
                 fin: false,
             },
             recv_state: TransactionRecvState::WaitingForResponseHeaders,
             stream_id,
             response_headers_state: ResponseHeadersState::NoHeaders,
             frame_reader: HFrameReader::new(),
@@ -286,17 +286,16 @@ impl TransactionClient {
             frame,
             self.recv_state
         );
         match frame {
             HFrame::Data { len } => self.handle_data_frame(len, fin),
             HFrame::PushPromise { .. } => Err(Error::HttpIdError),
             HFrame::Headers { .. } => {
                 // TODO implement trailers!
-                qwarn!([self], "Received trailers");
                 Err(Error::HttpFrameUnexpected)
             }
             _ => Err(Error::HttpFrameUnexpected),
         }
     }
 
     fn handle_data_frame(&mut self, len: u64, fin: bool) -> Res<()> {
         if len > 0 {
--- a/third_party/rust/neqo-http3/src/transaction_server.rs
+++ b/third_party/rust/neqo-http3/src/transaction_server.rs
@@ -37,19 +37,19 @@ pub struct TransactionServer {
     recv_state: TransactionRecvState,
     send_state: TransactionSendState,
     stream_id: u64,
     frame_reader: HFrameReader,
     conn_events: Http3ServerConnEvents,
 }
 
 impl TransactionServer {
-    pub fn new(stream_id: u64, conn_events: Http3ServerConnEvents) -> Self {
+    pub fn new(stream_id: u64, conn_events: Http3ServerConnEvents) -> TransactionServer {
         qinfo!("Create a request stream_id={}", stream_id);
-        Self {
+        TransactionServer {
             recv_state: TransactionRecvState::WaitingForHeaders,
             send_state: TransactionSendState::Initial,
             stream_id,
             frame_reader: HFrameReader::new(),
             conn_events,
         }
     }
 
--- a/third_party/rust/neqo-qpack/.cargo-checksum.json
+++ b/third_party/rust/neqo-qpack/.cargo-checksum.json
@@ -1,1 +1,1 @@
-{"files":{"Cargo.toml":"302de4e70e8ee1213674d0bb3334ad505bf2e3b4205c43d95912e236e4922b2f","src/decoder.rs":"aac6d5b3dfb19779351c2568a4c54c551e2de83d0e458246c818a6af15514477","src/encoder.rs":"992bb211273d48b9d85ab4bc6bad5c0dbc5c12e7f9e7c1bb35f1b0db5eb7cffe","src/huffman.rs":"720eedace45205098a0b2210c876906ce15b7be469a799e75e70baafac8adee8","src/huffman_decode_helper.rs":"e4734353591770dfe9a9047b0be5d9068150433e9cea8cad029444b42b0afa39","src/huffman_table.rs":"06fea766a6276ac56c7ee0326faed800a742c15fda1f33bf2513e6cc6a5e6d27","src/lib.rs":"fa5b76f6b7db74904fe0317bbc1214292494365328c2efa06b4146cbd2ee6c1b","src/qpack_helper.rs":"200ab8bcb60728e3bcacf25b7006fa54b544458bfee5e66e09fa472a614347fc","src/qpack_send_buf.rs":"471e3b0af9f8783aa1bfe11a1959bf5694e62bc2d8e1cf783c933af81e3f3cf9","src/static_table.rs":"fda9d5c6f38f94b0bf92d3afdf8432dce6e27e189736596e16727090c77b78ec","src/table.rs":"1043a6e0761d9ff05a35dfab3b5a0e871d1b1666e83bc4fbd9e97383ca44e59e"},"package":null}
\ No newline at end of file
+{"files":{"Cargo.toml":"7c469ea56bf87154c0eebf67eeb253ff0172453250bb165d74c08e71272cfea5","src/decoder.rs":"a8e20a9f82846e873197c75d1b5ab49270014c807e90b1331ebd2a449d2d84e0","src/encoder.rs":"78da509611b5869d320795c42bef944b6499c0f207c73818c1908f1a1cf001fc","src/huffman.rs":"720eedace45205098a0b2210c876906ce15b7be469a799e75e70baafac8adee8","src/huffman_decode_helper.rs":"e4734353591770dfe9a9047b0be5d9068150433e9cea8cad029444b42b0afa39","src/huffman_table.rs":"06fea766a6276ac56c7ee0326faed800a742c15fda1f33bf2513e6cc6a5e6d27","src/lib.rs":"9895f91624d58388cf4906a80b3d8e9109abf24c9df542af8acb34b3a6e2231e","src/qpack_helper.rs":"200ab8bcb60728e3bcacf25b7006fa54b544458bfee5e66e09fa472a614347fc","src/qpack_send_buf.rs":"471e3b0af9f8783aa1bfe11a1959bf5694e62bc2d8e1cf783c933af81e3f3cf9","src/static_table.rs":"fda9d5c6f38f94b0bf92d3afdf8432dce6e27e189736596e16727090c77b78ec","src/table.rs":"f4f09692bf6ec863b0f066c88837d99f59a1fc4a8ca61bee4ed76d45a77c3cc4"},"package":null}
\ No newline at end of file
--- a/third_party/rust/neqo-qpack/Cargo.toml
+++ b/third_party/rust/neqo-qpack/Cargo.toml
@@ -1,11 +1,11 @@
 [package]
 name = "neqo-qpack"
-version = "0.1.14"
+version = "0.1.12"
 authors = ["Dragana Damjanovic <dragana.damjano@gmail.com>"]
 edition = "2018"
 license = "MIT/Apache-2.0"
 
 [dependencies]
 neqo-common = { path = "./../neqo-common" }
 neqo-transport = { path = "./../neqo-transport" }
 neqo-crypto = { path = "./../neqo-crypto" }
--- a/third_party/rust/neqo-qpack/src/decoder.rs
+++ b/third_party/rust/neqo-qpack/src/decoder.rs
@@ -1,14 +1,15 @@
 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![allow(unused_variables, dead_code)]
 use crate::huffman::Huffman;
 use crate::qpack_helper::{
     read_prefixed_encoded_int_slice, read_prefixed_encoded_int_with_connection, BufWrapper,
 };
 use crate::qpack_send_buf::QPData;
 use crate::table::HeaderTable;
 use crate::Header;
 use crate::{Error, Res};
@@ -68,32 +69,34 @@ enum QPackDecoderState {
         cnt: u8,
     },
 }
 
 #[derive(Debug)]
 pub struct QPackDecoder {
     state: QPackDecoderState,
     table: HeaderTable,
+    increment: u64,
     total_num_of_inserts: u64,
     max_entries: u64,
     send_buf: QPData,
     local_stream_id: Option<u64>,
     remote_stream_id: Option<u64>,
     max_table_size: u32,
     max_blocked_streams: u16,
     blocked_streams: Vec<(u64, u64)>, //stream_id and requested inserts count.
 }
 
 impl QPackDecoder {
-    pub fn new(max_table_size: u32, max_blocked_streams: u16) -> Self {
+    pub fn new(max_table_size: u32, max_blocked_streams: u16) -> QPackDecoder {
         qdebug!("Decoder: creating a new qpack decoder.");
-        Self {
+        QPackDecoder {
             state: QPackDecoderState::ReadInstruction,
             table: HeaderTable::new(false),
+            increment: 0,
             total_num_of_inserts: 0,
             max_entries: (f64::from(max_table_size) / 32.0).floor() as u64,
             send_buf: QPData::default(),
             local_stream_id: None,
             remote_stream_id: None,
             max_table_size,
             max_blocked_streams,
             blocked_streams: Vec::new(),
@@ -204,16 +207,17 @@ impl QPackDecoder {
                         let mut cnt: u8 = 0;
                         let done = read_prefixed_encoded_int_with_connection_wrap(
                             conn, stream_id, &mut v, &mut cnt, 3, b[0], true,
                         )?;
                         if done {
                             qdebug!([label], "received instruction - duplicate index={}", v);
                             self.table.duplicate(v)?;
                             self.total_num_of_inserts += 1;
+                            self.increment += 1;
                             self.state = QPackDecoderState::ReadInstruction;
                         } else {
                             self.state = QPackDecoderState::Duplicate { index: v, cnt };
                             // wait for more data
                             break Ok(());
                         }
                     } else {
                         // Set Dynamic Table Capacity
@@ -305,19 +309,20 @@ impl QPackDecoder {
                                     value_to_insert = Huffman::default().decode(value)?;
                                 } else {
                                     mem::swap(&mut value_to_insert, value);
                                 }
                                 qdebug!([label], "received instruction - insert with name ref index={} static={} value={:x?}", name_index, name_static_table, value_to_insert);
                                 self.table.insert_with_name_ref(
                                     *name_static_table,
                                     *name_index,
-                                    &value_to_insert,
+                                    value_to_insert,
                                 )?;
                                 self.total_num_of_inserts += 1;
+                                self.increment += 1;
                                 self.state = QPackDecoderState::ReadInstruction;
                             } else {
                                 // waiting for more data
                                 break Ok(());
                             }
                         }
                     }
                 }
@@ -421,18 +426,19 @@ impl QPackDecoder {
                                 }
                                 let mut value_to_insert: Vec<u8> = Vec::new();
                                 if *value_is_huffman {
                                     value_to_insert = Huffman::default().decode(value)?;
                                 } else {
                                     mem::swap(&mut value_to_insert, value);
                                 }
                                 qdebug!([label], "received instruction - insert with name literal name={:x?} value={:x?}", name_to_insert, value_to_insert);
-                                self.table.insert(&name_to_insert, &value_to_insert)?;
+                                self.table.insert(name_to_insert, value_to_insert)?;
                                 self.total_num_of_inserts += 1;
+                                self.increment += 1;
                                 self.state = QPackDecoderState::ReadInstruction;
                             } else {
                                 // waiting for more data
                                 break Ok(());
                             }
                         }
                     }
                 }
@@ -442,16 +448,17 @@ impl QPackDecoder {
                 } => {
                     let done = read_prefixed_encoded_int_with_connection_wrap(
                         conn, stream_id, index, cnt, 0, 0x0, false,
                     )?;
                     if done {
                         qdebug!([label], "received instruction - duplicate index={}", index);
                         self.table.duplicate(*index)?;
                         self.total_num_of_inserts += 1;
+                        self.increment += 1;
                         self.state = QPackDecoderState::ReadInstruction;
                     } else {
                         // waiting for more data
                         break Ok(());
                     }
                 }
                 QPackDecoderState::Capacity {
                     ref mut capacity,
@@ -473,44 +480,36 @@ impl QPackDecoder {
         }
     }
 
     pub fn set_capacity(&mut self, cap: u64) -> Res<()> {
         qdebug!([self], "received instruction capacity cap={}", cap);
         if cap > u64::from(self.max_table_size) {
             return Err(Error::EncoderStreamError);
         }
-        self.table
-            .set_capacity(cap)
-            .map_err(|_| Error::EncoderStreamError)
+        self.table.set_capacity(cap);
+        Ok(())
     }
 
-    fn header_ack(&mut self, stream_id: u64, required_inserts: u64) {
-        let ack_increment_delta = required_inserts - self.table.get_acked_inserts_cnt();
+    fn header_ack(&mut self, stream_id: u64) {
         self.send_buf
             .encode_prefixed_encoded_int(0x80, 1, stream_id);
-        self.table
-            .increment_acked(ack_increment_delta)
-            .expect("This should never happen");
     }
 
     pub fn cancel_stream(&mut self, stream_id: u64) {
         self.send_buf
             .encode_prefixed_encoded_int(0x40, 1, stream_id);
     }
 
     pub fn send(&mut self, conn: &mut Connection) -> Res<()> {
         // Encode increment instruction if needed.
-        let ack_increment_delta = self.total_num_of_inserts - self.table.get_acked_inserts_cnt();
-        if ack_increment_delta > 0 {
+        if self.increment > 0 {
             self.send_buf
-                .encode_prefixed_encoded_int(0x00, 2, ack_increment_delta);
-            self.table
-                .increment_acked(ack_increment_delta)
-                .expect("This should never happen");
+                .encode_prefixed_encoded_int(0x00, 2, self.increment);
+            self.increment = 0;
         }
         if self.send_buf.len() == 0 {
             Ok(())
         } else if let Some(stream_id) = self.local_stream_id {
             match conn.stream_send(stream_id, &self.send_buf[..]) {
                 Err(_) => Err(Error::DecoderStreamError),
                 Ok(r) => {
                     qdebug!([self], "{} bytes sent.", r);
@@ -549,17 +548,17 @@ impl QPackDecoder {
             return Ok(None);
         }
         let mut h: Vec<Header> = Vec::new();
 
         loop {
             if reader.done() {
                 // Send header_ack
                 if req_inserts != 0 {
-                    self.header_ack(stream_id, req_inserts);
+                    self.header_ack(stream_id);
                 }
                 qdebug!([self], "done decoding header block.");
                 break Ok(Some(h));
             }
 
             let b = reader.peek()?;
             if b & 0x80 != 0 {
                 h.push(self.read_indexed(&mut reader, base)?);
@@ -785,321 +784,188 @@ fn read_prefixed_encoded_int_with_connec
         Err(_) => Err(Error::DecoderStreamError),
         Ok(done) => Ok(done),
     }
 }
 
 #[cfg(test)]
 mod tests {
     use super::*;
+    use neqo_transport::ConnectionEvent;
     use neqo_transport::StreamType;
     use std::convert::TryInto;
     use test_fixture::*;
 
-    struct TestDecoder {
-        decoder: QPackDecoder,
-        send_stream_id: u64,
-        recv_stream_id: u64,
-        conn: Connection,
-        peer_conn: Connection,
-    }
-
-    fn connect() -> TestDecoder {
-        let (mut conn, mut peer_conn) = test_fixture::connect();
+    fn connect() -> (QPackDecoder, Connection, Connection, u64, u64) {
+        let (mut conn_c, mut conn_s) = test_fixture::connect();
 
         // create a stream
-        let recv_stream_id = peer_conn.stream_create(StreamType::UniDi).unwrap();
-        let send_stream_id = conn.stream_create(StreamType::UniDi).unwrap();
+        let recv_stream_id = conn_s.stream_create(StreamType::UniDi).unwrap();
+        let send_stream_id = conn_c.stream_create(StreamType::UniDi).unwrap();
 
         // create a decoder
         let mut decoder = QPackDecoder::new(300, 100);
         decoder.add_send_stream(send_stream_id);
 
-        TestDecoder {
-            decoder,
-            send_stream_id,
-            recv_stream_id,
-            conn,
-            peer_conn,
-        }
-    }
-
-    fn recv_instruction(decoder: &mut TestDecoder, encoder_instruction: &[u8], res: Res<()>) {
-        let _ = decoder
-            .peer_conn
-            .stream_send(decoder.recv_stream_id, encoder_instruction);
-        let out = decoder.peer_conn.process(None, now());
-        decoder.conn.process(out.dgram(), now());
-        assert_eq!(
-            decoder
-                .decoder
-                .read_instructions(&mut decoder.conn, decoder.recv_stream_id),
-            res
-        );
-    }
-
-    fn send_instructions_and_check(decoder: &mut TestDecoder, decoder_instruction: &[u8]) {
-        decoder.decoder.send(&mut decoder.conn).unwrap();
-        let out = decoder.conn.process(None, now());
-        decoder.peer_conn.process(out.dgram(), now());
-        let mut buf = [0u8; 100];
-        let (amount, fin) = decoder
-            .peer_conn
-            .stream_recv(decoder.send_stream_id, &mut buf)
-            .unwrap();
-        assert_eq!(fin, false);
-        assert_eq!(&buf[..amount], decoder_instruction);
-    }
-
-    fn decode_headers(
-        decoder: &mut TestDecoder,
-        header_block: &[u8],
-        headers: &[Header],
-        stream_id: u64,
-    ) {
-        let decoded_headers = decoder
-            .decoder
-            .decode_header_block(header_block, stream_id)
-            .unwrap();
-        let h = decoded_headers.unwrap();
-        assert_eq!(h, headers);
+        (decoder, conn_c, conn_s, recv_stream_id, send_stream_id)
     }
 
     fn test_instruction(
         capacity: u64,
         instruction: &[u8],
-        res: Res<()>,
+        err: Option<Error>,
         decoder_instruction: &[u8],
         check_capacity: u64,
     ) {
-        let mut decoder = connect();
+        let (mut decoder, mut conn_c, mut conn_s, recv_stream_id, send_stream_id) = connect();
 
         if capacity > 0 {
-            assert!(decoder.decoder.set_capacity(capacity).is_ok());
+            assert!(decoder.set_capacity(capacity).is_ok());
+        }
+        // send an instruction
+        let _ = conn_s.stream_send(recv_stream_id, instruction);
+        let out = conn_s.process(None, now());
+        conn_c.process(out.dgram(), now());
+
+        let res = decoder.read_instructions(&mut conn_c, recv_stream_id);
+        assert_eq!(err.is_some(), res.is_err());
+        if let Some(expected_err) = err {
+            assert_eq!(expected_err, res.unwrap_err());
         }
 
-        // recv an instruction
-        recv_instruction(&mut decoder, instruction, res);
-
-        // send decoder instruction and check that is what we expect.
-        send_instructions_and_check(&mut decoder, decoder_instruction);
+        decoder.send(&mut conn_c).unwrap();
+        let out = conn_c.process(None, now());
+        conn_s.process(out.dgram(), now());
+        let mut found_instruction = false;
+        while let Some(e) = conn_s.next_event() {
+            if let ConnectionEvent::RecvStreamReadable { stream_id } = e {
+                let mut buf = [0u8; 100];
+                let (amount, fin) = conn_s.stream_recv(stream_id, &mut buf).unwrap();
+                assert_eq!(fin, false);
+                assert_eq!(buf[..amount], decoder_instruction[..]);
+                found_instruction = true;
+            }
+        }
+        assert_eq!(found_instruction, !decoder_instruction.is_empty());
 
         if check_capacity > 0 {
-            assert_eq!(decoder.decoder.capacity(), check_capacity);
+            assert_eq!(decoder.capacity(), check_capacity);
         }
     }
 
     // test insert_with_name_ref which fails because there is not enough space in the table
     #[test]
     fn test_recv_insert_with_name_ref_1() {
         test_instruction(
             0,
             &[0xc4, 0x04, 0x31, 0x32, 0x33, 0x34],
-            Err(Error::DecoderStreamError),
+            Some(Error::DecoderStreamError),
             &[0x03],
             0,
         );
     }
 
     // test insert_name_ref that succeeds
     #[test]
     fn test_recv_insert_with_name_ref_2() {
         test_instruction(
             100,
             &[0xc4, 0x04, 0x31, 0x32, 0x33, 0x34],
-            Ok(()),
+            None,
             &[0x03, 0x01],
             0,
         );
     }
 
     // test insert with name literal - succeeds
     #[test]
     fn test_recv_insert_with_name_litarel_2() {
         test_instruction(
             200,
             &[
                 0x4e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67, 0x74,
                 0x68, 0x04, 0x31, 0x32, 0x33, 0x34,
             ],
-            Ok(()),
+            None,
             &[0x03, 0x01],
             0,
         );
     }
 
     #[test]
     fn test_recv_change_capacity() {
-        test_instruction(0, &[0x3f, 0xa9, 0x01], Ok(()), &[0x03], 200);
+        test_instruction(0, &[0x3f, 0xa9, 0x01], None, &[0x03], 200);
     }
 
     #[test]
     fn test_recv_change_capacity_too_big() {
         test_instruction(
             0,
             &[0x3f, 0xf1, 0x02],
-            Err(Error::EncoderStreamError),
+            Some(Error::EncoderStreamError),
             &[0x03],
             0,
         );
     }
 
     // this test tests header decoding, the header acks command and the insert count increment command.
     #[test]
     fn test_duplicate() {
-        let mut decoder = connect();
+        let (mut decoder, mut conn_c, mut conn_s, recv_stream_id, send_stream_id) = connect();
 
-        assert!(decoder.decoder.set_capacity(100).is_ok());
+        assert!(decoder.set_capacity(100).is_ok());
 
-        // receive an instruction
-        recv_instruction(
-            &mut decoder,
+        // send an instruction
+        let _ = conn_s.stream_send(
+            recv_stream_id,
             &[
                 0x4e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67, 0x74,
                 0x68, 0x04, 0x31, 0x32, 0x33, 0x34,
             ],
-            Ok(()),
         );
+        let out = conn_s.process(None, now());
+        conn_c.process(out.dgram(), now());
+        assert!(decoder
+            .read_instructions(&mut conn_c, recv_stream_id)
+            .is_ok());
 
-        // receive the second instruction, a duplicate instruction.
-        recv_instruction(&mut decoder, &[0x00], Ok(()));
+        // send the second instruction, a duplicate instruction.
+        let _ = conn_s.stream_send(recv_stream_id, &[0x00]);
+        let out = conn_s.process(None, now());
+        conn_c.process(out.dgram(), now());
+        if decoder
+            .read_instructions(&mut conn_c, recv_stream_id)
+            .is_err()
+        {
+            panic!("failed to read")
+        }
 
-        send_instructions_and_check(&mut decoder, &[0x03, 0x02]);
+        decoder.send(&mut conn_c).unwrap();
+        let out = conn_c.process(None, now());
+        conn_s.process(out.dgram(), now());
+        let mut found_instruction = false;
+        while let Some(e) = conn_s.next_event() {
+            if let ConnectionEvent::RecvStreamReadable { stream_id } = e {
+                let mut buf = [0u8; 100];
+                let (amount, fin) = conn_s.stream_recv(stream_id, &mut buf).unwrap();
+                assert_eq!(fin, false);
+                assert_eq!(buf[..amount], [0x03, 0x02]);
+                found_instruction = true;
+            }
+        }
+        assert!(found_instruction);
     }
 
     struct TestElement {
         pub headers: Vec<Header>,
         pub header_block: &'static [u8],
         pub encoder_inst: &'static [u8],
     }
 
     #[test]
-    fn test_encode_incr_encode_header_ack_some() {
-        // 1. Decoder receives an instruction (header and value both as literal)
-        // 2. Decoder process the instruction and sends an increment instruction.
-        // 3. Decoder receives another two instruction (header and value both as literal) and
-        //    a header block.
-        // 4. Now it sends only a header ack and an increment instruction with increment==1.
-        let headers = vec![
-            (String::from("my-headera"), String::from("my-valuea")),
-            (String::from("my-headerb"), String::from("my-valueb")),
-        ];
-        let header_block = &[0x03, 0x81, 0x10, 0x11];
-        let first_encoder_inst = &[
-            0x4a, 0x6d, 0x79, 0x2d, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x61, 0x09, 0x6d, 0x79,
-            0x2d, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x61,
-        ];
-        let second_encoder_inst = &[
-            0x4a, 0x6d, 0x79, 0x2d, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x62, 0x09, 0x6d, 0x79,
-            0x2d, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x62, 0x4a, 0x6d, 0x79, 0x2d, 0x68, 0x65, 0x61,
-            0x64, 0x65, 0x72, 0x63, 0x09, 0x6d, 0x79, 0x2d, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x63,
-        ];
-
-        let mut decoder = connect();
-
-        assert!(decoder.decoder.set_capacity(200).is_ok());
-
-        recv_instruction(&mut decoder, first_encoder_inst, Ok(()));
-
-        send_instructions_and_check(&mut decoder, &[0x03, 0x1]);
-
-        recv_instruction(&mut decoder, second_encoder_inst, Ok(()));
-
-        decode_headers(&mut decoder, header_block, &headers, 0);
-
-        send_instructions_and_check(&mut decoder, &[0x80, 0x1]);
-    }
-
-    #[test]
-    fn test_encode_incr_encode_header_ack_all() {
-        // 1. Decoder receives an instruction (header and value both as literal)
-        // 2. Decoder process the instruction and sends an increment instruction.
-        // 3. Decoder receives another instruction (header and value both as literal) and
-        //    a header block.
-        // 4. Now it sends only a header ack.
-        let headers = vec![
-            (String::from("my-headera"), String::from("my-valuea")),
-            (String::from("my-headerb"), String::from("my-valueb")),
-        ];
-        let header_block = &[0x03, 0x81, 0x10, 0x11];
-        let first_encoder_inst = &[
-            0x4a, 0x6d, 0x79, 0x2d, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x61, 0x09, 0x6d, 0x79,
-            0x2d, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x61,
-        ];
-        let second_encoder_inst = &[
-            0x4a, 0x6d, 0x79, 0x2d, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x62, 0x09, 0x6d, 0x79,
-            0x2d, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x62,
-        ];
-
-        let mut decoder = connect();
-
-        assert!(decoder.decoder.set_capacity(200).is_ok());
-
-        recv_instruction(&mut decoder, first_encoder_inst, Ok(()));
-
-        send_instructions_and_check(&mut decoder, &[0x03, 0x1]);
-
-        recv_instruction(&mut decoder, second_encoder_inst, Ok(()));
-
-        decode_headers(&mut decoder, header_block, &headers, 0);
-
-        send_instructions_and_check(&mut decoder, &[0x80]);
-    }
-
-    #[test]
-    fn test_header_ack_all() {
-        // Send two instructions to insert values into the dynamic table and then send a header
-        // that references them both. The result should be only a header acknowledgement.
-        let headers = vec![
-            (String::from("my-headera"), String::from("my-valuea")),
-            (String::from("my-headerb"), String::from("my-valueb")),
-        ];
-        let header_block = &[0x03, 0x81, 0x10, 0x11];
-        let encoder_inst = &[
-            0x4a, 0x6d, 0x79, 0x2d, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x61, 0x09, 0x6d, 0x79,
-            0x2d, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x61, 0x4a, 0x6d, 0x79, 0x2d, 0x68, 0x65, 0x61,
-            0x64, 0x65, 0x72, 0x62, 0x09, 0x6d, 0x79, 0x2d, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x62,
-        ];
-
-        let mut decoder = connect();
-
-        assert!(decoder.decoder.set_capacity(200).is_ok());
-
-        recv_instruction(&mut decoder, encoder_inst, Ok(()));
-
-        decode_headers(&mut decoder, header_block, &headers, 0);
-
-        send_instructions_and_check(&mut decoder, &[0x03, 0x80]);
-    }
-
-    #[test]
-    fn test_header_ack_and_incr_instruction() {
-        // Send two instructions to insert values into the dynamic table and then send a header
-        // that references only the first. The result should be a header acknowledgement and a
-        // increment instruction.
-        let headers = vec![(String::from("my-headera"), String::from("my-valuea"))];
-        let header_block = &[0x02, 0x80, 0x10];
-        let encoder_inst = &[
-            0x4a, 0x6d, 0x79, 0x2d, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x61, 0x09, 0x6d, 0x79,
-            0x2d, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x61, 0x4a, 0x6d, 0x79, 0x2d, 0x68, 0x65, 0x61,
-            0x64, 0x65, 0x72, 0x62, 0x09, 0x6d, 0x79, 0x2d, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x62,
-        ];
-
-        let mut decoder = connect();
-
-        assert!(decoder.decoder.set_capacity(200).is_ok());
-
-        recv_instruction(&mut decoder, encoder_inst, Ok(()));
-
-        decode_headers(&mut decoder, header_block, &headers, 0);
-
-        send_instructions_and_check(&mut decoder, &[0x03, 0x80, 0x01]);
-    }
-
-    #[test]
     fn test_header_block_decoder() {
         let test_cases: [TestElement; 6] = [
             // test a header with ref to static - encode_indexed
             TestElement {
                 headers: vec![(String::from(":method"), String::from("GET"))],
                 header_block: &[0x00, 0x00, 0xd1],
                 encoder_inst: &[],
             },
@@ -1147,36 +1013,52 @@ mod tests {
                     0x00, 0x01, 0xd1, 0x51, 0x0a, 0x2f, 0x73, 0x6f, 0x6d, 0x65, 0x77, 0x68, 0x65,
                     0x72, 0x65, 0x50, 0x0b, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63,
                     0x6f, 0x6d, 0xd7,
                 ],
                 encoder_inst: &[],
             },
         ];
 
-        let mut decoder = connect();
-
-        assert!(decoder.decoder.set_capacity(200).is_ok());
+        let (mut decoder, mut conn_c, mut conn_s, recv_stream_id, send_stream_id) = connect();
 
+        assert!(decoder.set_capacity(200).is_ok());
         for (i, t) in test_cases.iter().enumerate() {
-            // receive an instruction
+            // send an instruction
             if !t.encoder_inst.is_empty() {
-                recv_instruction(&mut decoder, t.encoder_inst, Ok(()));
+                let _ = conn_s.stream_send(recv_stream_id, t.encoder_inst);
+                let out = conn_s.process(None, now());
+                conn_c.process(out.dgram(), now());
+                assert!(decoder
+                    .read_instructions(&mut conn_c, recv_stream_id)
+                    .is_ok());
             }
 
-            decode_headers(
-                &mut decoder,
-                t.header_block,
-                &t.headers,
-                i.try_into().unwrap(),
-            );
+            let headers = decoder
+                .decode_header_block(t.header_block, i.try_into().unwrap())
+                .unwrap();
+            let h = headers.unwrap();
+            assert_eq!(h, t.headers);
         }
 
         // test header acks and the insert count increment command
-        send_instructions_and_check(&mut decoder, &[0x03, 0x82, 0x83, 0x84]);
+        decoder.send(&mut conn_c).unwrap();
+        let out = conn_c.process(None, now());
+        conn_s.process(out.dgram(), now());
+        let mut found_instruction = false;
+        while let Some(e) = conn_s.next_event() {
+            if let ConnectionEvent::RecvStreamReadable { stream_id } = e {
+                let mut buf = [0u8; 100];
+                let (amount, fin) = conn_s.stream_recv(stream_id, &mut buf).unwrap();
+                assert_eq!(fin, false);
+                assert_eq!(buf[..amount], [0x03, 0x82, 0x83, 0x84, 0x1]);
+                found_instruction = true;
+            }
+        }
+        assert!(found_instruction);
     }
 
     #[test]
     fn test_header_block_decoder_huffman() {
         let test_cases: [TestElement; 6] = [
             // test a header with ref to static - encode_indexed
             TestElement {
                 headers: vec![(String::from(":method"), String::from("GET"))],
@@ -1225,30 +1107,47 @@ mod tests {
                 header_block: &[
                     0x00, 0x01, 0xd1, 0x51, 0x87, 0x61, 0x07, 0xa4, 0xbe, 0x27, 0x2d, 0x85, 0x50,
                     0x88, 0x2f, 0x91, 0xd3, 0x5d, 0x05, 0x5c, 0x87, 0xa7, 0xd7,
                 ],
                 encoder_inst: &[],
             },
         ];
 
-        let mut decoder = connect();
+        let (mut decoder, mut conn_c, mut conn_s, recv_stream_id, send_stream_id) = connect();
 
-        assert!(decoder.decoder.set_capacity(200).is_ok());
+        assert!(decoder.set_capacity(200).is_ok());
 
         for (i, t) in test_cases.iter().enumerate() {
-            // receive an instruction.
+            // send an instruction.
             if !t.encoder_inst.is_empty() {
-                recv_instruction(&mut decoder, t.encoder_inst, Ok(()));
+                let _ = conn_s.stream_send(recv_stream_id, t.encoder_inst);
+                let out = conn_s.process(None, now());
+                conn_c.process(out.dgram(), now());
+                // read the instruction.
+                assert!(decoder
+                    .read_instructions(&mut conn_c, recv_stream_id)
+                    .is_ok());
             }
 
-            decode_headers(
-                &mut decoder,
-                t.header_block,
-                &t.headers,
-                i.try_into().unwrap(),
-            );
+            let headers = decoder
+                .decode_header_block(t.header_block, i.try_into().unwrap())
+                .unwrap();
+            assert_eq!(headers.unwrap(), t.headers);
         }
 
         // test header acks and the insert count increment command
-        send_instructions_and_check(&mut decoder, &[0x03, 0x82, 0x83, 0x84]);
+        decoder.send(&mut conn_c).unwrap();
+        let out = conn_c.process(None, now());
+        conn_s.process(out.dgram(), now());
+        let mut found_instruction = false;
+        while let Some(e) = conn_s.next_event() {
+            if let ConnectionEvent::RecvStreamReadable { stream_id } = e {
+                let mut buf = [0u8; 100];
+                let (amount, fin) = conn_s.stream_recv(stream_id, &mut buf).unwrap();
+                assert_eq!(fin, false);
+                assert_eq!(buf[..amount], [0x03, 0x82, 0x83, 0x84, 0x1]);
+                found_instruction = true;
+            }
+        }
+        assert!(found_instruction);
     }
 }
--- a/third_party/rust/neqo-qpack/src/encoder.rs
+++ b/third_party/rust/neqo-qpack/src/encoder.rs
@@ -1,24 +1,24 @@
 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![allow(unused_variables, dead_code)]
+
 use crate::huffman::encode_huffman;
 use crate::qpack_helper::read_prefixed_encoded_int_with_connection;
 use crate::qpack_send_buf::QPData;
-use crate::table::{HeaderTable, LookupResult};
+use crate::table::HeaderTable;
 use crate::Header;
 use crate::{Error, Res};
 use neqo_common::{qdebug, qtrace};
 use neqo_transport::Connection;
-use std::collections::{HashMap, HashSet, VecDeque};
-use std::convert::TryInto;
 
 pub const QPACK_UNI_STREAM_TYPE_ENCODER: u64 = 0x2;
 
 #[derive(Debug)]
 enum DecoderInstructions {
     InsertCountIncrement,
     HeaderAck,
     StreamCancellation,
@@ -35,63 +35,61 @@ fn get_instruction(b: u8) -> DecoderInst
 }
 
 #[derive(Debug)]
 pub struct QPackEncoder {
     table: HeaderTable,
     send_buf: QPData,
     max_entries: u64,
     instruction_reader_current_inst: Option<DecoderInstructions>,
-    instruction_reader_value: u64, // this is instruction dependent value.
+    instruction_reader_value: u64, // this is instrunction dependent value.
     instruction_reader_cnt: u8, // this is helper variable for reading a prefixed integer encoded value
     local_stream_id: Option<u64>,
     remote_stream_id: Option<u64>,
     max_blocked_streams: u16,
-    // Remember header blocks that are referring to dynamic table.
-    // There can be multiple header blocks in one stream, headers, trailer, push stream request, etc.
-    // This HashMap maps a stream ID to a list of header blocks. Each header block is a list of
-    // referenced dynamic table entries.
-    unacked_header_blocks: HashMap<u64, VecDeque<HashSet<u64>>>,
-    blocked_stream_cnt: u16,
+    blocked_streams: Vec<u64>, // remember request insert counds for blocked streams.
+    // TODO we may also remember stream_id and use stream acks as indication that a stream has beed unblocked.
     use_huffman: bool,
 }
 
 impl QPackEncoder {
-    pub fn new(use_huffman: bool) -> Self {
-        Self {
+    pub fn new(use_huffman: bool) -> QPackEncoder {
+        QPackEncoder {
             table: HeaderTable::new(true),
             send_buf: QPData::default(),
             max_entries: 0,
             instruction_reader_current_inst: None,
             instruction_reader_value: 0,
             instruction_reader_cnt: 0,
             local_stream_id: None,
             remote_stream_id: None,
             max_blocked_streams: 0,
-            unacked_header_blocks: HashMap::new(),
-            blocked_stream_cnt: 0,
+            blocked_streams: Vec::new(),
             use_huffman,
         }
     }
 
     pub fn set_max_capacity(&mut self, cap: u64) -> Res<()> {
         if cap > (1 << 30) - 1 {
-            // TODO dragana check what is the correct error.
+            // TODO dragana check wat is the correct error.
             return Err(Error::EncoderStreamError);
         }
         qdebug!([self], "Set max capacity to {}.", cap);
         self.max_entries = (cap as f64 / 32.0).floor() as u64;
         // we also set our table to the max allowed. TODO we may not want to use max allowed.
-        self.change_capacity(cap)
+        self.change_capacity(cap);
+        Ok(())
     }
 
     pub fn set_max_blocked_streams(&mut self, blocked_streams: u64) -> Res<()> {
-        self.max_blocked_streams = blocked_streams
-            .try_into()
-            .or(Err(Error::EncoderStreamError))?;
+        if blocked_streams > (1 << 16) - 1 {
+            return Err(Error::EncoderStreamError);
+        }
+        qdebug!([self], "Set max blocked streams to {}.", blocked_streams);
+        self.max_blocked_streams = blocked_streams as u16;
         Ok(())
     }
 
     pub fn recv_if_encoder_stream(&mut self, conn: &mut Connection, stream_id: u64) -> Res<bool> {
         match self.remote_stream_id {
             Some(id) => {
                 if id == stream_id {
                     self.read_instructions(conn, stream_id)?;
@@ -133,17 +131,17 @@ impl QPackEncoder {
                         &mut self.instruction_reader_value,
                         &mut self.instruction_reader_cnt,
                         prefix_len,
                         b[0],
                         true,
                     ) {
                         Ok(done) => {
                             if done {
-                                self.call_instruction()?;
+                                self.call_instruction();
                             } else {
                                 // wait for more data.
                                 break Ok(());
                             }
                         }
                         Err(Error::ClosedCriticalStream) => break Err(Error::ClosedCriticalStream),
                         Err(_) => break Err(Error::EncoderStreamError),
                     }
@@ -155,166 +153,107 @@ impl QPackEncoder {
                         &mut self.instruction_reader_value,
                         &mut self.instruction_reader_cnt,
                         0,
                         0x0,
                         false,
                     ) {
                         Ok(done) => {
                             if done {
-                                self.call_instruction()?;
+                                self.call_instruction();
                             } else {
                                 // wait for more data.
                                 break Ok(());
                             }
                         }
                         Err(Error::ClosedCriticalStream) => break Err(Error::ClosedCriticalStream),
                         Err(_) => break Err(Error::EncoderStreamError),
                     }
                 }
             }
         }
     }
 
-    fn recalculate_blocked_streams(&mut self) {
-        let acked_inserts_cnt = self.table.get_acked_inserts_cnt();
-        self.blocked_stream_cnt = 0;
-        for (_, hb_list) in self.unacked_header_blocks.iter_mut() {
-            debug_assert!(!hb_list.is_empty());
-            if hb_list
-                .iter()
-                .flat_map(|hb| hb.iter())
-                .any(|e| *e >= acked_inserts_cnt)
-            {
-                self.blocked_stream_cnt += 1;
-            }
-        }
-    }
-
-    fn insert_count_instruction(&mut self, increment: u64) -> Res<()> {
-        self.table.increment_acked(increment)?;
-        self.recalculate_blocked_streams();
-        Ok(())
-    }
-
-    fn header_ack(&mut self, stream_id: u64) -> Res<()> {
-        let mut new_acked = self.table.get_acked_inserts_cnt();
-        if let Some(hb_list) = self.unacked_header_blocks.get_mut(&stream_id) {
-            if let Some(ref_list) = hb_list.pop_back() {
-                for iter in ref_list {
-                    self.table.remove_ref(iter);
-                    if iter >= new_acked {
-                        new_acked = iter + 1;
-                    }
-                }
-            } else {
-                debug_assert!(false, "We should have at least one header block.");
-            }
-            if hb_list.is_empty() {
-                self.unacked_header_blocks.remove(&stream_id);
-            }
-        }
-        if new_acked > self.table.get_acked_inserts_cnt() {
-            self.insert_count_instruction(new_acked - self.table.get_acked_inserts_cnt())
-                .expect("This should neve happen");
-        }
-        Ok(())
-    }
-
-    fn stream_cancellation(&mut self, stream_id: u64) -> Res<()> {
-        let mut was_blocker = false;
-        if let Some(hb_list) = self.unacked_header_blocks.get_mut(&stream_id) {
-            debug_assert!(!hb_list.is_empty());
-            while let Some(ref_list) = hb_list.pop_front() {
-                for iter in ref_list {
-                    self.table.remove_ref(iter);
-                    was_blocker = was_blocker || (iter >= self.table.get_acked_inserts_cnt());
-                }
-            }
-        }
-        if was_blocker {
-            debug_assert!(self.blocked_stream_cnt > 0);
-            self.blocked_stream_cnt -= 1;
-        }
-        Ok(())
-    }
-
-    fn call_instruction(&mut self) -> Res<()> {
+    fn call_instruction(&mut self) {
         if let Some(inst) = &self.instruction_reader_current_inst {
             qdebug!([self], "call intruction {:?}", inst);
             match inst {
                 DecoderInstructions::InsertCountIncrement => {
-                    self.insert_count_instruction(self.instruction_reader_value)?
+                    self.table.increment_acked(self.instruction_reader_value);
+                    let inserts = self.table.get_acked_inserts_cnt();
+                    self.blocked_streams.retain(|req| *req <= inserts);
                 }
-                DecoderInstructions::HeaderAck => self.header_ack(self.instruction_reader_value)?,
+                DecoderInstructions::HeaderAck => {
+                    self.table.header_ack(self.instruction_reader_value)
+                }
                 DecoderInstructions::StreamCancellation => {
-                    self.stream_cancellation(self.instruction_reader_value)?
+                    self.table.header_ack(self.instruction_reader_value)
                 }
-            };
+            }
             self.instruction_reader_current_inst = None;
             self.instruction_reader_value = 0;
             self.instruction_reader_cnt = 0;
         } else {
             panic!("We must have a instruction decoded beforewe call call_instruction");
         }
-        Ok(())
     }
 
     pub fn insert_with_name_ref(
         &mut self,
         name_static_table: bool,
         name_index: u64,
-        value: &[u8],
+        value: Vec<u8>,
     ) -> Res<()> {
         qdebug!(
             [self],
             "insert with name reference {} from {} value={:x?}.",
             name_index,
             if name_static_table {
                 "static table"
             } else {
                 "dynamic table"
             },
             value
         );
         self.table
             .insert_with_name_ref(name_static_table, name_index, value)?;
 
         // write instruction
+        let entry = self.table.get_last_added_entry().unwrap();
         let instr = 0x80 | (if name_static_table { 0x40 } else { 0x00 });
         self.send_buf
             .encode_prefixed_encoded_int(instr, 2, name_index);
-        encode_literal(self.use_huffman, &mut self.send_buf, 0x0, 0, value);
+        encode_literal(self.use_huffman, &mut self.send_buf, 0x0, 0, entry.value());
         Ok(())
     }
 
-    pub fn insert_with_name_literal(&mut self, name: &[u8], value: &[u8]) -> Res<u64> {
+    pub fn insert_with_name_literal(&mut self, name: Vec<u8>, value: Vec<u8>) -> Res<()> {
         qdebug!([self], "insert name {:x?}, value={:x?}.", name, value);
         // try to insert a new entry
-        let index = self.table.insert(name, value)?;
+        self.table.insert(name, value)?;
 
+        let entry = self.table.get_last_added_entry().unwrap();
         // encode instruction.
-        encode_literal(self.use_huffman, &mut self.send_buf, 0x40, 2, name);
-        encode_literal(self.use_huffman, &mut self.send_buf, 0x0, 0, value);
+        encode_literal(self.use_huffman, &mut self.send_buf, 0x40, 2, entry.name());
+        encode_literal(self.use_huffman, &mut self.send_buf, 0x0, 0, entry.value());
 
-        Ok(index)
+        Ok(())
     }
 
     pub fn duplicate(&mut self, index: u64) -> Res<()> {
         qdebug!([self], "duplicate entry {}.", index);
         self.table.duplicate(index)?;
         self.send_buf.encode_prefixed_encoded_int(0x00, 3, index);
         Ok(())
     }
 
-    pub fn change_capacity(&mut self, cap: u64) -> Res<()> {
+    pub fn change_capacity(&mut self, cap: u64) {
         qdebug!([self], "change capacity: {}", cap);
-        self.table.set_capacity(cap)?;
+        self.table.set_capacity(cap);
         self.send_buf.encode_prefixed_encoded_int(0x20, 3, cap);
-        Ok(())
     }
 
     pub fn send(&mut self, conn: &mut Connection) -> Res<()> {
         if self.send_buf.is_empty() {
             Ok(())
         } else if let Some(stream_id) = self.local_stream_id {
             match conn.stream_send(stream_id, &self.send_buf[..]) {
                 Err(_) => Err(Error::EncoderStreamError),
@@ -324,117 +263,120 @@ impl QPackEncoder {
                     Ok(())
                 }
             }
         } else {
             Ok(())
         }
     }
 
-    fn is_stream_blocker(&self, stream_id: u64) -> bool {
-        if let Some(hb_list) = self.unacked_header_blocks.get(&stream_id) {
-            debug_assert!(!hb_list.is_empty());
-            match hb_list.iter().flat_map(|hb| hb.iter()).max() {
-                Some(max_ref) => *max_ref >= self.table.get_acked_inserts_cnt(),
-                None => false,
-            }
-        } else {
-            false
-        }
-    }
-
     pub fn encode_header_block(&mut self, h: &[Header], stream_id: u64) -> QPData {
         qdebug!([self], "encoding headers.");
         let mut encoded_h = QPData::default();
         let base = self.table.base();
+        let mut req_insert_cnt = 0;
         self.encode_header_block_prefix(&mut encoded_h, false, 0, base, true);
-
-        let stream_is_blocker = self.is_stream_blocker(stream_id);
-        let can_block = self.blocked_stream_cnt < self.max_blocked_streams || stream_is_blocker;
-
-        let mut ref_entries = HashSet::new();
-
         for iter in h.iter() {
             let name = iter.0.clone().into_bytes();
             let value = iter.1.clone().into_bytes();
             qtrace!("encoding {:x?} {:x?}.", name, value);
 
-            if let Some(LookupResult {
-                index,
-                static_table,
-                value_matches,
-            }) = self.table.lookup(&name, &value, can_block)
+            let mut can_use = false;
+            let mut index: u64 = 0;
+            let mut value_as_well = false;
+            let mut is_dynamic = false;
+            let acked_inserts_cnt = self.table.get_acked_inserts_cnt(); // we need to read it here because of borrowing problem.
+            let can_be_blocked = self.blocked_streams.len() < self.max_blocked_streams as usize;
             {
-                qtrace!(
-                    [self],
-                    "found a {} entry, value-match={}",
-                    if static_table { "static" } else { "dynamic" },
-                    value_matches
-                );
-                if static_table {
-                    if value_matches {
+                let label = self.to_string();
+                // this is done in this way because otherwise it is complaining about mut borrow. TODO: look if we can do this better
+                let (e_s, e_d, found_value) = self.table.lookup(&name, &value);
+                if let Some(entry) = e_s {
+                    qtrace!([label], "found a static entry, value-match={}", found_value);
+                    can_use = true;
+                    index = entry.index();
+                    value_as_well = found_value;
+                }
+                if !can_use {
+                    if let Some(entry) = e_d {
+                        index = entry.index();
+                        can_use = index < acked_inserts_cnt || can_be_blocked;
+                        qtrace!(
+                            [label],
+                            "found a dynamic entry - can_use={} value-match={},",
+                            can_use,
+                            found_value
+                        );
+                        if can_use {
+                            value_as_well = found_value;
+                            is_dynamic = true;
+                            entry.add_ref(stream_id, 1);
+                        }
+                    }
+                }
+            }
+            if can_use {
+                if value_as_well {
+                    if !is_dynamic {
                         self.encode_indexed(&mut encoded_h, true, index);
+                    } else if index < base {
+                        self.encode_indexed(&mut encoded_h, false, base - index - 1);
                     } else {
-                        self.encode_literal_with_name_ref(&mut encoded_h, true, index, &value);
+                        self.encode_post_base_index(&mut encoded_h, index - base);
                     }
+                    if is_dynamic && req_insert_cnt < (index + 1) {
+                        req_insert_cnt = index + 1;
+                    }
+                    continue;
                 } else {
-                    if value_matches {
-                        if index < base {
-                            self.encode_indexed(&mut encoded_h, false, base - index - 1);
-                        } else {
-                            self.encode_post_base_index(&mut encoded_h, index - base);
-                        }
+                    if !is_dynamic {
+                        self.encode_literal_with_name_ref(&mut encoded_h, true, index, &value);
                     } else if index < base {
                         self.encode_literal_with_name_ref(
                             &mut encoded_h,
                             false,
                             base - index - 1,
                             &value,
                         );
                     } else {
                         self.encode_literal_with_post_based_name_ref(
                             &mut encoded_h,
                             index - base,
                             &value,
                         );
                     }
-                    ref_entries.insert(index);
+
+                    if is_dynamic && req_insert_cnt < (index + 1) {
+                        req_insert_cnt = index + 1;
+                    }
+                    continue;
                 }
-            } else if !can_block {
-                self.encode_literal_with_name_literal(&mut encoded_h, &name, &value);
-            } else {
-                match self.insert_with_name_literal(&name, &value) {
-                    Ok(index) => {
-                        self.encode_post_base_index(&mut encoded_h, index - base);
-                        ref_entries.insert(index);
+            }
+
+            let name2 = name.clone();
+            let value2 = value.clone();
+            match self.insert_with_name_literal(name2, value2) {
+                Err(_) => {
+                    self.encode_literal_with_name_literal(&mut encoded_h, &name, &value);
+                }
+                Ok(()) => {
+                    let index: u64;
+                    {
+                        let entry = self.table.get_last_added_entry().unwrap();
+                        entry.add_ref(stream_id, 1);
+                        index = entry.index();
                     }
-                    Err(_) => {
-                        self.encode_literal_with_name_literal(&mut encoded_h, &name, &value);
-                    }
+                    self.encode_post_base_index(&mut encoded_h, index - base);
+
+                    req_insert_cnt = index + 1;
                 }
             }
         }
-        for iter in &ref_entries {
-            self.table.add_ref(*iter);
-        }
-
-        if let Some(max_ref) = ref_entries.iter().max() {
-            self.fix_header_block_prefix(&mut encoded_h, base, *max_ref + 1);
-            // Check if it is already blocking
-            if !stream_is_blocker && *max_ref >= self.table.get_acked_inserts_cnt() {
-                debug_assert!(self.blocked_stream_cnt < self.max_blocked_streams);
-                self.blocked_stream_cnt += 1;
-            }
-        }
-
-        if !ref_entries.is_empty() {
-            self.unacked_header_blocks
-                .entry(stream_id)
-                .or_insert_with(VecDeque::new)
-                .push_front(ref_entries);
+        if req_insert_cnt > 0 {
+            self.fix_header_block_prefix(&mut encoded_h, base, req_insert_cnt);
         }
         encoded_h
     }
 
     fn encode_header_block_prefix(
         &self,
         buf: &mut QPData,
         fix: bool,
@@ -557,21 +499,16 @@ impl QPackEncoder {
         match self.remote_stream_id {
             Some(_) => Err(Error::WrongStreamCount),
             None => {
                 self.remote_stream_id = Some(stream_id);
                 Ok(())
             }
         }
     }
-
-    #[cfg(test)]
-    pub fn blocked_stream_cnt(&self) -> u16 {
-        self.blocked_stream_cnt
-    }
 }
 
 fn encode_literal(use_huffman: bool, buf: &mut QPData, prefix: u8, prefix_len: u8, value: &[u8]) {
     if use_huffman {
         let encoded = encode_huffman(value);
         buf.encode_prefixed_encoded_int(
             prefix | (0x80 >> prefix_len),
             prefix_len + 1,
@@ -588,183 +525,223 @@ impl ::std::fmt::Display for QPackEncode
     fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
         write!(f, "QPackEncoder")
     }
 }
 
 #[cfg(test)]
 mod tests {
     use super::*;
+    use neqo_transport::ConnectionEvent;
     use neqo_transport::StreamType;
     use test_fixture::*;
 
-    struct TestEncoder {
-        encoder: QPackEncoder,
-        send_stream_id: u64,
-        recv_stream_id: u64,
-        conn: Connection,
-        peer_conn: Connection,
-    }
-
-    fn connect(huffman: bool) -> TestEncoder {
-        let (mut conn, mut peer_conn) = test_fixture::connect();
+    fn connect(huffman: bool) -> (QPackEncoder, Connection, Connection, u64, u64) {
+        let (mut conn_c, mut conn_s) = test_fixture::connect();
 
         // create a stream
-        let recv_stream_id = peer_conn.stream_create(StreamType::UniDi).unwrap();
-        let send_stream_id = conn.stream_create(StreamType::UniDi).unwrap();
+        let recv_stream_id = conn_s.stream_create(StreamType::UniDi).unwrap();
+        let send_stream_id = conn_c.stream_create(StreamType::UniDi).unwrap();
 
         // create an encoder
         let mut encoder = QPackEncoder::new(huffman);
         encoder.add_send_stream(send_stream_id);
 
-        TestEncoder {
-            encoder,
-            send_stream_id,
-            recv_stream_id,
-            conn,
-            peer_conn,
-        }
-    }
-
-    fn send_instructions(encoder: &mut TestEncoder, encoder_instruction: &[u8]) {
-        encoder.encoder.send(&mut encoder.conn).unwrap();
-        let out = encoder.conn.process(None, now());
-        encoder.peer_conn.process(out.dgram(), now());
-        let mut buf = [0u8; 100];
-        let (amount, fin) = encoder
-            .peer_conn
-            .stream_recv(encoder.send_stream_id, &mut buf)
-            .unwrap();
-        assert_eq!(fin, false);
-        assert_eq!(buf[..amount], encoder_instruction[..]);
+        (encoder, conn_c, conn_s, recv_stream_id, send_stream_id)
     }
 
-    fn recv_instruction(encoder: &mut TestEncoder, decoder_instruction: &[u8]) {
-        encoder
-            .peer_conn
-            .stream_send(encoder.recv_stream_id, decoder_instruction)
-            .unwrap();
-        let out = encoder.peer_conn.process(None, now());
-        encoder.conn.process(out.dgram(), now());
-        assert!(encoder
-            .encoder
-            .read_instructions(&mut encoder.conn, encoder.recv_stream_id)
-            .is_ok());
+    fn test_sent_instructions(
+        encoder: &mut QPackEncoder,
+        mut conn_c: &mut Connection,
+        conn_s: &mut Connection,
+        recv_stream_id: u64,
+        send_stream_id: u64,
+        encoder_instruction: &[u8],
+    ) {
+        encoder.send(&mut conn_c).unwrap();
+        let out = conn_c.process(None, now());
+        conn_s.process(out.dgram(), now());
+        let mut found_instruction = false;
+        while let Some(e) = conn_s.next_event() {
+            if let ConnectionEvent::RecvStreamReadable { stream_id } = e {
+                let mut buf = [0u8; 100];
+                let (amount, fin) = conn_s.stream_recv(stream_id, &mut buf).unwrap();
+                assert_eq!(fin, false);
+                assert_eq!(buf[..amount], encoder_instruction[..]);
+                found_instruction = true;
+            }
+        }
+        assert_eq!(found_instruction, !encoder_instruction.is_empty());
     }
 
-    const CAP_INSTRUCTION_200: &[u8] = &[0x02, 0x3f, 0xa9, 0x01];
-    const CAP_INSTRUCTION_60: &[u8] = &[0x02, 0x3f, 0x1d];
-
-    const HEADER_CONTENT_LENGTH: &[u8] = &[
-        0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68,
-    ];
-    const VALUE_1: &[u8] = &[0x31, 0x32, 0x33, 0x34];
-    const VALUE_2: &[u8] = &[0x31, 0x32, 0x33, 0x34, 0x35];
-
-    // HEADER_CONTENT_LENGTH and VALUE_1 encoded by instruction insert_with_name_literal.
-    const HEADER_CONTENT_LENGTH_VALUE_1_NAME_LITERAL: &[u8] = &[
-        0x4e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68,
-        0x04, 0x31, 0x32, 0x33, 0x34,
-    ];
-
-    // HEADER_CONTENT_LENGTH and VALUE_2 encoded by instruction insert_with_name_literal.
-    const HEADER_CONTENT_LENGTH_VALUE_2_NAME_LITERAL: &[u8] = &[
-        0x4e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68,
-        0x05, 0x31, 0x32, 0x33, 0x34, 0x35,
-    ];
-
-    // Indexed Header Field that refers to the first entry in the dynamic table.
-    const ENCODE_INDEXED_REF_DYNAMIC: &[u8] = &[0x02, 0x00, 0x80];
-
-    const HEADER_ACK_STREAM_ID_1: &[u8] = &[0x81];
-    const HEADER_ACK_STREAM_ID_2: &[u8] = &[0x82];
-    const STREAM_CANCELED_ID_1: &[u8] = &[0x41];
-
     // test insert_with_name_ref which fails because there is not enough space in the table
     #[test]
     fn test_insert_with_name_ref_1() {
-        let mut encoder = connect(false);
+        let (mut encoder, mut conn_c, mut conn_s, recv_stream_id, send_stream_id) = connect(false);
         let e = encoder
-            .encoder
-            .insert_with_name_ref(true, 4, VALUE_1)
+            .insert_with_name_ref(true, 4, vec![0x31, 0x32, 0x33, 0x34])
             .unwrap_err();
         assert_eq!(Error::EncoderStreamError, e);
-        send_instructions(&mut encoder, &[0x02]);
+        test_sent_instructions(
+            &mut encoder,
+            &mut conn_c,
+            &mut conn_s,
+            recv_stream_id,
+            send_stream_id,
+            &[0x02],
+        );
     }
 
     // test insert_name_ref that succeeds
     #[test]
     fn test_insert_with_name_ref_2() {
-        let mut encoder = connect(false);
-        assert!(encoder.encoder.set_max_capacity(200).is_ok());
+        let (mut encoder, mut conn_c, mut conn_s, recv_stream_id, send_stream_id) = connect(false);
+        assert!(encoder.set_max_capacity(200).is_ok());
         // test the change capacity instruction.
-        send_instructions(&mut encoder, CAP_INSTRUCTION_200);
+        test_sent_instructions(
+            &mut encoder,
+            &mut conn_c,
+            &mut conn_s,
+            recv_stream_id,
+            send_stream_id,
+            &[0x02, 0x3f, 0xa9, 0x01],
+        );
 
         assert!(encoder
-            .encoder
-            .insert_with_name_ref(true, 4, VALUE_1)
+            .insert_with_name_ref(true, 4, vec![0x31, 0x32, 0x33, 0x34])
             .is_ok());
-        send_instructions(&mut encoder, &[0xc4, 0x04, 0x31, 0x32, 0x33, 0x34]);
+        test_sent_instructions(
+            &mut encoder,
+            &mut conn_c,
+            &mut conn_s,
+            recv_stream_id,
+            send_stream_id,
+            &[0xc4, 0x04, 0x31, 0x32, 0x33, 0x34],
+        );
     }
 
     // test insert_with_name_literal which fails because there is not enough space in the table
     #[test]
     fn test_insert_with_name_literal_1() {
-        let mut encoder = connect(false);
+        let (mut encoder, mut conn_c, mut conn_s, recv_stream_id, send_stream_id) = connect(false);
 
         // insert "content-length: 1234
-        let res = encoder
-            .encoder
-            .insert_with_name_literal(HEADER_CONTENT_LENGTH, VALUE_1);
+        let res = encoder.insert_with_name_literal(
+            vec![
+                0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68,
+            ],
+            vec![0x31, 0x32, 0x33, 0x34],
+        );
         assert_eq!(Error::EncoderStreamError, res.unwrap_err());
-        send_instructions(&mut encoder, &[0x02]);
+        test_sent_instructions(
+            &mut encoder,
+            &mut conn_c,
+            &mut conn_s,
+            recv_stream_id,
+            send_stream_id,
+            &[0x02],
+        );
     }
 
     // test insert_with_name_literal - succeeds
     #[test]
     fn test_insert_with_name_literal_2() {
-        let mut encoder = connect(false);
+        let (mut encoder, mut conn_c, mut conn_s, recv_stream_id, send_stream_id) = connect(false);
 
-        assert!(encoder.encoder.set_max_capacity(200).is_ok());
+        assert!(encoder.set_max_capacity(200).is_ok());
         // test the change capacity instruction.
-        send_instructions(&mut encoder, CAP_INSTRUCTION_200);
+        test_sent_instructions(
+            &mut encoder,
+            &mut conn_c,
+            &mut conn_s,
+            recv_stream_id,
+            send_stream_id,
+            &[0x02, 0x3f, 0xa9, 0x01],
+        );
 
         // insert "content-length: 1234
-        let res = encoder
-            .encoder
-            .insert_with_name_literal(HEADER_CONTENT_LENGTH, VALUE_1);
+        let res = encoder.insert_with_name_literal(
+            vec![
+                0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68,
+            ],
+            vec![0x31, 0x32, 0x33, 0x34],
+        );
         assert!(res.is_ok());
-        send_instructions(&mut encoder, HEADER_CONTENT_LENGTH_VALUE_1_NAME_LITERAL);
+        test_sent_instructions(
+            &mut encoder,
+            &mut conn_c,
+            &mut conn_s,
+            recv_stream_id,
+            send_stream_id,
+            &[
+                0x4e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67, 0x74,
+                0x68, 0x04, 0x31, 0x32, 0x33, 0x34,
+            ],
+        );
     }
 
     #[test]
     fn test_change_capacity() {
-        let mut encoder = connect(false);
+        let (mut encoder, mut conn_c, mut conn_s, recv_stream_id, send_stream_id) = connect(false);
 
-        assert!(encoder.encoder.set_max_capacity(200).is_ok());
-        send_instructions(&mut encoder, CAP_INSTRUCTION_200);
+        assert!(encoder.set_max_capacity(200).is_ok());
+        test_sent_instructions(
+            &mut encoder,
+            &mut conn_c,
+            &mut conn_s,
+            recv_stream_id,
+            send_stream_id,
+            &[0x02, 0x3f, 0xa9, 0x01],
+        );
     }
 
     #[test]
     fn test_duplicate() {
-        let mut encoder = connect(false);
+        let (mut encoder, mut conn_c, mut conn_s, recv_stream_id, send_stream_id) = connect(false);
 
-        assert!(encoder.encoder.set_max_capacity(200).is_ok());
+        assert!(encoder.set_max_capacity(200).is_ok());
         // test the change capacity instruction.
-        send_instructions(&mut encoder, CAP_INSTRUCTION_200);
+        test_sent_instructions(
+            &mut encoder,
+            &mut conn_c,
+            &mut conn_s,
+            recv_stream_id,
+            send_stream_id,
+            &[0x02, 0x3f, 0xa9, 0x01],
+        );
 
         // insert "content-length: 1234
-        let res = encoder
-            .encoder
-            .insert_with_name_literal(HEADER_CONTENT_LENGTH, VALUE_1);
+        let res = encoder.insert_with_name_literal(
+            vec![
+                0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68,
+            ],
+            vec![0x31, 0x32, 0x33, 0x34],
+        );
         assert!(res.is_ok());
-        send_instructions(&mut encoder, HEADER_CONTENT_LENGTH_VALUE_1_NAME_LITERAL);
+        test_sent_instructions(
+            &mut encoder,
+            &mut conn_c,
+            &mut conn_s,
+            recv_stream_id,
+            send_stream_id,
+            &[
+                0x4e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67, 0x74,
+                0x68, 0x04, 0x31, 0x32, 0x33, 0x34,
+            ],
+        );
 
-        assert!(encoder.encoder.duplicate(0).is_ok());
-        send_instructions(&mut encoder, &[0x00]);
+        assert!(encoder.duplicate(0).is_ok());
+        test_sent_instructions(
+            &mut encoder,
+            &mut conn_c,
+            &mut conn_s,
+            recv_stream_id,
+            send_stream_id,
+            &[0x00],
+        );
     }
 
     struct TestElement {
         pub headers: Vec<Header>,
         pub header_block: &'static [u8],
         pub encoder_inst: &'static [u8],
     }
 
@@ -793,17 +770,17 @@ mod tests {
                 encoder_inst: &[
                     0x49, 0x6d, 0x79, 0x2d, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x08, 0x6d, 0x79,
                     0x2d, 0x76, 0x61, 0x6c, 0x75, 0x65,
                 ],
             },
             // test encode_indexed with a ref to dynamic table.
             TestElement {
                 headers: vec![(String::from("my-header"), String::from("my-value"))],
-                header_block: ENCODE_INDEXED_REF_DYNAMIC,
+                header_block: &[0x02, 0x00, 0x80],
                 encoder_inst: &[],
             },
             // test encode_literal_with_name_ref.
             TestElement {
                 headers: vec![(String::from("my-header"), String::from("my-value2"))],
                 header_block: &[
                     0x02, 0x00, 0x40, 0x09, 0x6d, 0x79, 0x2d, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x32,
                 ],
@@ -821,28 +798,42 @@ mod tests {
                     0x00, 0x01, 0xd1, 0x51, 0x0a, 0x2f, 0x73, 0x6f, 0x6d, 0x65, 0x77, 0x68, 0x65,
                     0x72, 0x65, 0x50, 0x0b, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63,
                     0x6f, 0x6d, 0xd7,
                 ],
                 encoder_inst: &[],
             },
         ];
 
-        let mut encoder = connect(false);
+        let (mut encoder, mut conn_c, mut conn_s, recv_stream_id, send_stream_id) = connect(false);
 
-        encoder.encoder.set_max_blocked_streams(100).unwrap();
-        encoder.encoder.set_max_capacity(200).unwrap();
+        encoder.set_max_blocked_streams(100).unwrap();
+        encoder.set_max_capacity(200).unwrap();
 
         // test the change capacity instruction.
-        send_instructions(&mut encoder, CAP_INSTRUCTION_200);
+        test_sent_instructions(
+            &mut encoder,
+            &mut conn_c,
+            &mut conn_s,
+            recv_stream_id,
+            send_stream_id,
+            &[0x02, 0x3f, 0xa9, 0x01],
+        );
 
         for t in &test_cases {
-            let buf = encoder.encoder.encode_header_block(&t.headers, 1);
+            let buf = encoder.encode_header_block(&t.headers, 1);
             assert_eq!(&buf[..], t.header_block);
-            send_instructions(&mut encoder, t.encoder_inst);
+            test_sent_instructions(
+                &mut encoder,
+                &mut conn_c,
+                &mut conn_s,
+                recv_stream_id,
+                send_stream_id,
+                t.encoder_inst,
+            );
         }
     }
 
     #[test]
     fn test_header_block_encoder_huffman() {
         let test_cases: [TestElement; 6] = [
             // test a header with ref to static - encode_indexed
             TestElement {
@@ -865,17 +856,17 @@ mod tests {
                 encoder_inst: &[
                     0x67, 0xa7, 0xd2, 0xd3, 0x94, 0x72, 0x16, 0xcf, 0x86, 0xa7, 0xd2, 0xdd, 0xc7,
                     0x45, 0xa5,
                 ],
             },
             // test encode_indexed with a ref to dynamic table.
             TestElement {
                 headers: vec![(String::from("my-header"), String::from("my-value"))],
-                header_block: ENCODE_INDEXED_REF_DYNAMIC,
+                header_block: &[0x02, 0x00, 0x80],
                 encoder_inst: &[],
             },
             // test encode_literal_with_name_ref.
             TestElement {
                 headers: vec![(String::from("my-header"), String::from("my-value2"))],
                 header_block: &[
                     0x02, 0x00, 0x40, 0x87, 0xa7, 0xd2, 0xdd, 0xc7, 0x45, 0xa5, 0x17,
                 ],
@@ -892,605 +883,242 @@ mod tests {
                 header_block: &[
                     0x00, 0x01, 0xd1, 0x51, 0x87, 0x61, 0x07, 0xa4, 0xbe, 0x27, 0x2d, 0x85, 0x50,
                     0x88, 0x2f, 0x91, 0xd3, 0x5d, 0x05, 0x5c, 0x87, 0xa7, 0xd7,
                 ],
                 encoder_inst: &[],
             },
         ];
 
-        let mut encoder = connect(true);
+        let (mut encoder, mut conn_c, mut conn_s, recv_stream_id, send_stream_id) = connect(true);
 
-        encoder.encoder.set_max_blocked_streams(100).unwrap();
-        encoder.encoder.set_max_capacity(200).unwrap();
+        encoder.set_max_blocked_streams(100).unwrap();
+        encoder.set_max_capacity(200).unwrap();
 
         // test the change capacity instruction.
-        send_instructions(&mut encoder, CAP_INSTRUCTION_200);
+        test_sent_instructions(
+            &mut encoder,
+            &mut conn_c,
+            &mut conn_s,
+            recv_stream_id,
+            send_stream_id,
+            &[0x02, 0x3f, 0xa9, 0x01],
+        );
 
         for t in &test_cases {
-            let buf = encoder.encoder.encode_header_block(&t.headers, 1);
+            let buf = encoder.encode_header_block(&t.headers, 1);
             assert_eq!(&buf[..], t.header_block);
-            send_instructions(&mut encoder, t.encoder_inst);
+            test_sent_instructions(
+                &mut encoder,
+                &mut conn_c,
+                &mut conn_s,
+                recv_stream_id,
+                send_stream_id,
+                t.encoder_inst,
+            );
         }
     }
 
     // Test inserts block on waiting for an insert count increment.
     #[test]
     fn test_insertion_blocked_on_insert_count_feedback() {
-        let mut encoder = connect(false);
+        let (mut encoder, mut conn_c, mut conn_s, recv_stream_id, send_stream_id) = connect(false);
 
-        encoder.encoder.set_max_capacity(60).unwrap();
+        encoder.set_max_capacity(60).unwrap();
 
         // test the change capacity instruction.
-        send_instructions(&mut encoder, CAP_INSTRUCTION_60);
+        test_sent_instructions(
+            &mut encoder,
+            &mut conn_c,
+            &mut conn_s,
+            recv_stream_id,
+            send_stream_id,
+            &[0x02, 0x3f, 0x1d],
+        );
 
         // insert "content-length: 1234
-        let res = encoder
-            .encoder
-            .insert_with_name_literal(HEADER_CONTENT_LENGTH, VALUE_1);
+        let res = encoder.insert_with_name_literal(
+            vec![
+                0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68,
+            ],
+            vec![0x31, 0x32, 0x33, 0x34],
+        );
         assert!(res.is_ok());
-        send_instructions(&mut encoder, HEADER_CONTENT_LENGTH_VALUE_1_NAME_LITERAL);
+        test_sent_instructions(
+            &mut encoder,
+            &mut conn_c,
+            &mut conn_s,
+            recv_stream_id,
+            send_stream_id,
+            &[
+                0x4e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67, 0x74,
+                0x68, 0x04, 0x31, 0x32, 0x33, 0x34,
+            ],
+        );
 
         // insert "content-length: 12345 which will fail because the ntry in the table cannot be evicted.
-        let res = encoder
-            .encoder
-            .insert_with_name_literal(HEADER_CONTENT_LENGTH, VALUE_2);
+        let res = encoder.insert_with_name_literal(
+            vec![
+                0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68,
+            ],
+            vec![0x31, 0x32, 0x33, 0x34, 0x35],
+        );
         assert!(res.is_err());
-        send_instructions(&mut encoder, &[]);
+        test_sent_instructions(
+            &mut encoder,
+            &mut conn_c,
+            &mut conn_s,
+            recv_stream_id,
+            send_stream_id,
+            &[],
+        );
 
         // receive an insert count increment.
-        recv_instruction(&mut encoder, &[0x01]);
+        conn_s.stream_send(recv_stream_id, &[0x01]).unwrap();
+        let out = conn_s.process(None, now());
+        conn_c.process(out.dgram(), now());
+        assert!(encoder
+            .read_instructions(&mut conn_c, recv_stream_id)
+            .is_ok());
 
         // insert "content-length: 12345 again it will succeed.
-        let res = encoder
-            .encoder
-            .insert_with_name_literal(HEADER_CONTENT_LENGTH, VALUE_2);
+        let res = encoder.insert_with_name_literal(
+            vec![
+                0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68,
+            ],
+            vec![0x31, 0x32, 0x33, 0x34, 0x35],
+        );
         assert!(res.is_ok());
-        send_instructions(&mut encoder, HEADER_CONTENT_LENGTH_VALUE_2_NAME_LITERAL);
+        test_sent_instructions(
+            &mut encoder,
+            &mut conn_c,
+            &mut conn_s,
+            recv_stream_id,
+            send_stream_id,
+            &[
+                0x4e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67, 0x74,
+                0x68, 0x05, 0x31, 0x32, 0x33, 0x34, 0x35,
+            ],
+        );
     }
 
     // Test inserts block on waiting for acks
-    // test the table insertion is blocked:
-    // 0 - waiting for a header ack
-    // 2 - waiting for a stream cancel.
-    fn test_insertion_blocked_on_waiting_for_header_ack_or_stream_cancel(wait: u8) {
-        let mut encoder = connect(false);
+    // test the table inseriong blocking:
+    // 0 - waithing for a header ack
+    // 2 - waithing for a stream cancel.
+    fn test_insertion_blocked_on_waiting_forheader_ack_or_stream_cancel(wait: u8) {
+        let (mut encoder, mut conn_c, mut conn_s, recv_stream_id, send_stream_id) = connect(false);
 
-        assert!(encoder.encoder.set_max_capacity(60).is_ok());
+        assert!(encoder.set_max_capacity(60).is_ok());
         // test the change capacity instruction.
-        send_instructions(&mut encoder, CAP_INSTRUCTION_60);
+        test_sent_instructions(
+            &mut encoder,
+            &mut conn_c,
+            &mut conn_s,
+            recv_stream_id,
+            send_stream_id,
+            &[0x02, 0x3f, 0x1d],
+        );
 
         // insert "content-length: 1234
-        let res = encoder
-            .encoder
-            .insert_with_name_literal(HEADER_CONTENT_LENGTH, VALUE_1);
+        let res = encoder.insert_with_name_literal(
+            vec![
+                0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68,
+            ],
+            vec![0x31, 0x32, 0x33, 0x34],
+        );
         assert!(res.is_ok());
-        send_instructions(&mut encoder, HEADER_CONTENT_LENGTH_VALUE_1_NAME_LITERAL);
+        test_sent_instructions(
+            &mut encoder,
+            &mut conn_c,
+            &mut conn_s,
+            recv_stream_id,
+            send_stream_id,
+            &[
+                0x4e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67, 0x74,
+                0x68, 0x04, 0x31, 0x32, 0x33, 0x34,
+            ],
+        );
 
         // receive an insert count increment.
-        recv_instruction(&mut encoder, &[0x01]);
+        let _ = conn_s.stream_send(recv_stream_id, &[0x01]);
+        let out = conn_s.process(None, now());
+        conn_c.process(out.dgram(), now());
+        assert!(encoder
+            .read_instructions(&mut conn_c, recv_stream_id)
+            .is_ok());
 
         // send a header block
         let buf = encoder
-            .encoder
             .encode_header_block(&[(String::from("content-length"), String::from("1234"))], 1);
-        assert_eq!(&buf[..], ENCODE_INDEXED_REF_DYNAMIC);
-        send_instructions(&mut encoder, &[]);
+        assert_eq!(&buf[..], &[0x02, 0x00, 0x80]);
+        test_sent_instructions(
+            &mut encoder,
+            &mut conn_c,
+            &mut conn_s,
+            recv_stream_id,
+            send_stream_id,
+            &[],
+        );
 
-        // insert "content-length: 12345 which will fail because the entry in the table cannot be evicted
-        let res = encoder
-            .encoder
-            .insert_with_name_literal(HEADER_CONTENT_LENGTH, VALUE_2);
+        // insert "content-length: 12345 which will fail because the entry in the table cannot be evicted.
+        let res = encoder.insert_with_name_literal(
+            vec![
+                0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68,
+            ],
+            vec![0x31, 0x32, 0x33, 0x34, 0x35],
+        );
         assert!(res.is_err());
-        send_instructions(&mut encoder, &[]);
+        test_sent_instructions(
+            &mut encoder,
+            &mut conn_c,
+            &mut conn_s,
+            recv_stream_id,
+            send_stream_id,
+            &[],
+        );
 
         if wait == 0 {
             // receive a header_ack.
-            recv_instruction(&mut encoder, HEADER_ACK_STREAM_ID_1);
+            let _ = conn_s.stream_send(recv_stream_id, &[0x81]);
+            let out = conn_s.process(None, now());
+            conn_c.process(out.dgram(), now());
         } else {
-            // receive a stream canceled
-            recv_instruction(&mut encoder, STREAM_CANCELED_ID_1);
+            // reveice a stream canceled
+            let _ = conn_s.stream_send(recv_stream_id, &[0x41]);
+            let out = conn_s.process(None, now());
+            conn_c.process(out.dgram(), now());
         }
+        assert!(encoder
+            .read_instructions(&mut conn_c, recv_stream_id)
+            .is_ok());
 
         // insert "content-length: 12345 again it will succeed.
-        let res = encoder
-            .encoder
-            .insert_with_name_literal(HEADER_CONTENT_LENGTH, VALUE_2);
+        let res = encoder.insert_with_name_literal(
+            vec![
+                0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68,
+            ],
+            vec![0x31, 0x32, 0x33, 0x34, 0x35],
+        );
         assert!(res.is_ok());
-        send_instructions(&mut encoder, HEADER_CONTENT_LENGTH_VALUE_2_NAME_LITERAL);
+        test_sent_instructions(
+            &mut encoder,
+            &mut conn_c,
+            &mut conn_s,
+            recv_stream_id,
+            send_stream_id,
+            &[
+                0x4e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67, 0x74,
+                0x68, 0x05, 0x31, 0x32, 0x33, 0x34, 0x35,
+            ],
+        );
     }
 
     #[test]
     fn test_header_ack() {
-        test_insertion_blocked_on_waiting_for_header_ack_or_stream_cancel(0);
+        test_insertion_blocked_on_waiting_forheader_ack_or_stream_cancel(0);
     }
 
     #[test]
     fn test_stream_canceled() {
-        test_insertion_blocked_on_waiting_for_header_ack_or_stream_cancel(1);
-    }
-
-    fn assert_is_index_to_dynamic(buf: &[u8]) {
-        assert_eq!(buf[2] & 0xc0, 0x80);
-    }
-
-    fn assert_is_index_to_dynamic_post(buf: &[u8]) {
-        assert_eq!(buf[2] & 0xf0, 0x10);
-    }
-
-    fn assert_is_index_to_static_name_only(buf: &[u8]) {
-        assert_eq!(buf[2] & 0xf0, 0x50);
-    }
-
-    fn assert_is_literal_value_literal_name(buf: &[u8]) {
-        assert_eq!(buf[2] & 0xf0, 0x20);
-    }
-
-    #[test]
-    fn max_block_streams1() {
-        let mut encoder = connect(false);
-
-        assert!(encoder.encoder.set_max_capacity(60).is_ok());
-
-        // change capacity to 60.
-        send_instructions(&mut encoder, CAP_INSTRUCTION_60);
-
-        // insert "content-length: 1234
-        let res = encoder
-            .encoder
-            .insert_with_name_literal(HEADER_CONTENT_LENGTH, VALUE_1);
-
-        assert!(res.is_ok());
-        send_instructions(&mut encoder, HEADER_CONTENT_LENGTH_VALUE_1_NAME_LITERAL);
-
-        encoder.encoder.set_max_blocked_streams(1).unwrap();
-
-        // send a header block, it refers to unacked entry.
-        let buf = encoder
-            .encoder
-            .encode_header_block(&[(String::from("content-length"), String::from("1234"))], 1);
-        assert_is_index_to_dynamic(&buf);
-
-        assert_eq!(encoder.encoder.blocked_stream_cnt(), 1);
-
-        send_instructions(&mut encoder, &[]);
-
-        // The next one will not use the dynamic entry because it is exceeding the max_blocked_streams
-        // limit.
-        let buf = encoder
-            .encoder
-            .encode_header_block(&[(String::from("content-length"), String::from("1234"))], 2);
-        assert_is_index_to_static_name_only(&buf);
-
-        send_instructions(&mut encoder, &[]);
-        assert_eq!(encoder.encoder.blocked_stream_cnt(), 1);
-
-        // another header block to already blocked stream can still use the entry.
-        let buf = encoder
-            .encoder
-            .encode_header_block(&[(String::from("content-length"), String::from("1234"))], 1);
-        assert_is_index_to_dynamic(&buf);
-
-        assert_eq!(encoder.encoder.blocked_stream_cnt(), 1);
-    }
-
-    #[test]
-    fn max_block_streams2() {
-        let mut encoder = connect(false);
-
-        assert!(encoder.encoder.set_max_capacity(200).is_ok());
-
-        // change capacity to 200.
-        send_instructions(&mut encoder, CAP_INSTRUCTION_200);
-
-        // insert "content-length: 1234
-        let res = encoder
-            .encoder
-            .insert_with_name_literal(HEADER_CONTENT_LENGTH, VALUE_1);
-
-        assert!(res.is_ok());
-        send_instructions(&mut encoder, HEADER_CONTENT_LENGTH_VALUE_1_NAME_LITERAL);
-
-        // insert "content-length: 12345
-        let res = encoder
-            .encoder
-            .insert_with_name_literal(HEADER_CONTENT_LENGTH, VALUE_2);
-
-        assert!(res.is_ok());
-        send_instructions(&mut encoder, HEADER_CONTENT_LENGTH_VALUE_2_NAME_LITERAL);
-
-        encoder.encoder.set_max_blocked_streams(1).unwrap();
-
-        let stream_id = 1;
-        // send a header block, it refers to unacked entry.
-        let buf = encoder.encoder.encode_header_block(
-            &[(String::from("content-length"), String::from("1234"))],
-            stream_id,
-        );
-        assert_is_index_to_dynamic(&buf);
-
-        // encode another header block for the same stream that will refer to the second entry
-        // in the dynamic table.
-        // This should work because the stream is already a blocked stream
-        // send a header block, it refers to unacked entry.
-        let buf = encoder.encoder.encode_header_block(
-            &[(String::from("content-length"), String::from("12345"))],
-            stream_id,
-        );
-        assert_is_index_to_dynamic(&buf);
-    }
-
-    #[test]
-    fn max_block_streams3() {
-        let mut encoder = connect(false);
-
-        assert!(encoder.encoder.set_max_capacity(200).is_ok());
-
-        // change capacity to 200.
-        send_instructions(&mut encoder, CAP_INSTRUCTION_200);
-
-        encoder.encoder.set_max_blocked_streams(1).unwrap();
-
-        assert_eq!(encoder.encoder.blocked_stream_cnt(), 0);
-
-        // send a header block, that creates an new entry and refers to it.
-        let buf = encoder
-            .encoder
-            .encode_header_block(&[(String::from("name1"), String::from("value1"))], 1);
-        assert_is_index_to_dynamic_post(&buf);
-
-        assert_eq!(encoder.encoder.blocked_stream_cnt(), 1);
-
-        // The next one will not create a new entry because the encoder is on max_blocked_streams limit.
-        let buf = encoder
-            .encoder
-            .encode_header_block(&[(String::from("name2"), String::from("value2"))], 2);
-        assert_is_literal_value_literal_name(&buf);
-
-        assert_eq!(encoder.encoder.blocked_stream_cnt(), 1);
-
-        // another header block to already blocked stream can still create a new entry.
-        let buf = encoder
-            .encoder
-            .encode_header_block(&[(String::from("name2"), String::from("value2"))], 1);
-        assert_is_index_to_dynamic_post(&buf);
-
-        assert_eq!(encoder.encoder.blocked_stream_cnt(), 1);
-    }
-
-    #[test]
-    fn max_block_streams4() {
-        let mut encoder = connect(false);
-
-        assert!(encoder.encoder.set_max_capacity(200).is_ok());
-
-        // change capacity to 200.
-        send_instructions(&mut encoder, CAP_INSTRUCTION_200);
-
-        encoder.encoder.set_max_blocked_streams(1).unwrap();
-
-        assert_eq!(encoder.encoder.blocked_stream_cnt(), 0);
-
-        // send a header block, that creates an new entry and refers to it.
-        let buf = encoder
-            .encoder
-            .encode_header_block(&[(String::from("name1"), String::from("value1"))], 1);
-        assert_is_index_to_dynamic_post(&buf);
-
-        assert_eq!(encoder.encoder.blocked_stream_cnt(), 1);
-
-        // another header block to already blocked stream can still create a new entry.
-        let buf = encoder
-            .encoder
-            .encode_header_block(&[(String::from("name2"), String::from("value2"))], 1);
-        assert_is_index_to_dynamic_post(&buf);
-
-        assert_eq!(encoder.encoder.blocked_stream_cnt(), 1);
-
-        // receive a header_ack for the first header block.
-        recv_instruction(&mut encoder, HEADER_ACK_STREAM_ID_1);
-
-        // The stream is still blocking because the second header block is not acked.
-        assert_eq!(encoder.encoder.blocked_stream_cnt(), 1);
-    }
-
-    #[test]
-    fn max_block_streams5() {
-        let mut encoder = connect(false);
-
-        assert!(encoder.encoder.set_max_capacity(200).is_ok());
-
-        // change capacity to 200.
-        send_instructions(&mut encoder, CAP_INSTRUCTION_200);
-
-        encoder.encoder.set_max_blocked_streams(1).unwrap();
-
-        assert_eq!(encoder.encoder.blocked_stream_cnt(), 0);
-
-        // send a header block, that creates an new entry and refers to it.
-        let buf = encoder
-            .encoder
-            .encode_header_block(&[(String::from("name1"), String::from("value1"))], 1);
-        assert_is_index_to_dynamic_post(&buf);
-
-        assert_eq!(encoder.encoder.blocked_stream_cnt(), 1);
-
-        // another header block to already blocked stream can still create a new entry.
-        let buf = encoder
-            .encoder
-            .encode_header_block(&[(String::from("name1"), String::from("value1"))], 1);
-        assert_is_index_to_dynamic(&buf);
-
-        assert_eq!(encoder.encoder.blocked_stream_cnt(), 1);
-
-        // receive a header_ack for the first header block.
-        recv_instruction(&mut encoder, HEADER_ACK_STREAM_ID_1);
-
-        // The stream is not blocking anymore because header ack also acks the instruction.
-        assert_eq!(encoder.encoder.blocked_stream_cnt(), 0);
-    }
-
-    #[test]
-    fn max_block_streams6() {
-        let mut encoder = connect(false);
-
-        assert!(encoder.encoder.set_max_capacity(200).is_ok());
-
-        // change capacity to 200.
-        send_instructions(&mut encoder, CAP_INSTRUCTION_200);
-
-        encoder.encoder.set_max_blocked_streams(2).unwrap();
-
-        assert_eq!(encoder.encoder.blocked_stream_cnt(), 0);
-
-        // send a header block, that creates an new entry and refers to it.
-        let buf = encoder
-            .encoder
-            .encode_header_block(&[(String::from("name1"), String::from("value1"))], 1);
-        assert_is_index_to_dynamic_post(&buf);
-
-        assert_eq!(encoder.encoder.blocked_stream_cnt(), 1);
-
-        // header block for the next stream will create an new entry as well.
-        let buf = encoder
-            .encoder
-            .encode_header_block(&[(String::from("name2"), String::from("value2"))], 2);
-        assert_is_index_to_dynamic_post(&buf);
-
-        assert_eq!(encoder.encoder.blocked_stream_cnt(), 2);
-
-        // receive a header_ack for the second header block. This will ack the first as well
-        recv_instruction(&mut encoder, HEADER_ACK_STREAM_ID_2);
-
-        // The stream is not blocking anymore because header ack also acks the instruction.
-        assert_eq!(encoder.encoder.blocked_stream_cnt(), 0);
-    }
-
-    #[test]
-    fn max_block_streams7() {
-        let mut encoder = connect(false);
-
-        assert!(encoder.encoder.set_max_capacity(200).is_ok());
-
-        // change capacity to 200.
-        send_instructions(&mut encoder, CAP_INSTRUCTION_200);
-
-        encoder.encoder.set_max_blocked_streams(2).unwrap();
-
-        assert_eq!(encoder.encoder.blocked_stream_cnt(), 0);
-
-        // send a header block, that creates an new entry and refers to it.
-        let buf = encoder
-            .encoder
-            .encode_header_block(&[(String::from("name1"), String::from("value1"))], 1);
-        assert_is_index_to_dynamic_post(&buf);
-
-        assert_eq!(encoder.encoder.blocked_stream_cnt(), 1);
-
-        // header block for the next stream will create an new entry as well.
-        let buf = encoder
-            .encoder
-            .encode_header_block(&[(String::from("name1"), String::from("value1"))], 2);
-        assert_is_index_to_dynamic(&buf);
-
-        assert_eq!(encoder.encoder.blocked_stream_cnt(), 2);
-
-        // receive a stream cancel for the first stream.
-        // This will remove the first stream as blocking but it will not mark the instruction as acked.
-        // and the second steam will still be blocking.
-        recv_instruction(&mut encoder, STREAM_CANCELED_ID_1);
-
-        // The stream is not blocking anymore because header ack also acks the instruction.
-        assert_eq!(encoder.encoder.blocked_stream_cnt(), 1);
-    }
-
-    #[test]
-    fn max_block_stream8() {
-        let mut encoder = connect(false);
-
-        assert!(encoder.encoder.set_max_capacity(200).is_ok());
-
-        // change capacity to 200.
-        send_instructions(&mut encoder, CAP_INSTRUCTION_200);
-
-        encoder.encoder.set_max_blocked_streams(2).unwrap();
-
-        assert_eq!(encoder.encoder.blocked_stream_cnt(), 0);
-
-        // send a header block, that creates an new entry and refers to it.
-        let buf = encoder
-            .encoder
-            .encode_header_block(&[(String::from("name1"), String::from("value1"))], 1);
-        assert_is_index_to_dynamic_post(&buf);
-
-        assert_eq!(encoder.encoder.blocked_stream_cnt(), 1);
-
-        // header block for the next stream will refer to the same entry.
-        let buf = encoder
-            .encoder
-            .encode_header_block(&[(String::from("name1"), String::from("value1"))], 2);
-        assert_is_index_to_dynamic(&buf);
-
-        assert_eq!(encoder.encoder.blocked_stream_cnt(), 2);
-
-        // send another header block on stream 1.
-        let buf = encoder
-            .encoder
-            .encode_header_block(&[(String::from("name2"), String::from("value2"))], 1);
-        assert_is_index_to_dynamic_post(&buf);
-
-        assert_eq!(encoder.encoder.blocked_stream_cnt(), 2);
-
-        // stream 1 is block on entries 1 and 2; stream 2 is block only on 1.
-        // receive an Insert Count Increment for the first entry.
-        // After that only stream 1 will be blocking.
-        recv_instruction(&mut encoder, &[0x01]);
-
-        assert_eq!(encoder.encoder.blocked_stream_cnt(), 1);
-    }
-
-    #[test]
-    fn dynamic_table_can_evict1() {
-        let mut encoder = connect(false);
-
-        assert!(encoder.encoder.set_max_capacity(60).is_ok());
-
-        // change capacity to 60.
-        send_instructions(&mut encoder, CAP_INSTRUCTION_60);
-
-        encoder.encoder.set_max_blocked_streams(2).unwrap();
-
-        // insert "content-length: 1234
-        let res = encoder
-            .encoder
-            .insert_with_name_literal(HEADER_CONTENT_LENGTH, VALUE_1);
-
-        assert!(res.is_ok());
-        send_instructions(&mut encoder, HEADER_CONTENT_LENGTH_VALUE_1_NAME_LITERAL);
-
-        // send a header block, it refers to unacked entry.
-        let buf = encoder
-            .encoder
-            .encode_header_block(&[(String::from("content-length"), String::from("1234"))], 1);
-        assert_is_index_to_dynamic(&buf);
-
-        // trying to evict the entry will failed.
-        assert!(encoder.encoder.set_max_capacity(10).is_err());
-
-        // receive an Insert Count Increment for the entry.
-        recv_instruction(&mut encoder, &[0x01]);
-
-        // trying to evict the entry will failed. The stream is still referring to it.
-        assert!(encoder.encoder.set_max_capacity(10).is_err());
-
-        // receive a header_ack for the header block.
-        recv_instruction(&mut encoder, HEADER_ACK_STREAM_ID_1);
-
-        // now entry can be evicted.
-        assert!(encoder.encoder.set_max_capacity(10).is_ok());
-    }
-
-    #[test]
-    fn dynamic_table_can_evict2() {
-        let mut encoder = connect(false);
-
-        assert!(encoder.encoder.set_max_capacity(60).is_ok());
-
-        // change capacity to 60.
-        send_instructions(&mut encoder, CAP_INSTRUCTION_60);
-
-        encoder.encoder.set_max_blocked_streams(2).unwrap();
-
-        // insert "content-length: 1234
-        let res = encoder
-            .encoder
-            .insert_with_name_literal(HEADER_CONTENT_LENGTH, VALUE_1);
-
-        assert!(res.is_ok());
-        send_instructions(&mut encoder, HEADER_CONTENT_LENGTH_VALUE_1_NAME_LITERAL);
-
-        // send a header block, it refers to unacked entry.
-        let buf = encoder
-            .encoder
-            .encode_header_block(&[(String::from("content-length"), String::from("1234"))], 1);
-        assert_is_index_to_dynamic(&buf);
-
-        // trying to evict the entry will failed.
-        assert!(encoder.encoder.set_max_capacity(10).is_err());
-
-        // receive an Insert Count Increment for the entry.
-        recv_instruction(&mut encoder, &[0x01]);
-
-        // trying to evict the entry will failed. The stream is still referring to it.
-        assert!(encoder.encoder.set_max_capacity(10).is_err());
-
-        // receive a stream cancelled.
-        recv_instruction(&mut encoder, STREAM_CANCELED_ID_1);
-
-        // now entry can be evicted.
-        assert!(encoder.encoder.set_max_capacity(10).is_ok());
-    }
-
-    #[test]
-    fn dynamic_table_can_evict3() {
-        let mut encoder = connect(false);
-
-        assert!(encoder.encoder.set_max_capacity(60).is_ok());
-
-        // change capacity to 60.
-        send_instructions(&mut encoder, CAP_INSTRUCTION_60);
-
-        encoder.encoder.set_max_blocked_streams(2).unwrap();
-
-        // insert "content-length: 1234
-        let res = encoder
-            .encoder
-            .insert_with_name_literal(HEADER_CONTENT_LENGTH, VALUE_1);
-
-        assert!(res.is_ok());
-        send_instructions(&mut encoder, HEADER_CONTENT_LENGTH_VALUE_1_NAME_LITERAL);
-
-        // trying to evict the entry will failed, because the entry is not acked.
-        assert!(encoder.encoder.set_max_capacity(10).is_err());
-
-        // receive an Insert Count Increment for the entry.
-        recv_instruction(&mut encoder, &[0x01]);
-
-        // now entry can be evicted.
-        assert!(encoder.encoder.set_max_capacity(10).is_ok());
-    }
-
-    #[test]
-    fn dynamic_table_can_evict4() {
-        let mut encoder = connect(false);
-
-        assert!(encoder.encoder.set_max_capacity(60).is_ok());
-
-        // change capacity to 60.
-        send_instructions(&mut encoder, CAP_INSTRUCTION_60);
-
-        encoder.encoder.set_max_blocked_streams(2).unwrap();
-
-        // insert "content-length: 1234
-        let res = encoder
-            .encoder
-            .insert_with_name_literal(HEADER_CONTENT_LENGTH, VALUE_1);
-
-        assert!(res.is_ok());
-        send_instructions(&mut encoder, HEADER_CONTENT_LENGTH_VALUE_1_NAME_LITERAL);
-
-        // send a header block, it refers to unacked entry.
-        let buf = encoder
-            .encoder
-            .encode_header_block(&[(String::from("content-length"), String::from("1234"))], 1);
-        assert_is_index_to_dynamic(&buf);
-
-        // trying to evict the entry will failed. The stream is still referring to it and
-        // entry is not acked.
-        assert!(encoder.encoder.set_max_capacity(10).is_err());
-
-        // receive a header_ack for the header block. This will also ack the instruction.
-        recv_instruction(&mut encoder, HEADER_ACK_STREAM_ID_1);
-
-        // now entry can be evicted.
-        assert!(encoder.encoder.set_max_capacity(10).is_ok());
+        test_insertion_blocked_on_waiting_forheader_ack_or_stream_cancel(1);
     }
 }
--- a/third_party/rust/neqo-qpack/src/lib.rs
+++ b/third_party/rust/neqo-qpack/src/lib.rs
@@ -1,16 +1,15 @@
 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
 #![cfg_attr(feature = "deny-warnings", deny(warnings))]
-#![warn(clippy::use_self)]
 
 pub mod decoder;
 pub mod encoder;
 pub mod huffman;
 mod huffman_decode_helper;
 pub mod huffman_table;
 pub mod qpack_helper;
 mod qpack_send_buf;
@@ -21,58 +20,51 @@ pub type Header = (String, String);
 type Res<T> = Result<T, Error>;
 
 #[derive(Debug)]
 enum QPackSide {
     Encoder,
     Decoder,
 }
 
-impl ::std::fmt::Display for QPackSide {
-    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
-        write!(f, "{:?}", self)
-    }
-}
-
 #[derive(Clone, Debug, PartialEq)]
 pub enum Error {
     DecompressionFailed,
     EncoderStreamError,
     DecoderStreamError,
     ClosedCriticalStream,
 
     // These are internal errors, they will be transfromed into one of the above.
     HeaderLookupError,
     NoMoreData,
     IntegerOverflow,
     WrongStreamCount,
-    InternalError,
 
     TransportError(neqo_transport::Error),
 }
 
 impl Error {
     pub fn code(&self) -> neqo_transport::AppError {
         // TODO(mt): use real codes once QPACK defines some.
         3
     }
 }
 
 impl ::std::error::Error for Error {
     fn source(&self) -> Option<&(dyn ::std::error::Error + 'static)> {
         match self {
-            Self::TransportError(e) => Some(e),
+            Error::TransportError(e) => Some(e),
             _ => None,
         }
     }
 }
 
 impl ::std::fmt::Display for Error {
     fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
         write!(f, "QPACK error: {:?}", self)
     }
 }
 
 impl From<neqo_transport::Error> for Error {
     fn from(err: neqo_transport::Error) -> Self {
-        Self::TransportError(err)
+        Error::TransportError(err)
     }
 }
--- a/third_party/rust/neqo-qpack/src/table.rs
+++ b/third_party/rust/neqo-qpack/src/table.rs
@@ -1,51 +1,41 @@
 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
 use crate::static_table::{StaticTableEntry, HEADER_STATIC_TABLE};
 use crate::{Error, QPackSide, Res};
-use neqo_common::qtrace;
-use std::collections::VecDeque;
-use std::convert::TryFrom;
-
-pub struct LookupResult {
-    pub index: u64,
-    pub static_table: bool,
-    pub value_matches: bool,
-}
+use std::collections::{HashMap, VecDeque};
 
 #[derive(Debug)]
 pub struct DynamicTableEntry {
     base: u64,
     name: Vec<u8>,
     value: Vec<u8>,
-    /// Number of streams that refer this entry.
-    refs: u64,
+    refs: HashMap<u64, u8>, //TODO multiple header. value will be used for that: or of flags 0x1 for headers, ox2 for trailes.
 }
 
 impl DynamicTableEntry {
     pub fn can_reduce(&self, first_not_acked: u64) -> bool {
-        self.refs == 0 && self.base < first_not_acked
+        self.refs.is_empty() && self.base < first_not_acked
     }
 
     pub fn size(&self) -> u64 {
         (self.name.len() + self.value.len() + 32) as u64
     }
 
-    pub fn add_ref(&mut self) {
-        self.refs += 1;
+    pub fn add_ref(&mut self, stream_id: u64, _block: u8) {
+        self.refs.insert(stream_id, 1);
     }
 
-    pub fn remove_ref(&mut self) {
-        assert!(self.refs > 0);
-        self.refs -= 1;
+    pub fn remove_ref(&mut self, stream_id: u64, _block: u8) {
+        self.refs.remove(&stream_id);
     }
 
     pub fn name(&self) -> &[u8] {
         &self.name
     }
 
     pub fn value(&self) -> &[u8] {
         &self.value
@@ -55,271 +45,196 @@ impl DynamicTableEntry {
         self.base
     }
 }
 
 #[derive(Debug)]
 pub struct HeaderTable {
     qpack_side: QPackSide,
     dynamic: VecDeque<DynamicTableEntry>,
-    // The total capacity (in QPACK bytes) of the table. This is set by
+    // The total capacity (in HPACK bytes) of the table. This is set by
     // configuration.
     capacity: u64,
     // The amount of used capacity.
     used: u64,
     // The total number of inserts thus far.
     base: u64,
-    // This is number of inserts that are acked. this correspond to index of the first not acked.
     acked_inserts_cnt: u64,
 }
 
-impl ::std::fmt::Display for HeaderTable {
-    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
-        write!(f, "HeaderTable for {}", self.qpack_side)
-    }
-}
-
 impl HeaderTable {
-    pub fn new(encoder: bool) -> Self {
-        Self {
+    pub fn new(encoder: bool) -> HeaderTable {
+        HeaderTable {
             qpack_side: if encoder {
                 QPackSide::Encoder
             } else {
                 QPackSide::Decoder
             },
             dynamic: VecDeque::new(),
             capacity: 0,
             used: 0,
             base: 0,
-            acked_inserts_cnt: 0,
+            acked_inserts_cnt: if encoder { 0 } else { std::u64::MAX },
         }
     }
 
     pub fn base(&self) -> u64 {
         self.base
     }
 
     pub fn capacity(&self) -> u64 {
         self.capacity
     }
 
-    pub fn set_capacity(&mut self, cap: u64) -> Res<()> {
-        qtrace!([self], "set capacity to {}", cap);
-        if !self.evict_to(cap) {
-            return Err(Error::InternalError);
-        }
-        self.capacity = cap;
-        Ok(())
+    pub fn set_capacity(&mut self, c: u64) {
+        self.evict_to(c);
+        self.capacity = c;
     }
 
     pub fn get_static(&self, index: u64) -> Res<&StaticTableEntry> {
         if index > HEADER_STATIC_TABLE.len() as u64 {
             return Err(Error::HeaderLookupError);
         }
         let res = &HEADER_STATIC_TABLE[index as usize];
         Ok(res)
     }
 
-    fn get_dynamic_with_abs_index(&mut self, index: u64) -> Res<&mut DynamicTableEntry> {
-        if self.base <= index {
-            debug_assert!(false, "This is an iternal error");
-            return Err(Error::InternalError);
-        }
-        let inx = self.base - index - 1;
-        let inx = usize::try_from(inx).or(Err(Error::HeaderLookupError))?;
-        if inx >= self.dynamic.len() {
-            return Err(Error::HeaderLookupError);
-        }
-        Ok(&mut self.dynamic[inx])
-    }
-
-    fn get_dynamic_with_relative_index(&self, index: u64) -> Res<&DynamicTableEntry> {
-        let inx = usize::try_from(index).or(Err(Error::HeaderLookupError))?;
-        if inx >= self.dynamic.len() {
-            return Err(Error::HeaderLookupError);
-        }
-        Ok(&self.dynamic[inx])
-    }
-
-    pub fn get_dynamic(&self, index: u64, base: u64, post: bool) -> Res<&DynamicTableEntry> {
+    pub fn get_dynamic(&self, i: u64, base: u64, post: bool) -> Res<&DynamicTableEntry> {
         if self.base < base {
             return Err(Error::HeaderLookupError);
         }
         let inx: u64;
         let base_rel = self.base - base;
         if post {
-            if base_rel <= index {
+            if base_rel <= i {
                 return Err(Error::HeaderLookupError);
             }
-            inx = base_rel - index - 1;
+            inx = base_rel - i - 1;
         } else {
-            inx = base_rel + index;
+            inx = base_rel + i;
         }
-
-        self.get_dynamic_with_relative_index(inx)
+        if inx as usize >= self.dynamic.len() {
+            return Err(Error::HeaderLookupError);
+        }
+        let res = &self.dynamic[inx as usize];
+        Ok(res)
     }
 
-    pub fn remove_ref(&mut self, index: u64) {
-        qtrace!([self], "remove reference to entry {}", index);
-        self.get_dynamic_with_abs_index(index)
-            .expect("we should have the entry")
-            .remove_ref();
+    pub fn get_last_added_entry(&mut self) -> Option<&mut DynamicTableEntry> {
+        self.dynamic.front_mut()
     }
 
-    pub fn add_ref(&mut self, index: u64) {
-        qtrace!([self], "add reference to entry {}", index);
-        self.get_dynamic_with_abs_index(index)
-            .expect("we should have the entry")
-            .add_ref();
-    }
-
-    pub fn lookup(&mut self, name: &[u8], value: &[u8], can_block: bool) -> Option<LookupResult> {
-        qtrace!(
-            [self],
-            "lookup name:{:?} value {:?} can_block={}",
-            name,
-            value,
-            can_block
-        );
-        let mut name_match = None;
+    // separate lookups because static entries can not be return mut and we need dynamic entries mutable.
+    pub fn lookup(
+        &mut self,
+        name: &[u8],
+        value: &[u8],
+    ) -> (
+        Option<&StaticTableEntry>,
+        Option<&mut DynamicTableEntry>,
+        bool,
+    ) {
+        let mut name_match_s: Option<&StaticTableEntry> = None;
         for iter in HEADER_STATIC_TABLE.iter() {
             if iter.name() == name {
                 if iter.value() == value {
-                    return Some(LookupResult {
-                        index: iter.index(),
-                        static_table: true,
-                        value_matches: true,
-                    });
+                    return (Some(iter), None, true);
                 }
 
-                if name_match.is_none() {
-                    name_match = Some(LookupResult {
-                        index: iter.index(),
-                        static_table: true,
-                        value_matches: false,
-                    });
+                if name_match_s.is_none() {
+                    name_match_s = Some(iter);
                 }
             }
         }
 
+        let mut name_match_d: Option<&mut DynamicTableEntry> = None;
         for iter in self.dynamic.iter_mut() {
-            if !can_block && iter.index() >= self.acked_inserts_cnt {
-                continue;
-            }
             if iter.name == name {
                 if iter.value == value {
-                    return Some(LookupResult {
-                        index: iter.index(),
-                        static_table: false,
-                        value_matches: true,
-                    });
+                    return (None, Some(iter), true);
                 }
 
-                if name_match.is_none() {
-                    name_match = Some(LookupResult {
-                        index: iter.index(),
-                        static_table: false,
-                        value_matches: false,
-                    });
+                if name_match_s.is_none() && name_match_d.is_none() {
+                    name_match_d = Some(iter);
                 }
             }
         }
-        name_match
+
+        (name_match_s, name_match_d, false)
     }
 
     pub fn evict_to(&mut self, reduce: u64) -> bool {
-        qtrace!(
-            [self],
-            "reduce table to {}, currently used:{}",
-            reduce,
-            self.used
-        );
         while (!self.dynamic.is_empty()) && self.used > reduce {
             if let Some(e) = self.dynamic.back() {
-                if let QPackSide::Encoder = self.qpack_side {
-                    if !e.can_reduce(self.acked_inserts_cnt) {
-                        return false;
-                    }
+                if !e.can_reduce(self.acked_inserts_cnt) {
+                    return false;
                 }
                 self.used -= e.size();
                 self.dynamic.pop_back();
             }
         }
         true
     }
 
-    pub fn insert(&mut self, name: &[u8], value: &[u8]) -> Res<u64> {
-        qtrace!([self], "insert name={:?} value={:?}", name, value);
+    pub fn insert(&mut self, name: Vec<u8>, value: Vec<u8>) -> Res<()> {
         let entry = DynamicTableEntry {
-            name: name.to_vec(),
-            value: value.to_vec(),
+            name,
+            value,
             base: self.base,
-            refs: 0,
+            refs: HashMap::new(),
         };
+
         if entry.size() > self.capacity || !self.evict_to(self.capacity - entry.size()) {
             match self.qpack_side {
                 QPackSide::Encoder => return Err(Error::EncoderStreamError),
                 QPackSide::Decoder => return Err(Error::DecoderStreamError),
             }
         }
         self.base += 1;
         self.used += entry.size();
-        let index = entry.index();
         self.dynamic.push_front(entry);
-        Ok(index)
+        Ok(())
     }
 
     pub fn insert_with_name_ref(
         &mut self,
         name_static_table: bool,
         name_index: u64,
-        value: &[u8],
+        value: Vec<u8>,
     ) -> Res<()> {
-        qtrace!(
-            [self],
-            "insert with ref to index={} in {} value={:?}",
-            name_index,
-            if name_static_table {
-                "static"
-            } else {
-                "dynamic"
-            },
-            value
-        );
-        let name = if name_static_table {
-            self.get_static(name_index)?.name().to_vec()
+        let name;
+        if name_static_table {
+            let entry = self.get_static(name_index)?;
+            name = entry.name().to_vec()
         } else {
-            self.get_dynamic(name_index, self.base, false)?
-                .name()
-                .to_vec()
-        };
-        self.insert(&name, value)?;
-        Ok(())
+            let entry = self.get_dynamic(name_index, self.base, false)?;
+            name = entry.name().to_vec();
+        }
+        self.insert(name, value)
     }
 
     pub fn duplicate(&mut self, index: u64) -> Res<()> {
-        qtrace!([self], "dumplicate entry={}", index);
-        // need to remember name and value because insert may delete the entry.
+        // need to remember name and value because inser may delete the entry.
         let name: Vec<u8>;
         let value: Vec<u8>;
         {
             let entry = self.get_dynamic(index, self.base, false)?;
             name = entry.name().to_vec();
             value = entry.value().to_vec();
-            qtrace!([self], "dumplicate name={:?} value={:?}", name, value);
         }
-        self.insert(&name, &value)?;
+        self.insert(name, value)?;
         Ok(())
     }
 
-    pub fn increment_acked(&mut self, increment: u64) -> Res<()> {
-        qtrace!([self], "increment acked by {}", increment);
+    pub fn increment_acked(&mut self, increment: u64) {
         self.acked_inserts_cnt += increment;
-        if self.base < self.acked_inserts_cnt {
-            return Err(Error::InternalError);
-        }
-        Ok(())
     }
 
     pub fn get_acked_inserts_cnt(&self) -> u64 {
         self.acked_inserts_cnt
     }
+
+    pub fn header_ack(&mut self, stream_id: u64) {
+        for iter in self.dynamic.iter_mut() {
+            iter.remove_ref(stream_id, 1);
+        }
+    }
 }
--- a/third_party/rust/neqo-transport/.cargo-checksum.json
+++ b/third_party/rust/neqo-transport/.cargo-checksum.json
@@ -1,1 +1,1 @@
-{"files":{"Cargo.toml":"b8d9d003559098683d11011382a1232f0982679348f133e57b9c9995e945505b","TODO":"d759cb804b32fa9d96ea8d3574a3c4073da9fe6a0b02b708a0e22cce5a5b4a0f","src/cc.rs":"9fa6bebf5fc6d9dab2b53cff38f1ea88e6cc20b1c7dc9c55a30bc75599306c34","src/cid.rs":"4161a5add9285a9f670c4d0b88ac84833b710cd99cbd5ec080f4d2f097200abf","src/connection.rs":"5300a6a55bf32fd3abdd0857455ea10a28b93e1f6ae69ef81a283915b90be49d","src/crypto.rs":"2cf82dcdac073a5bd318c6ad2843fd48faf3ce752ae5e613cc1dbac60cd0367d","src/dump.rs":"d69ccb0e3b240823b886a791186afbac9f2e26d1f1f67a55dbf86f8cd3e6203e","src/events.rs":"07b1fa18efc538b96736ebfedba929b4854dffd460e1250ae02dc79cc86bb310","src/flow_mgr.rs":"0b1c6e7587e411635723207ecface6c62d1243599dd017745c61eb94805b9886","src/frame.rs":"2859a30e4889fd6b4124b9f88affcec5956f8f3914ccc7684525bfad085ef076","src/lib.rs":"dbaaf47b1025a5d976ceff86989e6d8729e993e525a3ef1d59046d45c0a09bf1","src/packet.rs":"a3b0b0414e8ddaddfe098fa343a89449b42fbb1ae44468d04994becebd7ab5cc","src/recovery.rs":"887a963dbc6e987caba0d74c0ce6b71212b96f87078cb1086ded560c4b930834","src/recv_stream.rs":"0a0c44a3414e6088a704c1a245fd98dd8a8ed502d80bfab90d2defe039bd37cb","src/send_stream.rs":"0c3e401bb6a7ea7babe47234d7a05c993f4a3c67f07f4130d81a8476085e746f","src/server.rs":"ca82b8bbfae29cf2fb6aadf4298d8482a734edbd460e569766d16b596aac0554","src/stats.rs":"a276bd9902939091578d3bcc95aa7dd0b32f5d29e528c12e7b93a0ab88474478","src/stream_id.rs":"b3158cf2c6072da79bf6e77a31f71f2f3b970429221142a9ff1dd6cd07df2442","src/tparams.rs":"4b328b0b146f06d805fbc77748f9cb578f9ee4a0c63565158bf36d7bd3151020","src/tracking.rs":"0df46cd244afc32ca3aef1dcd8eb9abcce364a330bd8053a2484740bb5b2b3fd","tests/conn_vectors.rs":"d42db769518162bb3e39667a999ab467541c72d7a7d42e92adbd39c16eca0811","tests/connection.rs":"a93985c199a9ef987106f4a20b35ebf803cdbbb855c07b1362b403eed7101ef8","tests/server.rs":"d516bf134a63377c98ff4ac2cca2d4cc6562f48ea262b10a23866d38c4c82af3"},"package":null}
\ No newline at end of file
+{"files":{"Cargo.toml":"e0f8a00f0862504bdc858abae1599f0403efb3cb69b54a3d0ea1031891790698","TODO":"d759cb804b32fa9d96ea8d3574a3c4073da9fe6a0b02b708a0e22cce5a5b4a0f","src/connection.rs":"e3af2eb9ab351538399e60f684641b6471f0d70e7c58543ed272d1f63176c4bd","src/crypto.rs":"606b705d2c91591bf91b56910f9839804ffa00d64e1b777562470c8a388ab86e","src/dump.rs":"e4058d89acf50c3dea7e9f067e6fa1d8abfe0a65a77acf187f5012b53ed2568d","src/events.rs":"07b1fa18efc538b96736ebfedba929b4854dffd460e1250ae02dc79cc86bb310","src/flow_mgr.rs":"a85aebc35358258ff5ede98cbc41a4af57d4f5528d3a168bcedbb0ff86fc9660","src/frame.rs":"13850d329895d3a44d4ba4f99ea4a0cd8f6b325361505f41ac373278e7a57f9e","src/lib.rs":"b2b8a2f67c96305870b05d7b73cff50e0f347061493480ed7a77f86b5e48149d","src/packet.rs":"9cb94fc6031d7f9590de53d6b3260b9d43fae297837a527752422453b8099436","src/recovery.rs":"66e92afd09c2aa97f606262ca65b6dfbc7a2a5f73aee9ae452d50564aea39a09","src/recv_stream.rs":"caed6677abc1bbd08ce57abf7182b6a151c31c7cb660622344ac50e96ab58653","src/send_stream.rs":"ef450a2e3e51815f50cb5016c257a02d0161a876519214cf4a71eb5bce54aa89","src/server.rs":"d391a1d585bb1e45d025cdd1adb25f986128302178926b71e17dce8105346dda","src/stats.rs":"dca5afcb6252f3f32f494513f76964cffb945afd6d18b8669dea98a7aeed1689","src/stream_id.rs":"b3158cf2c6072da79bf6e77a31f71f2f3b970429221142a9ff1dd6cd07df2442","src/tparams.rs":"325be72a070b22e03a5e9bf16c65fae4bf1835ca8b04d36b78baea3daee340f8","src/tracking.rs":"7bd00282689b7cb8d96c44105f83cd5739b46683d4aba6f58a5328d53ed18fb0","tests/conn_vectors.rs":"c594ea1c65ded6281ae1cc900cc4afa0143daa5c004adadbe29eb1ab900d8d71","tests/connection.rs":"e86725e3f59b30b9ea70e7961a5ded3ed36522f3bfcf2b7df1f502e18863121c","tests/server.rs":"938aed8ef27d5ff2dc01bf84ecb31691943c3ebe6299eab43f266de935c542da"},"package":null}
\ No newline at end of file
--- a/third_party/rust/neqo-transport/Cargo.toml
+++ b/third_party/rust/neqo-transport/Cargo.toml
@@ -1,19 +1,20 @@
 [package]
 name = "neqo-transport"
-version = "0.1.14"
+version = "0.1.12"
 authors = ["EKR <ekr@rtfm.com>", "Andy Grover <agrover@mozilla.com>"]
 edition = "2018"
 license = "MIT/Apache-2.0"
 
 [dependencies]
 neqo-crypto = { path = "../neqo-crypto" }
 neqo-common = { path = "../neqo-common" }
-lazy_static = "1.3.0"
+lazy_static = "1.0"
+rand = "0.7"
 log = "0.4.0"
 smallvec = "1.0.0"
 
 [dev-dependencies]
 test-fixture = { path = "../test-fixture" }
 
 [features]
 default = ["deny-warnings"]
deleted file mode 100644
--- a/third_party/rust/neqo-transport/src/cc.rs
+++ /dev/null
@@ -1,196 +0,0 @@
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// Congestion control
-
-use std::cmp::max;
-use std::fmt::{self, Display};
-use std::time::{Duration, Instant};
-
-use crate::tracking::SentPacket;
-use neqo_common::{const_max, const_min, qdebug, qinfo, qtrace};
-
-pub const MAX_DATAGRAM_SIZE: usize = 1232; // For ipv6, smaller than ipv4 (1252)
-pub const INITIAL_CWND_PKTS: usize = 10;
-const INITIAL_WINDOW: usize = const_min(
-    INITIAL_CWND_PKTS * MAX_DATAGRAM_SIZE,
-    const_max(2 * MAX_DATAGRAM_SIZE, 14720),
-);
-pub const MIN_CONG_WINDOW: usize = MAX_DATAGRAM_SIZE * 2;
-const PERSISTENT_CONG_THRESH: u32 = 3;
-
-#[derive(Debug)]
-pub struct CongestionControl {
-    congestion_window: usize, // = kInitialWindow
-    bytes_in_flight: usize,
-    congestion_recovery_start_time: Option<Instant>,
-    ssthresh: usize,
-}
-
-impl Default for CongestionControl {
-    fn default() -> Self {
-        Self {
-            congestion_window: INITIAL_WINDOW,
-            bytes_in_flight: 0,
-            congestion_recovery_start_time: None,
-            ssthresh: std::usize::MAX,
-        }
-    }
-}
-
-impl Display for CongestionControl {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(
-            f,
-            "CongCtrl {}/{} ssthresh {}",
-            self.bytes_in_flight, self.congestion_window, self.ssthresh
-        )
-    }
-}
-
-impl CongestionControl {
-    #[cfg(test)]
-    #[must_use]
-    pub fn cwnd(&self) -> usize {
-        self.congestion_window
-    }
-
-    #[cfg(test)]
-    #[must_use]
-    pub fn ssthresh(&self) -> usize {
-        self.ssthresh
-    }
-
-    #[must_use]
-    pub fn cwnd_avail(&self) -> usize {
-        // BIF can be higher than cwnd due to PTO packets, which are sent even
-        // if avail is 0, but still count towards BIF.
-        self.congestion_window.saturating_sub(self.bytes_in_flight)
-    }
-
-    // Multi-packet version of OnPacketAckedCC
-    pub fn on_packets_acked(&mut self, acked_pkts: &[SentPacket]) {
-        for pkt in acked_pkts
-            .iter()
-            .filter(|pkt| pkt.in_flight)
-            .filter(|pkt| pkt.time_declared_lost.is_none())
-        {
-            assert!(self.bytes_in_flight >= pkt.size);
-            self.bytes_in_flight -= pkt.size;
-
-            if self.in_congestion_recovery(pkt.time_sent) {
-                // Do not increase congestion window in recovery period.
-                continue;
-            }
-            if self.app_limited() {
-                // Do not increase congestion_window if application limited.
-                continue;
-            }
-
-            if self.congestion_window < self.ssthresh {
-                self.congestion_window += pkt.size;
-                qinfo!([self], "slow start");
-            } else {
-                self.congestion_window += (MAX_DATAGRAM_SIZE * pkt.size) / self.congestion_window;
-                qinfo!([self], "congestion avoidance");
-            }
-        }
-    }
-
-    pub fn on_packets_lost(
-        &mut self,
-        now: Instant,
-        largest_acked_sent: Option<Instant>,
-        pto: Duration,
-        lost_packets: &[SentPacket],
-    ) {
-        if lost_packets.is_empty() {
-            return;
-        }
-
-        for pkt in lost_packets.iter().filter(|pkt| pkt.in_flight) {
-            assert!(self.bytes_in_flight >= pkt.size);
-            self.bytes_in_flight -= pkt.size;
-        }
-
-        qdebug!([self], "Pkts lost {}", lost_packets.len());
-
-        let last_lost_pkt = lost_packets.last().unwrap();
-        self.on_congestion_event(now, last_lost_pkt.time_sent);
-
-        let in_persistent_congestion = {
-            let congestion_period = pto * PERSISTENT_CONG_THRESH;
-
-            match largest_acked_sent {
-                Some(las) => las < last_lost_pkt.time_sent - congestion_period,
-                None => {
-                    // Nothing has ever been acked. Could still be PC.
-                    let first_lost_pkt_sent = lost_packets.first().unwrap().time_sent;
-                    last_lost_pkt.time_sent - first_lost_pkt_sent > congestion_period
-                }
-            }
-        };
-        if in_persistent_congestion {
-            qinfo!([self], "persistent congestion");
-            self.congestion_window = MIN_CONG_WINDOW;
-        }
-    }
-
-    pub fn discard(&mut self, pkt: &SentPacket) {
-        if pkt.in_flight {
-            assert!(self.bytes_in_flight >= pkt.size);
-            self.bytes_in_flight -= pkt.size;
-            qtrace!([self], "Ignore pkt with size {}", pkt.size);
-        }
-    }
-
-    pub fn on_packet_sent(&mut self, pkt: &SentPacket) {
-        if !pkt.in_flight {
-            return;
-        }
-
-        self.bytes_in_flight += pkt.size;
-        qdebug!(
-            [self],
-            "Pkt Sent len {}, bif {}, cwnd {}",
-            pkt.size,
-            self.bytes_in_flight,
-            self.congestion_window
-        );
-        debug_assert!(self.bytes_in_flight <= self.congestion_window);
-    }
-
-    #[must_use]
-    pub fn in_congestion_recovery(&self, sent_time: Instant) -> bool {
-        self.congestion_recovery_start_time
-            .map(|start| sent_time <= start)
-            .unwrap_or(false)
-    }
-
-    fn on_congestion_event(&mut self, now: Instant, sent_time: Instant) {
-        // Start a new congestion event if packet was sent after the
-        // start of the previous congestion recovery period.
-        if !self.in_congestion_recovery(sent_time) {
-            self.congestion_recovery_start_time = Some(now);
-            self.congestion_window /= 2; // kLossReductionFactor = 0.5
-            self.congestion_window = max(self.congestion_window, MIN_CONG_WINDOW);
-            self.ssthresh = self.congestion_window;
-            qinfo!(
-                [self],
-                "Cong event -> recovery; cwnd {}, ssthresh {}",
-                self.congestion_window,
-                self.ssthresh
-            );
-        } else {
-            qdebug!([self], "Cong event but already in recovery");
-        }
-    }
-
-    fn app_limited(&self) -> bool {
-        //TODO(agrover): how do we get this info??
-        false
-    }
-}
deleted file mode 100644
--- a/third_party/rust/neqo-transport/src/cid.rs
+++ /dev/null
@@ -1,149 +0,0 @@
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// Encoding and decoding packets off the wire.
-
-use neqo_common::{hex, matches, Decoder};
-use neqo_crypto::random;
-
-use std::borrow::Borrow;
-use std::cmp::max;
-
-#[derive(Clone, Default, Eq, Hash, PartialEq)]
-pub struct ConnectionId {
-    pub(crate) cid: Vec<u8>,
-}
-
-impl ConnectionId {
-    pub fn generate(len: usize) -> Self {
-        assert!(matches!(len, 0..=20));
-        Self { cid: random(len) }
-    }
-
-    // Apply a wee bit of greasing here in picking a length between 8 and 20 bytes long.
-    pub fn generate_initial() -> Self {
-        let v = random(1);
-        // Bias selection toward picking 8 (>50% of the time).
-        let len: usize = max(8, 5 + (v[0] & (v[0] >> 4))).into();
-        Self::generate(len)
-    }
-
-    pub fn as_ref(&self) -> ConnectionIdRef {
-        ConnectionIdRef::from(&self.cid[..])
-    }
-}
-
-impl Borrow<[u8]> for ConnectionId {
-    fn borrow(&self) -> &[u8] {
-        &self.cid
-    }
-}
-
-impl From<&[u8]> for ConnectionId {
-    fn from(buf: &[u8]) -> Self {
-        Self {
-            cid: Vec::from(buf),
-        }
-    }
-}
-
-impl<'a> From<&ConnectionIdRef<'a>> for ConnectionId {
-    fn from(cidref: &ConnectionIdRef<'a>) -> Self {
-        Self {
-            cid: Vec::from(cidref.cid),
-        }
-    }
-}
-
-impl std::ops::Deref for ConnectionId {
-    type Target = [u8];
-
-    fn deref(&self) -> &Self::Target {
-        &self.cid
-    }
-}
-
-impl ::std::fmt::Debug for ConnectionId {
-    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
-        write!(f, "CID {}", hex(&self.cid))
-    }
-}
-
-impl ::std::fmt::Display for ConnectionId {
-    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
-        write!(f, "{}", hex(&self.cid))
-    }
-}
-
-impl<'a> PartialEq<ConnectionIdRef<'a>> for ConnectionId {
-    fn eq(&self, other: &ConnectionIdRef<'a>) -> bool {
-        &self.cid[..] == other.cid
-    }
-}
-
-#[derive(Hash, Eq, PartialEq)]
-pub struct ConnectionIdRef<'a> {
-    cid: &'a [u8],
-}
-
-impl<'a> ::std::fmt::Debug for ConnectionIdRef<'a> {
-    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
-        write!(f, "CID {}", hex(&self.cid))
-    }
-}
-
-impl<'a> ::std::fmt::Display for ConnectionIdRef<'a> {
-    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
-        write!(f, "{}", hex(&self.cid))
-    }
-}
-
-impl<'a> From<&'a [u8]> for ConnectionIdRef<'a> {
-    fn from(cid: &'a [u8]) -> Self {
-        Self { cid }
-    }
-}
-
-impl<'a> std::ops::Deref for ConnectionIdRef<'a> {
-    type Target = [u8];
-
-    fn deref(&self) -> &Self::Target {
-        &self.cid
-    }
-}
-
-impl<'a> PartialEq<ConnectionId> for ConnectionIdRef<'a> {
-    fn eq(&self, other: &ConnectionId) -> bool {
-        self.cid == &other.cid[..]
-    }
-}
-
-pub trait ConnectionIdDecoder {
-    fn decode_cid<'a>(&self, dec: &mut Decoder<'a>) -> Option<ConnectionIdRef<'a>>;
-}
-
-pub trait ConnectionIdManager: ConnectionIdDecoder {
-    fn generate_cid(&mut self) -> ConnectionId;
-    fn as_decoder(&self) -> &dyn ConnectionIdDecoder;
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use neqo_common::matches;
-    use test_fixture::fixture_init;
-
-    #[test]
-    fn generate_initial_cid() {
-        fixture_init();
-        for _ in 0..100 {
-            let cid = ConnectionId::generate_initial();
-            if !matches!(cid.len(), 8..=20) {
-                panic!("connection ID {:?}", cid);
-            }
-        }
-    }
-}
--- a/third_party/rust/neqo-transport/src/connection.rs
+++ b/third_party/rust/neqo-transport/src/connection.rs
@@ -5,68 +5,78 @@
 // except according to those terms.
 
 // The class implementing a QUIC connection.
 
 use std::cell::RefCell;
 use std::cmp::{max, min, Ordering};
 use std::collections::HashMap;
 use std::convert::TryFrom;
+use std::convert::TryInto;
 use std::fmt::{self, Debug};
 use std::net::SocketAddr;
 use std::rc::Rc;
 use std::time::{Duration, Instant};
 
 use smallvec::SmallVec;
 
 use neqo_common::{hex, matches, qdebug, qerror, qinfo, qtrace, qwarn, Datagram, Decoder, Encoder};
 use neqo_crypto::agent::CertificateInfo;
 use neqo_crypto::{
-    Agent, AntiReplay, AuthenticationStatus, Client, HandshakeState, Record, SecretAgentInfo,
-    Server,
+    Agent, AntiReplay, AuthenticationStatus, Client, Epoch, HandshakeState, Record,
+    SecretAgentInfo, Server,
 };
 
-use crate::cid::{ConnectionId, ConnectionIdDecoder, ConnectionIdManager, ConnectionIdRef};
-use crate::crypto::{Crypto, CryptoDxState};
+use crate::crypto::{Crypto, CryptoDxDirection, CryptoDxState, CryptoState};
 use crate::dump::*;
 use crate::events::{ConnectionEvent, ConnectionEvents};
 use crate::flow_mgr::FlowMgr;
-use crate::frame::{AckRange, Frame, FrameType, StreamType, TxMode};
-use crate::packet::{DecryptedPacket, PacketBuilder, PacketNumber, PacketType, PublicPacket};
-use crate::recovery::{LossRecovery, RecoveryToken};
+use crate::frame::{decode_frame, AckRange, Frame, FrameType, StreamType, TxMode};
+use crate::packet::{
+    decode_packet_hdr, decrypt_packet, encode_packet, ConnectionId, ConnectionIdDecoder, PacketHdr,
+    PacketNumberDecoder, PacketType,
+};
+use crate::recovery::{
+    LossRecovery, LossRecoveryMode, LossRecoveryState, RecoveryToken, SentPacket,
+};
 use crate::recv_stream::{RecvStream, RecvStreams, RX_STREAM_DATA_WINDOW};
 use crate::send_stream::{SendStream, SendStreams};
 use crate::stats::Stats;
 use crate::stream_id::{StreamId, StreamIndex, StreamIndexes};
 use crate::tparams::{
     tp_constants, TransportParameter, TransportParameters, TransportParametersHandler,
 };
-use crate::tracking::{AckTracker, PNSpace, SentPacket};
-use crate::{AppError, ConnectionError, Error, Res, LOCAL_IDLE_TIMEOUT};
+use crate::tracking::{AckTracker, PNSpace};
+use crate::QUIC_VERSION;
+use crate::{AppError, ConnectionError, Error, Res};
 
 #[derive(Debug, Default)]
 struct Packet(Vec<u8>);
 
+const NUM_EPOCHS: Epoch = 4;
+
 pub const LOCAL_STREAM_LIMIT_BIDI: u64 = 16;
 pub const LOCAL_STREAM_LIMIT_UNI: u64 = 16;
 
 const LOCAL_MAX_DATA: u64 = 0x3FFF_FFFF_FFFF_FFFF; // 2^62-1
 
+const LOCAL_IDLE_TIMEOUT: Duration = Duration::from_secs(60); // 1 minute
+
 #[derive(Debug, PartialEq, Copy, Clone)]
 /// Client or Server.
 pub enum Role {
     Client,
     Server,
 }
 
 impl Role {
     pub fn remote(self) -> Self {
         match self {
-            Self::Client => Self::Server,
-            Self::Server => Self::Client,
+            Role::Client => Role::Server,
+            Role::Server => Role::Client,
         }
     }
 }
 
 impl ::std::fmt::Display for Role {
     fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
         write!(f, "{:?}", self)
     }
@@ -74,64 +84,54 @@ impl ::std::fmt::Display for Role {
 
 #[derive(Clone, Debug, PartialEq, Ord, Eq)]
 /// The state of the Connection.
 pub enum State {
     Init,
     WaitInitial,
     Handshaking,
     Connected,
-    Confirmed,
     Closing {
         error: ConnectionError,
         frame_type: FrameType,
         msg: String,
         timeout: Instant,
     },
     Closed(ConnectionError),
 }
 
-impl State {
-    #[must_use]
-    pub fn connected(&self) -> bool {
-        matches!(self, Self::Connected | Self::Confirmed)
-    }
-}
-
 // Implement Ord so that we can enforce monotonic state progression.
 impl PartialOrd for State {
     #[allow(clippy::match_same_arms)] // Lint bug: rust-lang/rust-clippy#860
     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
         if std::mem::discriminant(self) == std::mem::discriminant(other) {
             return Some(Ordering::Equal);
         }
         Some(match (self, other) {
-            (Self::Init, _) => Ordering::Less,
-            (_, Self::Init) => Ordering::Greater,
-            (Self::WaitInitial, _) => Ordering::Less,
-            (_, Self::WaitInitial) => Ordering::Greater,
-            (Self::Handshaking, _) => Ordering::Less,
-            (_, Self::Handshaking) => Ordering::Greater,
-            (Self::Connected, _) => Ordering::Less,
-            (_, Self::Connected) => Ordering::Greater,
-            (Self::Confirmed, _) => Ordering::Less,
-            (_, Self::Confirmed) => Ordering::Greater,
-            (Self::Closing { .. }, _) => Ordering::Less,
-            (_, Self::Closing { .. }) => Ordering::Greater,
-            (Self::Closed(_), _) => unreachable!(),
+            (State::Init, _) => Ordering::Less,
+            (_, State::Init) => Ordering::Greater,
+            (State::WaitInitial, _) => Ordering::Less,
+            (_, State::WaitInitial) => Ordering::Greater,
+            (State::Handshaking, _) => Ordering::Less,
+            (_, State::Handshaking) => Ordering::Greater,
+            (State::Connected, _) => Ordering::Less,
+            (_, State::Connected) => Ordering::Greater,
+            (State::Closing { .. }, _) => Ordering::Less,
+            (_, State::Closing { .. }) => Ordering::Greater,
+            (State::Closed(_), _) => unreachable!(),
         })
     }
 }
 
 #[derive(Debug)]
 enum ZeroRttState {
     Init,
-    Sending,
+    Sending(CryptoDxState),
     AcceptedClient,
-    AcceptedServer,
+    AcceptedServer(CryptoDxState),
     Rejected,
 }
 
 #[derive(Clone, Debug, PartialEq)]
 struct Path {
     local: SocketAddr,
     remote: SocketAddr,
     local_cids: Vec<ConnectionId>,
@@ -172,192 +172,131 @@ pub enum Output {
     Datagram(Datagram),
     /// Connection requires `process_input()` be called when the `Duration`
     /// elapses.
     Callback(Duration),
 }
 
 impl Output {
     /// Convert into an `Option<Datagram>`.
-    #[must_use]
     pub fn dgram(self) -> Option<Datagram> {
         match self {
-            Self::Datagram(dg) => Some(dg),
+            Output::Datagram(dg) => Some(dg),
             _ => None,
         }
     }
 
     /// Get a reference to the Datagram, if any.
     pub fn as_dgram_ref(&self) -> Option<&Datagram> {
         match self {
-            Self::Datagram(dg) => Some(dg),
+            Output::Datagram(dg) => Some(dg),
             _ => None,
         }
     }
-
-    /// Ask how long the caller should wait before calling back.
-    #[must_use]
-    pub fn callback(&self) -> Duration {
-        match self {
-            Self::Callback(t) => *t,
-            _ => Duration::new(0, 0),
-        }
-    }
 }
 
+pub trait ConnectionIdManager: ConnectionIdDecoder {
+    fn generate_cid(&mut self) -> ConnectionId;
+    fn as_decoder(&self) -> &dyn ConnectionIdDecoder;
+}
 /// Alias the common form for ConnectionIdManager.
 type CidMgr = Rc<RefCell<dyn ConnectionIdManager>>;
 
 /// An FixedConnectionIdManager produces random connection IDs of a fixed length.
 pub struct FixedConnectionIdManager {
     len: usize,
 }
 impl FixedConnectionIdManager {
     pub fn new(len: usize) -> Self {
         Self { len }
     }
 }
 impl ConnectionIdDecoder for FixedConnectionIdManager {
-    fn decode_cid<'a>(&self, dec: &mut Decoder<'a>) -> Option<ConnectionIdRef<'a>> {
-        dec.decode(self.len).map(ConnectionIdRef::from)
+    fn decode_cid(&self, dec: &mut Decoder) -> Option<ConnectionId> {
+        dec.decode(self.len).map(ConnectionId::from)
     }
 }
 impl ConnectionIdManager for FixedConnectionIdManager {
     fn generate_cid(&mut self) -> ConnectionId {
         ConnectionId::generate(self.len)
     }
     fn as_decoder(&self) -> &dyn ConnectionIdDecoder {
         self
     }
 }
 
 struct RetryInfo {
     token: Vec<u8>,
     odcid: ConnectionId,
 }
 
-impl RetryInfo {
-    fn new(odcid: ConnectionId) -> Self {
-        Self {
-            token: Vec::new(),
-            odcid,
-        }
-    }
-}
-
 #[derive(Debug, Clone)]
 /// There's a little bit of different behavior for resetting idle timeout. See
 /// -transport 10.2 ("Idle Timeout").
 enum IdleTimeout {
     Init,
     PacketReceived(Instant),
     AckElicitingPacketSent(Instant),
 }
 
 impl Default for IdleTimeout {
     fn default() -> Self {
-        Self::Init
+        IdleTimeout::Init
     }
 }
 
 impl IdleTimeout {
     pub fn as_instant(&self) -> Option<Instant> {
         match self {
-            Self::Init => None,
-            Self::PacketReceived(t) | Self::AckElicitingPacketSent(t) => Some(*t),
+            IdleTimeout::Init => None,
+            IdleTimeout::PacketReceived(t) | IdleTimeout::AckElicitingPacketSent(t) => Some(*t),
         }
     }
 
     fn on_packet_sent(&mut self, now: Instant) {
         // Only reset idle timeout if we've received a packet since the last
         // time we reset the timeout here.
         match self {
-            Self::AckElicitingPacketSent(_) => {}
-            Self::Init | Self::PacketReceived(_) => {
-                *self = Self::AckElicitingPacketSent(now + LOCAL_IDLE_TIMEOUT);
+            IdleTimeout::AckElicitingPacketSent(_) => {}
+            IdleTimeout::Init | IdleTimeout::PacketReceived(_) => {
+                *self = IdleTimeout::AckElicitingPacketSent(now + LOCAL_IDLE_TIMEOUT);
             }
         }
     }
 
     fn on_packet_received(&mut self, now: Instant) {
-        *self = Self::PacketReceived(now + LOCAL_IDLE_TIMEOUT);
+        *self = IdleTimeout::PacketReceived(now + LOCAL_IDLE_TIMEOUT);
     }
 
     pub fn expired(&self, now: Instant) -> bool {
         if let Some(timeout) = self.as_instant() {
             now >= timeout
         } else {
             false
         }
     }
 }
 
-/// StateManagement manages whether we need to send HANDSHAKE_DONE and CONNECTION_CLOSE.
-/// Valid state transitions are:
-/// * Idle -> HandshakeDone: at the server when the handshake completes
-/// * HandshakeDone -> Idle: when a HANDSHAKE_DONE frame is sent
-/// * Idle/HandshakeDone -> ConnectionClose: when closing
-/// * ConnectionClose -> CloseSent: after sending CONNECTION_CLOSE
-/// * CloseSent -> ConnectionClose: any time a new CONNECTION_CLOSE is needed
-#[derive(Debug, Clone, PartialEq)]
-enum StateSignaling {
-    Idle,
-    HandshakeDone,
-    ConnectionClose,
-    CloseSent,
-}
-
-impl StateSignaling {
-    pub fn handshake_done(&mut self) {
-        if *self != Self::Idle {
-            debug_assert!(false, "StateSignaling must be in Idle state.");
-            return;
-        }
-        *self = Self::HandshakeDone
-    }
-
-    pub fn send_done(&mut self) -> Option<(Frame, Option<RecoveryToken>)> {
-        if *self == Self::HandshakeDone {
-            *self = Self::Idle;
-            Some((Frame::HandshakeDone, Some(RecoveryToken::HandshakeDone)))
-        } else {
-            None
-        }
-    }
-
-    pub fn closing(&self) -> bool {
-        *self == Self::ConnectionClose
-    }
-
-    pub fn close(&mut self) {
-        *self = Self::ConnectionClose
-    }
-
-    pub fn close_sent(&mut self) {
-        debug_assert!(self.closing());
-        *self = Self::CloseSent
-    }
-}
-
 /// A QUIC Connection
 ///
 /// First, create a new connection using `new_client()` or `new_server()`.
 ///
 /// For the life of the connection, handle activity in the following manner:
 /// 1. Perform operations using the `stream_*()` methods.
 /// 1. Call `process_input()` when a datagram is received or the timer
 /// expires. Obtain information on connection state changes by checking
 /// `events()`.
 /// 1. Having completed handling current activity, repeatedly call
 /// `process_output()` for packets to send, until it returns `Output::Callback`
 /// or `Output::None`.
 ///
 /// After the connection is closed (either by calling `close()` or by the
 /// remote) continue processing until `state()` returns `Closed`.
 pub struct Connection {
+    version: crate::packet::Version,
     role: Role,
     state: State,
     tps: Rc<RefCell<TransportParametersHandler>>,
     /// What we are doing with 0-RTT.
     zero_rtt_state: ZeroRttState,
     /// This object will generate connection IDs for the connection.
     cid_manager: CidMgr,
     /// Network paths.  Right now, this tracks at most one path, so it uses `Option`.
@@ -370,30 +309,30 @@ pub struct Connection {
     pub(crate) crypto: Crypto,
     pub(crate) acks: AckTracker,
     idle_timeout: IdleTimeout,
     pub(crate) indexes: StreamIndexes,
     connection_ids: HashMap<u64, (Vec<u8>, [u8; 16])>, // (sequence number, (connection id, reset token))
     pub(crate) send_streams: SendStreams,
     pub(crate) recv_streams: RecvStreams,
     pub(crate) flow_mgr: Rc<RefCell<FlowMgr>>,
-    state_signaling: StateSignaling,
     loss_recovery: LossRecovery,
+    loss_recovery_state: LossRecoveryState,
     events: ConnectionEvents,
     token: Option<Vec<u8>>,
     stats: Stats,
+    tx_mode: TxMode,
 }
 
 impl Debug for Connection {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(
-            f,
+        f.write_fmt(format_args!(
             "{:?} Connection: {:?} {:?}",
             self.role, self.state, self.path
-        )
+        ))
     }
 }
 
 impl Connection {
     /// Create a new QUIC connection with Client role.
     pub fn new_client(
         server_name: &str,
         protocols: &[impl AsRef<str>],
@@ -411,18 +350,17 @@ impl Connection {
             protocols,
             Some(Path {
                 local: local_addr,
                 remote: remote_addr,
                 local_cids,
                 remote_cid: dcid.clone(),
             }),
         );
-        c.crypto.states.init(Role::Client, &dcid);
-        c.retry_info = Some(RetryInfo::new(dcid));
+        c.crypto.create_initial_state(Role::Client, &dcid);
         Ok(c)
     }
 
     /// Create a new QUIC connection with Server role.
     pub fn new_server(
         certs: &[impl AsRef<str>],
         protocols: &[impl AsRef<str>],
         anti_replay: &AntiReplay,
@@ -457,37 +395,38 @@ impl Connection {
         );
         tps.set_integer(
             tp_constants::INITIAL_MAX_STREAMS_UNI,
             LOCAL_STREAM_LIMIT_UNI,
         );
         tps.set_integer(tp_constants::INITIAL_MAX_DATA, LOCAL_MAX_DATA);
         tps.set_integer(
             tp_constants::IDLE_TIMEOUT,
-            u64::try_from(LOCAL_IDLE_TIMEOUT.as_millis()).unwrap(),
+            LOCAL_IDLE_TIMEOUT.as_millis().try_into().unwrap(),
         );
         tps.set_empty(tp_constants::DISABLE_MIGRATION);
     }
 
     fn new(
-        role: Role,
+        r: Role,
         agent: Agent,
         cid_manager: CidMgr,
         anti_replay: Option<&AntiReplay>,
         protocols: &[impl AsRef<str>],
         path: Option<Path>,
     ) -> Self {
         let tphandler = Rc::new(RefCell::new(TransportParametersHandler::default()));
         Self::set_tp_defaults(&mut tphandler.borrow_mut().local);
         let crypto = Crypto::new(agent, protocols, tphandler.clone(), anti_replay)
             .expect("TLS should be configured successfully");
 
         Self {
-            role,
-            state: match role {
+            version: QUIC_VERSION,
+            role: r,
+            state: match r {
                 Role::Client => State::Init,
                 Role::Server => State::WaitInitial,
             },
             cid_manager,
             path,
             valid_cids: Vec::new(),
             tps: tphandler,
             zero_rtt_state: ZeroRttState::Init,
@@ -495,21 +434,22 @@ impl Connection {
             crypto,
             acks: AckTracker::default(),
             idle_timeout: IdleTimeout::default(),
             indexes: StreamIndexes::new(),
             connection_ids: HashMap::new(),
             send_streams: SendStreams::default(),
             recv_streams: RecvStreams::default(),
             flow_mgr: Rc::new(RefCell::new(FlowMgr::default())),
-            state_signaling: StateSignaling::Idle,
             loss_recovery: LossRecovery::new(),
+            loss_recovery_state: LossRecoveryState::default(),
             events: ConnectionEvents::default(),
             token: None,
             stats: Stats::default(),
+            tx_mode: TxMode::Normal,
         }
     }
 
     /// Set a local transport parameter, possibly overriding a default value.
     pub fn set_local_tparam(&self, key: u16, value: TransportParameter) -> Res<()> {
         if matches!(
             (self.role(), self.state()),
             (Role::Client, State::Init) | (Role::Server, State::WaitInitial)
@@ -536,17 +476,17 @@ impl Connection {
     /// higher preference.
     pub fn set_alpn(&mut self, protocols: &[impl AsRef<str>]) -> Res<()> {
         self.crypto.tls.set_alpn(protocols)?;
         Ok(())
     }
 
     /// Access the latest resumption token on the connection.
     pub fn resumption_token(&self) -> Option<Vec<u8>> {
-        if !self.state.connected() {
+        if self.state != State::Connected {
             return None;
         }
         match self.crypto.tls {
             Agent::Client(ref c) => match c.resumption_token() {
                 Some(ref t) => {
                     qtrace!("TLS token {}", hex(&t));
                     let mut enc = Encoder::default();
                     enc.encode_vvec_with(|enc_inner| {
@@ -626,17 +566,17 @@ impl Connection {
     /// Get the peer's certificate chain and other info.
     pub fn peer_certificate(&self) -> Option<CertificateInfo> {
         self.crypto.tls.peer_certificate()
     }
 
     /// Call by application when the peer cert has been verified
     pub fn authenticated(&mut self, status: AuthenticationStatus, now: Instant) {
         self.crypto.tls.authenticated(status);
-        let res = self.handshake(now, PNSpace::Handshake, None);
+        let res = self.handshake(now, 0, None);
         self.absorb_error(now, res);
     }
 
     /// Get the role of the connection.
     pub fn role(&self) -> Role {
         self.role
     }
 
@@ -679,82 +619,67 @@ impl Connection {
     }
 
     pub fn process_timer(&mut self, now: Instant) {
         if matches!(self.state(), State::Closing{..} | State::Closed{..}) {
             qinfo!("Timer fired while closing/closed");
             return;
         }
 
-        let res = self.crypto.states.check_key_update(now);
-        self.absorb_error(now, res);
-
         if self.idle_timeout.expired(now) {
             qinfo!("idle timeout expired");
             self.set_state(State::Closed(ConnectionError::Transport(
                 Error::IdleTimeout,
             )));
-        } else if let Some(packets) = self.loss_recovery.check_loss_detection_timeout(now) {
-            self.handle_lost_packets(&packets);
+        } else {
+            self.check_loss_detection_timeout(now);
         }
     }
 
     /// Call in to process activity on the connection. Either new packets have
     /// arrived or a timeout has expired (or both).
     pub fn process_input(&mut self, dgram: Datagram, now: Instant) {
         let res = self.input(dgram, now);
         self.absorb_error(now, res);
         self.cleanup_streams();
     }
 
     /// Just like above but returns frames parsed from the datagram
     #[cfg(test)]
-    pub fn test_process_input(&mut self, dgram: Datagram, now: Instant) -> Vec<(Frame, PNSpace)> {
+    pub fn test_process_input(&mut self, dgram: Datagram, now: Instant) -> Vec<(Frame, Epoch)> {
         let res = self.input(dgram, now);
         let frames = self.absorb_error(now, res).unwrap_or_default();
         self.cleanup_streams();
         frames
     }
 
     /// Get the time that we next need to be called back, relative to `now`.
     fn next_delay(&mut self, now: Instant) -> Duration {
-        qtrace!([self], "Get callback delay {:?}", now);
+        self.loss_recovery_state = self.loss_recovery.get_timer();
+
         let mut delays = SmallVec::<[_; 4]>::new();
 
-        if let Some(lr_time) = self.loss_recovery.calculate_timer() {
-            qtrace!([self], "Loss recovery timer {:?}", lr_time);
+        if let Some(lr_time) = self.loss_recovery_state.callback_time() {
             delays.push(lr_time);
         }
 
         if let Some(ack_time) = self.acks.ack_time() {
-            qtrace!([self], "Delayed ACK timer {:?}", ack_time);
             delays.push(ack_time);
         }
 
         if let Some(idle_time) = self.idle_timeout.as_instant() {
-            qtrace!([self], "Idle timer {:?}", idle_time);
             delays.push(idle_time);
         }
 
-        if let Some(key_update_time) = self.crypto.states.update_time() {
-            qtrace!([self], "Key update timer {:?}", key_update_time);
-            delays.push(key_update_time);
-        }
-
         // Should always at least have idle timeout, once connected
         assert!(!delays.is_empty());
         let earliest = delays.into_iter().min().unwrap();
 
         // TODO(agrover, mt) - need to analyze and fix #47
         // rather than just clamping to zero here.
-        qdebug!(
-            [self],
-            "delay duration {:?}",
-            max(now, earliest).duration_since(now)
-        );
         max(now, earliest).duration_since(now)
     }
 
     /// Get output packets, as a result of receiving packets, or actions taken
     /// by the application.
     /// Returns datagrams to send, and how long to wait before calling again
     /// even if no incoming packets.
     pub fn process_output(&mut self, now: Instant) -> Output {
@@ -792,603 +717,598 @@ impl Connection {
     pub fn process(&mut self, dgram: Option<Datagram>, now: Instant) -> Output {
         if let Some(d) = dgram {
             self.process_input(d, now);
         }
         self.process_timer(now);
         self.process_output(now)
     }
 
-    fn is_valid_cid(&self, cid: &ConnectionIdRef) -> bool {
-        let check = |c| c == cid;
-        self.valid_cids.iter().any(check)
-            || self.path.iter().any(|p| p.local_cids.iter().any(check))
+    fn is_valid_cid(&self, cid: &ConnectionId) -> bool {
+        self.valid_cids.contains(cid) || self.path.iter().any(|p| p.local_cids.contains(cid))
     }
 
-    fn handle_retry(&mut self, packet: PublicPacket) -> Res<()> {
+    fn is_valid_initial(&self, hdr: &PacketHdr) -> bool {
+        if let PacketType::Initial(token) = &hdr.tipe {
+            // Server checks the token, so if we have one,
+            // assume that the DCID is OK.
+            if hdr.dcid.len() < 8 {
+                if token.is_empty() {
+                    qinfo!([self], "Drop Initial with short DCID");
+                    false
+                } else {
+                    qinfo!([self], "Initial received with token, assuming OK");
+                    true
+                }
+            } else {
+                // This is the normal path. Don't log.
+                true
+            }
+        } else {
+            qdebug!([self], "Dropping non-Initial packet");
+            false
+        }
+    }
+
+    fn handle_retry(&mut self, scid: &ConnectionId, odcid: &ConnectionId, token: &[u8]) -> Res<()> {
         qdebug!([self], "received Retry");
-        debug_assert!(self.retry_info.is_some());
-        if !self.retry_info.as_ref().unwrap().token.is_empty() {
+        if self.retry_info.is_some() {
             qinfo!([self], "Dropping extra Retry");
-            self.stats.dropped_rx += 1;
-            return Ok(());
-        }
-        if packet.token().is_empty() {
-            qinfo!([self], "Dropping Retry without a token");
-            self.stats.dropped_rx += 1;
             return Ok(());
         }
-        if !packet.is_valid_retry(&self.retry_info.as_ref().unwrap().odcid) {
-            qinfo!([self], "Dropping Retry with bad integrity tag");
-            self.stats.dropped_rx += 1;
+        if token.is_empty() {
+            qinfo!([self], "Dropping Retry without a token");
             return Ok(());
         }
-        if let Some(p) = &mut self.path {
-            // At this point, we shouldn't have a remote connection ID for the path.
-            p.remote_cid = ConnectionId::from(packet.scid());
-        } else {
-            qinfo!([self], "No path, but we received a Retry");
-            return Err(Error::InternalError);
-        };
-        self.retry_info.as_mut().unwrap().token = packet.token().to_vec();
+        match self.path.iter_mut().find(|p| p.remote_cid == *odcid) {
+            None => {
+                qinfo!([self], "Ignoring Retry with mismatched ODCID");
+                return Ok(());
+            }
+            Some(path) => {
+                path.remote_cid = scid.clone();
+            }
+        }
         qinfo!(
             [self],
-            "Valid Retry received, token={}",
-            hex(packet.token())
+            "Valid Retry received, restarting with provided token"
         );
+        self.retry_info = Some(RetryInfo {
+            token: token.to_vec(),
+            odcid: odcid.clone(),
+        });
         let lost_packets = self.loss_recovery.retry();
         self.handle_lost_packets(&lost_packets);
 
         // Switching crypto state here might not happen eventually.
         // https://github.com/quicwg/base-drafts/issues/2823
-        self.crypto.states.init(self.role, packet.scid());
+        self.crypto.create_initial_state(self.role, scid);
         Ok(())
     }
 
-    fn discard_keys(&mut self, space: PNSpace) {
-        self.loss_recovery.discard(space);
-        self.crypto.discard(space);
-    }
-
-    fn input(&mut self, d: Datagram, now: Instant) -> Res<Vec<(Frame, PNSpace)>> {
+    fn input(&mut self, d: Datagram, now: Instant) -> Res<Vec<(Frame, Epoch)>> {
         let mut slc = &d[..];
         let mut frames = Vec::new();
 
-        qtrace!([self], "input {}", hex(&**d));
+        qdebug!([self], "input {}", hex(&**d));
 
         // Handle each packet in the datagram
         while !slc.is_empty() {
-            let (packet, remainder) =
-                match PublicPacket::decode(slc, self.cid_manager.borrow().as_decoder()) {
-                    Ok((packet, remainder)) => (packet, remainder),
-                    Err(e) => {
-                        qinfo!([self], "Garbage packet: {} {}", e, hex(slc));
-                        self.stats.dropped_rx += 1;
-                        return Ok(frames);
-                    }
-                }; // TODO(mt) use in place of res, and allow errors
+            let res = decode_packet_hdr(self.cid_manager.borrow().as_decoder(), slc);
+            let mut hdr = match res {
+                Ok(h) => h,
+                Err(e) => {
+                    qinfo!(
+                        [self],
+                        "Received indecipherable packet header {} {}",
+                        hex(slc),
+                        e
+                    );
+                    return Ok(frames); // Drop the remainder of the datagram.
+                }
+            };
             self.stats.packets_rx += 1;
-            match (packet.packet_type(), &self.state, &self.role) {
-                (PacketType::VersionNegotiation, State::WaitInitial, Role::Client) => {
+            match (&hdr.tipe, &self.state, &self.role) {
+                (PacketType::VN(_), State::WaitInitial, Role::Client) => {
                     self.set_state(State::Closed(ConnectionError::Transport(
                         Error::VersionNegotiation,
                     )));
                     return Err(Error::VersionNegotiation);
                 }
-                (PacketType::Retry, State::WaitInitial, Role::Client) => {
-                    self.handle_retry(packet)?;
+                (PacketType::Retry { odcid, token }, State::WaitInitial, Role::Client) => {
+                    self.handle_retry(hdr.scid.as_ref().unwrap(), odcid, token)?;
                     return Ok(frames);
                 }
-                (PacketType::VersionNegotiation, ..) | (PacketType::Retry, ..) => {
-                    qwarn!("dropping {:?}", packet.packet_type());
-                    self.stats.dropped_rx += 1;
+                (PacketType::VN(_), ..) | (PacketType::Retry { .. }, ..) => {
+                    qwarn!("dropping {:?}", hdr.tipe);
                     return Ok(frames);
                 }
                 _ => {}
             };
 
+            if let Some(version) = hdr.version {
+                if version != self.version {
+                    qwarn!(
+                        "Dropping packet from version {:x} (self.version={:x})",
+                        hdr.version.unwrap(),
+                        self.version,
+                    );
+                    return Ok(frames);
+                }
+            }
+
             match self.state {
                 State::Init => {
                     qinfo!([self], "Received message while in Init state");
-                    self.stats.dropped_rx += 1;
                     return Ok(frames);
                 }
                 State::WaitInitial => {
                     qinfo!([self], "Received packet in WaitInitial");
                     if self.role == Role::Server {
-                        if !packet.is_valid_initial() {
-                            self.stats.dropped_rx += 1;
+                        if !self.is_valid_initial(&hdr) {
                             return Ok(frames);
                         }
-                        self.crypto.states.init(self.role, &packet.dcid());
+                        self.crypto.create_initial_state(self.role, &hdr.dcid);
                     }
                 }
-                State::Handshaking | State::Connected | State::Confirmed => {
-                    if !self.is_valid_cid(packet.dcid()) {
-                        qinfo!([self], "Ignoring packet with CID {:?}", packet.dcid());
-                        self.stats.dropped_rx += 1;
+                State::Handshaking | State::Connected => {
+                    if !self.is_valid_cid(&hdr.dcid) {
+                        qinfo!([self], "Ignoring packet with CID {:?}", hdr.dcid);
                         return Ok(frames);
                     }
-                    if self.role == Role::Server && packet.packet_type() == PacketType::Handshake {
-                        // Server has received a Handshake packet -> discard Initial keys and states
-                        self.discard_keys(PNSpace::Initial);
-                    }
                 }
                 State::Closing { .. } => {
                     // Don't bother processing the packet. Instead ask to get a
                     // new close frame.
-                    self.state_signaling.close();
+                    self.flow_mgr.borrow_mut().set_need_close_frame(true);
                     return Ok(frames);
                 }
                 State::Closed(..) => {
                     // Do nothing.
-                    self.stats.dropped_rx += 1;
                     return Ok(frames);
                 }
             }
 
-            qtrace!([self], "Received unverified packet {:?}", packet);
-
-            let pto = self.loss_recovery.pto();
-            let payload = packet.decrypt(&mut self.crypto.states, now + pto);
-            slc = remainder;
-            if let Ok(payload) = payload {
+            qdebug!([self], "Received unverified packet {:?}", hdr);
+
+            let body = self.decrypt_body(&mut hdr, slc);
+            slc = &slc[hdr.hdr_len + hdr.body_len()..];
+            if let Some(body) = body {
                 // TODO(ekr@rtfm.com): Have the server blow away the initial
                 // crypto state if this fails? Otherwise, we will get a panic
                 // on the assert for doesn't exist.
                 // OK, we have a valid packet.
                 self.idle_timeout.on_packet_received(now);
-                dump_packet(
-                    self,
-                    "-> RX",
-                    payload.packet_type(),
-                    payload.pn(),
-                    &payload[..],
-                );
-                frames.extend(self.process_packet(&payload, now)?);
+                dump_packet(self, "-> RX", &hdr, &body);
+                frames.extend(self.process_packet(&hdr, body, now)?);
                 if matches!(self.state, State::WaitInitial) {
-                    self.start_handshake(&packet, &d)?;
+                    self.start_handshake(hdr, &d)?;
                 }
                 self.process_migrations(&d)?;
-            } else {
-                // Decryption failure, or not having keys is not fatal.
-                // If the state isn't available, or we can't decrypt the packet, drop
-                // the rest of the datagram on the floor, but don't generate an error.
-                self.stats.dropped_rx += 1;
             }
         }
         Ok(frames)
     }
 
+    fn obtain_epoch_rx_crypto_state(&mut self, epoch: Epoch) -> Option<&mut CryptoDxState> {
+        if (self.state == State::Handshaking) && (epoch == 3) && (self.role() == Role::Server) {
+            // We got a packet for epoch 3 but the connection is still in the Handshaking
+            // state -> discharge the packet.
+            // On the server side we have keys for epoch 3 before we enter the epoch,
+            // but we still need to discharge the packet.
+            None
+        } else if epoch != 1 {
+            match self
+                .crypto
+                .states
+                .obtain(self.role, epoch, &self.crypto.tls)
+            {
+                Ok(CryptoState { rx, .. }) => rx.as_mut(),
+                _ => None,
+            }
+        } else if self.role == Role::Server {
+            if let ZeroRttState::AcceptedServer(rx) = &mut self.zero_rtt_state {
+                return Some(rx);
+            }
+            None
+        } else {
+            None
+        }
+    }
+
+    fn decrypt_body(&mut self, mut hdr: &mut PacketHdr, slc: &[u8]) -> Option<Vec<u8>> {
+        // Decryption failure, or not having keys is not fatal.
+        // If the state isn't available, or we can't decrypt the packet, drop
+        // the rest of the datagram on the floor, but don't generate an error.
+        let largest_acknowledged = self
+            .loss_recovery
+            .largest_acknowledged_pn(PNSpace::from(hdr.epoch));
+        match self.obtain_epoch_rx_crypto_state(hdr.epoch) {
+            Some(rx) => {
+                let pn_decoder = PacketNumberDecoder::new(largest_acknowledged);
+                decrypt_packet(rx, pn_decoder, &mut hdr, slc).ok()
+            }
+            _ => None,
+        }
+    }
+
     /// Ok(true) if the packet is a duplicate
     fn process_packet(
         &mut self,
-        packet: &DecryptedPacket,
+        hdr: &PacketHdr,
+        body: Vec<u8>,
         now: Instant,
-    ) -> Res<Vec<(Frame, PNSpace)>> {
+    ) -> Res<Vec<(Frame, Epoch)>> {
         // TODO(ekr@rtfm.com): Have the server blow away the initial
         // crypto state if this fails? Otherwise, we will get a panic
         // on the assert for doesn't exist.
         // OK, we have a valid packet.
 
         // TODO(ekr@rtfm.com): Filter for valid for this epoch.
 
-        let space = PNSpace::from(packet.packet_type());
-        if self.acks[space].is_duplicate(packet.pn()) {
-            qdebug!([self], "Duplicate packet from {} pn={}", space, packet.pn());
+        let space = PNSpace::from(hdr.epoch);
+        if self.acks[space].is_duplicate(hdr.pn) {
+            qdebug!(
+                [self],
+                "Received duplicate packet epoch={} pn={}",
+                hdr.epoch,
+                hdr.pn
+            );
             self.stats.dups_rx += 1;
             return Ok(vec![]);
         }
 
         let mut ack_eliciting = false;
-        let mut d = Decoder::from(&packet[..]);
-        let mut consecutive_padding = 0;
+        let mut d = Decoder::from(&body[..]);
         #[allow(unused_mut)]
         let mut frames = Vec::new();
         while d.remaining() > 0 {
-            let mut f = Frame::decode(&mut d)?;
-
-            // Skip padding
-            while f == Frame::Padding && d.remaining() > 0 {
-                consecutive_padding += 1;
-                f = Frame::decode(&mut d)?;
-            }
-            if consecutive_padding > 0 {
-                qdebug!("PADDING frame repeated {} times", consecutive_padding);
-                consecutive_padding = 0;
-            }
-
+            let f = decode_frame(&mut d)?;
             if cfg!(test) {
-                frames.push((f.clone(), space));
+                frames.push((f.clone(), hdr.epoch));
             }
             ack_eliciting |= f.ack_eliciting();
             let t = f.get_type();
-            let res = self.input_frame(packet.packet_type(), f, now);
+            let res = self.input_frame(hdr.epoch, f, now);
             self.capture_error(now, t, res)?;
         }
-        self.acks[space].set_received(now, packet.pn(), ack_eliciting);
+        self.acks[space].set_received(now, hdr.pn, ack_eliciting);
 
         Ok(frames)
     }
 
-    fn start_handshake(&mut self, packet: &PublicPacket, d: &Datagram) -> Res<()> {
+    fn get_zero_rtt_crypto(&mut self) -> Option<CryptoDxState> {
+        match self.crypto.tls.preinfo() {
+            Err(_) => None,
+            Ok(preinfo) => {
+                match preinfo.early_data_cipher() {
+                    Some(cipher) => {
+                        match self.role {
+                            Role::Client => self.crypto.tls.write_secret(1).map(|ws| {
+                                CryptoDxState::new(CryptoDxDirection::Write, 1, ws, cipher)
+                            }),
+                            Role::Server => self.crypto.tls.read_secret(1).map(|rs| {
+                                CryptoDxState::new(CryptoDxDirection::Read, 1, rs, cipher)
+                            }),
+                        }
+                    }
+                    None => None,
+                }
+            }
+        }
+    }
+
+    fn start_handshake(&mut self, hdr: PacketHdr, d: &Datagram) -> Res<()> {
         if self.role == Role::Server {
-            assert_eq!(packet.packet_type(), PacketType::Initial);
+            assert!(matches!(hdr.tipe, PacketType::Initial(..)));
             // A server needs to accept the client's selected CID during the handshake.
-            self.valid_cids.push(ConnectionId::from(packet.dcid()));
+            self.valid_cids.push(hdr.dcid.clone());
             // Install a path.
             assert!(self.path.is_none());
-            let mut p = Path::new(&d, ConnectionId::from(packet.scid()));
+            let mut p = Path::new(&d, hdr.scid.unwrap());
             p.local_cids
                 .push(self.cid_manager.borrow_mut().generate_cid());
             self.path = Some(p);
 
-            self.zero_rtt_state = match self.crypto.enable_0rtt(self.role) {
-                Ok(true) => {
-                    qdebug!([self], "Accepted 0-RTT");
-                    ZeroRttState::AcceptedServer
+            // SecretAgentPreinfo::early_data() always returns false for a server,
+            // but a non-zero maximum tells us if we are accepting 0-RTT.
+            self.zero_rtt_state = if self.crypto.tls.preinfo()?.max_early_data() > 0 {
+                match self.get_zero_rtt_crypto() {
+                    Some(cs) => ZeroRttState::AcceptedServer(cs),
+                    None => {
+                        debug_assert!(false, "We must have zero-rtt keys.");
+                        ZeroRttState::Rejected
+                    }
                 }
-                _ => ZeroRttState::Rejected,
+            } else {
+                ZeroRttState::Rejected
             };
         } else {
-            qdebug!([self], "Changing to use Server CID={}", packet.scid());
+            qdebug!(
+                [self],
+                "Changing to use Server CID={}",
+                hdr.scid.as_ref().unwrap()
+            );
             let p = self
                 .path
                 .iter_mut()
                 .find(|p| p.received_on(&d))
                 .expect("should have a path for sending Initial");
-            p.remote_cid = ConnectionId::from(packet.scid());
+            p.remote_cid = hdr.scid.unwrap();
         }
         self.set_state(State::Handshaking);
         Ok(())
     }
 
     fn process_migrations(&self, d: &Datagram) -> Res<()> {
         if self.path.iter().any(|p| p.received_on(&d)) {
             Ok(())
         } else {
             // Right now, we don't support any form of migration.
             // So generate an error if a packet is received on a new path.
             Err(Error::InvalidMigration)
         }
     }
 
     fn output(&mut self, now: Instant) -> Option<Datagram> {
-        if let Some(mut path) = self.path.take() {
-            let res = match &self.state {
-                State::Init
-                | State::WaitInitial
-                | State::Handshaking
-                | State::Connected
-                | State::Confirmed => self.output_path(&mut path, now),
+        let mut out = None;
+        if self.path.is_some() {
+            match self.output_pkt_for_path(now) {
+                Ok(res) => {
+                    out = res;
+                }
+                Err(e) => {
+                    if !matches!(self.state, State::Closing{..}) {
+                        // An error here causes us to transition to closing.
+                        let err: Result<Option<Datagram>, Error> = Err(e);
+                        self.absorb_error(now, err);
+                        // Rerun to give a chance to send a CONNECTION_CLOSE.
+                        out = match self.output_pkt_for_path(now) {
+                            Ok(x) => x,
+                            Err(e) => {
+                                qwarn!([self], "two output_path errors in a row: {:?}", e);
+                                None
+                            }
+                        };
+                    }
+                }
+            };
+        }
+        out
+    }
+
+    #[allow(clippy::cognitive_complexity)]
+    #[allow(clippy::useless_let_if_seq)]
+    /// Build a datagram, possibly from multiple packets (for different PN
+    /// spaces) and each containing 1+ frames.
+    fn output_pkt_for_path(&mut self, now: Instant) -> Res<Option<Datagram>> {
+        let mut out_bytes = Vec::new();
+        let mut needs_padding = false;
+        let mut close_sent = false;
+        let path = self
+            .path
+            .take()
+            .expect("we know we have a path because calling fn checked");
+
+        // Frames for different epochs must go in different packets, but then these
+        // packets can go in a single datagram
+        for epoch in 0..NUM_EPOCHS {
+            let space = PNSpace::from(epoch);
+            let mut encoder = Encoder::default();
+            let mut tokens = Vec::new();
+
+            // Ensure we have tx crypto state for this epoch, or skip it.
+            let tx = if epoch == 1 && self.role == Role::Server {
+                continue;
+            } else if epoch == 1 {
+                match &mut self.zero_rtt_state {
+                    ZeroRttState::Sending(tx) => tx,
+                    _ => continue,
+                }
+            } else {
+                match self
+                    .crypto
+                    .states
+                    .obtain(self.role, epoch, &self.crypto.tls)
+                {
+                    Ok(CryptoState { tx: Some(tx), .. }) => tx,
+                    _ => continue,
+                }
+            };
+
+            let hdr = PacketHdr::new(
+                0,
+                match epoch {
+                    0 => {
+                        let token = match &self.retry_info {
+                            Some(v) => v.token.clone(),
+                            _ => Vec::new(),
+                        };
+                        PacketType::Initial(token)
+                    }
+                    1 => PacketType::ZeroRTT,
+                    2 => PacketType::Handshake,
+                    3 => PacketType::Short,
+                    _ => unimplemented!(), // TODO(ekr@rtfm.com): Key Update.
+                },
+                Some(self.version),
+                path.remote_cid.clone(),
+                path.local_cids.first().cloned(),
+                self.loss_recovery.next_pn(space),
+                epoch,
+            );
+
+            let mut ack_eliciting = false;
+            let mut has_padding = false;
+            let cong_avail = match self.tx_mode {
+                TxMode::Normal => usize::try_from(self.loss_recovery.cwnd_avail()).unwrap(),
+                TxMode::Pto => path.mtu(), // send one packet
+            };
+            let tx_mode = self.tx_mode;
+
+            match &self.state {
+                State::Init | State::WaitInitial | State::Handshaking | State::Connected => {
+                    loop {
+                        let used =
+                            out_bytes.len() + encoder.len() + hdr.overhead(&tx.aead, path.mtu());
+                        let remaining = min(
+                            path.mtu().saturating_sub(used),
+                            cong_avail.saturating_sub(used),
+                        );
+                        if remaining < 2 {
+                            // All useful frames are at least 2 bytes.
+                            break;
+                        }
+
+                        // Try to get a frame from frame sources
+                        let mut frame = None;
+                        if self.tx_mode == TxMode::Normal {
+                            frame = self.acks.get_frame(now, epoch);
+                        }
+                        if frame.is_none() {
+                            frame = self.crypto.streams.get_frame(epoch, tx_mode, remaining)
+                        }
+                        if frame.is_none() && self.tx_mode == TxMode::Normal {
+                            frame = self.flow_mgr.borrow_mut().get_frame(epoch, remaining);
+                        }
+                        if frame.is_none() {
+                            frame = self.send_streams.get_frame(epoch, tx_mode, remaining)
+                        }
+                        if frame.is_none() && self.tx_mode == TxMode::Pto {
+                            frame = Some((Frame::Ping, None));
+                        }
+
+                        if let Some((frame, token)) = frame {
+                            ack_eliciting |= frame.ack_eliciting();
+                            if let Frame::Padding = frame {
+                                has_padding |= true;
+                            }
+                            frame.marshal(&mut encoder);
+                            if let Some(t) = token {
+                                tokens.push(t);
+                            }
+
+                            // Pto only ever sends one frame, but it ALWAYS
+                            // sends one
+                            if self.tx_mode == TxMode::Pto {
+                                break;
+                            }
+                        } else {
+                            // No more frames to send.
+                            assert_eq!(self.tx_mode, TxMode::Normal);
+                            break;
+                        }
+                    }
+                }
                 State::Closing {
                     error,
                     frame_type,
                     msg,
                     ..
                 } => {
-                    let err = error.clone();
-                    let frame_type = *frame_type;
-                    let msg = msg.clone();
-                    self.output_close(&path, err, frame_type, msg)
+                    if self.flow_mgr.borrow().need_close_frame() {
+                        // ConnectionClose frame not allowed for 0RTT
+                        if epoch == 1 {
+                            continue;
+                        }
+                        // ConnectionError::Application only allowed at 1RTT
+                        if epoch != 3 && matches!(error, ConnectionError::Application(_)) {
+                            continue;
+                        }
+                        let frame = Frame::ConnectionClose {
+                            error_code: error.clone().into(),
+                            frame_type: *frame_type,
+                            reason_phrase: Vec::from(msg.clone()),
+                        };
+                        frame.marshal(&mut encoder);
+                        close_sent = true;
+                    }
                 }
-                State::Closed(_) => Ok(None),
-            };
-            let out = self.absorb_error(now, res).unwrap_or(None);
-            self.path = Some(path);
-            out
-        } else {
-            None
-        }
-    }
-
-    fn build_packet_header(
-        path: &Path,
-        space: PNSpace,
-        encoder: Encoder,
-        tx: &CryptoDxState,
-        retry_info: &Option<RetryInfo>,
-    ) -> (PacketType, PacketNumber, PacketBuilder) {
-        let pt = match space {
-            PNSpace::Initial => PacketType::Initial,
-            PNSpace::Handshake => PacketType::Handshake,
-            PNSpace::ApplicationData => {
-                if tx.is_0rtt() {
-                    PacketType::ZeroRtt
-                } else {
-                    PacketType::Short
-                }
+                State::Closed { .. } => unimplemented!(),
             }
-        };
-        let mut builder = if pt == PacketType::Short {
-            qdebug!("Building Short dcid {}", &path.remote_cid,);
-            PacketBuilder::short(encoder, tx.key_phase(), &path.remote_cid)
-        } else {
-            qdebug!(
-                "Building {:?} dcid {} scid {}",
-                pt,
-                &path.remote_cid,
-                path.local_cids.first().unwrap()
-            );
-
-            PacketBuilder::long(
-                encoder,
-                pt,
-                &path.remote_cid,
-                path.local_cids.first().unwrap(),
-            )
-        };
-        if pt == PacketType::Initial {
-            builder.initial_token(if let Some(info) = retry_info {
-                qtrace!("Initial token {}", hex(&info.token));
-                &info.token
-            } else {
-                &[]
-            });
-        }
-        // TODO(mt) work out packet number length based on `4*path CWND/path MTU`.
-        let pn = tx.next_pn();
-        builder.pn(pn, 3);
-        (pt, pn, builder)
-    }
-
-    fn output_close(
-        &mut self,
-        path: &Path,
-        error: ConnectionError,
-        frame_type: FrameType,
-        msg: String,
-    ) -> Res<Option<Datagram>> {
-        if !self.state_signaling.closing() {
-            return Ok(None);
-        }
-        let mut close_sent = false;
-        let mut encoder = Encoder::with_capacity(path.mtu());
-        for space in PNSpace::iter() {
-            let tx = if let Some(tx_state) = self.crypto.states.tx(*space) {
-                tx_state
-            } else {
-                continue;
-            };
-
-            // ConnectionClose frame not allowed for 0RTT.
-            if tx.is_0rtt() {
-                continue;
-            }
-
-            // ConnectionError::Application only allowed at 1RTT.
-            if *space != PNSpace::ApplicationData
-                && matches!(error, ConnectionError::Application(_))
-            {
+
+            assert!(encoder.len() <= path.mtu());
+            if encoder.len() == 0 {
                 continue;
             }
-            let (_, _, mut builder) = Self::build_packet_header(path, *space, encoder, tx, &None);
-            let frame = Frame::ConnectionClose {
-                error_code: error.clone().into(),
-                frame_type,
-                reason_phrase: Vec::from(msg.clone()),
-            };
-            frame.marshal(&mut builder);
-            encoder = builder.build(tx)?;
-            close_sent = true;
-        }
-
-        if close_sent {
-            self.state_signaling.close_sent();
-        }
-        Ok(Some(Datagram::new(path.local, path.remote, encoder)))
-    }
-
-    /// Add frames to the provided builder and
-    /// return whether any of them were ACK eliciting.
-    #[allow(clippy::useless_let_if_seq)]
-    fn add_frames(
-        &mut self,
-        builder: &mut PacketBuilder,
-        space: PNSpace,
-        tx_mode: TxMode,
-        limit: usize,
-        now: Instant,
-    ) -> (Vec<RecoveryToken>, bool) {
-        let mut tokens = Vec::new();
-        let mut ack_eliciting = false;
-        // All useful frames are at least 2 bytes.
-        while builder.len() + 2 < limit {
-            let remaining = limit - builder.len();
-            // Try to get a frame from frame sources
-            let mut frame = None;
-            if tx_mode == TxMode::Normal {
-                frame = self.acks.get_frame(now, space);
-            }
-            if frame.is_none() && space == PNSpace::ApplicationData && self.role == Role::Server {
-                frame = self.state_signaling.send_done();
-            }
-            if frame.is_none() {
-                frame = self.crypto.streams.get_frame(space, tx_mode, remaining)
-            }
-            if frame.is_none() && tx_mode == TxMode::Normal {
-                frame = self.flow_mgr.borrow_mut().get_frame(space, remaining);
-            }
-            if frame.is_none() {
-                frame = self.send_streams.get_frame(space, tx_mode, remaining);
-            }
-
-            if let Some((frame, token)) = frame {
-                ack_eliciting |= frame.ack_eliciting();
-                debug_assert_ne!(frame, Frame::Padding);
-                frame.marshal(builder);
-                if let Some(t) = token {
-                    tokens.push(t);
-                }
-            } else {
-                if tx_mode == TxMode::Pto {
-                    // Add a PING.
-                    builder.encode_varint(Frame::Ping.get_type());
-                    ack_eliciting = true;
-                }
-                return (tokens, ack_eliciting);
-            }
-
-            // PTO only ever sends one frame and they always elicit ACKs.
-            if tx_mode == TxMode::Pto {
-                debug_assert!(ack_eliciting);
-                return (tokens, true);
-            }
-        }
-        (tokens, ack_eliciting)
-    }
-
-    /// Build a datagram, possibly from multiple packets (for different PN
-    /// spaces) and each containing 1+ frames.
-    fn output_path(&mut self, path: &mut Path, now: Instant) -> Res<Option<Datagram>> {
-        let mut needs_padding = false;
-
-        // Check whether we are sending packets in PTO mode.
-        let (tx_mode, cong_avail, min_pn_space) =
-            if let Some((min_pto_pn_space, can_send)) = self.loss_recovery.get_pto_state() {
-                if !can_send {
-                    return Ok(None);
-                }
-                (TxMode::Pto, path.mtu(), min_pto_pn_space)
-            } else {
-                (
-                    TxMode::Normal,
-                    usize::try_from(self.loss_recovery.cwnd_avail()).unwrap(),
-                    PNSpace::Initial,
-                )
-            };
-
-        // Frames for different epochs must go in different packets, but then these
-        // packets can go in a single datagram
-        let mut encoder = Encoder::with_capacity(path.mtu());
-        for space in PNSpace::iter() {
-            if *space < min_pn_space {
-                continue;
-            }
-
-            // Ensure we have tx crypto state for this epoch, or skip it.
-            let tx = if let Some(tx_state) = self.crypto.states.tx(*space) {
-                tx_state
-            } else {
-                continue;
-            };
-
-            let header_start = encoder.len();
-            let (pt, pn, mut builder) =
-                Self::build_packet_header(path, *space, encoder, tx, &self.retry_info);
-            let payload_start = builder.len();
-
-            // Work out how much space we have in the congestion window.
-            let limit = min(path.mtu(), cong_avail);
-            if builder.len() + tx.expansion() > limit {
-                // No space for a packet of this type in the congestion window.
-                encoder = builder.abort();
-                continue;
-            }
-            let limit = limit - tx.expansion();
-
-            let (tokens, ack_eliciting) =
-                self.add_frames(&mut builder, *space, tx_mode, limit, now);
-            if builder.is_empty() {
-                // Nothing to include in this packet.
-                encoder = builder.abort();
-                continue;
-            }
-
-            dump_packet(self, "TX ->", pt, pn, &builder[payload_start..]);
-
-            qdebug!("Need to send a packet: {:?}", pt);
-            match pt {
+
+            qdebug!("Need to send a packet");
+            match epoch {
                 // Packets containing Initial packets need padding.
-                PacketType::Initial => needs_padding = true,
-                PacketType::ZeroRtt => (),
+                0 => needs_padding = true,
+                1 => (),
                 // ...unless they include higher epochs.
                 _ => needs_padding = false,
             }
 
             self.stats.packets_tx += 1;
-            encoder = builder.build(self.crypto.states.tx(*space).unwrap())?;
-            assert!(encoder.len() <= path.mtu());
-
-            if tx_mode != TxMode::Pto && ack_eliciting {
+            self.loss_recovery.inc_pn(space);
+
+            let mut packet = encode_packet(tx, &hdr, &encoder);
+
+            if self.tx_mode != TxMode::Pto && ack_eliciting {
                 self.idle_timeout.on_packet_sent(now);
             }
 
-            // Normal packets are in flight if they include PADDING frames,
-            // but we don't send those.
-            let in_flight = match tx_mode {
+            let in_flight = match self.tx_mode {
                 TxMode::Pto => false,
-                TxMode::Normal => ack_eliciting,
+                TxMode::Normal => ack_eliciting || has_padding,
             };
 
-            let sent = SentPacket::new(
-                now,
-                ack_eliciting,
-                tokens,
-                encoder.len() - header_start,
-                in_flight,
+            self.loss_recovery.on_packet_sent(
+                space,
+                hdr.pn,
+                SentPacket::new(now, ack_eliciting, tokens, packet.len(), in_flight),
             );
-            self.loss_recovery.on_packet_sent(*space, pn, sent);
-
-            if *space == PNSpace::Handshake && self.role == Role::Client {
-                // Client can send Handshake packets -> discard Initial keys and states
-                self.discard_keys(PNSpace::Initial);
-            }
-
-            if *space == PNSpace::Handshake
-                && self.role == Role::Server
-                && self.state == State::Confirmed
-            {
-                // We could discard handshake keys in set_state, but we are waiting to send an ack.
-                self.discard_keys(PNSpace::Handshake);
-            }
+
+            dump_packet(self, "TX ->", &hdr, &encoder);
+
+            out_bytes.append(&mut packet);
+        }
+
+        if close_sent {
+            self.flow_mgr.borrow_mut().set_need_close_frame(false);
         }
 
-        if encoder.len() == 0 {
-            assert!(tx_mode != TxMode::Pto);
+        // Sent a probe pkt. Another timeout will re-engage ProbeTimeout mode,
+        // but otherwise return to honoring CC.
+        if self.tx_mode == TxMode::Pto {
+            self.tx_mode = TxMode::Normal;
+        }
+
+        if out_bytes.is_empty() {
+            assert!(self.tx_mode != TxMode::Pto);
+            self.path = Some(path);
             Ok(None)
         } else {
-            debug_assert!(encoder.len() <= path.mtu());
             // Pad Initial packets sent by the client to mtu bytes.
-            let mut packets: Vec<u8> = encoder.into();
             if self.role == Role::Client && needs_padding {
                 qdebug!([self], "pad Initial to max_datagram_size");
-                packets.resize(path.mtu(), 0);
+                out_bytes.resize(path.mtu(), 0);
             }
-            Ok(Some(Datagram::new(path.local, path.remote, packets)))
+            let ret = Ok(Some(Datagram::new(path.local, path.remote, out_bytes)));
+            self.path = Some(path);
+            ret
         }
     }
 
-    pub fn initiate_key_update(&mut self) -> Res<()> {
-        if self.state == State::Confirmed {
-            let la = self
-                .loss_recovery
-                .largest_acknowledged_pn(PNSpace::ApplicationData);
-            qinfo!([self], "Initiating key update");
-            self.crypto.states.initiate_key_update(la)
-        } else {
-            Err(Error::NotConnected)
-        }
-    }
-
-    #[cfg(test)]
-    pub fn get_epochs(&self) -> (Option<usize>, Option<usize>) {
-        self.crypto.states.get_epochs()
-    }
-
     fn client_start(&mut self, now: Instant) -> Res<()> {
         qinfo!([self], "client_start");
-        self.handshake(now, PNSpace::Initial, None)?;
+        self.handshake(now, 0, None)?;
         self.set_state(State::WaitInitial);
-        self.zero_rtt_state = if self.crypto.enable_0rtt(self.role)? {
-            qdebug!([self], "Enabled 0-RTT");
-            ZeroRttState::Sending
-        } else {
-            ZeroRttState::Init
-        };
+        if self.crypto.tls.preinfo()?.early_data() {
+            qdebug!([self], "Enabling 0-RTT");
+            self.zero_rtt_state = match self.get_zero_rtt_crypto() {
+                Some(cs) => ZeroRttState::Sending(cs),
+                None => {
+                    debug_assert!(false, "We must have zero-rtt keys.");
+                    ZeroRttState::Rejected
+                }
+            };
+        }
         Ok(())
     }
 
     fn get_closing_period_time(&self, now: Instant) -> Instant {
         // Spec says close time should be at least PTO times 3.
         now + (self.loss_recovery.pto() * 3)
     }
 
@@ -1409,80 +1329,73 @@ impl Connection {
             StreamIndex::new(remote.get_integer(tp_constants::INITIAL_MAX_STREAMS_BIDI));
         self.indexes.remote_max_stream_uni =
             StreamIndex::new(remote.get_integer(tp_constants::INITIAL_MAX_STREAMS_UNI));
         self.flow_mgr
             .borrow_mut()
             .conn_increase_max_credit(remote.get_integer(tp_constants::INITIAL_MAX_DATA));
     }
 
-    fn validate_odcid(&mut self) -> Res<()> {
-        // Here we drop our Retry state then validate it.
-        if let Some(info) = self.retry_info.take() {
-            if info.token.is_empty() {
-                Ok(())
-            } else {
-                let tph = self.tps.borrow();
-                let tp = tph.remote().get_bytes(tp_constants::ORIGINAL_CONNECTION_ID);
-                if let Some(odcid_tp) = tp {
-                    if odcid_tp[..] == info.odcid[..] {
-                        Ok(())
-                    } else {
-                        Err(Error::InvalidRetry)
-                    }
+    fn validate_odcid(&self) -> Res<()> {
+        if let Some(info) = &self.retry_info {
+            let tph = self.tps.borrow();
+            let tp = tph.remote().get_bytes(tp_constants::ORIGINAL_CONNECTION_ID);
+            if let Some(odcid_tp) = tp {
+                if odcid_tp[..] == info.odcid[..] {
+                    Ok(())
                 } else {
                     Err(Error::InvalidRetry)
                 }
+            } else {
+                Err(Error::InvalidRetry)
             }
         } else {
-            debug_assert_eq!(self.role, Role::Server);
             Ok(())
         }
     }
 
-    fn handshake(&mut self, now: Instant, space: PNSpace, data: Option<&[u8]>) -> Res<()> {
-        qtrace!("Handshake space={} data={:0x?}", space, data);
-
-        let rec = data.map(|d| {
-            qtrace!([self], "Handshake received {:0x?} ", d);
-            Record {
-                ct: 22, // TODO(ekr@rtfm.com): Symbolic constants for CT. This is handshake.
-                epoch: space.into(),
-                data: d.to_vec(),
-            }
-        });
-        let try_update = rec.is_some();
+    fn handshake(&mut self, now: Instant, epoch: u16, data: Option<&[u8]>) -> Res<()> {
+        qdebug!("Handshake epoch={} data={:0x?}", epoch, data);
+
+        let rec = data
+            .map(|d| {
+                qdebug!([self], "Handshake received {:0x?} ", d);
+                Some(Record {
+                    ct: 22, // TODO(ekr@rtfm.com): Symbolic constants for CT. This is handshake.
+                    epoch,
+                    data: d.to_vec(),
+                })
+            })
+            .unwrap_or(None);
 
         match self.crypto.tls.handshake_raw(now, rec) {
             Err(e) => {
                 qwarn!([self], "Handshake failed");
                 return Err(match self.crypto.tls.alert() {
                     Some(a) => Error::CryptoAlert(*a),
                     _ => Error::CryptoError(e),
                 });
             }
             Ok(msgs) => self.crypto.buffer_records(msgs),
         }
 
-        match self.crypto.tls.state() {
-            HandshakeState::Authenticated(_) | HandshakeState::InProgress => (),
-            HandshakeState::AuthenticationPending => self.events.authentication_needed(),
-            HandshakeState::Complete(_) => {
-                if !self.state.connected() {
-                    self.set_connected(now)?;
-                }
+        if *self.crypto.tls.state() == HandshakeState::AuthenticationPending {
+            self.events.authentication_needed();
+        } else if matches!(self.crypto.tls.state(), HandshakeState::Complete(_)) {
+            qinfo!([self], "TLS handshake completed");
+
+            if self.crypto.tls.info().map(SecretAgentInfo::alpn).is_none() {
+                qwarn!([self], "No ALPN. Closing connection.");
+                // 120 = no_application_protocol
+                return Err(Error::CryptoAlert(120));
             }
-            _ => {
-                unreachable!("Crypto state should not be new or failed after successful handshake")
-            }
-        }
-        // There is a chance that this could be called less often, but getting the
-        // conditions right is a little tricky, so call it on every  CRYPTO frame.
-        if try_update {
-            self.crypto.install_keys(self.role);
+
+            self.validate_odcid()?;
+            self.set_state(State::Connected);
+            self.set_initial_limits();
         }
         Ok(())
     }
 
     fn handle_max_data(&mut self, maximum_data: u64) {
         let conn_was_blocked = self.flow_mgr.borrow().conn_credit_avail() == 0;
         let conn_credit_increased = self
             .flow_mgr
@@ -1495,36 +1408,35 @@ impl Connection {
                     // These may not actually all be writable if one
                     // uses up all the conn credit. Not our fault.
                     self.events.send_stream_writable(*id)
                 }
             }
         }
     }
 
-    fn input_frame(&mut self, ptype: PacketType, frame: Frame, now: Instant) -> Res<()> {
-        if !frame.is_allowed(ptype) {
-            qerror!("frame not allowed: {:?} {:?}", frame, ptype);
+    fn input_frame(&mut self, epoch: Epoch, frame: Frame, now: Instant) -> Res<()> {
+        if !frame.is_allowed(epoch) {
             return Err(Error::ProtocolViolation);
         }
         match frame {
             Frame::Padding => {
                 // Ignore
             }
             Frame::Ping => {
                 // Ack elicited with no further handling needed
             }
             Frame::Ack {
                 largest_acknowledged,
                 ack_delay,
                 first_ack_range,
                 ack_ranges,
             } => {
                 self.handle_ack(
-                    PNSpace::from(ptype),
+                    epoch,
                     largest_acknowledged,
                     ack_delay,
                     first_ack_range,
                     ack_ranges,
                     now,
                 )?;
             }
             Frame::ResetStream {
@@ -1543,30 +1455,29 @@ impl Connection {
             } => {
                 self.events
                     .send_stream_stop_sending(stream_id, application_error_code);
                 if let (Some(ss), _) = self.obtain_stream(stream_id)? {
                     ss.reset(application_error_code);
                 }
             }
             Frame::Crypto { offset, data } => {
-                let space = PNSpace::from(ptype);
-                qtrace!(
+                qdebug!(
                     [self],
-                    "Crypto frame on space={} offset={}, data={:0x?}",
-                    space,
+                    "Crypto frame on epoch={} offset={}, data={:0x?}",
+                    epoch,
                     offset,
                     &data
                 );
-                self.crypto.streams.inbound_frame(space, offset, data)?;
-                if self.crypto.streams.data_ready(space) {
+                self.crypto.streams.inbound_frame(epoch, offset, data)?;
+                if self.crypto.streams.data_ready(epoch) {
                     let mut buf = Vec::new();
-                    let read = self.crypto.streams.read_to_end(space, &mut buf)?;
+                    let read = self.crypto.streams.read_to_end(epoch, &mut buf)?;
                     qdebug!("Read {} bytes", read);
-                    self.handshake(now, space, Some(&buf))?;
+                    self.handshake(now, epoch, Some(&buf))?;
                 }
             }
             Frame::NewToken { token } => self.token = Some(token),
             Frame::Stream {
                 fin,
                 stream_id,
                 offset,
                 data,
@@ -1652,32 +1563,25 @@ impl Connection {
                 qwarn!([self], "Received Path Response");
             }
             Frame::ConnectionClose {
                 error_code,
                 frame_type,
                 reason_phrase,
             } => {
                 let reason_phrase = String::from_utf8_lossy(&reason_phrase);
-                qerror!(
+                qinfo!(
                     [self],
                     "ConnectionClose received. Error code: {:?} frame type {:x} reason {}",
                     error_code,
                     frame_type,
                     reason_phrase
                 );
                 self.set_state(State::Closed(error_code.into()));
             }
-            Frame::HandshakeDone => {
-                if self.role == Role::Server {
-                    return Err(Error::ProtocolViolation);
-                }
-                self.set_state(State::Confirmed);
-                self.discard_keys(PNSpace::Handshake);
-            }
         };
 
         Ok(())
     }
 
     fn handle_lost_packets(&mut self, lost_packets: &[SentPacket]) {
         for lost in lost_packets {
             for token in &lost.tokens {
@@ -1687,127 +1591,118 @@ impl Connection {
                     RecoveryToken::Stream(st) => self.send_streams.lost(&st),
                     RecoveryToken::Crypto(ct) => self.crypto.lost(&ct),
                     RecoveryToken::Flow(ft) => self.flow_mgr.borrow_mut().lost(
                         &ft,
                         &mut self.send_streams,
                         &mut self.recv_streams,
                         &mut self.indexes,
                     ),
-                    RecoveryToken::HandshakeDone => self.state_signaling.handshake_done(),
                 }
             }
         }
     }
 
     fn handle_ack(
         &mut self,
-        space: PNSpace,
+        epoch: Epoch,
         largest_acknowledged: u64,
         ack_delay: u64,
         first_ack_range: u64,
         ack_ranges: Vec<AckRange>,
         now: Instant,
     ) -> Res<()> {
         qinfo!(
             [self],
-            "Rx ACK space={}, largest_acked={}, first_ack_range={}, ranges={:?}",
-            space,
+            "Rx ACK epoch={}, largest_acked={}, first_ack_range={}, ranges={:?}",
+            epoch,
             largest_acknowledged,
             first_ack_range,
             ack_ranges
         );
 
         let acked_ranges =
             Frame::decode_ack_frame(largest_acknowledged, first_ack_range, ack_ranges)?;
         let (acked_packets, lost_packets) = self.loss_recovery.on_ack_received(
-            space,
+            PNSpace::from(epoch),
             largest_acknowledged,
             acked_ranges,
             Duration::from_millis(ack_delay),
             now,
         );
         for acked in acked_packets {
             for token in acked.tokens {
                 match token {
                     RecoveryToken::Ack(at) => self.acks.acked(&at),
                     RecoveryToken::Stream(st) => self.send_streams.acked(&st),
                     RecoveryToken::Crypto(ct) => self.crypto.acked(ct),
                     RecoveryToken::Flow(ft) => {
                         self.flow_mgr.borrow_mut().acked(ft, &mut self.send_streams)
                     }
-                    RecoveryToken::HandshakeDone => (),
                 }
             }
         }
         self.handle_lost_packets(&lost_packets);
         Ok(())
     }
 
     /// When the server rejects 0-RTT we need to drop a bunch of stuff.
     fn client_0rtt_rejected(&mut self) {
-        if !matches!(self.zero_rtt_state, ZeroRttState::Sending) {
+        if !matches!(self.zero_rtt_state, ZeroRttState::Sending(..)) {
             return;
         }
-        qdebug!([self], "0-RTT rejected");
 
         // Tell 0-RTT packets that they were "lost".
-        let dropped = self.loss_recovery.drop_0rtt();
-        self.handle_lost_packets(&dropped);
-
+        // TODO(mt) remove these from "bytes in flight" when we
+        // have a congestion controller.
+        for dropped in self.loss_recovery.drop_0rtt() {
+            for token in dropped.tokens {
+                match token {
+                    RecoveryToken::Ack(_) => {}
+                    RecoveryToken::Stream(st) => self.send_streams.lost(&st),
+                    RecoveryToken::Crypto(ct) => self.crypto.lost(&ct),
+                    RecoveryToken::Flow(ft) => self.flow_mgr.borrow_mut().lost(
+                        &ft,
+                        &mut self.send_streams,
+                        &mut self.recv_streams,
+                        &mut self.indexes,
+                    ),
+                }
+            }
+        }
         self.send_streams.clear();
         self.recv_streams.clear();
         self.indexes = StreamIndexes::new();
-        self.crypto.states.discard_0rtt_keys();
         self.events.client_0rtt_rejected();
     }
 
-    fn set_connected(&mut self, now: Instant) -> Res<()> {
-        qinfo!([self], "TLS connection complete");
-        if self.crypto.tls.info().map(SecretAgentInfo::alpn).is_none() {
-            qwarn!([self], "No ALPN. Closing connection.");
-            // 120 = no_application_protocol
-            return Err(Error::CryptoAlert(120));
-        }
-        if self.role == Role::Server {
-            // Remove the randomized client CID from the list of acceptable CIDs.
-            assert_eq!(1, self.valid_cids.len());
-            self.valid_cids.clear();
-        } else {
-            self.zero_rtt_state = if self.crypto.tls.info().unwrap().early_data_accepted() {
-                ZeroRttState::AcceptedClient
-            } else {
-                self.client_0rtt_rejected();
-                ZeroRttState::Rejected
-            };
-        }
-
-        // Setting application keys has to occur after 0-RTT rejection.
-        let pto = self.loss_recovery.pto();
-        self.crypto.install_application_keys(now + pto)?;
-        self.validate_odcid()?;
-        self.set_initial_limits();
-        self.set_state(State::Connected);
-        if self.role == Role::Server {
-            self.state_signaling.handshake_done();
-            self.set_state(State::Confirmed);
-        }
-        qinfo!([self], "Connection established");
-        Ok(())
-    }
-
     fn set_state(&mut self, state: State) {
         if state > self.state {
             qinfo!([self], "State change from {:?} -> {:?}", self.state, state);
             self.state = state.clone();
             match &self.state {
+                State::Connected => {
+                    if self.role == Role::Server {
+                        // Remove the randomized client CID from the list of acceptable CIDs.
+                        assert_eq!(1, self.valid_cids.len());
+                        self.valid_cids.clear();
+                    } else {
+                        self.zero_rtt_state =
+                            if self.crypto.tls.info().unwrap().early_data_accepted() {
+                                ZeroRttState::AcceptedClient
+                            } else {
+                                self.client_0rtt_rejected();
+                                ZeroRttState::Rejected
+                            }
+                    }
+                }
                 State::Closing { .. } => {
                     self.send_streams.clear();
                     self.recv_streams.clear();
-                    self.state_signaling.close();
+                    self.flow_mgr.borrow_mut().set_need_close_frame(true);
                 }
                 State::Closed(..) => {
                     // Equivalent to spec's "draining" state -- never send anything.
                     self.send_streams.clear();
                     self.recv_streams.clear();
                 }
                 _ => {}
             }
@@ -1856,23 +1751,19 @@ impl Connection {
     }
 
     /// Get or make a stream, and implicitly open additional streams as
     /// indicated by its stream id.
     fn obtain_stream(
         &mut self,
         stream_id: StreamId,
     ) -> Res<(Option<&mut SendStream>, Option<&mut RecvStream>)> {
-        if !self.state.connected()
-            && !matches!(
-                (&self.state, &self.zero_rtt_state),
-                (State::Handshaking, ZeroRttState::AcceptedServer)
-            )
-        {
-            return Err(Error::ConnectionState);
+        match (&self.state, &self.zero_rtt_state) {
+            (State::Connected, _) | (State::Handshaking, ZeroRttState::AcceptedServer(..)) => (),
+            _ => return Err(Error::ConnectionState),
         }
 
         // May require creating new stream(s)
         if stream_id.is_remote_initiated(self.role()) {
             let next_stream_idx = if stream_id.is_bidi() {
                 &mut self.indexes.local_next_stream_bidi
             } else {
                 &mut self.indexes.local_next_stream_uni
@@ -1969,17 +1860,17 @@ impl Connection {
 
     /// Create a stream.
     // Returns new stream id
     pub fn stream_create(&mut self, st: StreamType) -> Res<u64> {
         // Can't make streams while closing, otherwise rely on the stream limits.
         match self.state {
             State::Closing { .. } | State::Closed { .. } => return Err(Error::ConnectionState),
             State::WaitInitial | State::Handshaking => {
-                if !matches!(self.zero_rtt_state, ZeroRttState::Sending) {
+                if !matches!(self.zero_rtt_state, ZeroRttState::Sending(..)) {
                     return Err(Error::ConnectionState);
                 }
             }
             _ => (),
         }
         if self.tps.borrow().remote.is_none() && self.tps.borrow().remote_0rtt.is_none() {
             return Err(Error::ConnectionState);
         }
@@ -2142,29 +2033,103 @@ impl Connection {
     }
 
     /// Get events that indicate state changes on the connection. This method
     /// correctly handles cases where handling one event can obsolete
     /// previously-queued events, or cause new events to be generated.
     pub fn next_event(&mut self) -> Option<ConnectionEvent> {
         self.events.next_event()
     }
+
+    fn check_loss_detection_timeout(&mut self, now: Instant) {
+        qdebug!([self], "check_loss_timeouts");
+
+        if matches!(self.loss_recovery_state.mode(), LossRecoveryMode::None) {
+            // LR not the active timer
+            return;
+        }
+
+        if self.loss_recovery_state.callback_time() > Some(now) {
+            // LR timer, but hasn't expired.
+            return;
+        }
+
+        // Timer expired and LR was active timer.
+        match &mut self.loss_recovery_state.mode() {
+            LossRecoveryMode::None => unreachable!(),
+            LossRecoveryMode::LostPackets => {
+                // Time threshold loss detection
+                let (pn_space, _) = self
+                    .loss_recovery
+                    .get_earliest_loss_time()
+                    .expect("must be sent packets if in LostPackets mode");
+                let packets = self.loss_recovery.detect_lost_packets(pn_space, now);
+
+                qinfo!("lost packets: {}", packets.len());
+                for lost in packets {
+                    for token in lost.tokens {
+                        match token {
+                            RecoveryToken::Ack(_) => {} // Do nothing
+                            RecoveryToken::Stream(st) => self.send_streams.lost(&st),
+                            RecoveryToken::Crypto(ct) => self.crypto.lost(&ct),
+                            RecoveryToken::Flow(ft) => self.flow_mgr.borrow_mut().lost(
+                                &ft,
+                                &mut self.send_streams,
+                                &mut self.recv_streams,
+                                &mut self.indexes,
+                            ),
+                        }
+                    }
+                }
+            }
+            LossRecoveryMode::PTO => {
+                qinfo!(
+                    [self],
+                    "check_loss_detection_timeout -send_one_or_two_packets"
+                );
+                self.loss_recovery.increment_pto_count();
+                // TODO
+                // if (has unacknowledged crypto data):
+                //   RetransmitUnackedCryptoData()
+                // else if (endpoint is client without 1-RTT keys):
+                //   // Client sends an anti-deadlock packet: Initial is padded
+                //   // to earn more anti-amplification credit,
+                //   // a Handshake packet proves address ownership.
+                //   if (has Handshake keys):
+                //      SendOneHandshakePacket()
+                //    else:
+                //      SendOnePaddedInitialPacket()
+                // TODO
+                // SendOneOrTwoPackets()
+                // PTO. Send new data if available, else retransmit old data.
+                // If neither is available, send a single PING frame.
+
+                // TODO(agrover): determine if new data is available and if so
+                // send 2 packets worth
+                // TODO(agrover): else determine if old data is available and if
+                // so send 2 packets worth
+                // TODO(agrover): else send a single PING frame
+
+                self.tx_mode = TxMode::Pto;
+            }
+        }
+    }
 }
 
 impl ::std::fmt::Display for Connection {
     fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
-        write!(f, "{:?} {:p}", self.role, self as *const Self)
+        write!(f, "{:?} {:p}", self.role, self as *const Connection)
     }
 }
 
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::cc::{INITIAL_CWND_PKTS, MAX_DATAGRAM_SIZE, MIN_CONG_WINDOW};
     use crate::frame::{CloseError, StreamType};
+    use crate::recovery::{INITIAL_CWND_PKTS, MAX_DATAGRAM_SIZE, MIN_CONG_WINDOW};
     use neqo_common::matches;
     use std::mem;
     use test_fixture::{self, assertions, fixture_init, loopback, now};
 
     // This is fabulous: because test_fixture uses the public API for Connection,
     // it gets a different type to the ones that are referenced via super::*.
     // Thus, this code can't use default_client() and default_server() from
     // test_fixture because they produce different types.
@@ -2178,21 +2143,21 @@ mod tests {
             Rc::new(RefCell::new(FixedConnectionIdManager::new(3))),
             loopback(),
             loopback(),
         )
         .expect("create a default client");
 
         // limit dcid to a constant value to make testing easier
         let mut modded_path = c.path.take().unwrap();
-        let mut modded_cid = modded_path.remote_cid.to_vec();
-        modded_cid.truncate(8);
-        modded_path.remote_cid = ConnectionId::from(&modded_cid[..]);
+        modded_path.remote_cid.0.truncate(8);
+        let modded_dcid = modded_path.remote_cid.0.clone();
+        assert_eq!(modded_dcid.len(), 8);
         c.path = Some(modded_path);
-        c.crypto.states.init(Role::Client, &modded_cid);
+        c.crypto.create_initial_state(Role::Client, &modded_dcid);
         c
     }
     pub fn default_server() -> Connection {
         fixture_init();
         Connection::new_server(
             test_fixture::DEFAULT_KEYS,
             test_fixture::DEFAULT_ALPN,
             &test_fixture::anti_replay(),
@@ -2300,29 +2265,29 @@ mod tests {
         assert!(out.as_dgram_ref().is_none());
 
         assert!(maybe_authenticate(&mut client));
 
         qdebug!("---- client: SH..FIN -> FIN");
         let out = client.process(out.dgram(), now());
         assert!(out.as_dgram_ref().is_some());
         qdebug!("Output={:0x?}", out.as_dgram_ref());
-        assert_eq!(*client.state(), State::Connected);
 
         qdebug!("---- server: FIN -> ACKS");
         let out = server.process(out.dgram(), now());
         assert!(out.as_dgram_ref().is_some());
         qdebug!("Output={:0x?}", out.as_dgram_ref());
-        assert_eq!(*server.state(), State::Confirmed);
 
         qdebug!("---- client: ACKS -> 0");
         let out = client.process(out.dgram(), now());
         assert!(out.as_dgram_ref().is_none());
         qdebug!("Output={:0x?}", out.as_dgram_ref());
-        assert_eq!(*client.state(), State::Confirmed);
+
+        assert_eq!(*client.state(), State::Connected);
+        assert_eq!(*server.state(), State::Connected);
     }
 
     #[test]
     fn test_conn_handshake_failed_authentication() {
         qdebug!("---- client: generate CH");
         let mut client = default_client();
         let out = client.process(None, now());
         assert!(out.as_dgram_ref().is_some());
@@ -2397,19 +2362,19 @@ mod tests {
         assert!(out.as_dgram_ref().is_some());
         assert_eq!(*client.state(), State::Connected);
         qdebug!("Output={:0x?}", out.as_dgram_ref());
         // -->> Handshake[0]: CRYPTO[FIN], ACK[0]
 
         qdebug!("---- server");
         let out = server.process(out.dgram(), now());
         assert!(out.as_dgram_ref().is_some());
-        assert_eq!(*server.state(), State::Confirmed);
+        assert_eq!(*server.state(), State::Connected);
         qdebug!("Output={:0x?}", out.as_dgram_ref());
-        // ACK and HANDSHAKE_DONE
+        // ACKs
         // -->> nothing
 
         qdebug!("---- client");
         // Send
         let client_stream_id = client.stream_create(StreamType::UniDi).unwrap();
         client.stream_send(client_stream_id, &[6; 100]).unwrap();
         client.stream_send(client_stream_id, &[7; 40]).unwrap();
         client.stream_send(client_stream_id, &[8; 4000]).unwrap();
@@ -2422,27 +2387,26 @@ mod tests {
         // Sending this much takes a few datagrams.
         let mut datagrams = vec![];
         let mut out = client.process(out.dgram(), now());
         while let Some(d) = out.dgram() {
             datagrams.push(d);
             out = client.process(None, now());
         }
         assert_eq!(datagrams.len(), 4);
-        assert_eq!(*client.state(), State::Confirmed);
 
         qdebug!("---- server");
         let mut expect_ack = false;
         for d in datagrams {
             let out = server.process(Some(d), now());
             assert_eq!(out.as_dgram_ref().is_some(), expect_ack); // ACK every second.
             qdebug!("Output={:0x?}", out.as_dgram_ref());
             expect_ack = !expect_ack;
         }
-        assert_eq!(*server.state(), State::Confirmed);
+        assert_eq!(*server.state(), State::Connected);
 
         let mut buf = vec![0; 4000];
 
         let mut stream_ids = server.events().filter_map(|evt| match evt {
             ConnectionEvent::NewStream { stream_id, .. } => Some(stream_id),
             _ => None,
         });
         let stream_id = stream_ids.next().expect("should have a new stream event");
@@ -2462,36 +2426,38 @@ mod tests {
     }
 
     /// Drive the handshake between the client and server.
     fn handshake(client: &mut Connection, server: &mut Connection) {
         let mut a = client;
         let mut b = server;
         let mut datagram = None;
         let is_done = |c: &mut Connection| match c.state() {
-            State::Confirmed | State::Closing { .. } | State::Closed(..) => true,
+            // TODO(mt): Finish on Closed and not Closing.
+            State::Connected | State::Closing { .. } | State::Closed(..) => true,
             _ => false,
         };
         while !is_done(a) {
             let _ = maybe_authenticate(a);
             let d = a.process(datagram, now());
             datagram = d.dgram();
             mem::swap(&mut a, &mut b);
         }
         a.process(datagram, now());
     }
 
     fn connect(client: &mut Connection, server: &mut Connection) {
         handshake(client, server);
-        assert_eq!(*client.state(), State::Confirmed);
-        assert_eq!(*server.state(), State::Confirmed);
+        assert_eq!(*client.state(), State::Connected);
+        assert_eq!(*server.state(), State::Connected);
     }
 
     fn assert_error(c: &Connection, err: ConnectionError) {
         match c.state() {
+            // TODO(mt): Finish on Closed and not Closing.
             State::Closing { error, .. } | State::Closed(error) => {
                 assert_eq!(*error, err);
             }
             _ => panic!("bad state {:?}", c.state()),
         }
     }
 
     #[test]
@@ -2547,29 +2513,27 @@ mod tests {
         assert_eq!(2, client.stats().packets_rx);
         assert_eq!(0, client.stats().dups_rx);
 
         qdebug!("---- Dup, ignored");
         let out = client.process(out_to_rep.dgram(), now());
         assert!(out.as_dgram_ref().is_none());
         qdebug!("Output={:0x?}", out.as_dgram_ref());
 
-        // Four packets total received, 1 of them is a dup and one has been dropped because Initial keys
-        // are dropped.
+        // Four packets total received, two of them are dups
         assert_eq!(4, client.stats().packets_rx);
-        assert_eq!(1, client.stats().dups_rx);
-        assert_eq!(1, client.stats().dropped_rx);
+        assert_eq!(2, client.stats().dups_rx);
     }
 
     fn exchange_ticket(client: &mut Connection, server: &mut Connection) -> Vec<u8> {
         server.send_ticket(now(), &[]).expect("can send ticket");
-        let ticket = server.process_output(now()).dgram();
-        assert!(ticket.is_some());
-        client.process_input(ticket.unwrap(), now());
-        assert_eq!(*client.state(), State::Confirmed);
+        let out = server.process_output(now());
+        assert!(out.as_dgram_ref().is_some());
+        client.process_input(out.dgram().unwrap(), now());
+        assert_eq!(*client.state(), State::Connected);
         client.resumption_token().expect("should have token")
     }
 
     #[test]
     fn connection_close() {
         let mut client = default_client();
         let mut server = default_server();
         connect(&mut client, &mut server);
@@ -2584,17 +2548,17 @@ mod tests {
         assert_eq!(frames.len(), 1);
         assert!(matches!(
             frames[0],
             (
                 Frame::ConnectionClose {
                     error_code: CloseError::Application(42),
                     ..
                 },
-                PNSpace::ApplicationData,
+                3,
             )
         ));
     }
 
     #[test]
     fn resume() {
         let mut client = default_client();
         let mut server = default_server();
@@ -2775,17 +2739,17 @@ mod tests {
         assert_eq!(stream_id, stream_id_after_reject);
         let msg = &[1, 2, 3];
         client.stream_send(stream_id_after_reject, msg).unwrap();
         let client_after_reject = client.process(None, now());
         assert!(client_after_reject.as_dgram_ref().is_some());
 
         // The server should receive new stream
         let server_out = server.process(client_after_reject.dgram(), now());
-        assert!(server_out.as_dgram_ref().is_none()); // suppress the ack
+        assert!(server_out.as_dgram_ref().is_some()); // an ack
         let recvd_stream_evt = |e| matches!(e, ConnectionEvent::NewStream { .. });
         assert!(server.events().any(recvd_stream_evt));
     }
 
     #[test]
     // Send fin even if a peer closes a reomte bidi send stream before sending any data.
     fn report_fin_when_stream_closed_wo_data() {
         // Note that the two servers in this test will get different anti-replay filters.
@@ -2802,123 +2766,98 @@ mod tests {
 
         assert_eq!(Ok(()), server.stream_close_send(stream_id));
         let out = server.process(None, now());
         client.process(out.dgram(), now());
         let stream_readable = |e| matches!(e, ConnectionEvent::RecvStreamReadable {..});
         assert!(client.events().any(stream_readable));
     }
 
-    /// Getting the client and server to reach an idle state is surprisingly hard.
-    /// The server sends HANDSHAKE_DONE at the end of the handshake, and the client
-    /// doesn't immediately acknowledge it.
-
-    /// Force the client to send an ACK by having the server send two packets out
-    /// of order.
-    fn connect_force_idle(client: &mut Connection, server: &mut Connection) {
-        connect(client, server);
-        let p1 = send_something(server, now());
-        let p2 = send_something(server, now());
-        client.process_input(p2, now());
-        // Now the client really wants to send an ACK, but hold it back.
-        let ack = client.process(Some(p1), now()).dgram();
-        assert!(ack.is_some());
-        // Now the server has its ACK and both should be idle.
-        assert_eq!(
-            server.process(ack, now()),
-            Output::Callback(LOCAL_IDLE_TIMEOUT)
-        );
-        assert_eq!(
-            client.process_output(now()),
-            Output::Callback(LOCAL_IDLE_TIMEOUT)
-        );
-    }
-
     #[test]
     fn idle_timeout() {
         let mut client = default_client();
         let mut server = default_server();
-        connect_force_idle(&mut client, &mut server);
+        connect(&mut client, &mut server);
 
         let now = now();
 
         let res = client.process(None, now);
         assert_eq!(res, Output::Callback(Duration::from_secs(60)));
 
         // Still connected after 59 seconds. Idle timer not reset
         client.process(None, now + Duration::from_secs(59));
-        assert!(matches!(client.state(), State::Confirmed));
+        assert!(matches!(client.state(), State::Connected));
 
         client.process_timer(now + Duration::from_secs(60));
 
         // Not connected after 60 seconds.
         assert!(matches!(client.state(), State::Closed(_)));
     }
 
     #[test]
     fn idle_send_packet1() {
         let mut client = default_client();
         let mut server = default_server();
-        connect_force_idle(&mut client, &mut server);
+        connect(&mut client, &mut server);
 
         let now = now();
 
         let res = client.process(None, now);
         assert_eq!(res, Output::Callback(Duration::from_secs(60)));
 
         assert_eq!(client.stream_create(StreamType::UniDi).unwrap(), 2);
         assert_eq!(client.stream_send(2, b"hello").unwrap(), 5);
 
         let out = client.process(None, now + Duration::from_secs(10));
         let out = server.process(out.dgram(), now + Duration::from_secs(10));
 
         // Still connected after 69 seconds because idle timer reset by outgoing
         // packet
         client.process(out.dgram(), now + Duration::from_secs(69));
-        assert!(matches!(client.state(), State::Confirmed));
+        assert!(matches!(client.state(), State::Connected));
 
         // Not connected after 70 seconds.
         client.process_timer(now + Duration::from_secs(70));
         assert!(matches!(client.state(), State::Closed(_)));
     }
 
     #[test]
     fn idle_send_packet2() {
         let mut client = default_client();
         let mut server = default_server();
-        connect_force_idle(&mut client, &mut server);
+        connect(&mut client, &mut server);
 
         let now = now();
 
         let res = client.process(None, now);
         assert_eq!(res, Output::Callback(Duration::from_secs(60)));
 
         assert_eq!(client.stream_create(StreamType::UniDi).unwrap(), 2);
         assert_eq!(client.stream_send(2, b"hello").unwrap(), 5);
 
         let _out = client.process(None, now + Duration::from_secs(10));
 
         assert_eq!(client.stream_send(2, b"there").unwrap(), 5);
         let _out = client.process(None, now + Duration::from_secs(20));
 
         // Still connected after 69 seconds.
         client.process(None, now + Duration::from_secs(69));
-        assert!(matches!(client.state(), State::Confirmed));
+        assert!(matches!(client.state(), State::Connected));
 
         // Not connected after 70 seconds because timer not reset by second
         // outgoing packet
         client.process_timer(now + Duration::from_secs(70));
         assert!(matches!(client.state(), State::Closed(_)));
     }
 
     #[test]
     fn idle_recv_packet() {
         let mut client = default_client();
         let mut server = default_server();
-        connect_force_idle(&mut client, &mut server);
+        connect(&mut client, &mut server);
 
         let now = now();
 
         let res = client.process(None, now);
         assert_eq!(res, Output::Callback(Duration::from_secs(60)));
 
         assert_eq!(client.stream_create(StreamType::BiDi).unwrap(), 0);
         assert_eq!(client.stream_send(0, b"hello").unwrap(), 5);
@@ -2928,21 +2867,21 @@ mod tests {
         server.process_input(out.dgram().unwrap(), now + Duration::from_secs(10));
         assert_eq!(server.stream_send(0, b"world").unwrap(), 5);
         let out = server.process_output(now + Duration::from_secs(10));
         assert_ne!(out.as_dgram_ref(), None);
 
         // Still connected after 79 seconds because idle timer reset by received
         // packet
         client.process(out.dgram(), now + Duration::from_secs(20));
-        assert!(matches!(client.state(), State::Confirmed));
+        assert!(matches!(client.state(), State::Connected));
 
         // Still connected after 79 seconds.
         client.process_timer(now + Duration::from_secs(79));
-        assert!(matches!(client.state(), State::Confirmed));
+        assert!(matches!(client.state(), State::Connected));
 
         // Not connected after 80 seconds.
         client.process_timer(now + Duration::from_secs(80));
         assert!(matches!(client.state(), State::Closed(_)));
     }
 
     #[test]
     fn max_data() {
@@ -2965,17 +2904,17 @@ mod tests {
         assert_eq!(
             client.stream_avail_send_space(stream_id).unwrap(),
             SMALL_MAX_DATA
         );
         assert_eq!(
             client
                 .stream_send(stream_id, &[b'a'; RX_STREAM_DATA_WINDOW as usize])
                 .unwrap(),
-            usize::try_from(SMALL_MAX_DATA).unwrap()
+            SMALL_MAX_DATA.try_into().unwrap()
         );
         let evts = client.events().collect::<Vec<_>>();
         assert_eq!(evts.len(), 2); // SendStreamWritable, StateChange(connected)
         assert_eq!(client.stream_send(stream_id, b"hello").unwrap(), 0);
         let ss = client.send_streams.get_mut(stream_id.into()).unwrap();
         ss.mark_as_sent(0, 4096, false);
         ss.mark_as_acked(0, 4096, false);
 
@@ -3039,17 +2978,17 @@ mod tests {
         // One of these will contain data depending on whether Authentication was completed
         // after the first or second server packet.
         assert!(client3.as_dgram_ref().is_some() ^ client4.as_dgram_ref().is_some());
 
         let _ = server.process(client3.dgram(), now());
         let _ = server.process(client4.dgram(), now());
 
         assert_eq!(*client.state(), State::Connected);
-        assert_eq!(*server.state(), State::Confirmed);
+        assert_eq!(*server.state(), State::Connected);
     }
 
     #[test]
     fn set_local_tparam() {
         let client = default_client();
 
         client
             .set_local_tparam(
@@ -3179,17 +3118,17 @@ mod tests {
         let server_out = server.process(client_fin.dgram(), now());
         assert!(server_out.as_dgram_ref().is_some());
     }
 
     #[test]
     fn pto_works_basic() {
         let mut client = default_client();
         let mut server = default_server();
-        connect_force_idle(&mut client, &mut server);
+        connect(&mut client, &mut server);
 
         let now = now();
 
         let res = client.process(None, now);
         assert_eq!(res, Output::Callback(Duration::from_secs(60)));
 
         // Send data on two streams
         assert_eq!(client.stream_create(StreamType::UniDi).unwrap(), 2);
@@ -3206,28 +3145,27 @@ mod tests {
         let out = client.process(None, now + Duration::from_secs(10));
         assert!(matches!(out, Output::Callback(_)));
 
         // One second later, it should want to send PTO packet
         let out = client.process(None, now + Duration::from_secs(11));
 
         let frames = server.test_process_input(out.dgram().unwrap(), now + Duration::from_secs(11));
 
-        assert!(matches!(
-            frames[0],
-            (Frame::Stream { .. }, PNSpace::ApplicationData)
-        ));
+        assert_eq!(frames[0], (Frame::Ping, 0));
+        assert_eq!(frames[1], (Frame::Ping, 2));
+        assert!(matches!(frames[2], (Frame::Stream { .. }, 3)));
     }
 
     #[test]
     #[allow(clippy::cognitive_complexity)]
     fn pto_works_ping() {
         let mut client = default_client();
         let mut server = default_server();
-        connect_force_idle(&mut client, &mut server);
+        connect(&mut client, &mut server);
 
         let now = now();
 
         let res = client.process(None, now);
         assert_eq!(res, Output::Callback(Duration::from_secs(60)));
 
         // Send "zero" pkt
         assert_eq!(client.stream_create(StreamType::UniDi).unwrap(), 2);
@@ -3300,275 +3238,27 @@ mod tests {
             now + Duration::from_secs(10) + Duration::from_millis(110),
         );
 
         let frames = server.test_process_input(
             pkt6.dgram().unwrap(),
             now + Duration::from_secs(10) + Duration::from_millis(110),
         );
 
-        assert_eq!(frames[0], (Frame::Ping, PNSpace::ApplicationData));
-    }
-
-    #[test]
-    fn pto_initial() {
-        let mut now = now();
-
-        qdebug!("---- client: generate CH");
-        let mut client = default_client();
-        let pkt1 = client.process(None, now).dgram();
-        assert!(pkt1.is_some());
-        assert_eq!(pkt1.clone().unwrap().len(), 1232);
-
-        let out = client.process(None, now);
-        assert_eq!(out, Output::Callback(Duration::from_millis(120)));
-
-        // Resend initial after PTO.
-        now += Duration::from_millis(120);
-        let pkt2 = client.process(None, now).dgram();
-        assert!(pkt2.is_some());
-        assert_eq!(pkt2.unwrap().len(), 1232);
-
-        let out = client.process(None, now);
-        // PTO has doubled.
-        assert_eq!(out, Output::Callback(Duration::from_millis(240)));
-
-        // Server process the first initial pkt.
-        let mut server = default_server();
-        let out = server.process(pkt1, now).dgram();
-        assert!(out.is_some());
-
-        // Client receives ack for the first initial packet as well a Handshake packet.
-        // After the handshake packet the initial keys and the crypto stream for the initial
-        // packet number space will be discarded.
-        // Here only an ack for the Handshake packet will be sent.
-        now += Duration::from_millis(10);
-        let out = client.process(out, now).dgram();
-        assert!(out.is_some());
-
-        // We do not have PTO for the resent initial packet any more, because keys are discarded.
-        // The timeout will be an idle time out of 60s
-        let out = client.process(None, now);
-        assert_eq!(out, Output::Callback(Duration::from_secs(60)));
-    }
-
-    #[test]
-    fn pto_handshake() {
-        let mut now = now();
-        // start handshake
-        let mut client = default_client();
-        let mut server = default_server();
-
-        let pkt = client.process(None, now).dgram();
-        let out = client.process(None, now);
-        assert_eq!(out, Output::Callback(Duration::from_millis(120)));
-
-        now += Duration::from_millis(10);
-        let pkt = server.process(pkt, now).dgram();
-
-        now += Duration::from_millis(10);
-        let pkt = client.process(pkt, now).dgram();
-
-        let out = client.process(None, now);
-        assert_eq!(out, Output::Callback(Duration::from_secs(60)));
-
-        now += Duration::from_millis(10);
-        let pkt = server.process(pkt, now).dgram();
-        assert!(pkt.is_none());
-
-        now += Duration::from_millis(10);
-        client.authenticated(AuthenticationStatus::Ok, now);
-
-        qdebug!("---- client: SH..FIN -> FIN");
-        let pkt1 = client.process(None, now).dgram();
-        assert!(pkt1.is_some());
-
-        let out = client.process(None, now);
-        assert_eq!(out, Output::Callback(Duration::from_millis(60)));
-
-        // Wait for PTO o expire and resend a handshake packet
-        now += Duration::from_millis(60);
-        let pkt2 = client.process(None, now).dgram();
-        assert!(pkt2.is_some());
-
-        // PTO has been doubled.
-        let out = client.process(None, now);
-        assert_eq!(out, Output::Callback(Duration::from_millis(120)));
-
-        now += Duration::from_millis(10);
-        // Server receives the first packet.
-        // The output will be a Handshake packet with an ack and a app pn space packet with
-        // HANDSHAKE_DONE.
-        let pkt = server.process(pkt1, now).dgram();
-        assert!(pkt.is_some());
-
-        // Check that the second packet(pkt2) has a Handshake and an app pn space packet.
-        // The server has discarded the Handshake keys already, therefore the handshake packet
-        // will be dropped.
-        let dropped_before = server.stats().dropped_rx;
-        let frames = server.test_process_input(pkt2.unwrap(), now);
-        assert_eq!(1, server.stats().dropped_rx - dropped_before);
-        assert!(matches!(frames[0], (Frame::Ping, PNSpace::ApplicationData)));
-
-        now += Duration::from_millis(10);
-        // Client receive ack for the first packet
-        let out = client.process(pkt, now);
-        // Ack delay timer for the packet carrying HANDSHAKE_DONE.
-        assert_eq!(out, Output::Callback(Duration::from_millis(20)));
-
-        // Let the ack timer expire.
-        now += Duration::from_millis(20);
-        let out = client.process(None, now).dgram();
-        assert!(out.is_some());
-        let out = client.process(None, now);
-        // The handshake keys are discarded
-        // Return PTO timer for an app pn space packet (when the Handshake PTO timer has expired,
-        // a PING in the app pn space has been send as well).
-        // pto=142.5ms, the PTO packet was sent 40ms ago. The timer will be 102.5ms.
-        assert_eq!(out, Output::Callback(Duration::from_micros(102_500)));
-
-        // Let PTO expire. We will send a PING only in the APP pn space, the client has discarded
-        // Handshshake keys.
-        now += Duration::from_micros(102_500);
-        let out = client.process(None, now).dgram();
-        assert!(out.is_some());
-
-        now += Duration::from_millis(10);
-        let frames = server.test_process_input(out.unwrap(), now);
-
-        assert_eq!(frames[0], (Frame::Ping, PNSpace::ApplicationData));
-    }
-
-    #[test]
-    fn test_pto_handshake_and_app_data() {
-        let mut now = now();
-        qdebug!("---- client: generate CH");
-        let mut client = default_client();
-        let pkt = client.process(None, now);
-
-        now += Duration::from_millis(10);
-        qdebug!("---- server: CH -> SH, EE, CERT, CV, FIN");
-        let mut server = default_server();
-        let pkt = server.process(pkt.dgram(), now);
-
-        now += Duration::from_millis(10);
-        qdebug!("---- client: cert verification");
-        let pkt = client.process(pkt.dgram(), now);
-
-        now += Duration::from_millis(10);
-        let _pkt = server.process(pkt.dgram(), now);
-
-        now += Duration::from_millis(10);
-        client.authenticated(AuthenticationStatus::Ok, now);
-
-        assert_eq!(client.stream_create(StreamType::UniDi).unwrap(), 2);
-        assert_eq!(client.stream_send(2, b"zero").unwrap(), 4);
-        qdebug!("---- client: SH..FIN -> FIN and 1RTT packet");
-        let pkt1 = client.process(None, now).dgram();
-        assert!(pkt1.is_some());
-
-        // Get PTO timer.
-        let out = client.process(None, now);
-        assert_eq!(out, Output::Callback(Duration::from_millis(60)));
-
-        // Wait for PTO o expire and resend a handshake and 1rtt packet
-        now += Duration::from_millis(60);
-        let pkt2 = client.process(None, now).dgram();
-        assert!(pkt2.is_some());
-
-        now += Duration::from_millis(10);
-        let frames = server.test_process_input(pkt2.unwrap(), now);
-
-        assert!(matches!(
-            frames[0],