зеркало из https://github.com/mozilla/gecko-dev.git
Backed out changeset 848d0ab187f0 (bug 1614711) for causing build bustages with: use of unstable library feature 'mem_take' error
CLOSED TREE
This commit is contained in:
Родитель
ac5daf49e7
Коммит
c1fc568d90
|
@ -15,7 +15,7 @@ rev = "6a866fdad2ca880df9b87fcbc9921abac1e91914"
|
|||
[source."https://github.com/mozilla/neqo"]
|
||||
git = "https://github.com/mozilla/neqo"
|
||||
replace-with = "vendored-sources"
|
||||
tag = "v0.1.13"
|
||||
tag = "v0.1.12"
|
||||
|
||||
[source."https://github.com/kvark/spirv_cross"]
|
||||
branch = "wgpu"
|
||||
|
|
|
@ -2571,8 +2571,8 @@ checksum = "a2983372caf4480544083767bf2d27defafe32af49ab4df3a0b7fc90793a3664"
|
|||
|
||||
[[package]]
|
||||
name = "neqo-common"
|
||||
version = "0.1.13"
|
||||
source = "git+https://github.com/mozilla/neqo?tag=v0.1.13#1bc70df50cb3e22ec6820e2a746a506317977604"
|
||||
version = "0.1.12"
|
||||
source = "git+https://github.com/mozilla/neqo?tag=v0.1.12#ccb7c07326230c4b1dc68d98a15ce364a4718c15"
|
||||
dependencies = [
|
||||
"env_logger",
|
||||
"lazy_static",
|
||||
|
@ -2582,8 +2582,8 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "neqo-crypto"
|
||||
version = "0.1.13"
|
||||
source = "git+https://github.com/mozilla/neqo?tag=v0.1.13#1bc70df50cb3e22ec6820e2a746a506317977604"
|
||||
version = "0.1.12"
|
||||
source = "git+https://github.com/mozilla/neqo?tag=v0.1.12#ccb7c07326230c4b1dc68d98a15ce364a4718c15"
|
||||
dependencies = [
|
||||
"bindgen",
|
||||
"log",
|
||||
|
@ -2595,8 +2595,8 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "neqo-http3"
|
||||
version = "0.1.13"
|
||||
source = "git+https://github.com/mozilla/neqo?tag=v0.1.13#1bc70df50cb3e22ec6820e2a746a506317977604"
|
||||
version = "0.1.12"
|
||||
source = "git+https://github.com/mozilla/neqo?tag=v0.1.12#ccb7c07326230c4b1dc68d98a15ce364a4718c15"
|
||||
dependencies = [
|
||||
"log",
|
||||
"neqo-common",
|
||||
|
@ -2609,8 +2609,8 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "neqo-qpack"
|
||||
version = "0.1.13"
|
||||
source = "git+https://github.com/mozilla/neqo?tag=v0.1.13#1bc70df50cb3e22ec6820e2a746a506317977604"
|
||||
version = "0.1.12"
|
||||
source = "git+https://github.com/mozilla/neqo?tag=v0.1.12#ccb7c07326230c4b1dc68d98a15ce364a4718c15"
|
||||
dependencies = [
|
||||
"log",
|
||||
"neqo-common",
|
||||
|
@ -2620,13 +2620,14 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "neqo-transport"
|
||||
version = "0.1.13"
|
||||
source = "git+https://github.com/mozilla/neqo?tag=v0.1.13#1bc70df50cb3e22ec6820e2a746a506317977604"
|
||||
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",
|
||||
]
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
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 {
|
||||
|
|
|
@ -54,7 +54,7 @@ enum class SpdyVersion {
|
|||
};
|
||||
|
||||
extern const nsCString kHttp3Version;
|
||||
const char kHttp3VersionHEX[] = "ff00000019"; // this is draft 25.
|
||||
const char kHttp3VersionHEX[] = "ff00000018"; // this is draft 24.
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// http connection capabilities
|
||||
|
|
|
@ -8,16 +8,16 @@ edition = "2018"
|
|||
name = "neqo_glue"
|
||||
|
||||
[dependencies]
|
||||
neqo-http3 = { tag = "v0.1.13", git = "https://github.com/mozilla/neqo" }
|
||||
neqo-transport = { tag = "v0.1.13", git = "https://github.com/mozilla/neqo" }
|
||||
neqo-common = { tag = "v0.1.13", 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.13"
|
||||
tag = "v0.1.12"
|
||||
git = "https://github.com/mozilla/neqo"
|
||||
default-features = false
|
||||
features = ["gecko"]
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"679490817e9489b056470e592edd623a607cbe613195f7fbd75eb92811836cb0","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}
|
||||
{"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}
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "neqo-common"
|
||||
version = "0.1.13"
|
||||
version = "0.1.12"
|
||||
authors = ["Bobby Holley <bobbyholley@gmail.com>"]
|
||||
edition = "2018"
|
||||
license = "MIT/Apache-2.0"
|
||||
|
|
|
@ -29,12 +29,6 @@ impl<'a> Decoder<'a> {
|
|||
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);
|
||||
|
@ -80,7 +74,7 @@ impl<'a> Decoder<'a> {
|
|||
}
|
||||
|
||||
/// 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;
|
||||
}
|
||||
|
@ -120,13 +114,13 @@ impl<'a> Decoder<'a> {
|
|||
}
|
||||
|
||||
/// 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,
|
||||
|
@ -142,13 +136,13 @@ impl<'a> Decoder<'a> {
|
|||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
|
@ -339,11 +333,6 @@ impl Encoder {
|
|||
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 {
|
||||
|
|
|
@ -7,9 +7,7 @@
|
|||
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,
|
||||
|
@ -43,15 +41,3 @@ impl Deref for Datagram {
|
|||
&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)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
// except according to those terms.
|
||||
|
||||
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
|
||||
#![warn(clippy::use_self)]
|
||||
|
||||
use neqo_common::{qdebug, qerror, qinfo, qtrace, qwarn};
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"7590c2cd1d5286f1546c7a09fc3dd07b2b0182e81bcc551a323ff4965230f36c","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}
|
||||
{"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}
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "neqo-crypto"
|
||||
version = "0.1.13"
|
||||
version = "0.1.12"
|
||||
authors = ["Martin Thomson <mt@lowentropy.net>"]
|
||||
edition = "2018"
|
||||
build = "build.rs"
|
||||
|
|
|
@ -39,7 +39,6 @@ functions = [
|
|||
"SSL_ImportFD",
|
||||
"SSL_NamedGroupConfig",
|
||||
"SSL_OptionSet",
|
||||
"SSL_OptionGetDefault",
|
||||
"SSL_PeerCertificate",
|
||||
"SSL_PeerCertificateChain",
|
||||
"SSL_PeerSignedCertTimestamps",
|
||||
|
|
|
@ -129,10 +129,7 @@ fn get_bash() -> PathBuf {
|
|||
}
|
||||
|
||||
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 {
|
||||
|
@ -170,7 +167,12 @@ fn dynamic_link_both(extra_libs: &[&str]) {
|
|||
}
|
||||
}
|
||||
|
||||
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",
|
||||
|
@ -297,8 +299,9 @@ fn setup_standalone() -> Vec<String> {
|
|||
"cargo:rustc-link-search=native={}",
|
||||
nsslibdir.to_str().unwrap()
|
||||
);
|
||||
|
||||
if is_debug() {
|
||||
static_link();
|
||||
static_link(&nsstarget);
|
||||
} else {
|
||||
dynamic_link();
|
||||
}
|
||||
|
|
|
@ -53,13 +53,7 @@ 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) }
|
||||
|
@ -98,9 +92,6 @@ impl Aead {
|
|||
///
|
||||
/// 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,
|
||||
|
@ -130,9 +121,6 @@ impl Aead {
|
|||
/// 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,
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
// 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;
|
||||
|
@ -19,7 +19,7 @@ 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;
|
||||
|
@ -43,13 +43,11 @@ pub enum HandshakeState {
|
|||
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,7 +77,7 @@ fn get_alpn(fd: *mut ssl::PRFileDesc, pre: bool) -> Res<Option<String>> {
|
|||
}
|
||||
_ => None,
|
||||
};
|
||||
qtrace!([format!("{:p}", fd)], "got ALPN {:?}", alpn);
|
||||
qinfo!([format!("{:p}", fd)], "got ALPN {:?}", alpn);
|
||||
Ok(alpn)
|
||||
}
|
||||
|
||||
|
@ -125,7 +123,7 @@ impl SecretAgentPreInfo {
|
|||
}
|
||||
#[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> {
|
||||
|
@ -227,24 +225,24 @@ pub struct SecretAgent {
|
|||
|
||||
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.
|
||||
|
@ -254,7 +252,7 @@ impl SecretAgent {
|
|||
// 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()) };
|
||||
|
@ -264,14 +262,15 @@ impl SecretAgent {
|
|||
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(
|
||||
|
@ -321,7 +320,7 @@ impl SecretAgent {
|
|||
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,
|
||||
)
|
||||
})?;
|
||||
|
||||
|
@ -329,21 +328,24 @@ impl SecretAgent {
|
|||
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)?;
|
||||
|
@ -352,10 +354,6 @@ impl SecretAgent {
|
|||
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,
|
||||
|
@ -364,10 +362,6 @@ impl SecretAgent {
|
|||
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;
|
||||
|
@ -386,10 +380,6 @@ impl SecretAgent {
|
|||
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
|
||||
|
@ -404,9 +394,6 @@ impl SecretAgent {
|
|||
}
|
||||
|
||||
/// 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))
|
||||
|
@ -414,9 +401,6 @@ impl SecretAgent {
|
|||
}
|
||||
|
||||
/// Enable 0-RTT.
|
||||
///
|
||||
/// # Errors
|
||||
/// See `set_option`.
|
||||
pub fn enable_0rtt(&mut self) -> Res<()> {
|
||||
self.set_option(ssl::Opt::EarlyData, true)
|
||||
}
|
||||
|
@ -432,9 +416,6 @@ impl SecretAgent {
|
|||
///
|
||||
/// 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();
|
||||
|
@ -478,9 +459,6 @@ impl SecretAgent {
|
|||
/// 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,
|
||||
|
@ -521,9 +499,6 @@ impl SecretAgent {
|
|||
///
|
||||
/// 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)
|
||||
}
|
||||
|
@ -573,16 +548,13 @@ impl SecretAgent {
|
|||
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)?;
|
||||
|
@ -632,15 +604,12 @@ impl SecretAgent {
|
|||
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()?;
|
||||
|
@ -673,44 +642,18 @@ impl SecretAgent {
|
|||
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)
|
||||
pub fn read_secret(&self, epoch: Epoch) -> Option<&p11::SymKey> {
|
||||
self.secrets.read().get(epoch)
|
||||
}
|
||||
/// Take a write secret.
|
||||
#[must_use]
|
||||
pub fn write_secret(&mut self, epoch: Epoch) -> Option<p11::SymKey> {
|
||||
self.secrets.take_write(epoch)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for SecretAgent {
|
||||
fn drop(&mut self) {
|
||||
self.close();
|
||||
pub fn write_secret(&self, epoch: Epoch) -> Option<&p11::SymKey> {
|
||||
self.secrets.write().get(epoch)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -730,10 +673,6 @@ pub struct Client {
|
|||
}
|
||||
|
||||
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)?;
|
||||
|
@ -741,7 +680,7 @@ impl Client {
|
|||
agent.ready(false)?;
|
||||
let mut client = Self {
|
||||
agent,
|
||||
resumption: Box::pin(None),
|
||||
resumption: Pin::new(Box::new(None)),
|
||||
};
|
||||
client.ready()?;
|
||||
Ok(client)
|
||||
|
@ -763,12 +702,11 @@ impl Client {
|
|||
}
|
||||
|
||||
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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -780,10 +718,6 @@ impl Client {
|
|||
}
|
||||
|
||||
/// 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(
|
||||
|
@ -850,10 +784,6 @@ pub struct Server {
|
|||
}
|
||||
|
||||
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()?;
|
||||
|
||||
|
@ -923,22 +853,16 @@ impl Server {
|
|||
|
||||
/// 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);
|
||||
|
@ -950,9 +874,6 @@ impl Server {
|
|||
/// 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()?;
|
||||
|
|
|
@ -26,11 +26,6 @@ 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 {
|
||||
|
@ -117,10 +112,9 @@ impl RecordList {
|
|||
|
||||
/// 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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,15 +12,7 @@ use crate::ssl;
|
|||
// 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.
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
// 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::{
|
||||
|
@ -118,14 +117,11 @@ impl ExtensionTracker {
|
|||
}
|
||||
|
||||
/// Use the provided handler to manage an extension. This is quite unsafe.
|
||||
///
|
||||
/// # 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,
|
||||
|
@ -144,15 +140,16 @@ impl ExtensionTracker {
|
|||
// 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)
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ experimental_api!(SSL_HkdfExpandLabel(
|
|||
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);
|
||||
}
|
||||
|
@ -45,18 +45,11 @@ fn key_size(version: Version, cipher: Cipher) -> Res<usize> {
|
|||
})
|
||||
}
|
||||
|
||||
/// 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);
|
||||
|
@ -93,9 +86,6 @@ pub fn import_key(version: Version, cipher: Cipher, buf: &[u8]) -> Res<SymKey> {
|
|||
}
|
||||
|
||||
/// 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,
|
||||
|
@ -115,9 +105,6 @@ pub fn extract(
|
|||
}
|
||||
|
||||
/// 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,
|
||||
|
|
|
@ -29,20 +29,16 @@ experimental_api!(SSL_HkdfExpandLabelWithMech(
|
|||
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();
|
||||
|
@ -77,10 +73,6 @@ impl HpKey {
|
|||
}
|
||||
|
||||
/// 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;
|
||||
|
|
|
@ -41,7 +41,7 @@ pub use self::agent::{
|
|||
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;
|
||||
|
@ -104,18 +104,6 @@ pub fn init() {
|
|||
}
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
|
@ -147,9 +135,6 @@ pub fn init_db<P: Into<PathBuf>>(dir: P) {
|
|||
))
|
||||
.expect("SSL_ConfigServerSessionIDCache failed");
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
enable_ssl_trace();
|
||||
|
||||
NssLoaded::Db(path.into_boxed_path())
|
||||
});
|
||||
}
|
||||
|
|
|
@ -67,9 +67,6 @@ 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) })?;
|
||||
|
||||
|
@ -82,15 +79,6 @@ impl SymKey {
|
|||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
|
@ -102,14 +90,10 @@ impl std::fmt::Debug for 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)]
|
||||
|
|
|
@ -4,8 +4,11 @@
|
|||
// 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"));
|
||||
|
||||
|
|
|
@ -49,10 +49,6 @@ pub struct AntiReplay {
|
|||
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 {
|
||||
|
|
|
@ -90,7 +90,7 @@ mod tests {
|
|||
|
||||
#[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);
|
||||
|
|
|
@ -4,15 +4,14 @@
|
|||
// 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(
|
||||
|
@ -46,7 +45,7 @@ pub struct DirectionalSecrets {
|
|||
}
|
||||
|
||||
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());
|
||||
|
@ -54,11 +53,11 @@ impl DirectionalSecrets {
|
|||
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()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -87,10 +86,10 @@ impl Secrets {
|
|||
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,
|
||||
|
@ -98,34 +97,33 @@ impl Secrets {
|
|||
};
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,10 +27,9 @@ 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,
|
||||
|
@ -48,11 +47,9 @@ impl SelfEncrypt {
|
|||
}
|
||||
|
||||
/// 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;
|
||||
|
@ -64,9 +61,6 @@ impl SelfEncrypt {
|
|||
/// 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:
|
||||
|
@ -77,7 +71,7 @@ impl SelfEncrypt {
|
|||
// 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();
|
||||
|
||||
|
@ -117,10 +111,6 @@ impl SelfEncrypt {
|
|||
}
|
||||
|
||||
/// 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 {
|
||||
|
|
|
@ -4,8 +4,10 @@
|
|||
// 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::*;
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
|
||||
#![warn(clippy::pedantic)]
|
||||
|
||||
use neqo_crypto::aead::Aead;
|
||||
use neqo_crypto::constants::*;
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
|
||||
#![warn(clippy::pedantic)]
|
||||
|
||||
use neqo_crypto::*;
|
||||
|
||||
|
@ -49,7 +48,7 @@ fn basic() {
|
|||
// 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());
|
||||
|
@ -57,14 +56,14 @@ fn basic() {
|
|||
|
||||
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);
|
||||
|
@ -73,7 +72,7 @@ fn check_client_preinfo(client_preinfo: &SecretAgentPreInfo) {
|
|||
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);
|
||||
|
@ -94,14 +93,14 @@ fn raw() {
|
|||
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());
|
||||
|
@ -113,11 +112,11 @@ fn raw() {
|
|||
// 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();
|
||||
|
@ -354,21 +353,3 @@ fn reject_zero_rtt() {
|
|||
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.
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
|
||||
#![warn(clippy::pedantic)]
|
||||
|
||||
use neqo_crypto::*;
|
||||
use std::cell::RefCell;
|
||||
|
@ -60,10 +59,10 @@ impl ExtensionHandler for SimpleExtensionHandler {
|
|||
self.handled = true;
|
||||
if d.len() != 1 {
|
||||
ExtensionHandlerResult::Alert(50) // decode_error
|
||||
} else if d[0] == 77 {
|
||||
ExtensionHandlerResult::Ok
|
||||
} else {
|
||||
} else if d[0] != 77 {
|
||||
ExtensionHandlerResult::Alert(47) // illegal_parameter
|
||||
} else {
|
||||
ExtensionHandlerResult::Ok
|
||||
}
|
||||
}
|
||||
_ => ExtensionHandlerResult::Alert(110), // unsupported_extension
|
||||
|
|
|
@ -16,7 +16,7 @@ pub fn forward_records(
|
|||
_ => 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);
|
||||
|
||||
|
@ -30,14 +30,18 @@ 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 {
|
||||
|
@ -52,8 +56,8 @@ pub fn connect_at(now: Instant, client: &mut SecretAgent, server: &mut SecretAge
|
|||
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) {
|
||||
|
@ -62,8 +66,8 @@ pub fn connect(client: &mut SecretAgent, server: &mut SecretAgent) {
|
|||
|
||||
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)]
|
||||
|
@ -81,7 +85,7 @@ pub struct PermissiveZeroRttChecker {
|
|||
|
||||
impl Default for PermissiveZeroRttChecker {
|
||||
fn default() -> Self {
|
||||
Self { resuming: true }
|
||||
PermissiveZeroRttChecker { resuming: true }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
|
||||
#![warn(clippy::pedantic)]
|
||||
|
||||
use neqo_crypto::constants::*;
|
||||
use neqo_crypto::{hkdf, SymKey};
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
|
||||
#![warn(clippy::pedantic)]
|
||||
|
||||
use neqo_crypto::constants::*;
|
||||
use neqo_crypto::hkdf;
|
||||
|
@ -14,35 +13,37 @@ fn make_hp(cipher: Cipher) -> HpKey {
|
|||
|
||||
#[test]
|
||||
fn aes128() {
|
||||
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");
|
||||
const EXPECTED: &[u8] = &[
|
||||
0x04, 0x7b, 0xda, 0x65, 0xc3, 0x41, 0xcf, 0xbc, 0x5d, 0xe1, 0x75, 0x2b, 0x9d, 0x7d, 0xc3,
|
||||
0x14,
|
||||
];
|
||||
assert_eq!(mask, EXPECTED);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aes256() {
|
||||
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");
|
||||
const EXPECTED: &[u8] = &[
|
||||
0xb5, 0xea, 0xa2, 0x1c, 0x25, 0x77, 0x48, 0x18, 0xbf, 0x25, 0xea, 0xfa, 0xbd, 0x8d, 0x80,
|
||||
0x2b,
|
||||
];
|
||||
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,
|
||||
|
@ -50,10 +51,5 @@ fn chacha20_ctr() {
|
|||
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);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#![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,
|
||||
|
@ -12,9 +11,8 @@ 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"));
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
|
||||
#![warn(clippy::pedantic)]
|
||||
|
||||
use neqo_crypto::constants::*;
|
||||
use neqo_crypto::{init, selfencrypt::SelfEncrypt, Error};
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"418e6d9c20e2ae00d840b2dc57a94bec093194e4974a3b25863a6656be5d4257","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}
|
||||
{"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}
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "neqo-http3"
|
||||
version = "0.1.13"
|
||||
version = "0.1.12"
|
||||
authors = ["Dragana Damjanovic <dragana.damjano@gmail.com>"]
|
||||
edition = "2018"
|
||||
license = "MIT/Apache-2.0"
|
||||
|
|
|
@ -85,7 +85,7 @@ impl<T: Http3Transaction> Http3Connection<T> {
|
|||
if max_table_size > (1 << 30) - 1 {
|
||||
panic!("Wrong max_table_size");
|
||||
}
|
||||
Self {
|
||||
Http3Connection {
|
||||
state: Http3State::Initializing,
|
||||
local_settings: LocalSettings {
|
||||
max_table_size,
|
||||
|
@ -341,7 +341,6 @@ impl<T: Http3Transaction> Http3Connection<T> {
|
|||
}
|
||||
|
||||
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!(
|
||||
|
@ -364,7 +363,7 @@ impl<T: Http3Transaction> Http3Connection<T> {
|
|||
}
|
||||
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)
|
||||
|
|
|
@ -44,7 +44,7 @@ impl Http3Client {
|
|||
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,
|
||||
|
@ -52,7 +52,7 @@ impl Http3Client {
|
|||
}
|
||||
|
||||
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(),
|
||||
|
@ -184,7 +184,6 @@ impl Http3Client {
|
|||
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))
|
||||
|
@ -568,7 +567,7 @@ mod tests {
|
|||
|
||||
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.
|
||||
|
@ -2483,7 +2482,7 @@ mod tests {
|
|||
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());
|
||||
|
@ -2516,7 +2515,7 @@ mod tests {
|
|||
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());
|
||||
|
||||
|
@ -2608,6 +2607,7 @@ mod tests {
|
|||
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();
|
||||
|
@ -2643,7 +2643,7 @@ mod tests {
|
|||
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());
|
||||
|
@ -2661,7 +2661,7 @@ mod tests {
|
|||
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]
|
||||
|
@ -2679,6 +2679,7 @@ mod tests {
|
|||
HSetting::new(HSettingType::MaxHeaderListSize, 10000),
|
||||
],
|
||||
Http3State::Connected,
|
||||
State::Connected,
|
||||
ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION,
|
||||
);
|
||||
}
|
||||
|
@ -2697,6 +2698,7 @@ mod tests {
|
|||
HSetting::new(HSettingType::MaxHeaderListSize, 10000),
|
||||
],
|
||||
Http3State::Closing(CloseError::Application(265)),
|
||||
State::Connected,
|
||||
ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION,
|
||||
);
|
||||
}
|
||||
|
@ -2715,6 +2717,7 @@ mod tests {
|
|||
HSetting::new(HSettingType::MaxHeaderListSize, 10000),
|
||||
],
|
||||
Http3State::Closing(CloseError::Application(265)),
|
||||
State::Connected,
|
||||
ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION,
|
||||
);
|
||||
}
|
||||
|
@ -2733,6 +2736,7 @@ mod tests {
|
|||
HSetting::new(HSettingType::BlockedStreams, 100),
|
||||
],
|
||||
Http3State::Connected,
|
||||
State::Connected,
|
||||
ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION,
|
||||
);
|
||||
}
|
||||
|
@ -2752,6 +2756,7 @@ mod tests {
|
|||
HSetting::new(HSettingType::MaxHeaderListSize, 10000),
|
||||
],
|
||||
Http3State::Closing(CloseError::Application(265)),
|
||||
State::Connected,
|
||||
ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION,
|
||||
);
|
||||
}
|
||||
|
@ -2771,6 +2776,7 @@ mod tests {
|
|||
HSetting::new(HSettingType::MaxHeaderListSize, 10000),
|
||||
],
|
||||
Http3State::Closing(CloseError::Application(265)),
|
||||
State::Connected,
|
||||
ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION,
|
||||
);
|
||||
}
|
||||
|
@ -2790,6 +2796,7 @@ mod tests {
|
|||
HSetting::new(HSettingType::MaxHeaderListSize, 10000),
|
||||
],
|
||||
Http3State::Connected,
|
||||
State::Connected,
|
||||
ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION,
|
||||
);
|
||||
}
|
||||
|
@ -2809,6 +2816,7 @@ mod tests {
|
|||
HSetting::new(HSettingType::MaxHeaderListSize, 10000),
|
||||
],
|
||||
Http3State::Closing(CloseError::Application(265)),
|
||||
State::Connected,
|
||||
ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION,
|
||||
);
|
||||
}
|
||||
|
@ -2828,6 +2836,7 @@ mod tests {
|
|||
HSetting::new(HSettingType::MaxHeaderListSize, 20000),
|
||||
],
|
||||
Http3State::Connected,
|
||||
State::Connected,
|
||||
ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION,
|
||||
);
|
||||
}
|
||||
|
@ -2847,6 +2856,7 @@ mod tests {
|
|||
HSetting::new(HSettingType::MaxHeaderListSize, 5000),
|
||||
],
|
||||
Http3State::Closing(CloseError::Application(265)),
|
||||
State::Connected,
|
||||
ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION,
|
||||
);
|
||||
}
|
||||
|
@ -2866,6 +2876,7 @@ mod tests {
|
|||
HSetting::new(HSettingType::MaxHeaderListSize, 10000),
|
||||
],
|
||||
Http3State::Connected,
|
||||
State::Connected,
|
||||
ENCODER_STREAM_DATA,
|
||||
);
|
||||
}
|
||||
|
@ -2885,6 +2896,7 @@ mod tests {
|
|||
HSetting::new(HSettingType::MaxHeaderListSize, 10000),
|
||||
],
|
||||
Http3State::Connected,
|
||||
State::Connected,
|
||||
ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION,
|
||||
);
|
||||
}
|
||||
|
@ -2904,6 +2916,7 @@ mod tests {
|
|||
HSetting::new(HSettingType::MaxHeaderListSize, 10000),
|
||||
],
|
||||
Http3State::Closing(CloseError::Application(265)),
|
||||
State::Connected,
|
||||
ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ impl ::std::fmt::Display for Http3ServerHandler {
|
|||
|
||||
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(),
|
||||
}
|
||||
|
|
|
@ -24,8 +24,8 @@ impl ::std::fmt::Display for ControlStreamRemote {
|
|||
}
|
||||
|
||||
impl ControlStreamRemote {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
pub fn new() -> ControlStreamRemote {
|
||||
ControlStreamRemote {
|
||||
stream_id: None,
|
||||
frame_reader: HFrameReader::new(),
|
||||
fin: false,
|
||||
|
|
|
@ -65,14 +65,14 @@ pub enum HFrame {
|
|||
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,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -80,19 +80,19 @@ impl HFrame {
|
|||
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,
|
||||
} => {
|
||||
|
@ -100,17 +100,17 @@ impl HFrame {
|
|||
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);
|
||||
});
|
||||
|
@ -120,14 +120,14 @@ impl HFrame {
|
|||
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -158,8 +158,8 @@ impl Default for HFrameReader {
|
|||
}
|
||||
|
||||
impl HFrameReader {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
pub fn new() -> HFrameReader {
|
||||
HFrameReader {
|
||||
state: HFrameReaderState::BeforeFrame,
|
||||
hframe_type: 0,
|
||||
hframe_len: 0,
|
||||
|
|
|
@ -37,7 +37,7 @@ pub struct HSetting {
|
|||
|
||||
impl HSetting {
|
||||
pub fn new(setting_type: HSettingType, value: u64) -> Self {
|
||||
Self {
|
||||
HSetting {
|
||||
setting_type,
|
||||
value,
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ pub struct HSettings {
|
|||
|
||||
impl HSettings {
|
||||
pub fn new(settings: &[HSetting]) -> Self {
|
||||
Self {
|
||||
HSettings {
|
||||
settings: settings.to_vec(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
// except according to those terms.
|
||||
|
||||
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
|
||||
#![warn(clippy::use_self)]
|
||||
|
||||
mod client_events;
|
||||
mod connection;
|
||||
|
@ -23,9 +22,9 @@ 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;
|
||||
|
@ -64,84 +63,82 @@ pub enum Error {
|
|||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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<AppError> for Error {
|
||||
fn from(error: AppError) -> Self {
|
||||
pub fn from_code(error: neqo_transport::AppError) -> Error {
|
||||
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,
|
||||
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<neqo_transport::Error> for Error {
|
||||
fn from(err: neqo_transport::Error) -> Self {
|
||||
Error::TransportError(err)
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,8 +43,8 @@ impl Http3Server {
|
|||
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,
|
||||
|
|
|
@ -39,7 +39,7 @@ impl ClientRequestStream {
|
|||
handler: Rc<RefCell<Http3ServerHandler>>,
|
||||
stream_id: u64,
|
||||
) -> Self {
|
||||
Self {
|
||||
ClientRequestStream {
|
||||
conn,
|
||||
handler,
|
||||
stream_id,
|
||||
|
|
|
@ -14,8 +14,8 @@ pub struct NewStreamTypeReader {
|
|||
}
|
||||
|
||||
impl NewStreamTypeReader {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
pub fn new() -> NewStreamTypeReader {
|
||||
NewStreamTypeReader {
|
||||
reader: IncrementalDecoder::decode_varint(),
|
||||
fin: false,
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ 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;
|
||||
|
@ -36,8 +36,8 @@ struct Request {
|
|||
}
|
||||
|
||||
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(),
|
||||
|
@ -180,9 +180,9 @@ impl TransactionClient {
|
|||
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,
|
||||
|
@ -291,7 +291,6 @@ impl TransactionClient {
|
|||
HFrame::PushPromise { .. } => Err(Error::HttpIdError),
|
||||
HFrame::Headers { .. } => {
|
||||
// TODO implement trailers!
|
||||
qwarn!([self], "Received trailers");
|
||||
Err(Error::HttpFrameUnexpected)
|
||||
}
|
||||
_ => Err(Error::HttpFrameUnexpected),
|
||||
|
|
|
@ -42,9 +42,9 @@ pub struct TransactionServer {
|
|||
}
|
||||
|
||||
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,
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"b7caad70be20f74848df1286119f9b9a7895eb07de9f6be0394fef5020fdd993","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}
|
||||
{"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}
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "neqo-qpack"
|
||||
version = "0.1.13"
|
||||
version = "0.1.12"
|
||||
authors = ["Dragana Damjanovic <dragana.damjano@gmail.com>"]
|
||||
edition = "2018"
|
||||
license = "MIT/Apache-2.0"
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
// 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,
|
||||
|
@ -73,6 +74,7 @@ enum QPackDecoderState {
|
|||
pub struct QPackDecoder {
|
||||
state: QPackDecoderState,
|
||||
table: HeaderTable,
|
||||
increment: u64,
|
||||
total_num_of_inserts: u64,
|
||||
max_entries: u64,
|
||||
send_buf: QPData,
|
||||
|
@ -84,11 +86,12 @@ pub struct QPackDecoder {
|
|||
}
|
||||
|
||||
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(),
|
||||
|
@ -209,6 +212,7 @@ impl QPackDecoder {
|
|||
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 };
|
||||
|
@ -310,9 +314,10 @@ impl QPackDecoder {
|
|||
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
|
||||
|
@ -426,8 +431,9 @@ impl QPackDecoder {
|
|||
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
|
||||
|
@ -447,6 +453,7 @@ impl QPackDecoder {
|
|||
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
|
||||
|
@ -478,18 +485,13 @@ impl QPackDecoder {
|
|||
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) {
|
||||
|
@ -499,13 +501,10 @@ impl QPackDecoder {
|
|||
|
||||
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(())
|
||||
|
@ -554,7 +553,7 @@ impl QPackDecoder {
|
|||
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));
|
||||
|
@ -790,100 +789,65 @@ fn read_prefixed_encoded_int_with_connection_wrap(
|
|||
#[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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -893,7 +857,7 @@ mod tests {
|
|||
test_instruction(
|
||||
0,
|
||||
&[0xc4, 0x04, 0x31, 0x32, 0x33, 0x34],
|
||||
Err(Error::DecoderStreamError),
|
||||
Some(Error::DecoderStreamError),
|
||||
&[0x03],
|
||||
0,
|
||||
);
|
||||
|
@ -905,7 +869,7 @@ mod tests {
|
|||
test_instruction(
|
||||
100,
|
||||
&[0xc4, 0x04, 0x31, 0x32, 0x33, 0x34],
|
||||
Ok(()),
|
||||
None,
|
||||
&[0x03, 0x01],
|
||||
0,
|
||||
);
|
||||
|
@ -920,7 +884,7 @@ mod tests {
|
|||
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,
|
||||
);
|
||||
|
@ -928,7 +892,7 @@ mod tests {
|
|||
|
||||
#[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]
|
||||
|
@ -936,7 +900,7 @@ mod tests {
|
|||
test_instruction(
|
||||
0,
|
||||
&[0x3f, 0xf1, 0x02],
|
||||
Err(Error::EncoderStreamError),
|
||||
Some(Error::EncoderStreamError),
|
||||
&[0x03],
|
||||
0,
|
||||
);
|
||||
|
@ -945,24 +909,49 @@ mod tests {
|
|||
// 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 {
|
||||
|
@ -971,129 +960,6 @@ mod tests {
|
|||
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] = [
|
||||
|
@ -1152,26 +1018,42 @@ mod tests {
|
|||
},
|
||||
];
|
||||
|
||||
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]
|
||||
|
@ -1230,25 +1112,42 @@ mod tests {
|
|||
},
|
||||
];
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -5,7 +5,6 @@
|
|||
// except according to those terms.
|
||||
|
||||
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
|
||||
#![warn(clippy::use_self)]
|
||||
|
||||
pub mod decoder;
|
||||
pub mod encoder;
|
||||
|
@ -26,12 +25,6 @@ enum QPackSide {
|
|||
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,
|
||||
|
@ -44,7 +37,6 @@ pub enum Error {
|
|||
NoMoreData,
|
||||
IntegerOverflow,
|
||||
WrongStreamCount,
|
||||
InternalError,
|
||||
|
||||
TransportError(neqo_transport::Error),
|
||||
}
|
||||
|
@ -59,7 +51,7 @@ impl Error {
|
|||
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,
|
||||
}
|
||||
}
|
||||
|
@ -73,6 +65,6 @@ impl ::std::fmt::Display for Error {
|
|||
|
||||
impl From<neqo_transport::Error> for Error {
|
||||
fn from(err: neqo_transport::Error) -> Self {
|
||||
Self::TransportError(err)
|
||||
Error::TransportError(err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,41 +6,31 @@
|
|||
|
||||
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] {
|
||||
|
@ -60,26 +50,19 @@ impl DynamicTableEntry {
|
|||
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 {
|
||||
|
@ -89,7 +72,7 @@ impl HeaderTable {
|
|||
capacity: 0,
|
||||
used: 0,
|
||||
base: 0,
|
||||
acked_inserts_cnt: 0,
|
||||
acked_inserts_cnt: if encoder { 0 } else { std::u64::MAX },
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -101,13 +84,9 @@ impl HeaderTable {
|
|||
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> {
|
||||
|
@ -118,126 +97,75 @@ impl HeaderTable {
|
|||
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();
|
||||
|
@ -246,14 +174,14 @@ impl HeaderTable {
|
|||
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),
|
||||
|
@ -262,64 +190,51 @@ impl HeaderTable {
|
|||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"0eda0a1e898b0294949969055376633e22bd973d5ba3525f88a2fc5ef3afc0cd","TODO":"d759cb804b32fa9d96ea8d3574a3c4073da9fe6a0b02b708a0e22cce5a5b4a0f","src/cc.rs":"9fa6bebf5fc6d9dab2b53cff38f1ea88e6cc20b1c7dc9c55a30bc75599306c34","src/cid.rs":"4161a5add9285a9f670c4d0b88ac84833b710cd99cbd5ec080f4d2f097200abf","src/connection.rs":"5300a6a55bf32fd3abdd0857455ea10a28b93e1f6ae69ef81a283915b90be49d","src/crypto.rs":"78558a8312969285d3082574e31abd349f964590a66167352da8adec7e9c6ed2","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}
|
||||
{"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}
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "neqo-transport"
|
||||
version = "0.1.13"
|
||||
version = "0.1.12"
|
||||
authors = ["EKR <ekr@rtfm.com>", "Andy Grover <agrover@mozilla.com>"]
|
||||
edition = "2018"
|
||||
license = "MIT/Apache-2.0"
|
||||
|
@ -8,7 +8,8 @@ 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"
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -8,16 +8,16 @@
|
|||
// e.g. "RUST_LOG=neqo_transport::dump neqo-client ..."
|
||||
|
||||
use crate::connection::Connection;
|
||||
use crate::frame::Frame;
|
||||
use crate::packet::{PacketNumber, PacketType};
|
||||
use crate::frame::decode_frame;
|
||||
use crate::packet::PacketHdr;
|
||||
use neqo_common::{qdebug, Decoder};
|
||||
|
||||
#[allow(clippy::module_name_repetitions)]
|
||||
pub fn dump_packet(conn: &Connection, dir: &str, pt: PacketType, pn: PacketNumber, payload: &[u8]) {
|
||||
pub fn dump_packet(conn: &Connection, dir: &str, hdr: &PacketHdr, payload: &[u8]) {
|
||||
let mut s = String::from("");
|
||||
let mut d = Decoder::from(payload);
|
||||
while d.remaining() > 0 {
|
||||
let f = match Frame::decode(&mut d) {
|
||||
let f = match decode_frame(&mut d) {
|
||||
Ok(f) => f,
|
||||
Err(_) => {
|
||||
s.push_str(" [broken]...");
|
||||
|
@ -28,5 +28,5 @@ pub fn dump_packet(conn: &Connection, dir: &str, pt: PacketType, pn: PacketNumbe
|
|||
s.push_str(&format!("\n {} {}", dir, &x));
|
||||
}
|
||||
}
|
||||
qdebug!([conn], "pn={} type={:?}{}", pn, pt, s);
|
||||
qdebug!([conn], "pn={} type={:?}{}", hdr.pn, hdr.tipe, s);
|
||||
}
|
||||
|
|
|
@ -11,13 +11,13 @@ use std::collections::HashMap;
|
|||
use std::mem;
|
||||
|
||||
use neqo_common::{qinfo, qtrace, qwarn, Encoder};
|
||||
use neqo_crypto::Epoch;
|
||||
|
||||
use crate::frame::{Frame, StreamType};
|
||||
use crate::recovery::RecoveryToken;
|
||||
use crate::recv_stream::RecvStreams;
|
||||
use crate::send_stream::SendStreams;
|
||||
use crate::stream_id::{StreamId, StreamIndex, StreamIndexes};
|
||||
use crate::tracking::PNSpace;
|
||||
use crate::AppError;
|
||||
|
||||
pub type FlowControlRecoveryToken = Frame;
|
||||
|
@ -37,6 +37,8 @@ pub struct FlowMgr {
|
|||
|
||||
used_data: u64,
|
||||
max_data: u64,
|
||||
|
||||
need_close_frame: bool,
|
||||
}
|
||||
|
||||
impl FlowMgr {
|
||||
|
@ -122,16 +124,6 @@ impl FlowMgr {
|
|||
.insert((stream_id, mem::discriminant(&frame)), frame);
|
||||
}
|
||||
|
||||
/// Don't send stream data updates if no more data is coming
|
||||
pub fn clear_max_stream_data(&mut self, stream_id: StreamId) {
|
||||
let frame = Frame::MaxStreamData {
|
||||
stream_id,
|
||||
maximum_stream_data: 0,
|
||||
};
|
||||
self.from_streams
|
||||
.remove(&(stream_id, mem::discriminant(&frame)));
|
||||
}
|
||||
|
||||
/// Indicate to receiving remote we need more credits
|
||||
pub fn stream_data_blocked(&mut self, stream_id: StreamId, stream_data_limit: u64) {
|
||||
let frame = Frame::StreamDataBlocked {
|
||||
|
@ -174,6 +166,14 @@ impl FlowMgr {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn need_close_frame(&self) -> bool {
|
||||
self.need_close_frame
|
||||
}
|
||||
|
||||
pub fn set_need_close_frame(&mut self, new: bool) {
|
||||
self.need_close_frame = new
|
||||
}
|
||||
|
||||
pub(crate) fn acked(
|
||||
&mut self,
|
||||
token: FlowControlRecoveryToken,
|
||||
|
@ -291,10 +291,10 @@ impl FlowMgr {
|
|||
|
||||
pub(crate) fn get_frame(
|
||||
&mut self,
|
||||
space: PNSpace,
|
||||
epoch: Epoch,
|
||||
remaining: usize,
|
||||
) -> Option<(Frame, Option<RecoveryToken>)> {
|
||||
if space != PNSpace::ApplicationData {
|
||||
if epoch != 3 {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
|
||||
// Directly relating to QUIC frames.
|
||||
|
||||
use neqo_common::{matches, qdebug, qtrace, Decoder, Encoder};
|
||||
use neqo_common::{matches, qdebug, Decoder, Encoder};
|
||||
use neqo_crypto::Epoch;
|
||||
|
||||
use crate::packet::PacketType;
|
||||
use crate::stream_id::{StreamId, StreamIndex};
|
||||
use crate::{AppError, TransportError};
|
||||
use crate::{ConnectionError, Error, Res};
|
||||
|
@ -43,7 +43,6 @@ const FRAME_TYPE_PATH_CHALLENGE: FrameType = 0x1a;
|
|||
const FRAME_TYPE_PATH_RESPONSE: FrameType = 0x1b;
|
||||
const FRAME_TYPE_CONNECTION_CLOSE_TRANSPORT: FrameType = 0x1c;
|
||||
const FRAME_TYPE_CONNECTION_CLOSE_APPLICATION: FrameType = 0x1d;
|
||||
const FRAME_TYPE_HANDSHAKE_DONE: FrameType = 0x1e;
|
||||
|
||||
const STREAM_FRAME_BIT_FIN: u64 = 0x01;
|
||||
const STREAM_FRAME_BIT_LEN: u64 = 0x02;
|
||||
|
@ -59,15 +58,15 @@ pub enum StreamType {
|
|||
impl StreamType {
|
||||
fn frame_type_bit(self) -> u64 {
|
||||
match self {
|
||||
Self::BiDi => 0,
|
||||
Self::UniDi => 1,
|
||||
StreamType::BiDi => 0,
|
||||
StreamType::UniDi => 1,
|
||||
}
|
||||
}
|
||||
fn from_type_bit(bit: u64) -> Self {
|
||||
fn from_type_bit(bit: u64) -> StreamType {
|
||||
if (bit & 0x01) == 0 {
|
||||
Self::BiDi
|
||||
StreamType::BiDi
|
||||
} else {
|
||||
Self::UniDi
|
||||
StreamType::UniDi
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -81,22 +80,22 @@ pub enum CloseError {
|
|||
impl CloseError {
|
||||
fn frame_type_bit(self) -> u64 {
|
||||
match self {
|
||||
Self::Transport(_) => 0,
|
||||
Self::Application(_) => 1,
|
||||
CloseError::Transport(_) => 0,
|
||||
CloseError::Application(_) => 1,
|
||||
}
|
||||
}
|
||||
|
||||
fn from_type_bit(bit: u64, code: u64) -> Self {
|
||||
fn from_type_bit(bit: u64, code: u64) -> CloseError {
|
||||
if (bit & 0x01) == 0 {
|
||||
Self::Transport(code)
|
||||
CloseError::Transport(code)
|
||||
} else {
|
||||
Self::Application(code)
|
||||
CloseError::Application(code)
|
||||
}
|
||||
}
|
||||
|
||||
fn code(&self) -> u64 {
|
||||
match self {
|
||||
Self::Transport(c) | Self::Application(c) => *c,
|
||||
CloseError::Transport(c) | CloseError::Application(c) => *c,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -104,8 +103,8 @@ impl CloseError {
|
|||
impl From<ConnectionError> for CloseError {
|
||||
fn from(err: ConnectionError) -> Self {
|
||||
match err {
|
||||
ConnectionError::Transport(c) => Self::Transport(c.code()),
|
||||
ConnectionError::Application(c) => Self::Application(c),
|
||||
ConnectionError::Transport(c) => CloseError::Transport(c.code()),
|
||||
ConnectionError::Application(c) => CloseError::Application(c),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -191,20 +190,19 @@ pub enum Frame {
|
|||
frame_type: u64,
|
||||
reason_phrase: Vec<u8>,
|
||||
},
|
||||
HandshakeDone,
|
||||
}
|
||||
|
||||
impl Frame {
|
||||
pub fn get_type(&self) -> FrameType {
|
||||
match self {
|
||||
Self::Padding => FRAME_TYPE_PADDING,
|
||||
Self::Ping => FRAME_TYPE_PING,
|
||||
Self::Ack { .. } => FRAME_TYPE_ACK, // We don't do ACK ECN.
|
||||
Self::ResetStream { .. } => FRAME_TYPE_RST_STREAM,
|
||||
Self::StopSending { .. } => FRAME_TYPE_STOP_SENDING,
|
||||
Self::Crypto { .. } => FRAME_TYPE_CRYPTO,
|
||||
Self::NewToken { .. } => FRAME_TYPE_NEW_TOKEN,
|
||||
Self::Stream {
|
||||
Frame::Padding => FRAME_TYPE_PADDING,
|
||||
Frame::Ping => FRAME_TYPE_PING,
|
||||
Frame::Ack { .. } => FRAME_TYPE_ACK, // We don't do ACK ECN.
|
||||
Frame::ResetStream { .. } => FRAME_TYPE_RST_STREAM,
|
||||
Frame::StopSending { .. } => FRAME_TYPE_STOP_SENDING,
|
||||
Frame::Crypto { .. } => FRAME_TYPE_CRYPTO,
|
||||
Frame::NewToken { .. } => FRAME_TYPE_NEW_TOKEN,
|
||||
Frame::Stream {
|
||||
fin, offset, fill, ..
|
||||
} => {
|
||||
let mut t = FRAME_TYPE_STREAM;
|
||||
|
@ -219,29 +217,28 @@ impl Frame {
|
|||
}
|
||||
t
|
||||
}
|
||||
Self::MaxData { .. } => FRAME_TYPE_MAX_DATA,
|
||||
Self::MaxStreamData { .. } => FRAME_TYPE_MAX_STREAM_DATA,
|
||||
Self::MaxStreams { stream_type, .. } => {
|
||||
Frame::MaxData { .. } => FRAME_TYPE_MAX_DATA,
|
||||
Frame::MaxStreamData { .. } => FRAME_TYPE_MAX_STREAM_DATA,
|
||||
Frame::MaxStreams { stream_type, .. } => {
|
||||
FRAME_TYPE_MAX_STREAMS_BIDI + stream_type.frame_type_bit()
|
||||
}
|
||||
Self::DataBlocked { .. } => FRAME_TYPE_DATA_BLOCKED,
|
||||
Self::StreamDataBlocked { .. } => FRAME_TYPE_STREAM_DATA_BLOCKED,
|
||||
Self::StreamsBlocked { stream_type, .. } => {
|
||||
Frame::DataBlocked { .. } => FRAME_TYPE_DATA_BLOCKED,
|
||||
Frame::StreamDataBlocked { .. } => FRAME_TYPE_STREAM_DATA_BLOCKED,
|
||||
Frame::StreamsBlocked { stream_type, .. } => {
|
||||
FRAME_TYPE_STREAMS_BLOCKED_BIDI + stream_type.frame_type_bit()
|
||||
}
|
||||
Self::NewConnectionId { .. } => FRAME_TYPE_NEW_CONNECTION_ID,
|
||||
Self::RetireConnectionId { .. } => FRAME_TYPE_RETIRE_CONNECTION_ID,
|
||||
Self::PathChallenge { .. } => FRAME_TYPE_PATH_CHALLENGE,
|
||||
Self::PathResponse { .. } => FRAME_TYPE_PATH_RESPONSE,
|
||||
Self::ConnectionClose { error_code, .. } => {
|
||||
Frame::NewConnectionId { .. } => FRAME_TYPE_NEW_CONNECTION_ID,
|
||||
Frame::RetireConnectionId { .. } => FRAME_TYPE_RETIRE_CONNECTION_ID,
|
||||
Frame::PathChallenge { .. } => FRAME_TYPE_PATH_CHALLENGE,
|
||||
Frame::PathResponse { .. } => FRAME_TYPE_PATH_RESPONSE,
|
||||
Frame::ConnectionClose { error_code, .. } => {
|
||||
FRAME_TYPE_CONNECTION_CLOSE_TRANSPORT + error_code.frame_type_bit()
|
||||
}
|
||||
Self::HandshakeDone => FRAME_TYPE_HANDSHAKE_DONE,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a CRYPTO frame that fits the available space and its length.
|
||||
pub fn new_crypto(offset: u64, data: &[u8], space: usize) -> (Self, usize) {
|
||||
pub fn new_crypto(offset: u64, data: &[u8], space: usize) -> (Frame, usize) {
|
||||
// Subtract the frame type and offset from available space.
|
||||
let mut remaining = space - 1 - Encoder::varint_len(offset);
|
||||
// Then subtract space for the length field.
|
||||
|
@ -249,7 +246,7 @@ impl Frame {
|
|||
remaining -= Encoder::varint_len(u64::try_from(data_len).unwrap());
|
||||
remaining = min(data.len(), remaining);
|
||||
(
|
||||
Self::Crypto {
|
||||
Frame::Crypto {
|
||||
offset,
|
||||
data: data[..remaining].to_vec(),
|
||||
},
|
||||
|
@ -265,7 +262,7 @@ impl Frame {
|
|||
data: &[u8],
|
||||
fin: bool,
|
||||
space: usize,
|
||||
) -> Option<(Self, usize)> {
|
||||
) -> Option<(Frame, usize)> {
|
||||
let mut overhead = 1 + Encoder::varint_len(stream_id);
|
||||
if offset > 0 {
|
||||
overhead += Encoder::varint_len(offset);
|
||||
|
@ -312,7 +309,7 @@ impl Frame {
|
|||
);
|
||||
|
||||
Some((
|
||||
Self::Stream {
|
||||
Frame::Stream {
|
||||
stream_id: stream_id.into(),
|
||||
offset,
|
||||
data: data[..data_len].to_vec(),
|
||||
|
@ -327,8 +324,8 @@ impl Frame {
|
|||
enc.encode_varint(self.get_type());
|
||||
|
||||
match self {
|
||||
Self::Padding | Self::Ping => (),
|
||||
Self::Ack {
|
||||
Frame::Padding | Frame::Ping => (),
|
||||
Frame::Ack {
|
||||
largest_acknowledged,
|
||||
ack_delay,
|
||||
first_ack_range,
|
||||
|
@ -343,7 +340,7 @@ impl Frame {
|
|||
enc.encode_varint(r.range);
|
||||
}
|
||||
}
|
||||
Self::ResetStream {
|
||||
Frame::ResetStream {
|
||||
stream_id,
|
||||
application_error_code,
|
||||
final_size,
|
||||
|
@ -352,21 +349,21 @@ impl Frame {
|
|||
enc.encode_varint(*application_error_code);
|
||||
enc.encode_varint(*final_size);
|
||||
}
|
||||
Self::StopSending {
|
||||
Frame::StopSending {
|
||||
stream_id,
|
||||
application_error_code,
|
||||
} => {
|
||||
enc.encode_varint(stream_id.as_u64());
|
||||
enc.encode_varint(*application_error_code);
|
||||
}
|
||||
Self::Crypto { offset, data } => {
|
||||
Frame::Crypto { offset, data } => {
|
||||
enc.encode_varint(*offset);
|
||||
enc.encode_vvec(&data);
|
||||
}
|
||||
Self::NewToken { token } => {
|
||||
Frame::NewToken { token } => {
|
||||
enc.encode_vvec(token);
|
||||
}
|
||||
Self::Stream {
|
||||
Frame::Stream {
|
||||
stream_id,
|
||||
offset,
|
||||
data,
|
||||
|
@ -383,35 +380,35 @@ impl Frame {
|
|||
enc.encode_vvec(&data);
|
||||
}
|
||||
}
|
||||
Self::MaxData { maximum_data } => {
|
||||
Frame::MaxData { maximum_data } => {
|
||||
enc.encode_varint(*maximum_data);
|
||||
}
|
||||
Self::MaxStreamData {
|
||||
Frame::MaxStreamData {
|
||||
stream_id,
|
||||
maximum_stream_data,
|
||||
} => {
|
||||
enc.encode_varint(stream_id.as_u64());
|
||||
enc.encode_varint(*maximum_stream_data);
|
||||
}
|
||||
Self::MaxStreams {
|
||||
Frame::MaxStreams {
|
||||
maximum_streams, ..
|
||||
} => {
|
||||
enc.encode_varint(maximum_streams.as_u64());
|
||||
}
|
||||
Self::DataBlocked { data_limit } => {
|
||||
Frame::DataBlocked { data_limit } => {
|
||||
enc.encode_varint(*data_limit);
|
||||
}
|
||||
Self::StreamDataBlocked {
|
||||
Frame::StreamDataBlocked {
|
||||
stream_id,
|
||||
stream_data_limit,
|
||||
} => {
|
||||
enc.encode_varint(stream_id.as_u64());
|
||||
enc.encode_varint(*stream_data_limit);
|
||||
}
|
||||
Self::StreamsBlocked { stream_limit, .. } => {
|
||||
Frame::StreamsBlocked { stream_limit, .. } => {
|
||||
enc.encode_varint(stream_limit.as_u64());
|
||||
}
|
||||
Self::NewConnectionId {
|
||||
Frame::NewConnectionId {
|
||||
sequence_number,
|
||||
retire_prior,
|
||||
connection_id,
|
||||
|
@ -423,16 +420,16 @@ impl Frame {
|
|||
enc.encode(connection_id);
|
||||
enc.encode(stateless_reset_token);
|
||||
}
|
||||
Self::RetireConnectionId { sequence_number } => {
|
||||
Frame::RetireConnectionId { sequence_number } => {
|
||||
enc.encode_varint(*sequence_number);
|
||||
}
|
||||
Self::PathChallenge { data } => {
|
||||
Frame::PathChallenge { data } => {
|
||||
enc.encode(data);
|
||||
}
|
||||
Self::PathResponse { data } => {
|
||||
Frame::PathResponse { data } => {
|
||||
enc.encode(data);
|
||||
}
|
||||
Self::ConnectionClose {
|
||||
Frame::ConnectionClose {
|
||||
error_code,
|
||||
frame_type,
|
||||
reason_phrase,
|
||||
|
@ -441,12 +438,11 @@ impl Frame {
|
|||
enc.encode_varint(*frame_type);
|
||||
enc.encode_vvec(reason_phrase);
|
||||
}
|
||||
Self::HandshakeDone => (),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ack_eliciting(&self) -> bool {
|
||||
!matches!(self, Self::Ack { .. } | Self::Padding | Self::ConnectionClose { .. })
|
||||
!matches!(self, Frame::Ack { .. } | Frame::Padding | Frame::ConnectionClose { .. })
|
||||
}
|
||||
|
||||
/// Converts AckRanges as encoded in a ACK frame (see -transport
|
||||
|
@ -494,12 +490,12 @@ impl Frame {
|
|||
|
||||
pub fn dump(&self) -> Option<String> {
|
||||
match self {
|
||||
Self::Crypto { offset, data } => Some(format!(
|
||||
Frame::Crypto { offset, data } => Some(format!(
|
||||
"Crypto {{ offset: {}, len: {} }}",
|
||||
offset,
|
||||
data.len()
|
||||
)),
|
||||
Self::Stream {
|
||||
Frame::Stream {
|
||||
stream_id,
|
||||
offset,
|
||||
fill,
|
||||
|
@ -513,184 +509,187 @@ impl Frame {
|
|||
data.len(),
|
||||
fin,
|
||||
)),
|
||||
Self::Padding => None,
|
||||
Frame::Padding => None,
|
||||
_ => Some(format!("{:?}", self)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_allowed(&self, pt: PacketType) -> bool {
|
||||
if matches!(self, Self::Padding | Self::Ping) {
|
||||
pub fn is_allowed(&self, epoch: Epoch) -> bool {
|
||||
qdebug!("is_allowed {:?} {}", self, epoch);
|
||||
if matches!(self, Frame::Padding | Frame::Ping) {
|
||||
true
|
||||
} else if matches!(self, Self::Crypto {..} | Self::Ack {..} | Self::ConnectionClose { error_code: CloseError::Transport(_), .. })
|
||||
} else if matches!(self, Frame::Crypto {..} | Frame::Ack {..} | Frame::ConnectionClose { error_code: CloseError::Transport(_), .. })
|
||||
{
|
||||
pt != PacketType::ZeroRtt
|
||||
} else if matches!(self, Self::NewToken {..} | Self::ConnectionClose {..}) {
|
||||
pt == PacketType::Short
|
||||
epoch != 1
|
||||
} else if matches!(self, Frame::NewToken {..} | Frame::ConnectionClose {..}) {
|
||||
epoch >= 3
|
||||
} else {
|
||||
pt == PacketType::ZeroRtt || pt == PacketType::Short
|
||||
epoch == 1 || epoch >= 3 // Application data
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decode(dec: &mut Decoder) -> Res<Self> {
|
||||
macro_rules! d {
|
||||
($d:expr) => {
|
||||
match $d {
|
||||
Some(v) => v,
|
||||
_ => return Err(Error::NoMoreData),
|
||||
}
|
||||
};
|
||||
}
|
||||
macro_rules! dv {
|
||||
($d:expr) => {
|
||||
d!($d.decode_varint())
|
||||
};
|
||||
}
|
||||
|
||||
// TODO(ekr@rtfm.com): check for minimal encoding
|
||||
let t = d!(dec.decode_varint());
|
||||
match t {
|
||||
FRAME_TYPE_PADDING => Ok(Self::Padding),
|
||||
FRAME_TYPE_PING => Ok(Self::Ping),
|
||||
FRAME_TYPE_RST_STREAM => Ok(Self::ResetStream {
|
||||
stream_id: dv!(dec).into(),
|
||||
application_error_code: d!(dec.decode_varint()),
|
||||
final_size: match dec.decode_varint() {
|
||||
Some(v) => v,
|
||||
_ => return Err(Error::NoMoreData),
|
||||
},
|
||||
}),
|
||||
FRAME_TYPE_ACK | FRAME_TYPE_ACK_ECN => {
|
||||
let la = dv!(dec);
|
||||
let ad = dv!(dec);
|
||||
let nr = dv!(dec);
|
||||
let fa = dv!(dec);
|
||||
let mut arr: Vec<AckRange> = Vec::with_capacity(nr as usize);
|
||||
for _ in 0..nr {
|
||||
let ar = AckRange {
|
||||
gap: dv!(dec),
|
||||
range: dv!(dec),
|
||||
};
|
||||
arr.push(ar);
|
||||
}
|
||||
|
||||
// Now check for the values for ACK_ECN.
|
||||
if t == FRAME_TYPE_ACK_ECN {
|
||||
dv!(dec);
|
||||
dv!(dec);
|
||||
dv!(dec);
|
||||
}
|
||||
|
||||
Ok(Self::Ack {
|
||||
largest_acknowledged: la,
|
||||
ack_delay: ad,
|
||||
first_ack_range: fa,
|
||||
ack_ranges: arr,
|
||||
})
|
||||
#[allow(clippy::module_name_repetitions)]
|
||||
pub fn decode_frame(dec: &mut Decoder) -> Res<Frame> {
|
||||
macro_rules! d {
|
||||
($d:expr) => {
|
||||
match $d {
|
||||
Some(v) => v,
|
||||
_ => return Err(Error::NoMoreData),
|
||||
}
|
||||
FRAME_TYPE_STOP_SENDING => Ok(Self::StopSending {
|
||||
stream_id: dv!(dec).into(),
|
||||
application_error_code: d!(dec.decode_varint()),
|
||||
}),
|
||||
FRAME_TYPE_CRYPTO => {
|
||||
let o = dv!(dec);
|
||||
Ok(Self::Crypto {
|
||||
offset: o,
|
||||
data: d!(dec.decode_vvec()).to_vec(), // TODO(mt) unnecessary copy
|
||||
})
|
||||
}
|
||||
FRAME_TYPE_NEW_TOKEN => {
|
||||
Ok(Self::NewToken {
|
||||
token: d!(dec.decode_vvec()).to_vec(), // TODO(mt) unnecessary copy
|
||||
})
|
||||
}
|
||||
FRAME_TYPE_STREAM..=FRAME_TYPE_STREAM_MAX => {
|
||||
let s = dv!(dec);
|
||||
let o = if t & STREAM_FRAME_BIT_OFF == 0 {
|
||||
0
|
||||
} else {
|
||||
dv!(dec)
|
||||
};
|
||||
}
|
||||
macro_rules! dv {
|
||||
($d:expr) => {
|
||||
d!($d.decode_varint())
|
||||
};
|
||||
}
|
||||
|
||||
// TODO(ekr@rtfm.com): check for minimal encoding
|
||||
let t = d!(dec.decode_varint());
|
||||
qdebug!("Frame type byte={:0x}", t);
|
||||
match t {
|
||||
FRAME_TYPE_PADDING => Ok(Frame::Padding),
|
||||
FRAME_TYPE_PING => Ok(Frame::Ping),
|
||||
FRAME_TYPE_RST_STREAM => Ok(Frame::ResetStream {
|
||||
stream_id: dv!(dec).into(),
|
||||
application_error_code: d!(dec.decode_varint()),
|
||||
final_size: match dec.decode_varint() {
|
||||
Some(v) => v,
|
||||
_ => return Err(Error::NoMoreData),
|
||||
},
|
||||
}),
|
||||
FRAME_TYPE_ACK | FRAME_TYPE_ACK_ECN => {
|
||||
let la = dv!(dec);
|
||||
let ad = dv!(dec);
|
||||
let nr = dv!(dec);
|
||||
let fa = dv!(dec);
|
||||
let mut arr: Vec<AckRange> = Vec::with_capacity(nr as usize);
|
||||
for _ in 0..nr {
|
||||
let ar = AckRange {
|
||||
gap: dv!(dec),
|
||||
range: dv!(dec),
|
||||
};
|
||||
let fill = (t & STREAM_FRAME_BIT_LEN) == 0;
|
||||
let data = if fill {
|
||||
qtrace!("STREAM frame, extends to the end of the packet");
|
||||
dec.decode_remainder()
|
||||
} else {
|
||||
qtrace!("STREAM frame, with length");
|
||||
d!(dec.decode_vvec())
|
||||
};
|
||||
Ok(Self::Stream {
|
||||
fin: (t & STREAM_FRAME_BIT_FIN) != 0,
|
||||
stream_id: s.into(),
|
||||
offset: o,
|
||||
data: data.to_vec(), // TODO(mt) unnecessary copy.
|
||||
fill,
|
||||
})
|
||||
arr.push(ar);
|
||||
}
|
||||
FRAME_TYPE_MAX_DATA => Ok(Self::MaxData {
|
||||
maximum_data: dv!(dec),
|
||||
}),
|
||||
FRAME_TYPE_MAX_STREAM_DATA => Ok(Self::MaxStreamData {
|
||||
stream_id: dv!(dec).into(),
|
||||
maximum_stream_data: dv!(dec),
|
||||
}),
|
||||
FRAME_TYPE_MAX_STREAMS_BIDI | FRAME_TYPE_MAX_STREAMS_UNIDI => Ok(Self::MaxStreams {
|
||||
|
||||
// Now check for the values for ACK_ECN.
|
||||
if t == FRAME_TYPE_ACK_ECN {
|
||||
dv!(dec);
|
||||
dv!(dec);
|
||||
dv!(dec);
|
||||
}
|
||||
|
||||
Ok(Frame::Ack {
|
||||
largest_acknowledged: la,
|
||||
ack_delay: ad,
|
||||
first_ack_range: fa,
|
||||
ack_ranges: arr,
|
||||
})
|
||||
}
|
||||
FRAME_TYPE_STOP_SENDING => Ok(Frame::StopSending {
|
||||
stream_id: dv!(dec).into(),
|
||||
application_error_code: d!(dec.decode_varint()),
|
||||
}),
|
||||
FRAME_TYPE_CRYPTO => {
|
||||
let o = dv!(dec);
|
||||
Ok(Frame::Crypto {
|
||||
offset: o,
|
||||
data: d!(dec.decode_vvec()).to_vec(), // TODO(mt) unnecessary copy
|
||||
})
|
||||
}
|
||||
FRAME_TYPE_NEW_TOKEN => {
|
||||
Ok(Frame::NewToken {
|
||||
token: d!(dec.decode_vvec()).to_vec(), // TODO(mt) unnecessary copy
|
||||
})
|
||||
}
|
||||
FRAME_TYPE_STREAM..=FRAME_TYPE_STREAM_MAX => {
|
||||
let s = dv!(dec);
|
||||
let o = if t & STREAM_FRAME_BIT_OFF == 0 {
|
||||
0
|
||||
} else {
|
||||
dv!(dec)
|
||||
};
|
||||
qdebug!("STREAM {}", t);
|
||||
let fill = (t & STREAM_FRAME_BIT_LEN) == 0;
|
||||
let data = if fill {
|
||||
qdebug!("STREAM frame extends to the end of the packet");
|
||||
dec.decode_remainder()
|
||||
} else {
|
||||
qdebug!("STREAM frame has a length");
|
||||
d!(dec.decode_vvec())
|
||||
};
|
||||
Ok(Frame::Stream {
|
||||
fin: (t & STREAM_FRAME_BIT_FIN) != 0,
|
||||
stream_id: s.into(),
|
||||
offset: o,
|
||||
data: data.to_vec(), // TODO(mt) unnecessary copy.
|
||||
fill,
|
||||
})
|
||||
}
|
||||
FRAME_TYPE_MAX_DATA => Ok(Frame::MaxData {
|
||||
maximum_data: dv!(dec),
|
||||
}),
|
||||
FRAME_TYPE_MAX_STREAM_DATA => Ok(Frame::MaxStreamData {
|
||||
stream_id: dv!(dec).into(),
|
||||
maximum_stream_data: dv!(dec),
|
||||
}),
|
||||
FRAME_TYPE_MAX_STREAMS_BIDI | FRAME_TYPE_MAX_STREAMS_UNIDI => Ok(Frame::MaxStreams {
|
||||
stream_type: StreamType::from_type_bit(t),
|
||||
maximum_streams: StreamIndex::new(dv!(dec)),
|
||||
}),
|
||||
|
||||
FRAME_TYPE_DATA_BLOCKED => Ok(Frame::DataBlocked {
|
||||
data_limit: dv!(dec),
|
||||
}),
|
||||
FRAME_TYPE_STREAM_DATA_BLOCKED => Ok(Frame::StreamDataBlocked {
|
||||
stream_id: dv!(dec).into(),
|
||||
stream_data_limit: dv!(dec),
|
||||
}),
|
||||
FRAME_TYPE_STREAMS_BLOCKED_BIDI | FRAME_TYPE_STREAMS_BLOCKED_UNIDI => {
|
||||
Ok(Frame::StreamsBlocked {
|
||||
stream_type: StreamType::from_type_bit(t),
|
||||
maximum_streams: StreamIndex::new(dv!(dec)),
|
||||
}),
|
||||
|
||||
FRAME_TYPE_DATA_BLOCKED => Ok(Self::DataBlocked {
|
||||
data_limit: dv!(dec),
|
||||
}),
|
||||
FRAME_TYPE_STREAM_DATA_BLOCKED => Ok(Self::StreamDataBlocked {
|
||||
stream_id: dv!(dec).into(),
|
||||
stream_data_limit: dv!(dec),
|
||||
}),
|
||||
FRAME_TYPE_STREAMS_BLOCKED_BIDI | FRAME_TYPE_STREAMS_BLOCKED_UNIDI => {
|
||||
Ok(Self::StreamsBlocked {
|
||||
stream_type: StreamType::from_type_bit(t),
|
||||
stream_limit: StreamIndex::new(dv!(dec)),
|
||||
})
|
||||
}
|
||||
FRAME_TYPE_NEW_CONNECTION_ID => {
|
||||
let s = dv!(dec);
|
||||
let retire_prior = dv!(dec);
|
||||
let cid = d!(dec.decode_vec(1)).to_vec(); // TODO(mt) unnecessary copy
|
||||
let srt = d!(dec.decode(16));
|
||||
let mut srtv: [u8; 16] = [0; 16];
|
||||
srtv.copy_from_slice(&srt);
|
||||
|
||||
Ok(Self::NewConnectionId {
|
||||
sequence_number: s,
|
||||
retire_prior,
|
||||
connection_id: cid,
|
||||
stateless_reset_token: srtv,
|
||||
})
|
||||
}
|
||||
FRAME_TYPE_RETIRE_CONNECTION_ID => Ok(Self::RetireConnectionId {
|
||||
sequence_number: dv!(dec),
|
||||
}),
|
||||
FRAME_TYPE_PATH_CHALLENGE => {
|
||||
let data = d!(dec.decode(8));
|
||||
let mut datav: [u8; 8] = [0; 8];
|
||||
datav.copy_from_slice(&data);
|
||||
Ok(Self::PathChallenge { data: datav })
|
||||
}
|
||||
FRAME_TYPE_PATH_RESPONSE => {
|
||||
let data = d!(dec.decode(8));
|
||||
let mut datav: [u8; 8] = [0; 8];
|
||||
datav.copy_from_slice(&data);
|
||||
Ok(Self::PathResponse { data: datav })
|
||||
}
|
||||
FRAME_TYPE_CONNECTION_CLOSE_TRANSPORT | FRAME_TYPE_CONNECTION_CLOSE_APPLICATION => {
|
||||
Ok(Self::ConnectionClose {
|
||||
error_code: CloseError::from_type_bit(t, d!(dec.decode_varint())),
|
||||
frame_type: dv!(dec),
|
||||
reason_phrase: d!(dec.decode_vvec()).to_vec(), // TODO(mt) unnecessary copy
|
||||
})
|
||||
}
|
||||
FRAME_TYPE_HANDSHAKE_DONE => Ok(Self::HandshakeDone),
|
||||
_ => Err(Error::UnknownFrameType),
|
||||
stream_limit: StreamIndex::new(dv!(dec)),
|
||||
})
|
||||
}
|
||||
FRAME_TYPE_NEW_CONNECTION_ID => {
|
||||
let s = dv!(dec);
|
||||
let retire_prior = dv!(dec);
|
||||
let cid = d!(dec.decode_vec(1)).to_vec(); // TODO(mt) unnecessary copy
|
||||
let srt = d!(dec.decode(16));
|
||||
let mut srtv: [u8; 16] = [0; 16];
|
||||
srtv.copy_from_slice(&srt);
|
||||
|
||||
Ok(Frame::NewConnectionId {
|
||||
sequence_number: s,
|
||||
retire_prior,
|
||||
connection_id: cid,
|
||||
stateless_reset_token: srtv,
|
||||
})
|
||||
}
|
||||
FRAME_TYPE_RETIRE_CONNECTION_ID => Ok(Frame::RetireConnectionId {
|
||||
sequence_number: dv!(dec),
|
||||
}),
|
||||
FRAME_TYPE_PATH_CHALLENGE => {
|
||||
let data = d!(dec.decode(8));
|
||||
let mut datav: [u8; 8] = [0; 8];
|
||||
datav.copy_from_slice(&data);
|
||||
Ok(Frame::PathChallenge { data: datav })
|
||||
}
|
||||
FRAME_TYPE_PATH_RESPONSE => {
|
||||
let data = d!(dec.decode(8));
|
||||
let mut datav: [u8; 8] = [0; 8];
|
||||
datav.copy_from_slice(&data);
|
||||
Ok(Frame::PathResponse { data: datav })
|
||||
}
|
||||
FRAME_TYPE_CONNECTION_CLOSE_TRANSPORT | FRAME_TYPE_CONNECTION_CLOSE_APPLICATION => {
|
||||
Ok(Frame::ConnectionClose {
|
||||
error_code: CloseError::from_type_bit(t, d!(dec.decode_varint())),
|
||||
frame_type: dv!(dec),
|
||||
reason_phrase: d!(dec.decode_vvec()).to_vec(), // TODO(mt) unnecessary copy
|
||||
})
|
||||
}
|
||||
_ => Err(Error::UnknownFrameType),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -711,7 +710,7 @@ mod tests {
|
|||
f.marshal(&mut d);
|
||||
assert_eq!(d, Encoder::from_hex(s));
|
||||
|
||||
let f2 = Frame::decode(&mut d.as_decoder()).unwrap();
|
||||
let f2 = decode_frame(&mut d.as_decoder()).unwrap();
|
||||
assert_eq!(*f, f2);
|
||||
}
|
||||
|
||||
|
@ -743,12 +742,12 @@ mod tests {
|
|||
// Try to parse ACK_ECN without ECN values
|
||||
let enc = Encoder::from_hex("035234523502523601020304");
|
||||
let mut dec = enc.as_decoder();
|
||||
assert_eq!(Frame::decode(&mut dec).unwrap_err(), Error::NoMoreData);
|
||||
assert_eq!(decode_frame(&mut dec).unwrap_err(), Error::NoMoreData);
|
||||
|
||||
// Try to parse ACK_ECN without ECN values
|
||||
let enc = Encoder::from_hex("035234523502523601020304010203");
|
||||
let mut dec = enc.as_decoder();
|
||||
assert_eq!(Frame::decode(&mut dec).unwrap(), f);
|
||||
assert_eq!(decode_frame(&mut dec).unwrap(), f);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -992,7 +991,7 @@ mod tests {
|
|||
ack_frame.marshal(&mut enc);
|
||||
println!("Encoded ACK={}", hex(&enc[..]));
|
||||
|
||||
let f = Frame::decode(&mut enc.as_decoder()).unwrap();
|
||||
let f = decode_frame(&mut enc.as_decoder()).unwrap();
|
||||
if let Frame::Ack {
|
||||
largest_acknowledged,
|
||||
ack_delay,
|
||||
|
|
|
@ -5,13 +5,10 @@
|
|||
// except according to those terms.
|
||||
|
||||
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
|
||||
#![warn(clippy::use_self)]
|
||||
|
||||
use neqo_common::qinfo;
|
||||
use neqo_crypto;
|
||||
|
||||
mod cc;
|
||||
mod cid;
|
||||
mod connection;
|
||||
mod crypto;
|
||||
mod dump;
|
||||
|
@ -28,18 +25,16 @@ mod stream_id;
|
|||
mod tparams;
|
||||
mod tracking;
|
||||
|
||||
pub use self::cid::ConnectionIdManager;
|
||||
pub use self::connection::{Connection, FixedConnectionIdManager, Output, Role, State};
|
||||
pub use self::connection::{
|
||||
Connection, ConnectionIdManager, FixedConnectionIdManager, Output, Role, State,
|
||||
};
|
||||
pub use self::events::{ConnectionEvent, ConnectionEvents};
|
||||
pub use self::frame::CloseError;
|
||||
pub use self::frame::StreamType;
|
||||
pub use self::tparams::{tp_constants, TransportParameter};
|
||||
|
||||
/// The supported version of the QUIC protocol.
|
||||
pub type Version = u32;
|
||||
pub const QUIC_VERSION: Version = 0xff00_0000 + 25;
|
||||
|
||||
const LOCAL_IDLE_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(60); // 1 minute
|
||||
pub const QUIC_VERSION: u32 = 0xff00_0018;
|
||||
|
||||
type TransportError = u64;
|
||||
|
||||
|
@ -73,38 +68,31 @@ pub enum Error {
|
|||
InvalidResumptionToken,
|
||||
InvalidRetry,
|
||||
InvalidStreamId,
|
||||
// Packet protection keys aren't available yet, or they have been discarded.
|
||||
KeysNotFound,
|
||||
// An attempt to update keys can be blocked if
|
||||
// a packet sent with the current keys hasn't been acknowledged.
|
||||
KeyUpdateBlocked,
|
||||
NoMoreData,
|
||||
NotConnected,
|
||||
PacketNumberOverlap,
|
||||
PeerError(TransportError),
|
||||
TooMuchData,
|
||||
UnexpectedMessage,
|
||||
UnknownFrameType,
|
||||
VersionNegotiation,
|
||||
WrongRole,
|
||||
KeysDiscarded,
|
||||
}
|
||||
|
||||
impl Error {
|
||||
pub fn code(&self) -> TransportError {
|
||||
match self {
|
||||
Self::NoError => 0,
|
||||
Self::ServerBusy => 2,
|
||||
Self::FlowControlError => 3,
|
||||
Self::StreamLimitError => 4,
|
||||
Self::StreamStateError => 5,
|
||||
Self::FinalSizeError => 6,
|
||||
Self::FrameEncodingError => 7,
|
||||
Self::TransportParameterError => 8,
|
||||
Self::ProtocolViolation => 10,
|
||||
Self::InvalidMigration => 12,
|
||||
Self::CryptoAlert(a) => 0x100 + u64::from(*a),
|
||||
Self::PeerError(a) => *a,
|
||||
Error::NoError => 0,
|
||||
Error::ServerBusy => 2,
|
||||
Error::FlowControlError => 3,
|
||||
Error::StreamLimitError => 4,
|
||||
Error::StreamStateError => 5,
|
||||
Error::FinalSizeError => 6,
|
||||
Error::FrameEncodingError => 7,
|
||||
Error::TransportParameterError => 8,
|
||||
Error::ProtocolViolation => 10,
|
||||
Error::InvalidMigration => 12,
|
||||
Error::CryptoAlert(a) => 0x100 + u64::from(*a),
|
||||
Error::PeerError(a) => *a,
|
||||
// All the rest are internal errors.
|
||||
_ => 1,
|
||||
}
|
||||
|
@ -114,20 +102,20 @@ impl Error {
|
|||
impl From<neqo_crypto::Error> for Error {
|
||||
fn from(err: neqo_crypto::Error) -> Self {
|
||||
qinfo!("Crypto operation failed {:?}", err);
|
||||
Self::CryptoError(err)
|
||||
Error::CryptoError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::num::TryFromIntError> for Error {
|
||||
fn from(_: std::num::TryFromIntError) -> Self {
|
||||
Self::IntegerOverflow
|
||||
Error::IntegerOverflow
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::error::Error for Error {
|
||||
fn source(&self) -> Option<&(dyn ::std::error::Error + 'static)> {
|
||||
match self {
|
||||
Self::CryptoError(e) => Some(e),
|
||||
Error::CryptoError(e) => Some(e),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -150,7 +138,7 @@ pub enum ConnectionError {
|
|||
impl ConnectionError {
|
||||
pub fn app_code(&self) -> Option<AppError> {
|
||||
match self {
|
||||
Self::Application(e) => Some(*e),
|
||||
ConnectionError::Application(e) => Some(*e),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -159,8 +147,8 @@ impl ConnectionError {
|
|||
impl From<CloseError> for ConnectionError {
|
||||
fn from(err: CloseError) -> Self {
|
||||
match err {
|
||||
CloseError::Transport(c) => Self::Transport(Error::PeerError(c)),
|
||||
CloseError::Application(c) => Self::Application(c),
|
||||
CloseError::Transport(c) => ConnectionError::Transport(Error::PeerError(c)),
|
||||
CloseError::Application(c) => ConnectionError::Application(c),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -8,25 +8,33 @@
|
|||
|
||||
use std::cmp::{max, min};
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt::{self, Display};
|
||||
use std::ops::{Index, IndexMut};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use neqo_common::{qdebug, qinfo, qtrace};
|
||||
use neqo_common::{const_max, const_min, qdebug, qinfo};
|
||||
|
||||
use crate::cc::CongestionControl;
|
||||
use crate::crypto::CryptoRecoveryToken;
|
||||
use crate::flow_mgr::FlowControlRecoveryToken;
|
||||
use crate::send_stream::StreamRecoveryToken;
|
||||
use crate::tracking::{AckToken, PNSpace, SentPacket};
|
||||
use crate::LOCAL_IDLE_TIMEOUT;
|
||||
use crate::tracking::{AckToken, PNSpace};
|
||||
|
||||
const GRANULARITY: Duration = Duration::from_millis(20);
|
||||
// Defined in -recovery 6.2 as 500ms but using lower value until we have RTT
|
||||
// caching. See https://github.com/mozilla/neqo/issues/79
|
||||
const INITIAL_RTT: Duration = Duration::from_millis(100);
|
||||
|
||||
const PACKET_THRESHOLD: u64 = 3;
|
||||
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, Clone)]
|
||||
pub enum RecoveryToken {
|
||||
|
@ -34,7 +42,37 @@ pub enum RecoveryToken {
|
|||
Stream(StreamRecoveryToken),
|
||||
Crypto(CryptoRecoveryToken),
|
||||
Flow(FlowControlRecoveryToken),
|
||||
HandshakeDone,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SentPacket {
|
||||
ack_eliciting: bool,
|
||||
time_sent: Instant,
|
||||
pub tokens: Vec<RecoveryToken>,
|
||||
|
||||
time_declared_lost: Option<Instant>,
|
||||
|
||||
in_flight: bool,
|
||||
size: usize,
|
||||
}
|
||||
|
||||
impl SentPacket {
|
||||
pub fn new(
|
||||
time_sent: Instant,
|
||||
ack_eliciting: bool,
|
||||
tokens: Vec<RecoveryToken>,
|
||||
size: usize,
|
||||
in_flight: bool,
|
||||
) -> SentPacket {
|
||||
SentPacket {
|
||||
time_sent,
|
||||
ack_eliciting,
|
||||
tokens,
|
||||
time_declared_lost: None,
|
||||
size,
|
||||
in_flight,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
|
@ -80,14 +118,8 @@ impl RttVals {
|
|||
self.smoothed_rtt.unwrap_or(self.latest_rtt)
|
||||
}
|
||||
|
||||
fn pto(&self, pn_space: PNSpace) -> Duration {
|
||||
self.rtt()
|
||||
+ max(4 * self.rttvar, GRANULARITY)
|
||||
+ if pn_space != PNSpace::ApplicationData {
|
||||
Duration::from_millis(0)
|
||||
} else {
|
||||
self.max_ack_delay
|
||||
}
|
||||
fn pto(&self) -> Duration {
|
||||
self.rtt() + max(4 * self.rttvar, GRANULARITY) + self.max_ack_delay
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -98,8 +130,8 @@ pub(crate) struct LossRecoveryState {
|
|||
}
|
||||
|
||||
impl LossRecoveryState {
|
||||
fn new(mode: LossRecoveryMode, callback_time: Option<Instant>) -> Self {
|
||||
Self {
|
||||
fn new(mode: LossRecoveryMode, callback_time: Option<Instant>) -> LossRecoveryState {
|
||||
LossRecoveryState {
|
||||
mode,
|
||||
callback_time,
|
||||
}
|
||||
|
@ -112,28 +144,11 @@ impl LossRecoveryState {
|
|||
pub fn mode(&self) -> LossRecoveryMode {
|
||||
self.mode
|
||||
}
|
||||
|
||||
pub fn get_pto_state(&mut self) -> Option<(PNSpace, bool)> {
|
||||
if let LossRecoveryMode::PtoExpired {
|
||||
dgram_available,
|
||||
min_pn_space,
|
||||
} = &mut self.mode
|
||||
{
|
||||
if *dgram_available > 0 {
|
||||
*dgram_available -= 1;
|
||||
Some((*min_pn_space, true))
|
||||
} else {
|
||||
Some((*min_pn_space, false))
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for LossRecoveryState {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
fn default() -> LossRecoveryState {
|
||||
LossRecoveryState {
|
||||
mode: LossRecoveryMode::None,
|
||||
callback_time: None,
|
||||
}
|
||||
|
@ -143,20 +158,15 @@ impl Default for LossRecoveryState {
|
|||
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||
pub(crate) enum LossRecoveryMode {
|
||||
None,
|
||||
LostPacketsTimer, // lost packet timer is armed.
|
||||
PtoTimer, // pto timer is armed
|
||||
PtoExpired {
|
||||
dgram_available: usize,
|
||||
min_pn_space: PNSpace,
|
||||
}, // pto expired, in this state we should send pto packets.
|
||||
LostPackets,
|
||||
PTO,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub(crate) struct LossRecoverySpace {
|
||||
tx_pn: u64,
|
||||
largest_acked: Option<u64>,
|
||||
largest_acked_sent_time: Option<Instant>,
|
||||
time_of_last_sent_ack_eliciting_packet: Option<Instant>,
|
||||
ack_eliciting_outstanding: u64,
|
||||
sent_packets: BTreeMap<u64, SentPacket>,
|
||||
}
|
||||
|
||||
|
@ -174,43 +184,6 @@ impl LossRecoverySpace {
|
|||
earliest
|
||||
}
|
||||
|
||||
pub fn get_largest_acked(&self) -> Option<u64> {
|
||||
self.largest_acked
|
||||
}
|
||||
|
||||
pub fn ack_eliciting_outstanding(&self) -> bool {
|
||||
self.ack_eliciting_outstanding > 0
|
||||
}
|
||||
|
||||
pub fn time_of_last_sent_ack_eliciting_packet(&self) -> Option<Instant> {
|
||||
if self.ack_eliciting_outstanding() {
|
||||
debug_assert!(self.time_of_last_sent_ack_eliciting_packet.is_some());
|
||||
self.time_of_last_sent_ack_eliciting_packet
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn on_packet_sent(&mut self, packet_number: u64, sent_packet: SentPacket) {
|
||||
if sent_packet.ack_eliciting {
|
||||
self.time_of_last_sent_ack_eliciting_packet = Some(sent_packet.time_sent);
|
||||
self.ack_eliciting_outstanding += 1;
|
||||
}
|
||||
self.sent_packets.insert(packet_number, sent_packet);
|
||||
}
|
||||
|
||||
pub fn remove_packet(&mut self, pn: u64) -> Option<SentPacket> {
|
||||
if let Some(sent) = self.sent_packets.remove(&pn) {
|
||||
if sent.ack_eliciting {
|
||||
debug_assert!(self.ack_eliciting_outstanding > 0);
|
||||
self.ack_eliciting_outstanding -= 1;
|
||||
}
|
||||
Some(sent)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// Remove all the acked packets. Returns them in ascending order -- largest
|
||||
// (i.e. highest PN) acked packet is last.
|
||||
fn remove_acked(&mut self, acked_ranges: Vec<(u64, u64)>) -> (Vec<SentPacket>, bool) {
|
||||
|
@ -219,7 +192,7 @@ impl LossRecoverySpace {
|
|||
for (end, start) in acked_ranges {
|
||||
// ^^ Notabug: see Frame::decode_ack_frame()
|
||||
for pn in start..=end {
|
||||
if let Some(sent) = self.remove_packet(pn) {
|
||||
if let Some(sent) = self.sent_packets.remove(&pn) {
|
||||
qdebug!("acked={}", pn);
|
||||
eliciting |= sent.ack_eliciting;
|
||||
acked_packets.insert(pn, sent);
|
||||
|
@ -233,10 +206,11 @@ impl LossRecoverySpace {
|
|||
}
|
||||
|
||||
/// Remove all tracked packets from the space.
|
||||
/// This is called by a client when 0-RTT packets are dropped, when a Retry is received
|
||||
/// and when keys are dropped.
|
||||
/// This is called by a client when 0-RTT packets are dropped and when a Retry is received.
|
||||
fn remove_ignored(&mut self) -> impl Iterator<Item = SentPacket> {
|
||||
self.ack_eliciting_outstanding = 0;
|
||||
// The largest acknowledged or loss_time should still be unset.
|
||||
// The client should not have received any ACK frames when it drops 0-RTT.
|
||||
assert!(self.largest_acked.is_none());
|
||||
std::mem::replace(&mut self.sent_packets, BTreeMap::default())
|
||||
.into_iter()
|
||||
.map(|(_, v)| v)
|
||||
|
@ -269,20 +243,182 @@ impl LossRecoverySpaces {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
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 {
|
||||
CongestionControl {
|
||||
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)]
|
||||
pub fn cwnd(&self) -> usize {
|
||||
self.congestion_window
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn ssthresh(&self) -> usize {
|
||||
self.ssthresh
|
||||
}
|
||||
|
||||
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
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub(crate) struct LossRecovery {
|
||||
pto_count: u32,
|
||||
time_of_last_sent_ack_eliciting_packet: Option<Instant>,
|
||||
rtt_vals: RttVals,
|
||||
|
||||
cc: CongestionControl,
|
||||
|
||||
enable_timed_loss_detection: bool,
|
||||
spaces: LossRecoverySpaces,
|
||||
loss_recovery_state: LossRecoveryState,
|
||||
}
|
||||
|
||||
impl LossRecovery {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
pub fn new() -> LossRecovery {
|
||||
LossRecovery {
|
||||
rtt_vals: RttVals {
|
||||
min_rtt: Duration::from_secs(u64::max_value()),
|
||||
max_ack_delay: Duration::from_millis(25),
|
||||
|
@ -290,7 +426,7 @@ impl LossRecovery {
|
|||
..RttVals::default()
|
||||
},
|
||||
|
||||
..Self::default()
|
||||
..LossRecovery::default()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -308,24 +444,28 @@ impl LossRecovery {
|
|||
self.cc.cwnd_avail()
|
||||
}
|
||||
|
||||
pub fn next_pn(&mut self, pn_space: PNSpace) -> u64 {
|
||||
self.spaces[pn_space].tx_pn
|
||||
}
|
||||
|
||||
pub fn inc_pn(&mut self, pn_space: PNSpace) {
|
||||
self.spaces[pn_space].tx_pn += 1;
|
||||
}
|
||||
|
||||
pub fn increment_pto_count(&mut self) {
|
||||
self.pto_count += 1;
|
||||
}
|
||||
|
||||
pub fn largest_acknowledged_pn(&self, pn_space: PNSpace) -> Option<u64> {
|
||||
self.spaces[pn_space].largest_acked
|
||||
}
|
||||
|
||||
pub fn pto(&self) -> Duration {
|
||||
self.rtt_vals.pto(PNSpace::ApplicationData)
|
||||
self.rtt_vals.pto()
|
||||
}
|
||||
|
||||
pub fn drop_0rtt(&mut self) -> Vec<SentPacket> {
|
||||
// The largest acknowledged or loss_time should still be unset.
|
||||
// The client should not have received any ACK frames when it drops 0-RTT.
|
||||
assert!(self.spaces[PNSpace::ApplicationData]
|
||||
.get_largest_acked()
|
||||
.is_none());
|
||||
self.spaces[PNSpace::ApplicationData]
|
||||
.remove_ignored()
|
||||
.inspect(|p| self.cc.discard(&p))
|
||||
.collect()
|
||||
pub fn drop_0rtt(&mut self) -> impl Iterator<Item = SentPacket> {
|
||||
self.spaces[PNSpace::ApplicationData].remove_ignored()
|
||||
}
|
||||
|
||||
pub fn on_packet_sent(
|
||||
|
@ -335,8 +475,14 @@ impl LossRecovery {
|
|||
sent_packet: SentPacket,
|
||||
) {
|
||||
qdebug!([self], "packet {:?}-{} sent.", pn_space, packet_number);
|
||||
if sent_packet.ack_eliciting {
|
||||
self.time_of_last_sent_ack_eliciting_packet = Some(sent_packet.time_sent);
|
||||
}
|
||||
self.cc.on_packet_sent(&sent_packet);
|
||||
self.spaces[pn_space].on_packet_sent(packet_number, sent_packet);
|
||||
|
||||
self.spaces[pn_space]
|
||||
.sent_packets
|
||||
.insert(packet_number, sent_packet);
|
||||
}
|
||||
|
||||
/// Returns (acked packets, lost packets)
|
||||
|
@ -388,7 +534,7 @@ impl LossRecovery {
|
|||
self.cc.on_packets_lost(
|
||||
now,
|
||||
prev_largest_acked_sent_time,
|
||||
self.rtt_vals.pto(pn_space),
|
||||
self.rtt_vals.pto(),
|
||||
&lost_packets,
|
||||
);
|
||||
|
||||
|
@ -406,32 +552,15 @@ impl LossRecovery {
|
|||
max(rtt * 9 / 8, GRANULARITY)
|
||||
}
|
||||
|
||||
// Calculate PTO duration
|
||||
fn pto_timeout(&self, pn_space: PNSpace) -> Duration {
|
||||
self.rtt_vals
|
||||
.pto(pn_space)
|
||||
.checked_mul(1 << self.pto_count)
|
||||
.unwrap_or(LOCAL_IDLE_TIMEOUT * 2)
|
||||
}
|
||||
|
||||
/// When receiving a retry, get all the sent packets so that they can be flushed.
|
||||
/// We also need to pretend that they never happened for the purposes of congestion control.
|
||||
pub fn retry(&mut self) -> Vec<SentPacket> {
|
||||
let cc = &mut self.cc;
|
||||
self.spaces
|
||||
.iter_mut()
|
||||
.flat_map(|spc| spc.remove_ignored())
|
||||
.inspect(|p| cc.discard(&p))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Discard state for a given packet number space.
|
||||
pub fn discard(&mut self, pn_space: PNSpace) {
|
||||
for p in self.spaces[pn_space].remove_ignored() {
|
||||
self.cc.discard(&p);
|
||||
}
|
||||
}
|
||||
|
||||
/// Detect packets whose contents may need to be retransmitted.
|
||||
pub fn detect_lost_packets(&mut self, pn_space: PNSpace, now: Instant) -> Vec<SentPacket> {
|
||||
self.enable_timed_loss_detection = false;
|
||||
|
@ -503,7 +632,8 @@ impl LossRecovery {
|
|||
|
||||
for pn in really_lost_pns {
|
||||
packet_space
|
||||
.remove_packet(pn)
|
||||
.sent_packets
|
||||
.remove(&pn)
|
||||
.expect("PN must be in sent_packets");
|
||||
}
|
||||
|
||||
|
@ -520,55 +650,52 @@ impl LossRecovery {
|
|||
lost_packets
|
||||
}
|
||||
|
||||
pub fn callback_time(&mut self) -> Option<Instant> {
|
||||
self.loss_recovery_state.callback_time()
|
||||
}
|
||||
pub fn get_timer(&mut self) -> LossRecoveryState {
|
||||
qdebug!([self], "get_loss_detection_timer.");
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn state_mode(&self) -> LossRecoveryMode {
|
||||
self.loss_recovery_state.mode()
|
||||
}
|
||||
|
||||
pub fn calculate_timer(&mut self) -> Option<Instant> {
|
||||
qtrace!([self], "get_loss_detection_timer.");
|
||||
|
||||
let has_ack_eliciting_out = self.spaces.iter().any(|sp| sp.ack_eliciting_outstanding());
|
||||
let has_ack_eliciting_out = self
|
||||
.spaces
|
||||
.iter()
|
||||
.flat_map(|spc| spc.sent_packets.values())
|
||||
.any(|sp| sp.ack_eliciting);
|
||||
|
||||
qdebug!([self], "has_ack_eliciting_out={}", has_ack_eliciting_out,);
|
||||
|
||||
if !has_ack_eliciting_out {
|
||||
self.loss_recovery_state = LossRecoveryState::new(LossRecoveryMode::None, None);
|
||||
return None;
|
||||
return LossRecoveryState::new(LossRecoveryMode::None, None);
|
||||
}
|
||||
|
||||
qinfo!(
|
||||
[self],
|
||||
"sent packets init:({} ack_eliciting:{}), hs:({} ack_eliciting:{}), app:({} ack_eliciting:{})",
|
||||
"sent packets {} {} {}",
|
||||
self.spaces[PNSpace::Initial].sent_packets.len(),
|
||||
self.spaces[PNSpace::Initial].ack_eliciting_outstanding(),
|
||||
self.spaces[PNSpace::Handshake].sent_packets.len(),
|
||||
self.spaces[PNSpace::Handshake].ack_eliciting_outstanding(),
|
||||
self.spaces[PNSpace::ApplicationData].sent_packets.len(),
|
||||
self.spaces[PNSpace::ApplicationData].ack_eliciting_outstanding()
|
||||
self.spaces[PNSpace::ApplicationData].sent_packets.len()
|
||||
);
|
||||
|
||||
// QUIC only has one timer, but it does double duty because it falls
|
||||
// back to other uses if first use is not needed: first the loss
|
||||
// detection timer, and then the probe timeout (PTO).
|
||||
|
||||
self.loss_recovery_state = if let Some((_, earliest_time)) = self.get_earliest_loss_time() {
|
||||
LossRecoveryState::new(LossRecoveryMode::LostPacketsTimer, Some(earliest_time))
|
||||
let (mode, maybe_timer) = if let Some((_, earliest_time)) = self.get_earliest_loss_time() {
|
||||
(LossRecoveryMode::LostPackets, Some(earliest_time))
|
||||
} else {
|
||||
LossRecoveryState::new(LossRecoveryMode::PtoTimer, self.get_min_pto())
|
||||
// Calculate PTO duration
|
||||
let timeout = self.rtt_vals.pto() * 2_u32.pow(self.pto_count);
|
||||
(
|
||||
LossRecoveryMode::PTO,
|
||||
self.time_of_last_sent_ack_eliciting_packet
|
||||
.map(|i| i + timeout),
|
||||
)
|
||||
};
|
||||
|
||||
qdebug!(
|
||||
[self],
|
||||
"loss_detection_timer mode={:?} timer={:?}",
|
||||
self.loss_recovery_state.mode(),
|
||||
self.loss_recovery_state.callback_time()
|
||||
mode,
|
||||
maybe_timer
|
||||
);
|
||||
self.loss_recovery_state.callback_time()
|
||||
LossRecoveryState::new(mode, maybe_timer)
|
||||
}
|
||||
|
||||
/// Find when the earliest sent packet should be considered lost.
|
||||
|
@ -590,87 +717,6 @@ impl LossRecovery {
|
|||
.min_by_key(|&(_, time)| time)
|
||||
.map(|(spc, val)| (spc, val + self.loss_delay()))
|
||||
}
|
||||
|
||||
fn pto_time_for_pn(&self, pn_space: PNSpace) -> Option<Instant> {
|
||||
if let Some(time) = self.spaces[pn_space].time_of_last_sent_ack_eliciting_packet() {
|
||||
Some(time + self.pto_timeout(pn_space))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Find when the last ack eliciting packet was sent.
|
||||
pub fn get_min_pto(&self) -> Option<Instant> {
|
||||
// TODO ignore PNSpace::Application until handshake is done -> a server side problem.
|
||||
PNSpace::iter()
|
||||
.filter_map(|spc| self.pto_time_for_pn(*spc))
|
||||
.min_by_key(|&time| time)
|
||||
}
|
||||
|
||||
pub fn get_min_pto_pn_space(&self, now: Instant) -> Option<PNSpace> {
|
||||
PNSpace::iter()
|
||||
.filter_map(|spc| {
|
||||
if let Some(time) = self.pto_time_for_pn(*spc) {
|
||||
if time <= now {
|
||||
Some(*spc)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.min_by_key(|&spc| spc)
|
||||
}
|
||||
|
||||
pub fn check_loss_detection_timeout(&mut self, now: Instant) -> Option<Vec<SentPacket>> {
|
||||
qdebug!([self], "check_loss_timeouts");
|
||||
|
||||
if self.loss_recovery_state.mode() == LossRecoveryMode::None {
|
||||
// LR not the active timer
|
||||
return None;
|
||||
}
|
||||
|
||||
if self.callback_time() > Some(now) {
|
||||
// LR timer, but hasn't expired.
|
||||
return None;
|
||||
}
|
||||
|
||||
// Timer expired and LR was active timer.
|
||||
match self.loss_recovery_state.mode() {
|
||||
LossRecoveryMode::None => unreachable!(),
|
||||
LossRecoveryMode::LostPacketsTimer => {
|
||||
// Time threshold loss detection
|
||||
let (pn_space, _) = self
|
||||
.get_earliest_loss_time()
|
||||
.expect("must be sent packets if in LostPackets mode");
|
||||
return Some(self.detect_lost_packets(pn_space, now));
|
||||
}
|
||||
LossRecoveryMode::PtoTimer => {
|
||||
qinfo!(
|
||||
[self],
|
||||
"check_loss_detection_timeout -send_one_or_two_packets"
|
||||
);
|
||||
|
||||
if let Some(min_pn_space) = self.get_min_pto_pn_space(now) {
|
||||
self.loss_recovery_state = LossRecoveryState::new(
|
||||
LossRecoveryMode::PtoExpired {
|
||||
dgram_available: 1,
|
||||
min_pn_space,
|
||||
},
|
||||
Some(now),
|
||||
);
|
||||
self.pto_count += 1;
|
||||
}
|
||||
}
|
||||
_ => {} // We are already in PtoExpired state
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn get_pto_state(&mut self) -> Option<(PNSpace, bool)> {
|
||||
self.loss_recovery_state.get_pto_state()
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for LossRecovery {
|
||||
|
@ -927,11 +973,11 @@ mod tests {
|
|||
assert_sent_times(&lr, None, None, Some(pn1_sent_time));
|
||||
|
||||
// After time elapses, pn 1 is marked lost.
|
||||
let callback_time = lr.calculate_timer();
|
||||
let lr_state = lr.get_timer();
|
||||
let pn1_lost_time = pn1_sent_time + (INITIAL_RTT * 9 / 8);
|
||||
assert_eq!(callback_time, Some(pn1_lost_time));
|
||||
match lr.state_mode() {
|
||||
LossRecoveryMode::LostPacketsTimer => {
|
||||
assert_eq!(lr_state.callback_time, Some(pn1_lost_time));
|
||||
match lr_state.mode {
|
||||
LossRecoveryMode::LostPackets => {
|
||||
let packets = lr.detect_lost_packets(PNSpace::ApplicationData, pn1_lost_time);
|
||||
|
||||
assert_eq!(packets.len(), 1)
|
||||
|
|
|
@ -29,7 +29,7 @@ pub(crate) type RecvStreams = BTreeMap<StreamId, RecvStream>;
|
|||
|
||||
/// Holds data not yet read by application. Orders and dedupes data ranges
|
||||
/// from incoming STREAM frames.
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Debug, Default, PartialEq)]
|
||||
pub struct RxStreamOrderer {
|
||||
data_ranges: BTreeMap<u64, Vec<u8>>, // (start_offset, data)
|
||||
retired: u64, // Number of bytes the application has read
|
||||
|
@ -274,9 +274,7 @@ impl RxStreamOrderer {
|
|||
}
|
||||
|
||||
/// QUIC receiving states, based on -transport 3.2.
|
||||
#[derive(Debug)]
|
||||
#[allow(dead_code)]
|
||||
// Because a dead_code warning is easier than clippy::unused_self, see https://github.com/rust-lang/rust/issues/68408
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum RecvStreamState {
|
||||
Recv {
|
||||
recv_buf: RxStreamOrderer,
|
||||
|
@ -297,7 +295,7 @@ enum RecvStreamState {
|
|||
|
||||
impl RecvStreamState {
|
||||
fn new(max_bytes: u64) -> Self {
|
||||
Self::Recv {
|
||||
RecvStreamState::Recv {
|
||||
recv_buf: RxStreamOrderer::new(),
|
||||
max_bytes,
|
||||
max_stream_data: max_bytes,
|
||||
|
@ -306,29 +304,34 @@ impl RecvStreamState {
|
|||
|
||||
fn name(&self) -> &str {
|
||||
match self {
|
||||
Self::Recv { .. } => "Recv",
|
||||
Self::SizeKnown { .. } => "SizeKnown",
|
||||
Self::DataRecvd { .. } => "DataRecvd",
|
||||
Self::DataRead => "DataRead",
|
||||
Self::ResetRecvd => "ResetRecvd",
|
||||
RecvStreamState::Recv { .. } => "Recv",
|
||||
RecvStreamState::SizeKnown { .. } => "SizeKnown",
|
||||
RecvStreamState::DataRecvd { .. } => "DataRecvd",
|
||||
RecvStreamState::DataRead => "DataRead",
|
||||
RecvStreamState::ResetRecvd => "ResetRecvd",
|
||||
}
|
||||
}
|
||||
|
||||
fn recv_buf(&self) -> Option<&RxStreamOrderer> {
|
||||
match self {
|
||||
Self::Recv { recv_buf, .. }
|
||||
| Self::SizeKnown { recv_buf, .. }
|
||||
| Self::DataRecvd { recv_buf } => Some(recv_buf),
|
||||
Self::DataRead | Self::ResetRecvd => None,
|
||||
RecvStreamState::Recv { recv_buf, .. }
|
||||
| RecvStreamState::SizeKnown { recv_buf, .. }
|
||||
| RecvStreamState::DataRecvd { recv_buf } => Some(recv_buf),
|
||||
RecvStreamState::DataRead | RecvStreamState::ResetRecvd => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn final_size(&self) -> Option<u64> {
|
||||
match self {
|
||||
Self::SizeKnown { final_size, .. } => Some(*final_size),
|
||||
RecvStreamState::SizeKnown { final_size, .. } => Some(*final_size),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn transition(&mut self, new_state: Self) {
|
||||
qtrace!("RecvStream state {} -> {}", self.name(), new_state.name());
|
||||
*self = new_state;
|
||||
}
|
||||
}
|
||||
|
||||
/// Implement a QUIC receive stream.
|
||||
|
@ -355,27 +358,6 @@ impl RecvStream {
|
|||
}
|
||||
}
|
||||
|
||||
fn set_state(&mut self, new_state: RecvStreamState) {
|
||||
debug_assert_ne!(
|
||||
mem::discriminant(&self.state),
|
||||
mem::discriminant(&new_state)
|
||||
);
|
||||
qtrace!(
|
||||
"RecvStream {} state {} -> {}",
|
||||
self.stream_id.as_u64(),
|
||||
self.state.name(),
|
||||
new_state.name()
|
||||
);
|
||||
|
||||
if let RecvStreamState::Recv { .. } = &self.state {
|
||||
self.flow_mgr
|
||||
.borrow_mut()
|
||||
.clear_max_stream_data(self.stream_id)
|
||||
}
|
||||
|
||||
self.state = new_state;
|
||||
}
|
||||
|
||||
pub fn inbound_stream_frame(&mut self, fin: bool, offset: u64, data: Vec<u8>) -> Res<()> {
|
||||
let new_end = offset + data.len() as u64;
|
||||
|
||||
|
@ -406,9 +388,10 @@ impl RecvStream {
|
|||
|
||||
let buf = mem::replace(recv_buf, RxStreamOrderer::new());
|
||||
if final_size == buf.retired() + buf.bytes_ready() as u64 {
|
||||
self.set_state(RecvStreamState::DataRecvd { recv_buf: buf });
|
||||
self.state
|
||||
.transition(RecvStreamState::DataRecvd { recv_buf: buf });
|
||||
} else {
|
||||
self.set_state(RecvStreamState::SizeKnown {
|
||||
self.state.transition(RecvStreamState::SizeKnown {
|
||||
recv_buf: buf,
|
||||
final_size,
|
||||
});
|
||||
|
@ -424,7 +407,8 @@ impl RecvStream {
|
|||
recv_buf.inbound_frame(offset, data)?;
|
||||
if *final_size == recv_buf.retired() + recv_buf.bytes_ready() as u64 {
|
||||
let buf = mem::replace(recv_buf, RxStreamOrderer::new());
|
||||
self.set_state(RecvStreamState::DataRecvd { recv_buf: buf });
|
||||
self.state
|
||||
.transition(RecvStreamState::DataRecvd { recv_buf: buf });
|
||||
}
|
||||
}
|
||||
RecvStreamState::DataRecvd { .. }
|
||||
|
@ -446,7 +430,7 @@ impl RecvStream {
|
|||
RecvStreamState::Recv { .. } | RecvStreamState::SizeKnown { .. } => {
|
||||
self.conn_events
|
||||
.recv_stream_reset(self.stream_id, application_error_code);
|
||||
self.set_state(RecvStreamState::ResetRecvd);
|
||||
self.state.transition(RecvStreamState::ResetRecvd);
|
||||
}
|
||||
_ => {
|
||||
// Ignore reset if in DataRecvd, DataRead, or ResetRecvd
|
||||
|
@ -505,7 +489,7 @@ impl RecvStream {
|
|||
let bytes_read = recv_buf.read(buf)?;
|
||||
let fin_read = recv_buf.buffered() == 0;
|
||||
if fin_read {
|
||||
self.set_state(RecvStreamState::DataRead)
|
||||
self.state.transition(RecvStreamState::DataRead)
|
||||
}
|
||||
Ok((bytes_read, fin_read))
|
||||
}
|
||||
|
@ -519,10 +503,10 @@ impl RecvStream {
|
|||
qtrace!("stop_sending called when in state {}", self.state.name());
|
||||
match &self.state {
|
||||
RecvStreamState::Recv { .. } | RecvStreamState::SizeKnown { .. } => {
|
||||
self.set_state(RecvStreamState::ResetRecvd);
|
||||
self.state.transition(RecvStreamState::ResetRecvd);
|
||||
self.flow_mgr.borrow_mut().stop_sending(self.stream_id, err)
|
||||
}
|
||||
RecvStreamState::DataRecvd { .. } => self.set_state(RecvStreamState::DataRead),
|
||||
RecvStreamState::DataRecvd { .. } => self.state.transition(RecvStreamState::DataRead),
|
||||
RecvStreamState::DataRead | RecvStreamState::ResetRecvd => {
|
||||
// Already in terminal state
|
||||
}
|
||||
|
@ -533,8 +517,6 @@ impl RecvStream {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::frame::Frame;
|
||||
use neqo_common::matches;
|
||||
|
||||
#[test]
|
||||
fn test_stream_rx() {
|
||||
|
@ -766,26 +748,4 @@ mod tests {
|
|||
assert_eq!(rx_ord.buffered(), 15);
|
||||
assert_eq!(rx_ord.retired(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_stream_flowc_event_after_exiting_recv() {
|
||||
let flow_mgr = Rc::new(RefCell::new(FlowMgr::default()));
|
||||
let conn_events = ConnectionEvents::default();
|
||||
|
||||
let frame1 = vec![0; RX_STREAM_DATA_WINDOW as usize];
|
||||
|
||||
let mut s = RecvStream::new(
|
||||
67.into(),
|
||||
RX_STREAM_DATA_WINDOW,
|
||||
Rc::clone(&flow_mgr),
|
||||
conn_events,
|
||||
);
|
||||
|
||||
s.inbound_stream_frame(false, 0, frame1).unwrap();
|
||||
flow_mgr.borrow_mut().max_stream_data(67.into(), 100);
|
||||
assert!(matches!(s.flow_mgr.borrow().peek().unwrap(), Frame::MaxStreamData{..}));
|
||||
s.inbound_stream_frame(true, RX_STREAM_DATA_WINDOW, vec![])
|
||||
.unwrap();
|
||||
assert!(matches!(s.flow_mgr.borrow().peek(), None));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,6 @@ use crate::flow_mgr::FlowMgr;
|
|||
use crate::frame::{Frame, TxMode};
|
||||
use crate::recovery::RecoveryToken;
|
||||
use crate::stream_id::StreamId;
|
||||
use crate::tracking::PNSpace;
|
||||
use crate::{AppError, Error, Res};
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||
|
@ -284,17 +283,17 @@ impl TxBuffer {
|
|||
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
send_buf: VecDeque::with_capacity(Self::BUFFER_SIZE),
|
||||
send_buf: VecDeque::with_capacity(TxBuffer::BUFFER_SIZE),
|
||||
..Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt to add some or all of the passed-in buffer to the TxBuffer.
|
||||
pub fn send(&mut self, buf: &[u8]) -> usize {
|
||||
let can_buffer = min(Self::BUFFER_SIZE - self.buffered(), buf.len());
|
||||
let can_buffer = min(TxBuffer::BUFFER_SIZE - self.buffered(), buf.len());
|
||||
if can_buffer > 0 {
|
||||
self.send_buf.extend(&buf[..can_buffer]);
|
||||
assert!(self.send_buf.len() <= Self::BUFFER_SIZE);
|
||||
assert!(self.send_buf.len() <= TxBuffer::BUFFER_SIZE);
|
||||
}
|
||||
can_buffer
|
||||
}
|
||||
|
@ -363,7 +362,7 @@ impl TxBuffer {
|
|||
}
|
||||
|
||||
fn avail(&self) -> usize {
|
||||
Self::BUFFER_SIZE - self.buffered()
|
||||
TxBuffer::BUFFER_SIZE - self.buffered()
|
||||
}
|
||||
|
||||
pub fn highest_sent(&self) -> u64 {
|
||||
|
@ -393,44 +392,60 @@ enum SendStreamState {
|
|||
impl SendStreamState {
|
||||
fn tx_buf(&self) -> Option<&TxBuffer> {
|
||||
match self {
|
||||
Self::Send { send_buf } | Self::DataSent { send_buf, .. } => Some(send_buf),
|
||||
Self::Ready | Self::DataRecvd { .. } | Self::ResetSent | Self::ResetRecvd => None,
|
||||
SendStreamState::Send { send_buf } | SendStreamState::DataSent { send_buf, .. } => {
|
||||
Some(send_buf)
|
||||
}
|
||||
SendStreamState::Ready
|
||||
| SendStreamState::DataRecvd { .. }
|
||||
| SendStreamState::ResetSent
|
||||
| SendStreamState::ResetRecvd => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn tx_buf_mut(&mut self) -> Option<&mut TxBuffer> {
|
||||
match self {
|
||||
Self::Send { send_buf } | Self::DataSent { send_buf, .. } => Some(send_buf),
|
||||
Self::Ready | Self::DataRecvd { .. } | Self::ResetSent | Self::ResetRecvd => None,
|
||||
SendStreamState::Send { send_buf } | SendStreamState::DataSent { send_buf, .. } => {
|
||||
Some(send_buf)
|
||||
}
|
||||
SendStreamState::Ready
|
||||
| SendStreamState::DataRecvd { .. }
|
||||
| SendStreamState::ResetSent
|
||||
| SendStreamState::ResetRecvd => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn tx_avail(&self) -> u64 {
|
||||
match self {
|
||||
// In Ready, TxBuffer not yet allocated but size is known
|
||||
Self::Ready => TxBuffer::BUFFER_SIZE.try_into().unwrap(),
|
||||
Self::Send { send_buf } | Self::DataSent { send_buf, .. } => {
|
||||
SendStreamState::Ready => TxBuffer::BUFFER_SIZE.try_into().unwrap(),
|
||||
SendStreamState::Send { send_buf } | SendStreamState::DataSent { send_buf, .. } => {
|
||||
send_buf.avail().try_into().unwrap()
|
||||
}
|
||||
Self::DataRecvd { .. } | Self::ResetSent | Self::ResetRecvd => 0,
|
||||
SendStreamState::DataRecvd { .. }
|
||||
| SendStreamState::ResetSent
|
||||
| SendStreamState::ResetRecvd => 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn final_size(&self) -> Option<u64> {
|
||||
match self {
|
||||
Self::DataSent { final_size, .. } | Self::DataRecvd { final_size } => Some(*final_size),
|
||||
Self::Ready | Self::Send { .. } | Self::ResetSent | Self::ResetRecvd => None,
|
||||
SendStreamState::DataSent { final_size, .. }
|
||||
| SendStreamState::DataRecvd { final_size } => Some(*final_size),
|
||||
SendStreamState::Ready
|
||||
| SendStreamState::Send { .. }
|
||||
| SendStreamState::ResetSent
|
||||
| SendStreamState::ResetRecvd => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
match self {
|
||||
Self::Ready => "Ready",
|
||||
Self::Send { .. } => "Send",
|
||||
Self::DataSent { .. } => "DataSent",
|
||||
Self::DataRecvd { .. } => "DataRecvd",
|
||||
Self::ResetSent => "ResetSent",
|
||||
Self::ResetRecvd => "ResetRecvd",
|
||||
SendStreamState::Ready => "Ready",
|
||||
SendStreamState::Send { .. } => "Send",
|
||||
SendStreamState::DataSent { .. } => "DataSent",
|
||||
SendStreamState::DataRecvd { .. } => "DataRecvd",
|
||||
SendStreamState::ResetSent => "ResetSent",
|
||||
SendStreamState::ResetRecvd => "ResetRecvd",
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -744,11 +759,11 @@ impl SendStreams {
|
|||
|
||||
pub(crate) fn get_frame(
|
||||
&mut self,
|
||||
space: PNSpace,
|
||||
epoch: u16,
|
||||
mode: TxMode,
|
||||
remaining: usize,
|
||||
) -> Option<(Frame, Option<RecoveryToken>)> {
|
||||
if space != PNSpace::ApplicationData {
|
||||
if epoch != 3 && epoch != 1 {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
@ -759,11 +774,11 @@ impl SendStreams {
|
|||
Frame::new_stream(stream_id.as_u64(), offset, data, complete, remaining)
|
||||
{
|
||||
qdebug!(
|
||||
"Stream {} sending bytes {}-{}, space {:?}, mode {:?}",
|
||||
"Stream {} sending bytes {}-{}, epoch {}, mode {:?}",
|
||||
stream_id.as_u64(),
|
||||
offset,
|
||||
offset + length as u64,
|
||||
space,
|
||||
epoch,
|
||||
mode,
|
||||
);
|
||||
let fin = complete && length == data.len();
|
||||
|
|
|
@ -15,10 +15,12 @@ use neqo_crypto::{
|
|||
AntiReplay,
|
||||
};
|
||||
|
||||
use crate::cid::{ConnectionId, ConnectionIdDecoder, ConnectionIdManager, ConnectionIdRef};
|
||||
use crate::connection::{Connection, Output, State};
|
||||
use crate::packet::{PacketBuilder, PacketType, PublicPacket};
|
||||
use crate::Res;
|
||||
use crate::connection::{Connection, ConnectionIdManager, Output, State};
|
||||
use crate::packet::{
|
||||
decode_packet_hdr, encode_packet_vn, encode_retry, ConnectionId, ConnectionIdDecoder,
|
||||
PacketHdr, PacketType, Version,
|
||||
};
|
||||
use crate::{Res, QUIC_VERSION};
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::collections::{HashMap, HashSet, VecDeque};
|
||||
|
@ -82,7 +84,7 @@ struct RetryToken {
|
|||
|
||||
impl RetryToken {
|
||||
fn new(now: Instant) -> Res<Self> {
|
||||
Ok(Self {
|
||||
Ok(RetryToken {
|
||||
require_retry: false,
|
||||
self_encrypt: SelfEncrypt::new(TLS_VERSION_1_3, TLS_AES_128_GCM_SHA256)?,
|
||||
start_time: now,
|
||||
|
@ -122,7 +124,7 @@ impl RetryToken {
|
|||
let end_millis = u32::try_from(end.duration_since(self.start_time).as_millis())?;
|
||||
token.encode_uint(4, end_millis);
|
||||
token.encode(dcid);
|
||||
let peer_addr = Self::encode_peer_address(peer_address);
|
||||
let peer_addr = RetryToken::encode_peer_address(peer_address);
|
||||
Ok(self.self_encrypt.seal(&peer_addr, &token)?)
|
||||
}
|
||||
|
||||
|
@ -138,7 +140,7 @@ impl RetryToken {
|
|||
peer_address: SocketAddr,
|
||||
now: Instant,
|
||||
) -> Option<ConnectionId> {
|
||||
let peer_addr = Self::encode_peer_address(peer_address);
|
||||
let peer_addr = RetryToken::encode_peer_address(peer_address);
|
||||
let data = if let Ok(d) = self.self_encrypt.open(&peer_addr, token) {
|
||||
d
|
||||
} else {
|
||||
|
@ -159,18 +161,22 @@ impl RetryToken {
|
|||
|
||||
pub fn validate(
|
||||
&self,
|
||||
token: &[u8],
|
||||
hdr: &PacketHdr,
|
||||
peer_address: SocketAddr,
|
||||
now: Instant,
|
||||
) -> RetryTokenResult {
|
||||
if token.is_empty() {
|
||||
if self.require_retry {
|
||||
RetryTokenResult::Validate
|
||||
if let PacketType::Initial(token) = &hdr.tipe {
|
||||
if token.is_empty() {
|
||||
if self.require_retry {
|
||||
RetryTokenResult::Validate
|
||||
} else {
|
||||
RetryTokenResult::Pass
|
||||
}
|
||||
} else if let Some(cid) = self.decrypt_token(token, peer_address, now) {
|
||||
RetryTokenResult::Valid(cid)
|
||||
} else {
|
||||
RetryTokenResult::Pass
|
||||
RetryTokenResult::Invalid
|
||||
}
|
||||
} else if let Some(cid) = self.decrypt_token(token, peer_address, now) {
|
||||
RetryTokenResult::Valid(cid)
|
||||
} else {
|
||||
RetryTokenResult::Invalid
|
||||
}
|
||||
|
@ -178,6 +184,8 @@ impl RetryToken {
|
|||
}
|
||||
|
||||
pub struct Server {
|
||||
/// The version this server supports (currently just one).
|
||||
version: Version,
|
||||
/// The names of certificates.
|
||||
certs: Vec<String>,
|
||||
/// The ALPN values that the server supports.
|
||||
|
@ -214,6 +222,7 @@ impl Server {
|
|||
cid_manager: CidMgr,
|
||||
) -> Res<Self> {
|
||||
Ok(Self {
|
||||
version: QUIC_VERSION,
|
||||
certs: certs.iter().map(|x| String::from(x.as_ref())).collect(),
|
||||
protocols: protocols.iter().map(|x| String::from(x.as_ref())).collect(),
|
||||
anti_replay,
|
||||
|
@ -226,6 +235,20 @@ impl Server {
|
|||
})
|
||||
}
|
||||
|
||||
fn create_vn(&self, hdr: &PacketHdr, received: Datagram) -> Datagram {
|
||||
let vn = encode_packet_vn(&PacketHdr::new(
|
||||
0,
|
||||
// Actual version we support and a greased value.
|
||||
PacketType::VN(vec![self.version, 0xaaba_cada]),
|
||||
Some(0),
|
||||
hdr.scid.as_ref().unwrap().clone(),
|
||||
Some(hdr.dcid.clone()),
|
||||
0, // unused
|
||||
0, // unused
|
||||
));
|
||||
Datagram::new(received.destination(), received.source(), vn)
|
||||
}
|
||||
|
||||
pub fn set_retry_required(&mut self, require_retry: bool) {
|
||||
self.retry.set_retry_required(require_retry);
|
||||
}
|
||||
|
@ -273,8 +296,8 @@ impl Server {
|
|||
out.dgram()
|
||||
}
|
||||
|
||||
fn connection(&self, cid: &ConnectionIdRef) -> Option<StateRef> {
|
||||
if let Some(c) = self.connections.borrow().get(&cid[..]) {
|
||||
fn connection(&self, cid: &ConnectionId) -> Option<StateRef> {
|
||||
if let Some(c) = self.connections.borrow().get(cid) {
|
||||
Some(c.clone())
|
||||
} else {
|
||||
None
|
||||
|
@ -283,35 +306,38 @@ impl Server {
|
|||
|
||||
fn handle_initial(
|
||||
&mut self,
|
||||
dcid: ConnectionId,
|
||||
scid: ConnectionId,
|
||||
token: Vec<u8>,
|
||||
hdr: PacketHdr,
|
||||
dgram: Datagram,
|
||||
now: Instant,
|
||||
) -> Option<Datagram> {
|
||||
match self.retry.validate(&token, dgram.source(), now) {
|
||||
match self.retry.validate(&hdr, dgram.source(), now) {
|
||||
RetryTokenResult::Invalid => None,
|
||||
RetryTokenResult::Pass => self.accept_connection(None, dgram, now),
|
||||
RetryTokenResult::Valid(dcid) => self.accept_connection(Some(dcid), dgram, now),
|
||||
RetryTokenResult::Validate => {
|
||||
qinfo!([self], "Send retry for {:?}", dcid);
|
||||
qinfo!([self], "Send retry for {:?}", hdr.dcid);
|
||||
|
||||
let res = self.retry.generate_token(&dcid, dgram.source(), now);
|
||||
let res = self.retry.generate_token(&hdr.dcid, dgram.source(), now);
|
||||
let token = if let Ok(t) = res {
|
||||
t
|
||||
} else {
|
||||
qerror!([self], "unable to generate token, dropping packet");
|
||||
return None;
|
||||
};
|
||||
let new_dcid = self.cid_manager.borrow_mut().generate_cid();
|
||||
let packet = PacketBuilder::retry(&scid, &new_dcid, &token, &dcid);
|
||||
if let Ok(p) = packet {
|
||||
let retry = Datagram::new(dgram.destination(), dgram.source(), p);
|
||||
Some(retry)
|
||||
} else {
|
||||
qerror!([self], "unable to encode retry, dropping packet");
|
||||
None
|
||||
}
|
||||
let payload = encode_retry(&PacketHdr::new(
|
||||
0, // tbyte (unused on encode)
|
||||
PacketType::Retry {
|
||||
odcid: hdr.dcid.clone(),
|
||||
token,
|
||||
},
|
||||
Some(self.version),
|
||||
hdr.scid.as_ref().unwrap().clone(),
|
||||
Some(self.cid_manager.borrow_mut().generate_cid()),
|
||||
0, // Packet number
|
||||
0, // Epoch
|
||||
));
|
||||
let retry = Datagram::new(dgram.destination(), dgram.source(), payload);
|
||||
Some(retry)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -354,9 +380,9 @@ impl Server {
|
|||
|
||||
// This is only looking at the first packet header in the datagram.
|
||||
// All packets in the datagram are routed to the same connection.
|
||||
let res = PublicPacket::decode(&dgram[..], self.cid_manager.borrow().as_decoder());
|
||||
let (packet, _remainder) = match res {
|
||||
Ok(res) => res,
|
||||
let res = decode_packet_hdr(self.cid_manager.borrow().as_decoder(), &dgram[..]);
|
||||
let hdr = match res {
|
||||
Ok(h) => h,
|
||||
_ => {
|
||||
qtrace!([self], "Discarding {:?}", dgram);
|
||||
return None;
|
||||
|
@ -364,11 +390,11 @@ impl Server {
|
|||
};
|
||||
|
||||
// Finding an existing connection. Should be the most common case.
|
||||
if let Some(c) = self.connection(packet.dcid()) {
|
||||
if let Some(c) = self.connection(&hdr.dcid) {
|
||||
return self.process_connection(c, Some(dgram), now);
|
||||
}
|
||||
|
||||
if packet.packet_type() == PacketType::Short {
|
||||
if hdr.tipe == PacketType::Short {
|
||||
// TODO send a stateless reset here.
|
||||
qtrace!([self], "Short header packet for an unknown connection");
|
||||
return None;
|
||||
|
@ -378,16 +404,12 @@ impl Server {
|
|||
qtrace!([self], "Bogus packet: too short");
|
||||
return None;
|
||||
}
|
||||
if packet.packet_type() == PacketType::OtherVersion {
|
||||
let vn = PacketBuilder::version_negotiation(packet.scid(), packet.dcid());
|
||||
return Some(Datagram::new(dgram.destination(), dgram.source(), vn));
|
||||
|
||||
if hdr.version != Some(self.version) {
|
||||
return Some(self.create_vn(&hdr, dgram));
|
||||
}
|
||||
|
||||
// Copy values from `packet` because they are currently still borrowing from `dgram`.
|
||||
let dcid = ConnectionId::from(packet.dcid());
|
||||
let scid = ConnectionId::from(packet.scid());
|
||||
let token = packet.token().to_vec();
|
||||
self.handle_initial(dcid, scid, token, dgram, now)
|
||||
self.handle_initial(hdr, dgram, now)
|
||||
}
|
||||
|
||||
/// Iterate through the pending connections looking for any that might want
|
||||
|
@ -495,7 +517,7 @@ struct ServerConnectionIdManager {
|
|||
}
|
||||
|
||||
impl ConnectionIdDecoder for ServerConnectionIdManager {
|
||||
fn decode_cid<'a>(&self, dec: &mut Decoder<'a>) -> Option<ConnectionIdRef<'a>> {
|
||||
fn decode_cid(&self, dec: &mut Decoder) -> Option<ConnectionId> {
|
||||
self.cid_manager.borrow_mut().decode_cid(dec)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,11 +10,9 @@
|
|||
/// Connection statistics
|
||||
pub struct Stats {
|
||||
/// Total packets received
|
||||
pub packets_rx: usize,
|
||||
pub packets_rx: u64,
|
||||
/// Total packets sent
|
||||
pub packets_tx: usize,
|
||||
pub packets_tx: u64,
|
||||
/// Duplicate packets received
|
||||
pub dups_rx: usize,
|
||||
/// Dropped datagrams, or parts thereof
|
||||
pub dropped_rx: usize,
|
||||
pub dups_rx: u64,
|
||||
}
|
||||
|
|
|
@ -59,15 +59,15 @@ impl TransportParameter {
|
|||
fn encode(&self, enc: &mut Encoder, tipe: u16) {
|
||||
enc.encode_uint(2, tipe);
|
||||
match self {
|
||||
Self::Bytes(a) => {
|
||||
TransportParameter::Bytes(a) => {
|
||||
enc.encode_vec(2, a);
|
||||
}
|
||||
Self::Integer(a) => {
|
||||
TransportParameter::Integer(a) => {
|
||||
enc.encode_vec_with(2, |enc_inner| {
|
||||
enc_inner.encode_varint(*a);
|
||||
});
|
||||
}
|
||||
Self::Empty => {
|
||||
TransportParameter::Empty => {
|
||||
enc.encode_uint(2, 0_u64);
|
||||
}
|
||||
};
|
||||
|
@ -85,12 +85,12 @@ impl TransportParameter {
|
|||
qtrace!("TP {:x} length {:x}", tipe, content.len());
|
||||
let mut d = Decoder::from(content);
|
||||
let tp = match tipe {
|
||||
ORIGINAL_CONNECTION_ID => Self::Bytes(d.decode_remainder().to_vec()), // TODO(mt) unnecessary copy
|
||||
ORIGINAL_CONNECTION_ID => TransportParameter::Bytes(d.decode_remainder().to_vec()), // TODO(mt) unnecessary copy
|
||||
STATELESS_RESET_TOKEN => {
|
||||
if d.remaining() != 16 {
|
||||
return Err(Error::TransportParameterError);
|
||||
}
|
||||
Self::Bytes(d.decode_remainder().to_vec()) // TODO(mt) unnecessary copy
|
||||
TransportParameter::Bytes(d.decode_remainder().to_vec()) // TODO(mt) unnecessary copy
|
||||
}
|
||||
IDLE_TIMEOUT
|
||||
| INITIAL_MAX_DATA
|
||||
|
@ -100,21 +100,21 @@ impl TransportParameter {
|
|||
| INITIAL_MAX_STREAMS_BIDI
|
||||
| INITIAL_MAX_STREAMS_UNI
|
||||
| MAX_ACK_DELAY => match d.decode_varint() {
|
||||
Some(v) => Self::Integer(v),
|
||||
Some(v) => TransportParameter::Integer(v),
|
||||
None => return Err(Error::TransportParameterError),
|
||||
},
|
||||
|
||||
MAX_PACKET_SIZE => match d.decode_varint() {
|
||||
Some(v) if v >= 1200 => Self::Integer(v),
|
||||
Some(v) if v >= 1200 => TransportParameter::Integer(v),
|
||||
_ => return Err(Error::TransportParameterError),
|
||||
},
|
||||
|
||||
ACK_DELAY_EXPONENT => match d.decode_varint() {
|
||||
Some(v) if v <= 20 => Self::Integer(v),
|
||||
Some(v) if v <= 20 => TransportParameter::Integer(v),
|
||||
_ => return Err(Error::TransportParameterError),
|
||||
},
|
||||
|
||||
DISABLE_MIGRATION => Self::Empty,
|
||||
DISABLE_MIGRATION => TransportParameter::Empty,
|
||||
// Skip.
|
||||
_ => return Ok(None),
|
||||
};
|
||||
|
@ -315,7 +315,7 @@ impl ExtensionHandler for TransportParametersHandler {
|
|||
}
|
||||
|
||||
fn handle(&mut self, msg: HandshakeMessage, d: &[u8]) -> ExtensionHandlerResult {
|
||||
qtrace!(
|
||||
qdebug!(
|
||||
"Handling transport parameters, msg={:?} value={}",
|
||||
msg,
|
||||
hex(d),
|
||||
|
|
|
@ -13,21 +13,19 @@ use std::ops::{Index, IndexMut};
|
|||
use std::time::{Duration, Instant};
|
||||
|
||||
use neqo_common::{qdebug, qinfo, qtrace, qwarn};
|
||||
use neqo_crypto::{Epoch, TLS_EPOCH_APPLICATION_DATA, TLS_EPOCH_HANDSHAKE, TLS_EPOCH_INITIAL};
|
||||
use neqo_crypto::constants::Epoch;
|
||||
|
||||
use crate::frame::{AckRange, Frame};
|
||||
use crate::packet::{PacketNumber, PacketType};
|
||||
use crate::recovery::RecoveryToken;
|
||||
|
||||
// TODO(mt) look at enabling EnumMap for this: https://stackoverflow.com/a/44905797/1375574
|
||||
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Ord, Eq)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum PNSpace {
|
||||
Initial,
|
||||
Handshake,
|
||||
ApplicationData,
|
||||
Initial = 0,
|
||||
Handshake = 1,
|
||||
ApplicationData = 2,
|
||||
}
|
||||
|
||||
#[allow(clippy::use_self)] // https://github.com/rust-lang/rust-clippy/issues/3410
|
||||
impl PNSpace {
|
||||
pub fn iter() -> impl Iterator<Item = &'static PNSpace> {
|
||||
const SPACES: &[PNSpace] = &[
|
||||
|
@ -42,80 +40,17 @@ impl PNSpace {
|
|||
impl From<Epoch> for PNSpace {
|
||||
fn from(epoch: Epoch) -> Self {
|
||||
match epoch {
|
||||
TLS_EPOCH_INITIAL => Self::Initial,
|
||||
TLS_EPOCH_HANDSHAKE => Self::Handshake,
|
||||
_ => Self::ApplicationData,
|
||||
0 => PNSpace::Initial,
|
||||
2 => PNSpace::Handshake,
|
||||
_ => PNSpace::ApplicationData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PacketType> for PNSpace {
|
||||
fn from(pt: PacketType) -> Self {
|
||||
match pt {
|
||||
PacketType::Initial => Self::Initial,
|
||||
PacketType::Handshake => Self::Handshake,
|
||||
PacketType::ZeroRtt | PacketType::Short => Self::ApplicationData,
|
||||
_ => panic!("Attempted to get space from wrong packet type"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SentPacket {
|
||||
pub ack_eliciting: bool,
|
||||
pub time_sent: Instant,
|
||||
pub tokens: Vec<RecoveryToken>,
|
||||
|
||||
pub time_declared_lost: Option<Instant>,
|
||||
|
||||
pub in_flight: bool,
|
||||
pub size: usize,
|
||||
}
|
||||
|
||||
impl SentPacket {
|
||||
pub fn new(
|
||||
time_sent: Instant,
|
||||
ack_eliciting: bool,
|
||||
tokens: Vec<RecoveryToken>,
|
||||
size: usize,
|
||||
in_flight: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
time_sent,
|
||||
ack_eliciting,
|
||||
tokens,
|
||||
time_declared_lost: None,
|
||||
size,
|
||||
in_flight,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Epoch> for PNSpace {
|
||||
fn into(self) -> Epoch {
|
||||
match self {
|
||||
Self::Initial => TLS_EPOCH_INITIAL,
|
||||
Self::Handshake => TLS_EPOCH_HANDSHAKE,
|
||||
// Our epoch progresses forward, but the TLS epoch is fixed to 3.
|
||||
Self::ApplicationData => TLS_EPOCH_APPLICATION_DATA,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for PNSpace {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
f.write_str(match self {
|
||||
Self::Initial => "in",
|
||||
Self::Handshake => "hs",
|
||||
Self::ApplicationData => "ap",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct PacketRange {
|
||||
largest: PacketNumber,
|
||||
smallest: PacketNumber,
|
||||
largest: u64,
|
||||
smallest: u64,
|
||||
ack_needed: bool,
|
||||
}
|
||||
|
||||
|
@ -209,7 +144,7 @@ pub struct RecvdPackets {
|
|||
space: PNSpace,
|
||||
ranges: VecDeque<PacketRange>,
|
||||
/// The packet number of the lowest number packet that we are tracking.
|
||||
min_tracked: PacketNumber,
|
||||
min_tracked: u64,
|
||||
/// The time we got the largest acknowledged.
|
||||
largest_pn_time: Option<Instant>,
|
||||
// The time that we should be sending an ACK.
|
||||
|
@ -267,7 +202,7 @@ impl RecvdPackets {
|
|||
/// Add the packet to the tracked set.
|
||||
pub fn set_received(&mut self, now: Instant, pn: u64, ack_eliciting: bool) {
|
||||
let next_in_order_pn = self.ranges.front().map_or(0, |pr| pr.largest + 1);
|
||||
qdebug!([self], "next in order pn: {}", next_in_order_pn);
|
||||
qdebug!("next in order pn: {}", next_in_order_pn);
|
||||
let i = self.add(pn);
|
||||
|
||||
// The new addition was the largest, so update the time we use for calculating ACK delay.
|
||||
|
@ -302,7 +237,7 @@ impl RecvdPackets {
|
|||
}
|
||||
|
||||
/// Check if the packet is a duplicate.
|
||||
pub fn is_duplicate(&self, pn: PacketNumber) -> bool {
|
||||
pub fn is_duplicate(&self, pn: u64) -> bool {
|
||||
if pn < self.min_tracked {
|
||||
return true;
|
||||
}
|
||||
|
@ -333,7 +268,7 @@ impl RecvdPackets {
|
|||
|
||||
impl ::std::fmt::Display for RecvdPackets {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
write!(f, "Recvd-{}", self.space)
|
||||
write!(f, "Recvd{:?}", self.space)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -368,9 +303,9 @@ impl AckTracker {
|
|||
pub(crate) fn get_frame(
|
||||
&mut self,
|
||||
now: Instant,
|
||||
pn_space: PNSpace,
|
||||
epoch: Epoch,
|
||||
) -> Option<(Frame, Option<RecoveryToken>)> {
|
||||
let space = &mut self[pn_space];
|
||||
let space = &mut self[PNSpace::from(epoch)];
|
||||
|
||||
// Check that we aren't delaying ACKs.
|
||||
if !space.ack_now(now) {
|
||||
|
@ -421,7 +356,7 @@ impl AckTracker {
|
|||
Some((
|
||||
ack,
|
||||
Some(RecoveryToken::Ack(AckToken {
|
||||
space: pn_space,
|
||||
space: PNSpace::from(epoch),
|
||||
ranges,
|
||||
})),
|
||||
))
|
||||
|
|
|
@ -6,13 +6,11 @@
|
|||
|
||||
// Tests with the test vectors from the spec.
|
||||
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
|
||||
#![warn(clippy::pedantic)]
|
||||
|
||||
use neqo_common::{Datagram, Encoder};
|
||||
use neqo_transport::State;
|
||||
use test_fixture::*;
|
||||
|
||||
const INITIAL_PACKET: &str = "c0ff000019088394c8f03e5157080000\
|
||||
const INITIAL_PACKET: &str = "c0ff000018088394c8f03e5157080000\
|
||||
449e3b343aa8535064a4268a0d9d7b1c\
|
||||
9d250ae355162276e9b1e3011ef6bbc0\
|
||||
ab48ad5bcc2681e953857ca62becd752\
|
||||
|
@ -86,7 +84,7 @@ const INITIAL_PACKET: &str = "c0ff000019088394c8f03e5157080000\
|
|||
d2bee680d8f41a597c262648bb18bcfc\
|
||||
13c8b3d97b1a77b2ac3af745d61a34cc\
|
||||
4709865bac824a94bb19058015e4e42d\
|
||||
aebe13f98ec51170a4aad0a8324bb768";
|
||||
0488c1b9a230f7c894193cbb54ae795e";
|
||||
|
||||
#[test]
|
||||
fn process_client_initial() {
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
// except according to those terms.
|
||||
|
||||
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
|
||||
#![warn(clippy::use_self)]
|
||||
|
||||
use neqo_common::Datagram;
|
||||
use neqo_transport::State;
|
||||
use test_fixture::{self, default_client, default_server, now};
|
||||
|
||||
#[test]
|
||||
|
@ -38,8 +38,8 @@ fn truncate_long_packet() {
|
|||
let dgram = client.process(None, now()).dgram();
|
||||
assert!(dgram.is_some());
|
||||
|
||||
assert!(client.state().connected());
|
||||
assert_eq!(*client.state(), State::Connected);
|
||||
let dgram = server.process(dgram, now()).dgram();
|
||||
assert!(dgram.is_some());
|
||||
assert!(server.state().connected());
|
||||
assert_eq!(*server.state(), State::Connected);
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
// except according to those terms.
|
||||
|
||||
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
|
||||
#![warn(clippy::pedantic)]
|
||||
|
||||
use neqo_common::{hex, matches, qdebug, qtrace, Datagram, Decoder, Encoder};
|
||||
use neqo_crypto::{
|
||||
|
@ -43,7 +42,7 @@ fn default_server() -> Server {
|
|||
fn connected_server(server: &mut Server) -> ActiveConnectionRef {
|
||||
let server_connections = server.active_connections();
|
||||
assert_eq!(server_connections.len(), 1);
|
||||
assert_eq!(*server_connections[0].borrow().state(), State::Confirmed);
|
||||
assert_eq!(*server_connections[0].borrow().state(), State::Connected);
|
||||
server_connections[0].clone()
|
||||
}
|
||||
|
||||
|
@ -68,13 +67,7 @@ fn connect(client: &mut Connection, server: &mut Server) -> ActiveConnectionRef
|
|||
assert!(dgram.is_some());
|
||||
assert_eq!(*client.state(), State::Connected);
|
||||
let dgram = server.process(dgram, now()).dgram();
|
||||
assert!(dgram.is_some()); // ACK + HANDSHAKE_DONE + NST
|
||||
|
||||
// Have the client process the HANDSHAKE_DONE.
|
||||
let dgram = client.process(dgram, now()).dgram();
|
||||
assert!(dgram.is_none());
|
||||
assert_eq!(*client.state(), State::Confirmed);
|
||||
|
||||
assert!(dgram.is_some()); // ACK + NST
|
||||
connected_server(server)
|
||||
}
|
||||
|
||||
|
@ -232,7 +225,7 @@ fn retry_after_initial() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn retry_bad_integrity() {
|
||||
fn retry_bad_odcid() {
|
||||
let mut server = default_server();
|
||||
server.set_retry_required(true);
|
||||
let mut client = default_client();
|
||||
|
@ -245,9 +238,17 @@ fn retry_bad_integrity() {
|
|||
let retry = &dgram.as_ref().unwrap();
|
||||
assertions::assert_retry(retry);
|
||||
|
||||
let mut tweaked = retry.to_vec();
|
||||
tweaked[retry.len() - 1] ^= 0x45; // damage the auth tag
|
||||
let tweaked_packet = Datagram::new(retry.source(), retry.destination(), tweaked);
|
||||
let mut dec = Decoder::new(retry); // Start after version.
|
||||
dec.skip(5);
|
||||
dec.skip_vec(1); // DCID
|
||||
dec.skip_vec(1); // SCID
|
||||
let odcid_len = dec.decode_byte().unwrap();
|
||||
assert_ne!(odcid_len, 0);
|
||||
let odcid_offset = retry.len() - dec.remaining();
|
||||
assert!(odcid_offset < retry.len());
|
||||
let mut tweaked_retry = retry[..].to_vec();
|
||||
tweaked_retry[odcid_offset] ^= 0x45; // damage the ODCID
|
||||
let tweaked_packet = Datagram::new(retry.source(), retry.destination(), tweaked_retry);
|
||||
|
||||
// The client should ignore this packet.
|
||||
let dgram = client.process(Some(tweaked_packet), now()).dgram();
|
||||
|
@ -318,7 +319,6 @@ fn client_initial_aead_and_hp(dcid: &[u8]) -> (Aead, HpKey) {
|
|||
// at least 8 bytes long. Otherwise, the second Initial won't have a
|
||||
// long enough connection ID.
|
||||
#[test]
|
||||
#[allow(clippy::shadow_unrelated)]
|
||||
fn mitm_retry() {
|
||||
let mut client = default_client();
|
||||
let mut retry_server = default_server();
|
||||
|
@ -493,12 +493,8 @@ fn closed() {
|
|||
let mut client = default_client();
|
||||
connect(&mut client, &mut server);
|
||||
|
||||
// The server will have sent a few things, so it will be on PTO.
|
||||
let res = server.process(None, now());
|
||||
assert!(res.callback() > Duration::new(0, 0));
|
||||
// The client will be on the delayed ACK timer.
|
||||
let res = client.process(None, now());
|
||||
assert!(res.callback() > Duration::new(0, 0));
|
||||
assert_eq!(res, Output::Callback(Duration::from_secs(60)));
|
||||
|
||||
qtrace!("60s later");
|
||||
let res = server.process(None, now() + Duration::from_secs(60));
|
||||
|
|
Загрузка…
Ссылка в новой задаче