зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1880110 - neqo 0.7.1, r=necko-reviewers,supply-chain-reviewers,jesup
Differential Revision: https://phabricator.services.mozilla.com/D201728
This commit is contained in:
Родитель
9f664c10ed
Коммит
737d9c536c
|
@ -20,11 +20,6 @@ git = "https://github.com/chris-zen/coremidi.git"
|
|||
rev = "fc68464b5445caf111e41f643a2e69ccce0b4f83"
|
||||
replace-with = "vendored-sources"
|
||||
|
||||
[source."git+https://github.com/cloudflare/quiche?rev=09ea4b244096a013071cfe2175bbf2945fb7f8d1"]
|
||||
git = "https://github.com/cloudflare/quiche"
|
||||
rev = "09ea4b244096a013071cfe2175bbf2945fb7f8d1"
|
||||
replace-with = "vendored-sources"
|
||||
|
||||
[source."git+https://github.com/franziskuskiefer/cose-rust?rev=43c22248d136c8b38fe42ea709d08da6355cf04b"]
|
||||
git = "https://github.com/franziskuskiefer/cose-rust"
|
||||
rev = "43c22248d136c8b38fe42ea709d08da6355cf04b"
|
||||
|
@ -90,9 +85,9 @@ git = "https://github.com/mozilla/mp4parse-rust"
|
|||
rev = "a138e40ec1c603615873e524b5b22e11c0ec4820"
|
||||
replace-with = "vendored-sources"
|
||||
|
||||
[source."git+https://github.com/mozilla/neqo?tag=v0.7.0"]
|
||||
[source."git+https://github.com/mozilla/neqo?tag=v0.7.1"]
|
||||
git = "https://github.com/mozilla/neqo"
|
||||
tag = "v0.7.0"
|
||||
tag = "v0.7.1"
|
||||
replace-with = "vendored-sources"
|
||||
|
||||
[source."git+https://github.com/mozilla/uniffi-rs.git?rev=afb29ebdc1d9edf15021b1c5332fc9f285bbe13b"]
|
||||
|
|
|
@ -3865,12 +3865,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "neqo-common"
|
||||
version = "0.7.0"
|
||||
source = "git+https://github.com/mozilla/neqo?tag=v0.7.0#9489511f7c82786f55bc9c713cddbff825507ed7"
|
||||
version = "0.7.1"
|
||||
source = "git+https://github.com/mozilla/neqo?tag=v0.7.1#721a1eff430f644e23a3e06ef4c5a248a0cbf592"
|
||||
dependencies = [
|
||||
"enum-map",
|
||||
"env_logger",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"qlog",
|
||||
"time 0.3.23",
|
||||
|
@ -3879,8 +3878,8 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "neqo-crypto"
|
||||
version = "0.7.0"
|
||||
source = "git+https://github.com/mozilla/neqo?tag=v0.7.0#9489511f7c82786f55bc9c713cddbff825507ed7"
|
||||
version = "0.7.1"
|
||||
source = "git+https://github.com/mozilla/neqo?tag=v0.7.1#721a1eff430f644e23a3e06ef4c5a248a0cbf592"
|
||||
dependencies = [
|
||||
"bindgen 0.69.4",
|
||||
"log",
|
||||
|
@ -3893,11 +3892,10 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "neqo-http3"
|
||||
version = "0.7.0"
|
||||
source = "git+https://github.com/mozilla/neqo?tag=v0.7.0#9489511f7c82786f55bc9c713cddbff825507ed7"
|
||||
version = "0.7.1"
|
||||
source = "git+https://github.com/mozilla/neqo?tag=v0.7.1#721a1eff430f644e23a3e06ef4c5a248a0cbf592"
|
||||
dependencies = [
|
||||
"enumset",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"neqo-common",
|
||||
"neqo-crypto",
|
||||
|
@ -3911,10 +3909,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "neqo-qpack"
|
||||
version = "0.7.0"
|
||||
source = "git+https://github.com/mozilla/neqo?tag=v0.7.0#9489511f7c82786f55bc9c713cddbff825507ed7"
|
||||
version = "0.7.1"
|
||||
source = "git+https://github.com/mozilla/neqo?tag=v0.7.1#721a1eff430f644e23a3e06ef4c5a248a0cbf592"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"log",
|
||||
"neqo-common",
|
||||
"neqo-crypto",
|
||||
|
@ -3925,11 +3922,10 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "neqo-transport"
|
||||
version = "0.7.0"
|
||||
source = "git+https://github.com/mozilla/neqo?tag=v0.7.0#9489511f7c82786f55bc9c713cddbff825507ed7"
|
||||
version = "0.7.1"
|
||||
source = "git+https://github.com/mozilla/neqo?tag=v0.7.1#721a1eff430f644e23a3e06ef4c5a248a0cbf592"
|
||||
dependencies = [
|
||||
"indexmap 1.9.3",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"neqo-common",
|
||||
"neqo-crypto",
|
||||
|
@ -4572,8 +4568,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "qlog"
|
||||
version = "0.11.0"
|
||||
source = "git+https://github.com/cloudflare/quiche?rev=09ea4b244096a013071cfe2175bbf2945fb7f8d1#09ea4b244096a013071cfe2175bbf2945fb7f8d1"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c0407438c69b3d99714a796a135cbfb2d60744e4747fb2b46a87acd1c1fcd0e"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_derive",
|
||||
|
|
|
@ -9,16 +9,16 @@ license = "MPL-2.0"
|
|||
name = "neqo_glue"
|
||||
|
||||
[dependencies]
|
||||
neqo-http3 = { tag = "v0.7.0", git = "https://github.com/mozilla/neqo" }
|
||||
neqo-transport = { tag = "v0.7.0", git = "https://github.com/mozilla/neqo" }
|
||||
neqo-common = { tag = "v0.7.0", git = "https://github.com/mozilla/neqo" }
|
||||
neqo-qpack = { tag = "v0.7.0", git = "https://github.com/mozilla/neqo" }
|
||||
neqo-http3 = { tag = "v0.7.1", git = "https://github.com/mozilla/neqo" }
|
||||
neqo-transport = { tag = "v0.7.1", git = "https://github.com/mozilla/neqo" }
|
||||
neqo-common = { tag = "v0.7.1", git = "https://github.com/mozilla/neqo" }
|
||||
neqo-qpack = { tag = "v0.7.1", 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.2.1", features = ["gecko-ffi"] }
|
||||
log = "0.4.0"
|
||||
qlog = { git = "https://github.com/cloudflare/quiche", rev = "09ea4b244096a013071cfe2175bbf2945fb7f8d1" }
|
||||
qlog = "0.12"
|
||||
libc = "0.2.0"
|
||||
static_prefs = { path = "../../../modules/libpref/init/static_prefs"}
|
||||
uuid = { version = "1.0", features = ["v4"] }
|
||||
|
@ -27,7 +27,7 @@ uuid = { version = "1.0", features = ["v4"] }
|
|||
winapi = {version = "0.3", features = ["ws2def"] }
|
||||
|
||||
[dependencies.neqo-crypto]
|
||||
tag = "v0.7.0"
|
||||
tag = "v0.7.1"
|
||||
git = "https://github.com/mozilla/neqo"
|
||||
default-features = false
|
||||
features = ["gecko"]
|
||||
|
|
|
@ -6,10 +6,10 @@ edition = "2018"
|
|||
license = "MPL-2.0"
|
||||
|
||||
[dependencies]
|
||||
neqo-transport = { tag = "v0.7.0", git = "https://github.com/mozilla/neqo" }
|
||||
neqo-common = { tag = "v0.7.0", git = "https://github.com/mozilla/neqo" }
|
||||
neqo-http3 = { tag = "v0.7.0", git = "https://github.com/mozilla/neqo" }
|
||||
neqo-qpack = { tag = "v0.7.0", git = "https://github.com/mozilla/neqo" }
|
||||
neqo-transport = { tag = "v0.7.1", git = "https://github.com/mozilla/neqo" }
|
||||
neqo-common = { tag = "v0.7.1", git = "https://github.com/mozilla/neqo" }
|
||||
neqo-http3 = { tag = "v0.7.1", git = "https://github.com/mozilla/neqo" }
|
||||
neqo-qpack = { tag = "v0.7.1", git = "https://github.com/mozilla/neqo" }
|
||||
mio = "0.6.17"
|
||||
mio-extras = "2.0.5"
|
||||
log = "0.4.0"
|
||||
|
@ -21,7 +21,7 @@ tokio = { version = "1", features = ["rt-multi-thread"] }
|
|||
mozilla-central-workspace-hack = { version = "0.1", features = ["http3server"], optional = true }
|
||||
|
||||
[dependencies.neqo-crypto]
|
||||
tag = "v0.7.0"
|
||||
tag = "v0.7.1"
|
||||
git = "https://github.com/mozilla/neqo"
|
||||
default-features = false
|
||||
features = ["gecko"]
|
||||
|
|
|
@ -3127,8 +3127,7 @@ delta = "0.9.0 -> 0.11.0"
|
|||
[[audits.qlog]]
|
||||
who = "Kershaw Chang <kershaw@mozilla.com>"
|
||||
criteria = "safe-to-deploy"
|
||||
delta = "0.11.0 -> 0.11.0@git:09ea4b244096a013071cfe2175bbf2945fb7f8d1"
|
||||
importable = false
|
||||
delta = "0.11.0 -> 0.12.0"
|
||||
|
||||
[[audits.quote]]
|
||||
who = "Nika Layzell <nika@thelayzells.com>"
|
||||
|
|
|
@ -165,10 +165,6 @@ notes = "This is a first-party crate which is entirely unrelated to the crates.i
|
|||
audit-as-crates-io = true
|
||||
notes = "This is a first-party crate which is also published to crates.io, but we should publish audits for it for the benefit of the ecosystem."
|
||||
|
||||
[policy.qlog]
|
||||
audit-as-crates-io = true
|
||||
notes = "Use this revision (09ea4b244096a013071cfe2175bbf2945fb7f8d1) of qlog temporarily."
|
||||
|
||||
[policy.rure]
|
||||
audit-as-crates-io = true
|
||||
notes = "Identical to upstream, but with cdylib and staticlib targets disabled to avoid unnecessary build artifacts and linker errors."
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"dbb5500f87df7aee6e680ac210ddb56b833aa82d6be5c407474de0895cee14e9","build.rs":"a17b1bb1bd3de3fc958f72d4d1357f7bc4432faa26640c95b5fbfccf40579d67","src/codec.rs":"8c14f09864b095e28ff52e7d96a12a6591fc9c4b20a9cafca6720d132c80efdc","src/datagram.rs":"1a7028d96a2e7385e94265de53189eb824b7cf12e0e2de5d67c3f3f8751b6043","src/event.rs":"4ef9e6f3f5168f2eacb7be982e062e743c64a64e809765d2139122839aa407e5","src/header.rs":"467b947f78bfe354d8bb51e8df0c2be69e75a45e2be688d81f0d268aa77c89ef","src/hrtime.rs":"d7c8849e9ec7a312878ea2bc28939717fa03969fb9aee259a4a516351ee37643","src/incrdecoder.rs":"577c32b9ace51f2daaf940be6d0c391c4f55cd42ef6848c68c1ffc970d8c57b5","src/lib.rs":"47c14084c6d475ebb855f3ed9302b31fa42780b93a816bf098c96987ffe33572","src/log.rs":"c68099eae0e9014be35173ac802165b128433d973390e1111c08df56e71df063","src/qlog.rs":"3f43dc4e5fdccb9d6ee74d9e7b3ff29da63e4eb9f631e4e35446e452d8ec7af6","src/timer.rs":"50a2de20933b7b5884337aded69e59e2523503481308f25de1bba1a11d505be8","src/tos.rs":"5b5a61c699266716afce2f5bda7c98151db3223ede41ce451c390863198e30a2","tests/log.rs":"480b165b7907ec642c508b303d63005eee1427115d6973a349eaf6b2242ed18d"},"package":null}
|
||||
{"files":{"Cargo.toml":"a5d8880c97023af584bb1116bf480ff957395e4e744c97aa52051b60b0bc5f80","build.rs":"a17b1bb1bd3de3fc958f72d4d1357f7bc4432faa26640c95b5fbfccf40579d67","src/codec.rs":"c652182a23ada920e55d6c73c5ede3ed3d01696db0b1826136585cfa926370b4","src/datagram.rs":"1a7028d96a2e7385e94265de53189eb824b7cf12e0e2de5d67c3f3f8751b6043","src/event.rs":"4ef9e6f3f5168f2eacb7be982e062e743c64a64e809765d2139122839aa407e5","src/header.rs":"467b947f78bfe354d8bb51e8df0c2be69e75a45e2be688d81f0d268aa77c89ef","src/hrtime.rs":"d7c8849e9ec7a312878ea2bc28939717fa03969fb9aee259a4a516351ee37643","src/incrdecoder.rs":"577c32b9ace51f2daaf940be6d0c391c4f55cd42ef6848c68c1ffc970d8c57b5","src/lib.rs":"47c14084c6d475ebb855f3ed9302b31fa42780b93a816bf098c96987ffe33572","src/log.rs":"8b8e12f4317892fd130c6d021a00d5125c535dea4c1b2c94c1a48d6a65b3aa84","src/qlog.rs":"3f43dc4e5fdccb9d6ee74d9e7b3ff29da63e4eb9f631e4e35446e452d8ec7af6","src/timer.rs":"d5d0e1ae7cc96f06fb2a8a53ef9236e11c4e1b0be2e9312f16c64e48c76acad2","src/tos.rs":"5b5a61c699266716afce2f5bda7c98151db3223ede41ce451c390863198e30a2","tests/log.rs":"480b165b7907ec642c508b303d63005eee1427115d6973a349eaf6b2242ed18d"},"package":null}
|
|
@ -11,16 +11,18 @@
|
|||
|
||||
[package]
|
||||
edition = "2018"
|
||||
rust-version = "1.70.0"
|
||||
rust-version = "1.74.0"
|
||||
name = "neqo-common"
|
||||
version = "0.7.0"
|
||||
authors = ["Bobby Holley <bobbyholley@gmail.com>"]
|
||||
version = "0.7.1"
|
||||
authors = ["The Neqo Authors <necko@mozilla.com>"]
|
||||
build = "build.rs"
|
||||
homepage = "https://github.com/mozilla/neqo/"
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/mozilla/neqo/"
|
||||
|
||||
[dependencies]
|
||||
enum-map = "2.7"
|
||||
lazy_static = "1.4"
|
||||
qlog = "0.12"
|
||||
|
||||
[dependencies.env_logger]
|
||||
version = "0.10"
|
||||
|
@ -30,12 +32,8 @@ default-features = false
|
|||
version = "0.4"
|
||||
default-features = false
|
||||
|
||||
[dependencies.qlog]
|
||||
git = "https://github.com/cloudflare/quiche"
|
||||
rev = "09ea4b244096a013071cfe2175bbf2945fb7f8d1"
|
||||
|
||||
[dependencies.time]
|
||||
version = "0.3.23"
|
||||
version = "0.3"
|
||||
features = ["formatting"]
|
||||
|
||||
[dev-dependencies.test-fixture]
|
||||
|
|
|
@ -112,9 +112,7 @@ impl<'a> Decoder<'a> {
|
|||
|
||||
/// Decodes a QUIC varint.
|
||||
pub fn decode_varint(&mut self) -> Option<u64> {
|
||||
let Some(b1) = self.decode_byte() else {
|
||||
return None;
|
||||
};
|
||||
let b1 = self.decode_byte()?;
|
||||
match b1 >> 6 {
|
||||
0 => Some(u64::from(b1 & 0x3f)),
|
||||
1 => Some((u64::from(b1 & 0x3f) << 8) | self.decode_uint(1)?),
|
||||
|
|
|
@ -6,10 +6,13 @@
|
|||
|
||||
#![allow(clippy::module_name_repetitions)]
|
||||
|
||||
use std::{io::Write, sync::Once, time::Instant};
|
||||
use std::{
|
||||
io::Write,
|
||||
sync::{Once, OnceLock},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use env_logger::Builder;
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! do_log {
|
||||
|
@ -42,17 +45,17 @@ macro_rules! log_subject {
|
|||
}};
|
||||
}
|
||||
|
||||
static INIT_ONCE: Once = Once::new();
|
||||
|
||||
lazy_static! {
|
||||
static ref START_TIME: Instant = Instant::now();
|
||||
fn since_start() -> Duration {
|
||||
static START_TIME: OnceLock<Instant> = OnceLock::new();
|
||||
START_TIME.get_or_init(Instant::now).elapsed()
|
||||
}
|
||||
|
||||
pub fn init() {
|
||||
static INIT_ONCE: Once = Once::new();
|
||||
INIT_ONCE.call_once(|| {
|
||||
let mut builder = Builder::from_env("RUST_LOG");
|
||||
builder.format(|buf, record| {
|
||||
let elapsed = START_TIME.elapsed();
|
||||
let elapsed = since_start();
|
||||
writeln!(
|
||||
buf,
|
||||
"{}s{:3}ms {} {}",
|
||||
|
|
|
@ -247,49 +247,50 @@ impl<T> Timer<T> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use lazy_static::lazy_static;
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use super::{Duration, Instant, Timer};
|
||||
|
||||
lazy_static! {
|
||||
static ref NOW: Instant = Instant::now();
|
||||
fn now() -> Instant {
|
||||
static NOW: OnceLock<Instant> = OnceLock::new();
|
||||
*NOW.get_or_init(Instant::now)
|
||||
}
|
||||
|
||||
const GRANULARITY: Duration = Duration::from_millis(10);
|
||||
const CAPACITY: usize = 10;
|
||||
#[test]
|
||||
fn create() {
|
||||
let t: Timer<()> = Timer::new(*NOW, GRANULARITY, CAPACITY);
|
||||
let t: Timer<()> = Timer::new(now(), GRANULARITY, CAPACITY);
|
||||
assert_eq!(t.span(), Duration::from_millis(100));
|
||||
assert_eq!(None, t.next_time());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn immediate_entry() {
|
||||
let mut t = Timer::new(*NOW, GRANULARITY, CAPACITY);
|
||||
t.add(*NOW, 12);
|
||||
assert_eq!(*NOW, t.next_time().expect("should have an entry"));
|
||||
let values: Vec<_> = t.take_until(*NOW).collect();
|
||||
let mut t = Timer::new(now(), GRANULARITY, CAPACITY);
|
||||
t.add(now(), 12);
|
||||
assert_eq!(now(), t.next_time().expect("should have an entry"));
|
||||
let values: Vec<_> = t.take_until(now()).collect();
|
||||
assert_eq!(vec![12], values);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn same_time() {
|
||||
let mut t = Timer::new(*NOW, GRANULARITY, CAPACITY);
|
||||
let mut t = Timer::new(now(), GRANULARITY, CAPACITY);
|
||||
let v1 = 12;
|
||||
let v2 = 13;
|
||||
t.add(*NOW, v1);
|
||||
t.add(*NOW, v2);
|
||||
assert_eq!(*NOW, t.next_time().expect("should have an entry"));
|
||||
let values: Vec<_> = t.take_until(*NOW).collect();
|
||||
t.add(now(), v1);
|
||||
t.add(now(), v2);
|
||||
assert_eq!(now(), t.next_time().expect("should have an entry"));
|
||||
let values: Vec<_> = t.take_until(now()).collect();
|
||||
assert!(values.contains(&v1));
|
||||
assert!(values.contains(&v2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add() {
|
||||
let mut t = Timer::new(*NOW, GRANULARITY, CAPACITY);
|
||||
let near_future = *NOW + Duration::from_millis(17);
|
||||
let mut t = Timer::new(now(), GRANULARITY, CAPACITY);
|
||||
let near_future = now() + Duration::from_millis(17);
|
||||
let v = 9;
|
||||
t.add(near_future, v);
|
||||
assert_eq!(near_future, t.next_time().expect("should return a value"));
|
||||
|
@ -305,8 +306,8 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn add_future() {
|
||||
let mut t = Timer::new(*NOW, GRANULARITY, CAPACITY);
|
||||
let future = *NOW + Duration::from_millis(117);
|
||||
let mut t = Timer::new(now(), GRANULARITY, CAPACITY);
|
||||
let future = now() + Duration::from_millis(117);
|
||||
let v = 9;
|
||||
t.add(future, v);
|
||||
assert_eq!(future, t.next_time().expect("should return a value"));
|
||||
|
@ -315,8 +316,8 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn add_far_future() {
|
||||
let mut t = Timer::new(*NOW, GRANULARITY, CAPACITY);
|
||||
let far_future = *NOW + Duration::from_millis(892);
|
||||
let mut t = Timer::new(now(), GRANULARITY, CAPACITY);
|
||||
let far_future = now() + Duration::from_millis(892);
|
||||
let v = 9;
|
||||
t.add(far_future, v);
|
||||
assert_eq!(far_future, t.next_time().expect("should return a value"));
|
||||
|
@ -333,12 +334,12 @@ mod test {
|
|||
];
|
||||
|
||||
fn with_times() -> Timer<usize> {
|
||||
let mut t = Timer::new(*NOW, GRANULARITY, CAPACITY);
|
||||
let mut t = Timer::new(now(), GRANULARITY, CAPACITY);
|
||||
for (i, time) in TIMES.iter().enumerate() {
|
||||
t.add(*NOW + *time, i);
|
||||
t.add(now() + *time, i);
|
||||
}
|
||||
assert_eq!(
|
||||
*NOW + *TIMES.iter().min().unwrap(),
|
||||
now() + *TIMES.iter().min().unwrap(),
|
||||
t.next_time().expect("should have a time")
|
||||
);
|
||||
t
|
||||
|
@ -348,7 +349,7 @@ mod test {
|
|||
#[allow(clippy::needless_collect)] // false positive
|
||||
fn multiple_values() {
|
||||
let mut t = with_times();
|
||||
let values: Vec<_> = t.take_until(*NOW + *TIMES.iter().max().unwrap()).collect();
|
||||
let values: Vec<_> = t.take_until(now() + *TIMES.iter().max().unwrap()).collect();
|
||||
for i in 0..TIMES.len() {
|
||||
assert!(values.contains(&i));
|
||||
}
|
||||
|
@ -358,7 +359,7 @@ mod test {
|
|||
#[allow(clippy::needless_collect)] // false positive
|
||||
fn take_far_future() {
|
||||
let mut t = with_times();
|
||||
let values: Vec<_> = t.take_until(*NOW + Duration::from_secs(100)).collect();
|
||||
let values: Vec<_> = t.take_until(now() + Duration::from_secs(100)).collect();
|
||||
for i in 0..TIMES.len() {
|
||||
assert!(values.contains(&i));
|
||||
}
|
||||
|
@ -368,15 +369,15 @@ mod test {
|
|||
fn remove_each() {
|
||||
let mut t = with_times();
|
||||
for (i, time) in TIMES.iter().enumerate() {
|
||||
assert_eq!(Some(i), t.remove(*NOW + *time, |&x| x == i));
|
||||
assert_eq!(Some(i), t.remove(now() + *time, |&x| x == i));
|
||||
}
|
||||
assert_eq!(None, t.next_time());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_future() {
|
||||
let mut t = Timer::new(*NOW, GRANULARITY, CAPACITY);
|
||||
let future = *NOW + Duration::from_millis(117);
|
||||
let mut t = Timer::new(now(), GRANULARITY, CAPACITY);
|
||||
let future = now() + Duration::from_millis(117);
|
||||
let v = 9;
|
||||
t.add(future, v);
|
||||
|
||||
|
@ -385,9 +386,9 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn remove_too_far_future() {
|
||||
let mut t = Timer::new(*NOW, GRANULARITY, CAPACITY);
|
||||
let future = *NOW + Duration::from_millis(117);
|
||||
let too_far_future = *NOW + t.span() + Duration::from_millis(117);
|
||||
let mut t = Timer::new(now(), GRANULARITY, CAPACITY);
|
||||
let future = now() + Duration::from_millis(117);
|
||||
let too_far_future = now() + t.span() + Duration::from_millis(117);
|
||||
let v = 9;
|
||||
t.add(future, v);
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"7f7348b55033e19bbe51b07ee50313c87237fe09b56b338af9ab24e00aab32c6","bindings/bindings.toml":"0660c1661318b8a5094834c2f1bb12266287ef467307f66947eff7762528f70a","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":"e712c16cb830a83eb4ea1f50dd341a4c30e1cce95d8c45af97030bc8ad0ae829","src/aead.rs":"b7cda4b89298cfd122cd2e1e94c462840e966c60f4832eb441106563ac332e00","src/aead_fuzzing.rs":"c3e590572314e0bb3fafa13dac3c831358b8a7b5570fe9cfe592752fce8cbdee","src/agent.rs":"c4fe47f9f5b0af20e3418da2e2ddce0ac2ca9665c0502115904f66a554e486ee","src/agentio.rs":"847ac63f6406e33bf20a861cadbfe6301ffa15bd73a5291298ffa93511b87dd5","src/auth.rs":"ced1a18f691894984244088020ea25dc1ee678603317f0c7dfc8b8842fa750b4","src/cert.rs":"6fc09012f994300ff4a7951bf8981aa266220521f58b8ff0989fee6dc1f27df9","src/constants.rs":"f22bf16bd8cb539862cb1e47138dbba79e93fe738f4b907e465891326f98883c","src/ech.rs":"58b7e0a1d2d52c59889cf8b735902577f7c3df93dfb89c72af2646b7aef29f39","src/err.rs":"fca0222167883231a5e0a569a593f44214501819adf5aadf814be27891c87c24","src/exp.rs":"cec59d61fc95914f9703d2fb6490a8507af993c9db710dde894f2f8fd38123c7","src/ext.rs":"c6ab9aefbbca531466dea938d853b1e42ed51816238afe400b20dbdb0111690b","src/hkdf.rs":"8e6cc5dce0f36efa4e13f5a24e2879bdbf10fb9a2b7dc8f13692e47d8959cdc8","src/hp.rs":"62ec073d99cf8bf3a123838c7d9b51bfdf68887148961f6307288e8dd56ac711","src/lib.rs":"40d9ac97c307c8161c2bf48156cc82377f81ad6e709f99cfd7dc0131dc192f86","src/once.rs":"b9850384899a1a016e839743d3489c0d4d916e1973746ef8c89872105d7d9736","src/p11.rs":"6c0f2f1b18e9bf9088a5ca5bdc99e789bb42234f7d2fe24d0b463bc957cb84a2","src/prio.rs":"e5e169296c0ac69919c59fb6c1f8bd6bf079452eaa13d75da0edd41d435d3f6f","src/replay.rs":"1ff4a12f6135ef2c42aef2b0947e26fd6241cd4b359020245608046452a7fcb0","src/result.rs":"0587cbb6aace71a7f9765ef7c01dcd9f73a49dcc6331e1d8fe4de2aef6ca65b6","src/secrets.rs":"4ffaa66f25df47dadf042063bff5953effa7bf2f4920cafe827757d6a659cb58","src/selfencrypt.rs":"4d2f4a6ea0fc94502130413ab5e2ea82612228f38a96a1865bf7d2b3f440620e","src/ssl.rs":"c83baa5518b81dd06f2e4072ea3c2d666ccdeb8b1ff6e3746eea9f1af47023a6","src/time.rs":"9204f3a384fb9dd2c3816c88666ad61ac3538f9e2f028954e81fd335a1479070","tests/aead.rs":"efdb92a060ca1957d890da1604513369559cb43195ee54149ed3ab47958dad59","tests/agent.rs":"0e55354595ae5f0e1ab83731564da57ba88a296e00692147c47df7067a0f416a","tests/ext.rs":"54657b45bd86d2561bb0f548736bc6f141bb664a5b043506f428422919ab95d4","tests/handshake.rs":"40701bc22f16d1ba9b9bd9683738e52b96faafee4119f7057437dae705f7867a","tests/hkdf.rs":"4160978b96505c1f1b7d6c4b5f43536ff7bd791c8746f9546c9fbc0fce5cf1c7","tests/hp.rs":"8eeee21a439e0f991145dff07b01283ae39ccd4b8dac4d011d43a464f73db670","tests/init.rs":"fc9e392b1efa0d8efb28952f73ffc05e5348e7b2b69207b60e375c3888a252a2","tests/selfencrypt.rs":"6edd0914b8466d79ecfb569c6d86995fd364b0dc71be2a0554e82f736ebd6b7c"},"package":null}
|
||||
{"files":{"Cargo.toml":"780c81b4f5abd5d90126084127f22b61fb82773c175db6f3c4ec9d97a6a01aa2","bindings/bindings.toml":"0660c1661318b8a5094834c2f1bb12266287ef467307f66947eff7762528f70a","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":"e712c16cb830a83eb4ea1f50dd341a4c30e1cce95d8c45af97030bc8ad0ae829","src/aead.rs":"b7cda4b89298cfd122cd2e1e94c462840e966c60f4832eb441106563ac332e00","src/aead_fuzzing.rs":"c3e590572314e0bb3fafa13dac3c831358b8a7b5570fe9cfe592752fce8cbdee","src/agent.rs":"f1f570913afb6f7b2cedebd69c387e1f80eb49f48feac05f8b06108c8ff66a8a","src/agentio.rs":"34342094ce2de9bea7cfee8c61ef314e44c0f1a127a73ea22df1a7b84a9c51e4","src/auth.rs":"ced1a18f691894984244088020ea25dc1ee678603317f0c7dfc8b8842fa750b4","src/cert.rs":"ee40428e9f49ff9eacc30be499cb512e1fd846620f270bd93a97802c80a0514a","src/constants.rs":"f22bf16bd8cb539862cb1e47138dbba79e93fe738f4b907e465891326f98883c","src/ech.rs":"d43ee1df69ae0864eb38f322c5c6b1f7d7ec5f2b3a9c88be875cebdb543c29d7","src/err.rs":"fca0222167883231a5e0a569a593f44214501819adf5aadf814be27891c87c24","src/exp.rs":"cec59d61fc95914f9703d2fb6490a8507af993c9db710dde894f2f8fd38123c7","src/ext.rs":"663f56ff1f2dd471117f22005ba4663be146858ee78165d9f684eafe213cd633","src/hkdf.rs":"364d9bdd57edf0e2e1ca38b5067e0c34ac6fda53f9182b77307708477ab785f9","src/hp.rs":"0c0196f2def1b8429915c6559219414a70ff7326177aa11c127c97e2fd09152e","src/lib.rs":"61ae448c8de034eb6fd2970b81b01a573849c9c15ec739c639c90880b1bbca74","src/p11.rs":"d31464782b44b3b4be4616b1224af40e27a1e0e8a37973aaeb509902b1d71679","src/prio.rs":"e5e169296c0ac69919c59fb6c1f8bd6bf079452eaa13d75da0edd41d435d3f6f","src/replay.rs":"1ff4a12f6135ef2c42aef2b0947e26fd6241cd4b359020245608046452a7fcb0","src/result.rs":"0587cbb6aace71a7f9765ef7c01dcd9f73a49dcc6331e1d8fe4de2aef6ca65b6","src/secrets.rs":"4ffaa66f25df47dadf042063bff5953effa7bf2f4920cafe827757d6a659cb58","src/selfencrypt.rs":"ac65b13f5bade9d03ab4709364f9ec937fa4ca009965c77ca73b481534a0a470","src/ssl.rs":"c83baa5518b81dd06f2e4072ea3c2d666ccdeb8b1ff6e3746eea9f1af47023a6","src/time.rs":"9a617965cd4d926368838e7af9536030f294071c0e1dd0f1d0c4831d29d3274c","tests/aead.rs":"efdb92a060ca1957d890da1604513369559cb43195ee54149ed3ab47958dad59","tests/agent.rs":"0e55354595ae5f0e1ab83731564da57ba88a296e00692147c47df7067a0f416a","tests/ext.rs":"54657b45bd86d2561bb0f548736bc6f141bb664a5b043506f428422919ab95d4","tests/handshake.rs":"40701bc22f16d1ba9b9bd9683738e52b96faafee4119f7057437dae705f7867a","tests/hkdf.rs":"4160978b96505c1f1b7d6c4b5f43536ff7bd791c8746f9546c9fbc0fce5cf1c7","tests/hp.rs":"8eeee21a439e0f991145dff07b01283ae39ccd4b8dac4d011d43a464f73db670","tests/init.rs":"fc9e392b1efa0d8efb28952f73ffc05e5348e7b2b69207b60e375c3888a252a2","tests/selfencrypt.rs":"6edd0914b8466d79ecfb569c6d86995fd364b0dc71be2a0554e82f736ebd6b7c"},"package":null}
|
|
@ -11,15 +11,17 @@
|
|||
|
||||
[package]
|
||||
edition = "2018"
|
||||
rust-version = "1.70.0"
|
||||
rust-version = "1.74.0"
|
||||
name = "neqo-crypto"
|
||||
version = "0.7.0"
|
||||
authors = ["Martin Thomson <mt@lowentropy.net>"]
|
||||
version = "0.7.1"
|
||||
authors = ["The Neqo Authors <necko@mozilla.com>"]
|
||||
build = "build.rs"
|
||||
homepage = "https://github.com/mozilla/neqo/"
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/mozilla/neqo/"
|
||||
|
||||
[dependencies.log]
|
||||
version = "~0.4.17"
|
||||
version = "0.4"
|
||||
default-features = false
|
||||
|
||||
[dependencies.neqo-common]
|
||||
|
@ -29,12 +31,12 @@ path = "../neqo-common"
|
|||
path = "../test-fixture"
|
||||
|
||||
[build-dependencies]
|
||||
serde = "1.0.195"
|
||||
serde_derive = "1.0.195"
|
||||
toml = "0.5.11"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
toml = "0.5"
|
||||
|
||||
[build-dependencies.bindgen]
|
||||
version = "0.69.1"
|
||||
version = "0.69"
|
||||
features = ["runtime"]
|
||||
default-features = false
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@ use crate::{
|
|||
ech,
|
||||
err::{is_blocked, secstatus_to_res, Error, PRErrorCode, Res},
|
||||
ext::{ExtensionHandler, ExtensionTracker},
|
||||
null_safe_slice,
|
||||
p11::{self, PrivateKey, PublicKey},
|
||||
prio,
|
||||
replay::AntiReplay,
|
||||
|
@ -897,7 +898,7 @@ impl Client {
|
|||
let resumption = arg.cast::<Vec<ResumptionToken>>().as_mut().unwrap();
|
||||
let len = usize::try_from(len).unwrap();
|
||||
let mut v = Vec::with_capacity(len);
|
||||
v.extend_from_slice(std::slice::from_raw_parts(token, len));
|
||||
v.extend_from_slice(null_safe_slice(token, len));
|
||||
qinfo!(
|
||||
[format!("{fd:p}")],
|
||||
"Got resumption token {}",
|
||||
|
@ -1105,11 +1106,7 @@ impl Server {
|
|||
}
|
||||
|
||||
let check_state = arg.cast::<ZeroRttCheckState>().as_mut().unwrap();
|
||||
let token = if client_token.is_null() {
|
||||
&[]
|
||||
} else {
|
||||
std::slice::from_raw_parts(client_token, usize::try_from(client_token_len).unwrap())
|
||||
};
|
||||
let token = null_safe_slice(client_token, usize::try_from(client_token_len).unwrap());
|
||||
match check_state.checker.check(token) {
|
||||
ZeroRttCheckResult::Accept => ssl::SSLHelloRetryRequestAction::ssl_hello_retry_accept,
|
||||
ZeroRttCheckResult::Fail => ssl::SSLHelloRetryRequestAction::ssl_hello_retry_fail,
|
||||
|
|
|
@ -20,7 +20,7 @@ use neqo_common::{hex, hex_with_len, qtrace};
|
|||
use crate::{
|
||||
constants::{ContentType, Epoch},
|
||||
err::{nspr, Error, PR_SetError, Res},
|
||||
prio, ssl,
|
||||
null_safe_slice, prio, ssl,
|
||||
};
|
||||
|
||||
// Alias common types.
|
||||
|
@ -100,7 +100,7 @@ impl RecordList {
|
|||
) -> ssl::SECStatus {
|
||||
let records = arg.cast::<Self>().as_mut().unwrap();
|
||||
|
||||
let slice = std::slice::from_raw_parts(data, len as usize);
|
||||
let slice = null_safe_slice(data, len);
|
||||
records.append(epoch, ContentType::try_from(ct).unwrap(), slice);
|
||||
ssl::SECSuccess
|
||||
}
|
||||
|
@ -178,6 +178,7 @@ impl AgentIoInput {
|
|||
return Err(Error::NoDataAvailable);
|
||||
}
|
||||
|
||||
#[allow(clippy::disallowed_methods)] // We just checked if this was empty.
|
||||
let src = unsafe { std::slice::from_raw_parts(self.input, amount) };
|
||||
qtrace!([self], "read {}", hex(src));
|
||||
let dst = unsafe { std::slice::from_raw_parts_mut(buf, amount) };
|
||||
|
@ -232,7 +233,7 @@ impl AgentIo {
|
|||
|
||||
// Stage output from TLS into the output buffer.
|
||||
fn save_output(&mut self, buf: *const u8, count: usize) {
|
||||
let slice = unsafe { std::slice::from_raw_parts(buf, count) };
|
||||
let slice = unsafe { null_safe_slice(buf, count) };
|
||||
qtrace!([self], "save output {}", hex(slice));
|
||||
self.output.extend_from_slice(slice);
|
||||
}
|
||||
|
|
|
@ -7,13 +7,13 @@
|
|||
use std::{
|
||||
convert::TryFrom,
|
||||
ptr::{addr_of, NonNull},
|
||||
slice,
|
||||
};
|
||||
|
||||
use neqo_common::qerror;
|
||||
|
||||
use crate::{
|
||||
err::secstatus_to_res,
|
||||
null_safe_slice,
|
||||
p11::{CERTCertListNode, CERT_GetCertificateDer, CertList, Item, SECItem, SECItemArray},
|
||||
ssl::{
|
||||
PRFileDesc, SSL_PeerCertificateChain, SSL_PeerSignedCertTimestamps,
|
||||
|
@ -52,7 +52,7 @@ fn stapled_ocsp_responses(fd: *mut PRFileDesc) -> Option<Vec<Vec<u8>>> {
|
|||
};
|
||||
for idx in 0..len {
|
||||
let itemp: *const SECItem = unsafe { ocsp_ptr.as_ref().items.offset(idx).cast() };
|
||||
let item = unsafe { slice::from_raw_parts((*itemp).data, (*itemp).len as usize) };
|
||||
let item = unsafe { null_safe_slice((*itemp).data, (*itemp).len) };
|
||||
ocsp_helper.push(item.to_owned());
|
||||
}
|
||||
Some(ocsp_helper)
|
||||
|
@ -68,9 +68,8 @@ fn signed_cert_timestamp(fd: *mut PRFileDesc) -> Option<Vec<u8>> {
|
|||
if unsafe { sct_ptr.as_ref().len == 0 || sct_ptr.as_ref().data.is_null() } {
|
||||
Some(Vec::new())
|
||||
} else {
|
||||
let sct_slice = unsafe {
|
||||
slice::from_raw_parts(sct_ptr.as_ref().data, sct_ptr.as_ref().len as usize)
|
||||
};
|
||||
let sct_slice =
|
||||
unsafe { null_safe_slice(sct_ptr.as_ref().data, sct_ptr.as_ref().len) };
|
||||
Some(sct_slice.to_owned())
|
||||
}
|
||||
}
|
||||
|
@ -105,7 +104,7 @@ impl<'a> Iterator for &'a mut CertificateInfo {
|
|||
let cert = unsafe { *self.cursor }.cert;
|
||||
secstatus_to_res(unsafe { CERT_GetCertificateDer(cert, &mut item) })
|
||||
.expect("getting DER from certificate should work");
|
||||
Some(unsafe { std::slice::from_raw_parts(item.data, item.len as usize) })
|
||||
Some(unsafe { null_safe_slice(item.data, item.len) })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ use neqo_common::qtrace;
|
|||
|
||||
use crate::{
|
||||
err::{ssl::SSL_ERROR_ECH_RETRY_WITH_ECH, Error, Res},
|
||||
experimental_api,
|
||||
experimental_api, null_safe_slice,
|
||||
p11::{
|
||||
self, Item, PrivateKey, PublicKey, SECITEM_FreeItem, SECItem, SECKEYPrivateKey,
|
||||
SECKEYPublicKey, Slot,
|
||||
|
@ -76,7 +76,7 @@ pub fn convert_ech_error(fd: *mut PRFileDesc, err: Error) -> Error {
|
|||
return Error::InternalError;
|
||||
}
|
||||
let buf = unsafe {
|
||||
let slc = std::slice::from_raw_parts(item.data, usize::try_from(item.len).unwrap());
|
||||
let slc = null_safe_slice(item.data, item.len);
|
||||
let buf = Vec::from(slc);
|
||||
SECITEM_FreeItem(&mut item, PRBool::from(false));
|
||||
buf
|
||||
|
@ -101,8 +101,7 @@ pub fn generate_keys() -> Res<(PrivateKey, PublicKey)> {
|
|||
|
||||
let oid_data = unsafe { p11::SECOID_FindOIDByTag(p11::SECOidTag::SEC_OID_CURVE25519) };
|
||||
let oid = unsafe { oid_data.as_ref() }.ok_or(Error::InternalError)?;
|
||||
let oid_slc =
|
||||
unsafe { std::slice::from_raw_parts(oid.oid.data, usize::try_from(oid.oid.len).unwrap()) };
|
||||
let oid_slc = unsafe { null_safe_slice(oid.oid.data, oid.oid.len) };
|
||||
let mut params: Vec<u8> = Vec::with_capacity(oid_slc.len() + 2);
|
||||
params.push(u8::try_from(p11::SEC_ASN1_OBJECT_ID).unwrap());
|
||||
params.push(u8::try_from(oid.oid.len).unwrap());
|
||||
|
@ -113,7 +112,6 @@ pub fn generate_keys() -> Res<(PrivateKey, PublicKey)> {
|
|||
|
||||
// If we have tracing on, try to ensure that key data can be read.
|
||||
let insensitive_secret_ptr = if log::log_enabled!(log::Level::Trace) {
|
||||
#[allow(clippy::useless_conversion)] // TODO: Remove when we bump the MSRV to 1.74.0.
|
||||
unsafe {
|
||||
p11::PK11_GenerateKeyPairWithOpFlags(
|
||||
*slot,
|
||||
|
@ -131,7 +129,6 @@ pub fn generate_keys() -> Res<(PrivateKey, PublicKey)> {
|
|||
};
|
||||
assert_eq!(insensitive_secret_ptr.is_null(), public_ptr.is_null());
|
||||
let secret_ptr = if insensitive_secret_ptr.is_null() {
|
||||
#[allow(clippy::useless_conversion)] // TODO: Remove when we bump the MSRV to 1.74.0.
|
||||
unsafe {
|
||||
p11::PK11_GenerateKeyPairWithOpFlags(
|
||||
*slot,
|
||||
|
|
|
@ -16,6 +16,7 @@ use crate::{
|
|||
agentio::as_c_void,
|
||||
constants::{Extension, HandshakeMessage, TLS_HS_CLIENT_HELLO, TLS_HS_ENCRYPTED_EXTENSIONS},
|
||||
err::Res,
|
||||
null_safe_slice,
|
||||
ssl::{
|
||||
PRBool, PRFileDesc, SECFailure, SECStatus, SECSuccess, SSLAlertDescription,
|
||||
SSLExtensionHandler, SSLExtensionWriter, SSLHandshakeType,
|
||||
|
@ -105,7 +106,7 @@ impl ExtensionTracker {
|
|||
alert: *mut SSLAlertDescription,
|
||||
arg: *mut c_void,
|
||||
) -> SECStatus {
|
||||
let d = std::slice::from_raw_parts(data, len as usize);
|
||||
let d = null_safe_slice(data, len);
|
||||
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
|
||||
Self::wrap_handler_call(arg, |handler| {
|
||||
// Cast is safe here because the message type is always part of the enum
|
||||
|
|
|
@ -17,9 +17,10 @@ use crate::{
|
|||
},
|
||||
err::{Error, Res},
|
||||
p11::{
|
||||
random, Item, PK11Origin, PK11SymKey, PK11_ImportDataKey, Slot, SymKey, CKA_DERIVE,
|
||||
Item, PK11Origin, PK11SymKey, PK11_ImportDataKey, Slot, SymKey, CKA_DERIVE,
|
||||
CKM_HKDF_DERIVE, CK_ATTRIBUTE_TYPE, CK_MECHANISM_TYPE,
|
||||
},
|
||||
random,
|
||||
};
|
||||
|
||||
experimental_api!(SSL_HkdfExtract(
|
||||
|
@ -40,24 +41,32 @@ experimental_api!(SSL_HkdfExpandLabel(
|
|||
secret: *mut *mut PK11SymKey,
|
||||
));
|
||||
|
||||
fn key_size(version: Version, cipher: Cipher) -> Res<usize> {
|
||||
const MAX_KEY_SIZE: usize = 48;
|
||||
const fn key_size(version: Version, cipher: Cipher) -> Res<usize> {
|
||||
if version != TLS_VERSION_1_3 {
|
||||
return Err(Error::UnsupportedVersion);
|
||||
}
|
||||
Ok(match cipher {
|
||||
let size = match cipher {
|
||||
TLS_AES_128_GCM_SHA256 | TLS_CHACHA20_POLY1305_SHA256 => 32,
|
||||
TLS_AES_256_GCM_SHA384 => 48,
|
||||
_ => return Err(Error::UnsupportedCipher),
|
||||
})
|
||||
};
|
||||
debug_assert!(size <= MAX_KEY_SIZE);
|
||||
Ok(size)
|
||||
}
|
||||
|
||||
/// Generate a random key of the right size for the given suite.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Only if NSS fails.
|
||||
/// If the ciphersuite or protocol version is not supported.
|
||||
pub fn generate_key(version: Version, cipher: Cipher) -> Res<SymKey> {
|
||||
import_key(version, &random(key_size(version, cipher)?))
|
||||
// With generic_const_expr, this becomes:
|
||||
// import_key(version, &random::<{ key_size(version, cipher) }>())
|
||||
import_key(
|
||||
version,
|
||||
&random::<MAX_KEY_SIZE>()[0..key_size(version, cipher)?],
|
||||
)
|
||||
}
|
||||
|
||||
/// Import a symmetric key for use with HKDF.
|
||||
|
@ -70,7 +79,6 @@ pub fn import_key(version: Version, buf: &[u8]) -> Res<SymKey> {
|
|||
return Err(Error::UnsupportedVersion);
|
||||
}
|
||||
let slot = Slot::internal()?;
|
||||
#[allow(clippy::useless_conversion)] // TODO: Remove when we bump the MSRV to 1.74.0.
|
||||
let key_ptr = unsafe {
|
||||
PK11_ImportDataKey(
|
||||
*slot,
|
||||
|
|
|
@ -76,7 +76,6 @@ impl HpKey {
|
|||
let l = label.as_bytes();
|
||||
let mut secret: *mut PK11SymKey = null_mut();
|
||||
|
||||
#[allow(clippy::useless_conversion)] // TODO: Remove when we bump the MSRV to 1.74.0.
|
||||
let (mech, key_size) = match cipher {
|
||||
TLS_AES_128_GCM_SHA256 => (CK_MECHANISM_TYPE::from(CKM_AES_ECB), 16),
|
||||
TLS_AES_256_GCM_SHA384 => (CK_MECHANISM_TYPE::from(CKM_AES_ECB), 32),
|
||||
|
@ -104,8 +103,6 @@ impl HpKey {
|
|||
|
||||
let res = match cipher {
|
||||
TLS_AES_128_GCM_SHA256 | TLS_AES_256_GCM_SHA384 => {
|
||||
// TODO: Remove when we bump the MSRV to 1.74.0.
|
||||
#[allow(clippy::useless_conversion)]
|
||||
let context_ptr = unsafe {
|
||||
PK11_CreateContextBySymKey(
|
||||
mech,
|
||||
|
@ -181,8 +178,6 @@ impl HpKey {
|
|||
};
|
||||
let mut output_len: c_uint = 0;
|
||||
let mut param_item = Item::wrap_struct(¶ms);
|
||||
// TODO: Remove when we bump the MSRV to 1.74.0.
|
||||
#[allow(clippy::useless_conversion)]
|
||||
secstatus_to_res(unsafe {
|
||||
PK11_Encrypt(
|
||||
**key,
|
||||
|
|
|
@ -27,7 +27,6 @@ mod exp;
|
|||
pub mod ext;
|
||||
pub mod hkdf;
|
||||
pub mod hp;
|
||||
mod once;
|
||||
#[macro_use]
|
||||
mod p11;
|
||||
mod prio;
|
||||
|
@ -38,9 +37,11 @@ mod ssl;
|
|||
mod time;
|
||||
|
||||
use std::{
|
||||
convert::TryFrom,
|
||||
ffi::CString,
|
||||
path::{Path, PathBuf},
|
||||
ptr::null,
|
||||
sync::OnceLock,
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "fuzzing"))]
|
||||
|
@ -49,7 +50,6 @@ pub use self::aead::RealAead as Aead;
|
|||
pub use self::aead::RealAead;
|
||||
#[cfg(feature = "fuzzing")]
|
||||
pub use self::aead_fuzzing::FuzzingAead as Aead;
|
||||
use self::once::OnceResult;
|
||||
pub use self::{
|
||||
agent::{
|
||||
Agent, AllowZeroRtt, Client, HandshakeState, Record, RecordList, ResumptionToken,
|
||||
|
@ -64,7 +64,7 @@ pub use self::{
|
|||
},
|
||||
err::{Error, PRErrorCode, Res},
|
||||
ext::{ExtensionHandler, ExtensionHandlerResult, ExtensionWriterResult},
|
||||
p11::{random, PrivateKey, PublicKey, SymKey},
|
||||
p11::{random, randomize, PrivateKey, PublicKey, SymKey},
|
||||
replay::AntiReplay,
|
||||
secrets::SecretDirection,
|
||||
ssl::Opt,
|
||||
|
@ -100,7 +100,7 @@ impl Drop for NssLoaded {
|
|||
}
|
||||
}
|
||||
|
||||
static mut INITIALIZED: OnceResult<NssLoaded> = OnceResult::new();
|
||||
static INITIALIZED: OnceLock<NssLoaded> = OnceLock::new();
|
||||
|
||||
fn already_initialized() -> bool {
|
||||
unsafe { nss::NSS_IsInitialized() != 0 }
|
||||
|
@ -124,19 +124,18 @@ fn version_check() {
|
|||
pub fn init() {
|
||||
// Set time zero.
|
||||
time::init();
|
||||
unsafe {
|
||||
INITIALIZED.call_once(|| {
|
||||
version_check();
|
||||
if already_initialized() {
|
||||
return NssLoaded::External;
|
||||
}
|
||||
_ = INITIALIZED.get_or_init(|| {
|
||||
version_check();
|
||||
if already_initialized() {
|
||||
return NssLoaded::External;
|
||||
}
|
||||
|
||||
secstatus_to_res(nss::NSS_NoDB_Init(null())).expect("NSS_NoDB_Init failed");
|
||||
secstatus_to_res(nss::NSS_SetDomesticPolicy()).expect("NSS_SetDomesticPolicy failed");
|
||||
secstatus_to_res(unsafe { nss::NSS_NoDB_Init(null()) }).expect("NSS_NoDB_Init failed");
|
||||
secstatus_to_res(unsafe { nss::NSS_SetDomesticPolicy() })
|
||||
.expect("NSS_SetDomesticPolicy failed");
|
||||
|
||||
NssLoaded::NoDb
|
||||
});
|
||||
}
|
||||
NssLoaded::NoDb
|
||||
});
|
||||
}
|
||||
|
||||
/// This enables SSLTRACE by calling a simple, harmless function to trigger its
|
||||
|
@ -158,51 +157,71 @@ fn enable_ssl_trace() {
|
|||
/// If NSS cannot be initialized.
|
||||
pub fn init_db<P: Into<PathBuf>>(dir: P) {
|
||||
time::init();
|
||||
unsafe {
|
||||
INITIALIZED.call_once(|| {
|
||||
version_check();
|
||||
if already_initialized() {
|
||||
return NssLoaded::External;
|
||||
}
|
||||
_ = INITIALIZED.get_or_init(|| {
|
||||
version_check();
|
||||
if already_initialized() {
|
||||
return NssLoaded::External;
|
||||
}
|
||||
|
||||
let path = dir.into();
|
||||
assert!(path.is_dir());
|
||||
let pathstr = path.to_str().expect("path converts to string").to_string();
|
||||
let dircstr = CString::new(pathstr).unwrap();
|
||||
let empty = CString::new("").unwrap();
|
||||
secstatus_to_res(nss::NSS_Initialize(
|
||||
let path = dir.into();
|
||||
assert!(path.is_dir());
|
||||
let pathstr = path.to_str().expect("path converts to string").to_string();
|
||||
let dircstr = CString::new(pathstr).unwrap();
|
||||
let empty = CString::new("").unwrap();
|
||||
secstatus_to_res(unsafe {
|
||||
nss::NSS_Initialize(
|
||||
dircstr.as_ptr(),
|
||||
empty.as_ptr(),
|
||||
empty.as_ptr(),
|
||||
nss::SECMOD_DB.as_ptr().cast(),
|
||||
nss::NSS_INIT_READONLY,
|
||||
))
|
||||
.expect("NSS_Initialize failed");
|
||||
)
|
||||
})
|
||||
.expect("NSS_Initialize failed");
|
||||
|
||||
secstatus_to_res(nss::NSS_SetDomesticPolicy()).expect("NSS_SetDomesticPolicy failed");
|
||||
secstatus_to_res(ssl::SSL_ConfigServerSessionIDCache(
|
||||
1024,
|
||||
0,
|
||||
0,
|
||||
dircstr.as_ptr(),
|
||||
))
|
||||
.expect("SSL_ConfigServerSessionIDCache failed");
|
||||
secstatus_to_res(unsafe { nss::NSS_SetDomesticPolicy() })
|
||||
.expect("NSS_SetDomesticPolicy failed");
|
||||
secstatus_to_res(unsafe {
|
||||
ssl::SSL_ConfigServerSessionIDCache(1024, 0, 0, dircstr.as_ptr())
|
||||
})
|
||||
.expect("SSL_ConfigServerSessionIDCache failed");
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
enable_ssl_trace();
|
||||
#[cfg(debug_assertions)]
|
||||
enable_ssl_trace();
|
||||
|
||||
NssLoaded::Db(path.into_boxed_path())
|
||||
});
|
||||
}
|
||||
NssLoaded::Db(path.into_boxed_path())
|
||||
});
|
||||
}
|
||||
|
||||
/// # Panics
|
||||
///
|
||||
/// If NSS isn't initialized.
|
||||
pub fn assert_initialized() {
|
||||
unsafe {
|
||||
INITIALIZED.call_once(|| {
|
||||
panic!("NSS not initialized with init or init_db");
|
||||
});
|
||||
INITIALIZED
|
||||
.get()
|
||||
.expect("NSS not initialized with init or init_db");
|
||||
}
|
||||
|
||||
/// NSS tends to return empty "slices" with a null pointer, which will cause
|
||||
/// `std::slice::from_raw_parts` to panic if passed directly. This wrapper avoids
|
||||
/// that issue. It also performs conversion for lengths, as a convenience.
|
||||
///
|
||||
/// # Panics
|
||||
/// If the provided length doesn't fit into a `usize`.
|
||||
///
|
||||
/// # Safety
|
||||
/// The caller must adhere to the safety constraints of `std::slice::from_raw_parts`,
|
||||
/// except that this will accept a null value for `data`.
|
||||
unsafe fn null_safe_slice<'a, T>(data: *const u8, len: T) -> &'a [u8]
|
||||
where
|
||||
usize: TryFrom<T>,
|
||||
{
|
||||
if data.is_null() {
|
||||
&[]
|
||||
} else if let Ok(len) = usize::try_from(len) {
|
||||
#[allow(clippy::disallowed_methods)]
|
||||
std::slice::from_raw_parts(data, len)
|
||||
} else {
|
||||
panic!("null_safe_slice: size overflow");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,44 +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.
|
||||
|
||||
use std::sync::Once;
|
||||
|
||||
#[allow(clippy::module_name_repetitions)]
|
||||
pub struct OnceResult<T> {
|
||||
once: Once,
|
||||
v: Option<T>,
|
||||
}
|
||||
|
||||
impl<T> OnceResult<T> {
|
||||
#[must_use]
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
once: Once::new(),
|
||||
v: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn call_once<F: FnOnce() -> T>(&mut self, f: F) -> &T {
|
||||
let v = &mut self.v;
|
||||
self.once.call_once(|| {
|
||||
*v = Some(f());
|
||||
});
|
||||
self.v.as_ref().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::OnceResult;
|
||||
|
||||
static mut STATIC_ONCE_RESULT: OnceResult<u64> = OnceResult::new();
|
||||
|
||||
#[test]
|
||||
fn static_update() {
|
||||
assert_eq!(*unsafe { STATIC_ONCE_RESULT.call_once(|| 23) }, 23);
|
||||
assert_eq!(*unsafe { STATIC_ONCE_RESULT.call_once(|| 24) }, 23);
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@
|
|||
#![allow(non_snake_case)]
|
||||
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
convert::TryFrom,
|
||||
mem,
|
||||
ops::{Deref, DerefMut},
|
||||
|
@ -19,7 +20,10 @@ use std::{
|
|||
|
||||
use neqo_common::hex_with_len;
|
||||
|
||||
use crate::err::{secstatus_to_res, Error, Res};
|
||||
use crate::{
|
||||
err::{secstatus_to_res, Error, Res},
|
||||
null_safe_slice,
|
||||
};
|
||||
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
#[allow(clippy::unreadable_literal)]
|
||||
|
@ -139,7 +143,6 @@ impl PrivateKey {
|
|||
/// When the values are too large to fit. So never.
|
||||
pub fn key_data(&self) -> Res<Vec<u8>> {
|
||||
let mut key_item = Item::make_empty();
|
||||
#[allow(clippy::useless_conversion)] // TODO: Remove when we bump the MSRV to 1.74.0.
|
||||
secstatus_to_res(unsafe {
|
||||
PK11_ReadRawAttribute(
|
||||
PK11ObjectType::PK11_TypePrivKey,
|
||||
|
@ -148,9 +151,7 @@ impl PrivateKey {
|
|||
&mut key_item,
|
||||
)
|
||||
})?;
|
||||
let slc = unsafe {
|
||||
std::slice::from_raw_parts(key_item.data, usize::try_from(key_item.len).unwrap())
|
||||
};
|
||||
let slc = unsafe { null_safe_slice(key_item.data, key_item.len) };
|
||||
let key = Vec::from(slc);
|
||||
// The data that `key_item` refers to needs to be freed, but we can't
|
||||
// use the scoped `Item` implementation. This is OK as long as nothing
|
||||
|
@ -206,7 +207,7 @@ impl SymKey {
|
|||
// This is accessing a value attached to the key, so we can treat this as a borrow.
|
||||
match unsafe { key_item.as_mut() } {
|
||||
None => Err(Error::InternalError),
|
||||
Some(key) => Ok(unsafe { std::slice::from_raw_parts(key.data, key.len as usize) }),
|
||||
Some(key) => Ok(unsafe { null_safe_slice(key.data, key.len) }),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -285,36 +286,112 @@ impl Item {
|
|||
let b = self.ptr.as_ref().unwrap();
|
||||
// Sanity check the type, as some types don't count bytes in `Item::len`.
|
||||
assert_eq!(b.type_, SECItemType::siBuffer);
|
||||
let slc = std::slice::from_raw_parts(b.data, usize::try_from(b.len).unwrap());
|
||||
let slc = null_safe_slice(b.data, b.len);
|
||||
Vec::from(slc)
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate a randomized buffer.
|
||||
/// Fill a buffer with randomness.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// When `size` is too large or NSS fails.
|
||||
pub fn randomize<B: AsMut<[u8]>>(mut buf: B) -> B {
|
||||
let m_buf = buf.as_mut();
|
||||
let len = c_int::try_from(m_buf.len()).unwrap();
|
||||
secstatus_to_res(unsafe { PK11_GenerateRandom(m_buf.as_mut_ptr(), len) }).unwrap();
|
||||
buf
|
||||
}
|
||||
|
||||
struct RandomCache {
|
||||
cache: [u8; Self::SIZE],
|
||||
used: usize,
|
||||
}
|
||||
|
||||
impl RandomCache {
|
||||
const SIZE: usize = 256;
|
||||
const CUTOFF: usize = 32;
|
||||
|
||||
fn new() -> Self {
|
||||
RandomCache {
|
||||
cache: [0; Self::SIZE],
|
||||
used: Self::SIZE,
|
||||
}
|
||||
}
|
||||
|
||||
fn randomize<B: AsMut<[u8]>>(&mut self, mut buf: B) -> B {
|
||||
let m_buf = buf.as_mut();
|
||||
debug_assert!(m_buf.len() <= Self::CUTOFF);
|
||||
let avail = Self::SIZE - self.used;
|
||||
if m_buf.len() <= avail {
|
||||
m_buf.copy_from_slice(&self.cache[self.used..self.used + m_buf.len()]);
|
||||
self.used += m_buf.len();
|
||||
} else {
|
||||
if avail > 0 {
|
||||
m_buf[..avail].copy_from_slice(&self.cache[self.used..]);
|
||||
}
|
||||
randomize(&mut self.cache[..]);
|
||||
self.used = m_buf.len() - avail;
|
||||
m_buf[avail..].copy_from_slice(&self.cache[..self.used]);
|
||||
}
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate a randomized array.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// When `size` is too large or NSS fails.
|
||||
#[must_use]
|
||||
pub fn random(size: usize) -> Vec<u8> {
|
||||
let mut buf = vec![0; size];
|
||||
secstatus_to_res(unsafe {
|
||||
PK11_GenerateRandom(buf.as_mut_ptr(), c_int::try_from(buf.len()).unwrap())
|
||||
})
|
||||
.unwrap();
|
||||
buf
|
||||
pub fn random<const N: usize>() -> [u8; N] {
|
||||
thread_local! { static CACHE: RefCell<RandomCache> = RefCell::new(RandomCache::new()) };
|
||||
|
||||
let buf = [0; N];
|
||||
if N <= RandomCache::CUTOFF {
|
||||
CACHE.with_borrow_mut(|c| c.randomize(buf))
|
||||
} else {
|
||||
randomize(buf)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use test_fixture::fixture_init;
|
||||
|
||||
use super::random;
|
||||
use super::RandomCache;
|
||||
use crate::{random, randomize};
|
||||
|
||||
#[test]
|
||||
fn randomness() {
|
||||
fixture_init();
|
||||
// If this ever fails, there is either a bug, or it's time to buy a lottery ticket.
|
||||
assert_ne!(random(16), random(16));
|
||||
// If any of these ever fail, there is either a bug, or it's time to buy a lottery ticket.
|
||||
assert_ne!(random::<16>(), randomize([0; 16]));
|
||||
assert_ne!([0; 16], random::<16>());
|
||||
assert_ne!([0; 64], random::<64>());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_random_lengths() {
|
||||
const ZERO: [u8; 256] = [0; 256];
|
||||
|
||||
fixture_init();
|
||||
let mut cache = RandomCache::new();
|
||||
let mut buf = [0; 256];
|
||||
let bits = usize::BITS - (RandomCache::CUTOFF - 1).leading_zeros();
|
||||
let mask = 0xff >> (u8::BITS - bits);
|
||||
|
||||
for _ in 0..100 {
|
||||
let len = loop {
|
||||
let len = usize::from(random::<1>()[0] & mask) + 1;
|
||||
if len <= RandomCache::CUTOFF {
|
||||
break len;
|
||||
}
|
||||
};
|
||||
buf.fill(0);
|
||||
if len >= 16 {
|
||||
assert_ne!(&cache.randomize(&mut buf[..len])[..len], &ZERO[..len]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -82,7 +82,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 cipher = self.make_aead(&self.key, &salt)?;
|
||||
let encoded_len = 2 + salt.len() + plaintext.len() + cipher.expansion();
|
||||
|
||||
|
|
|
@ -12,13 +12,13 @@ use std::{
|
|||
ops::Deref,
|
||||
os::raw::c_void,
|
||||
pin::Pin,
|
||||
sync::OnceLock,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
agentio::as_c_void,
|
||||
err::{Error, Res},
|
||||
once::OnceResult,
|
||||
ssl::{PRFileDesc, SSLTimeFunc},
|
||||
};
|
||||
|
||||
|
@ -67,14 +67,13 @@ impl TimeZero {
|
|||
}
|
||||
}
|
||||
|
||||
static mut BASE_TIME: OnceResult<TimeZero> = OnceResult::new();
|
||||
static BASE_TIME: OnceLock<TimeZero> = OnceLock::new();
|
||||
|
||||
fn get_base() -> &'static TimeZero {
|
||||
let f = || TimeZero {
|
||||
BASE_TIME.get_or_init(|| TimeZero {
|
||||
instant: Instant::now(),
|
||||
prtime: unsafe { PR_Now() },
|
||||
};
|
||||
unsafe { BASE_TIME.call_once(f) }
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn init() {
|
||||
|
@ -97,9 +96,8 @@ impl Deref for Time {
|
|||
impl From<Instant> for Time {
|
||||
/// Convert from an Instant into a Time.
|
||||
fn from(t: Instant) -> Self {
|
||||
// Call `TimeZero::baseline(t)` so that time zero can be set.
|
||||
let f = || TimeZero::baseline(t);
|
||||
_ = unsafe { BASE_TIME.call_once(f) };
|
||||
// Initialize `BASE_TIME` using `TimeZero::baseline(t)`.
|
||||
BASE_TIME.get_or_init(|| TimeZero::baseline(t));
|
||||
Self { t }
|
||||
}
|
||||
}
|
||||
|
@ -108,14 +106,17 @@ impl TryFrom<PRTime> for Time {
|
|||
type Error = Error;
|
||||
fn try_from(prtime: PRTime) -> Res<Self> {
|
||||
let base = get_base();
|
||||
if let Some(delta) = prtime.checked_sub(base.prtime) {
|
||||
let d = Duration::from_micros(delta.try_into()?);
|
||||
base.instant
|
||||
.checked_add(d)
|
||||
.map_or(Err(Error::TimeTravelError), |t| Ok(Self { t }))
|
||||
let delta = prtime
|
||||
.checked_sub(base.prtime)
|
||||
.ok_or(Error::TimeTravelError)?;
|
||||
let d = Duration::from_micros(u64::try_from(delta.abs())?);
|
||||
let t = if delta >= 0 {
|
||||
base.instant.checked_add(d)
|
||||
} else {
|
||||
Err(Error::TimeTravelError)
|
||||
}
|
||||
base.instant.checked_sub(d)
|
||||
};
|
||||
let t = t.ok_or(Error::TimeTravelError)?;
|
||||
Ok(Self { t })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -123,14 +124,21 @@ impl TryInto<PRTime> for Time {
|
|||
type Error = Error;
|
||||
fn try_into(self) -> Res<PRTime> {
|
||||
let base = get_base();
|
||||
let delta = self
|
||||
.t
|
||||
.checked_duration_since(base.instant)
|
||||
.ok_or(Error::TimeTravelError)?;
|
||||
if let Ok(d) = PRTime::try_from(delta.as_micros()) {
|
||||
d.checked_add(base.prtime).ok_or(Error::TimeTravelError)
|
||||
|
||||
if let Some(delta) = self.t.checked_duration_since(base.instant) {
|
||||
if let Ok(d) = PRTime::try_from(delta.as_micros()) {
|
||||
d.checked_add(base.prtime).ok_or(Error::TimeTravelError)
|
||||
} else {
|
||||
Err(Error::TimeTravelError)
|
||||
}
|
||||
} else {
|
||||
Err(Error::TimeTravelError)
|
||||
// Try to go backwards from the base time.
|
||||
let backwards = base.instant - self.t; // infallible
|
||||
if let Ok(d) = PRTime::try_from(backwards.as_micros()) {
|
||||
base.prtime.checked_sub(d).ok_or(Error::TimeTravelError)
|
||||
} else {
|
||||
Err(Error::TimeTravelError)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -228,16 +236,23 @@ mod test {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn past_time() {
|
||||
fn past_prtime() {
|
||||
const DELTA: Duration = Duration::from_secs(1);
|
||||
init();
|
||||
let base = get_base();
|
||||
assert!(Time::try_from(base.prtime - 1).is_err());
|
||||
let delta_micros = PRTime::try_from(DELTA.as_micros()).unwrap();
|
||||
println!("{} - {}", base.prtime, delta_micros);
|
||||
let t = Time::try_from(base.prtime - delta_micros).unwrap();
|
||||
assert_eq!(Instant::from(t) + DELTA, base.instant);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn negative_time() {
|
||||
fn past_instant() {
|
||||
const DELTA: Duration = Duration::from_secs(1);
|
||||
init();
|
||||
assert!(Time::try_from(-1).is_err());
|
||||
let base = get_base();
|
||||
let t = Time::from(base.instant.checked_sub(DELTA).unwrap());
|
||||
assert_eq!(Instant::from(t) + DELTA, base.instant);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"fe3c1114cfbb94004bf56740c0d373568cb459efdb12504e67f31923fbd436e1","src/buffered_send_stream.rs":"f45bdf9ad2a04b3828c74ff5440681d3c9d1af39b55470e4f729842dc2412295","src/client_events.rs":"e1392e7bbb62fb0505a4d8bcd27559699bbf38f3c94e7d8cae7291db82e6334c","src/conn_params.rs":"224a8ea6ef632930a7788a1cabf47ce69ad41bd4bc8dcf3053fbd998fdb38e82","src/connection.rs":"09aeb123f8dc6b903dd7d30579e5bb09ed8f70bfae563fb2fcc1871c67d604d4","src/connection_client.rs":"ed1c9ebf443f49dbf12c193953a71ec0e6b95555e1927afce813d2a8324758be","src/connection_server.rs":"ca33b50650bd1ca2a952851b72712d55ec2e48b48f1f06e4184c808b8e1e009a","src/control_stream_local.rs":"d6ecc0adc926e1d5cec9a378317f9dfcfeeb9840a0873a2afb380c2d252d8c54","src/control_stream_remote.rs":"59eb4041e366d92f9f294e8446755caa5e91fd943bba7b79b726698ba13be248","src/features/extended_connect/mod.rs":"3b02f6b18627f3855465a81b1d9b285e6f13839e75a8a6db648ed9082908d7f0","src/features/extended_connect/tests/mod.rs":"fd6aee37243713e80fc526552f21f0222338cec9890409b6575a2a637b17ec1f","src/features/extended_connect/tests/webtransport/datagrams.rs":"7e3bdd591b9c7d02f69954629f889d52bd54f13dbca11d555e614138c2a55107","src/features/extended_connect/tests/webtransport/mod.rs":"fed03f0ded21a9f17be5be99e4572e16dd0c8598e37044df3228990ea7fcc9f4","src/features/extended_connect/tests/webtransport/negotiation.rs":"98254ef8446581ec520026b04ef9549645602181b61602c9936f6660141edf0b","src/features/extended_connect/tests/webtransport/sessions.rs":"de3d836f666c2bec31e70b33bdc2669572cabbe17df2225db7282613a224a364","src/features/extended_connect/tests/webtransport/streams.rs":"8b3c34cac1b2171252a4bb53d420ac2098549a20309c327bf56e2e9ba9e33538","src/features/extended_connect/webtransport_session.rs":"a6472eca50a2d097aa6ba8a76b45ae69fe2edd2696b2953945faa3ce6e7417f9","src/features/extended_connect/webtransport_streams.rs":"a9a106eefc93a9f6e9e1c246df64904353de1c4fbcd394b338e6b117f6c677f5","src/features/mod.rs":"925aae4427ad82e4d019354802b223d53db5e5585d4a940f5417a24a9503d7ee","src/frames/hframe.rs":"726842108261c9af1e7576bc546e7bd7bea86fbef4a5804f4b45a2b4612e2679","src/frames/mod.rs":"7d0a46ca147336d14781edb8dbee8b03c2e4bcd6646f5473a9d93d31fe73fecb","src/frames/reader.rs":"4883e25064da1fb3a6ae46b5d15e6bcfec9c5bbea55a1937ecdb9465b62a93b2","src/frames/tests/hframe.rs":"01ec74eb3eb25d95042aa0263f9267f89535e6b7b8c1161fab4ba9ee5352d4a7","src/frames/tests/mod.rs":"0610609b316767a6a022837d32ee0452e37ea296fde37e51bec87e7c77e923a3","src/frames/tests/reader.rs":"2bfadc7afbc41bff9f5f930b31550259a8a92484d35f6c5d8dd8fd9acfb88f5b","src/frames/tests/wtframe.rs":"589ebe1e62ce4da63b37b7d22cde7ba572ddbf29336fdcdbbcd0a745f79dacd8","src/frames/wtframe.rs":"c80518d1569de277767c7ccb7441898aadbfc5fb2afb968c1d5105f8d175ccff","src/headers_checks.rs":"44891c16dda6b7ef742058ecb0a8d34e219c51cae1216c09c661cf72d9a5e7d5","src/lib.rs":"ed8da14e573cc5a97afb012a78af7f076eb83b5cc20cb4fe432eb7136a3ffe52","src/priority.rs":"10d9dcfcd4585f2ca256daf254c78a428297c41976c6548f19cd3ed2222b7cd2","src/push_controller.rs":"eb27c7c2a52c6108c0e4d040b021775a2b573f32d78b7ac8652ff46fd549f780","src/qlog.rs":"b1e6108b018abb077f218d1806e0a83370afa87709e26b3d51f482ae5d9b9c82","src/qpack_decoder_receiver.rs":"c927dfc3e58c71d282210ba79280f6f03e789733bc3bedc247e68bab516b9e9e","src/qpack_encoder_receiver.rs":"d0ac03cc111b6e1c555a8654d3234116f2b135b5b040edac23cefe2d640beba9","src/recv_message.rs":"06666c22101cda41de14682dc7e2e6721f2821bd45baefc22caceae4ccfcf2e0","src/request_target.rs":"6041a69a0a74969ec08bc164509c055e9bad99f53bbeb16c0aa17d108dd68b8c","src/send_message.rs":"70f8a91d85515f42a64a88bd2a9480175b12596bc082f77587cc5bcff9ce996c","src/server.rs":"ab6d4c80cb5f6c070f74d8df27e7bd62d5c8a8e7756ff9d1a31d3f9ff91327a1","src/server_connection_events.rs":"12d353ca6301467f6d475dde3b789951a5716c89ddd7dbf1383efef8082361f3","src/server_events.rs":"c96cff96d5893a9ab7165d17e3d1afaafc5492418b30003c1c26ca8f489ab7ca","src/settings.rs":"476b154b5eea4c8d69a4a790fee3e527cef4d375df1cfb5eed04ec56406fe15a","src/stream_type_reader.rs":"7a7226b7911d69f7e00ec4987c2a32a5e8a33463203398cbee1e6645d2691478","tests/httpconn.rs":"bb6927801a8c75e4f05eb6cdb1e7f2d57be69b74e68ddad2a1614f2aeed04369","tests/priority.rs":"3418be17fbdfdbcfd80dc4532f9365f405925442fabc916f2b22f90aee89629f","tests/send_message.rs":"1e893216d9252e6fb69a0fb291b4f8b8ea954847c346ff7f9347d7895618cabf","tests/webtransport.rs":"cb30d348c0ce05efb722abac3b1c524216fa4cbde8b62a1d1e3238c3fadecbe7"},"package":null}
|
||||
{"files":{"Cargo.toml":"d91d814a49a77d19195fbeaed449de6730802ad429b2f6a7e06524e599fcf0e2","src/buffered_send_stream.rs":"f45bdf9ad2a04b3828c74ff5440681d3c9d1af39b55470e4f729842dc2412295","src/client_events.rs":"e1392e7bbb62fb0505a4d8bcd27559699bbf38f3c94e7d8cae7291db82e6334c","src/conn_params.rs":"224a8ea6ef632930a7788a1cabf47ce69ad41bd4bc8dcf3053fbd998fdb38e82","src/connection.rs":"09aeb123f8dc6b903dd7d30579e5bb09ed8f70bfae563fb2fcc1871c67d604d4","src/connection_client.rs":"c38ebb402ce410e872866e4093f806f6c1bae25b1de320e22a0f99efb847ac5e","src/connection_server.rs":"ca33b50650bd1ca2a952851b72712d55ec2e48b48f1f06e4184c808b8e1e009a","src/control_stream_local.rs":"d6ecc0adc926e1d5cec9a378317f9dfcfeeb9840a0873a2afb380c2d252d8c54","src/control_stream_remote.rs":"59eb4041e366d92f9f294e8446755caa5e91fd943bba7b79b726698ba13be248","src/features/extended_connect/mod.rs":"3b02f6b18627f3855465a81b1d9b285e6f13839e75a8a6db648ed9082908d7f0","src/features/extended_connect/tests/mod.rs":"fd6aee37243713e80fc526552f21f0222338cec9890409b6575a2a637b17ec1f","src/features/extended_connect/tests/webtransport/datagrams.rs":"7e3bdd591b9c7d02f69954629f889d52bd54f13dbca11d555e614138c2a55107","src/features/extended_connect/tests/webtransport/mod.rs":"a30ea715f5271a826a739278b18e145964dedbce7026eed45f1b7d0355c407d5","src/features/extended_connect/tests/webtransport/negotiation.rs":"98254ef8446581ec520026b04ef9549645602181b61602c9936f6660141edf0b","src/features/extended_connect/tests/webtransport/sessions.rs":"de3d836f666c2bec31e70b33bdc2669572cabbe17df2225db7282613a224a364","src/features/extended_connect/tests/webtransport/streams.rs":"8b3c34cac1b2171252a4bb53d420ac2098549a20309c327bf56e2e9ba9e33538","src/features/extended_connect/webtransport_session.rs":"a6472eca50a2d097aa6ba8a76b45ae69fe2edd2696b2953945faa3ce6e7417f9","src/features/extended_connect/webtransport_streams.rs":"a9a106eefc93a9f6e9e1c246df64904353de1c4fbcd394b338e6b117f6c677f5","src/features/mod.rs":"925aae4427ad82e4d019354802b223d53db5e5585d4a940f5417a24a9503d7ee","src/frames/hframe.rs":"56c36ac597504f28c73cf2370acd82104f8c7a7b9ffc0f6d222378abc524482d","src/frames/mod.rs":"7d0a46ca147336d14781edb8dbee8b03c2e4bcd6646f5473a9d93d31fe73fecb","src/frames/reader.rs":"4883e25064da1fb3a6ae46b5d15e6bcfec9c5bbea55a1937ecdb9465b62a93b2","src/frames/tests/hframe.rs":"01ec74eb3eb25d95042aa0263f9267f89535e6b7b8c1161fab4ba9ee5352d4a7","src/frames/tests/mod.rs":"0610609b316767a6a022837d32ee0452e37ea296fde37e51bec87e7c77e923a3","src/frames/tests/reader.rs":"2bfadc7afbc41bff9f5f930b31550259a8a92484d35f6c5d8dd8fd9acfb88f5b","src/frames/tests/wtframe.rs":"589ebe1e62ce4da63b37b7d22cde7ba572ddbf29336fdcdbbcd0a745f79dacd8","src/frames/wtframe.rs":"c80518d1569de277767c7ccb7441898aadbfc5fb2afb968c1d5105f8d175ccff","src/headers_checks.rs":"44891c16dda6b7ef742058ecb0a8d34e219c51cae1216c09c661cf72d9a5e7d5","src/lib.rs":"ed8da14e573cc5a97afb012a78af7f076eb83b5cc20cb4fe432eb7136a3ffe52","src/priority.rs":"10d9dcfcd4585f2ca256daf254c78a428297c41976c6548f19cd3ed2222b7cd2","src/push_controller.rs":"eb27c7c2a52c6108c0e4d040b021775a2b573f32d78b7ac8652ff46fd549f780","src/qlog.rs":"b1e6108b018abb077f218d1806e0a83370afa87709e26b3d51f482ae5d9b9c82","src/qpack_decoder_receiver.rs":"c927dfc3e58c71d282210ba79280f6f03e789733bc3bedc247e68bab516b9e9e","src/qpack_encoder_receiver.rs":"d0ac03cc111b6e1c555a8654d3234116f2b135b5b040edac23cefe2d640beba9","src/recv_message.rs":"06666c22101cda41de14682dc7e2e6721f2821bd45baefc22caceae4ccfcf2e0","src/request_target.rs":"6041a69a0a74969ec08bc164509c055e9bad99f53bbeb16c0aa17d108dd68b8c","src/send_message.rs":"70f8a91d85515f42a64a88bd2a9480175b12596bc082f77587cc5bcff9ce996c","src/server.rs":"ab6d4c80cb5f6c070f74d8df27e7bd62d5c8a8e7756ff9d1a31d3f9ff91327a1","src/server_connection_events.rs":"12d353ca6301467f6d475dde3b789951a5716c89ddd7dbf1383efef8082361f3","src/server_events.rs":"c96cff96d5893a9ab7165d17e3d1afaafc5492418b30003c1c26ca8f489ab7ca","src/settings.rs":"476b154b5eea4c8d69a4a790fee3e527cef4d375df1cfb5eed04ec56406fe15a","src/stream_type_reader.rs":"7a7226b7911d69f7e00ec4987c2a32a5e8a33463203398cbee1e6645d2691478","tests/httpconn.rs":"bb6927801a8c75e4f05eb6cdb1e7f2d57be69b74e68ddad2a1614f2aeed04369","tests/priority.rs":"3418be17fbdfdbcfd80dc4532f9365f405925442fabc916f2b22f90aee89629f","tests/send_message.rs":"b5435045b16429d9e626ea94a8f10e2937e1a5a878af0035763a4f5ec09bf53c","tests/webtransport.rs":"25794305017ff58e57dc3c3b9b078e5bfc1814ea82a521b7b7156228e613c092"},"package":null}
|
|
@ -11,21 +11,23 @@
|
|||
|
||||
[package]
|
||||
edition = "2018"
|
||||
rust-version = "1.70.0"
|
||||
rust-version = "1.74.0"
|
||||
name = "neqo-http3"
|
||||
version = "0.7.0"
|
||||
authors = ["Dragana Damjanovic <dragana.damjano@gmail.com>"]
|
||||
version = "0.7.1"
|
||||
authors = ["The Neqo Authors <necko@mozilla.com>"]
|
||||
homepage = "https://github.com/mozilla/neqo/"
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/mozilla/neqo/"
|
||||
|
||||
[dependencies]
|
||||
enumset = "1.1.2"
|
||||
lazy_static = "1.4"
|
||||
sfv = "0.9.3"
|
||||
smallvec = "1.11.1"
|
||||
enumset = "1.1"
|
||||
qlog = "0.12"
|
||||
sfv = "0.9"
|
||||
smallvec = "1.11"
|
||||
url = "2.5"
|
||||
|
||||
[dependencies.log]
|
||||
version = "0.4.17"
|
||||
version = "0.4"
|
||||
default-features = false
|
||||
|
||||
[dependencies.neqo-common]
|
||||
|
@ -40,10 +42,6 @@ path = "./../neqo-qpack"
|
|||
[dependencies.neqo-transport]
|
||||
path = "./../neqo-transport"
|
||||
|
||||
[dependencies.qlog]
|
||||
git = "https://github.com/cloudflare/quiche"
|
||||
rev = "09ea4b244096a013071cfe2175bbf2945fb7f8d1"
|
||||
|
||||
[dev-dependencies.test-fixture]
|
||||
path = "../test-fixture"
|
||||
|
||||
|
|
|
@ -1306,8 +1306,9 @@ mod tests {
|
|||
StreamType, Version, RECV_BUFFER_SIZE, SEND_BUFFER_SIZE,
|
||||
};
|
||||
use test_fixture::{
|
||||
addr, anti_replay, default_server_h3, fixture_init, new_server, now,
|
||||
CountingConnectionIdGenerator, DEFAULT_ALPN_H3, DEFAULT_KEYS, DEFAULT_SERVER_NAME,
|
||||
anti_replay, default_server_h3, fixture_init, new_server, now,
|
||||
CountingConnectionIdGenerator, DEFAULT_ADDR, DEFAULT_ALPN_H3, DEFAULT_KEYS,
|
||||
DEFAULT_SERVER_NAME,
|
||||
};
|
||||
|
||||
use super::{
|
||||
|
@ -1340,8 +1341,8 @@ mod tests {
|
|||
Http3Client::new(
|
||||
DEFAULT_SERVER_NAME,
|
||||
Rc::new(RefCell::new(CountingConnectionIdGenerator::default())),
|
||||
addr(),
|
||||
addr(),
|
||||
DEFAULT_ADDR,
|
||||
DEFAULT_ADDR,
|
||||
Http3Parameters::default()
|
||||
.connection_parameters(
|
||||
// Disable compatible upgrade, which complicates tests.
|
||||
|
|
|
@ -14,7 +14,7 @@ use neqo_common::event::Provider;
|
|||
use neqo_crypto::AuthenticationStatus;
|
||||
use neqo_transport::{ConnectionParameters, StreamId, StreamType};
|
||||
use test_fixture::{
|
||||
addr, anti_replay, fixture_init, now, CountingConnectionIdGenerator, DEFAULT_ALPN_H3,
|
||||
anti_replay, fixture_init, now, CountingConnectionIdGenerator, DEFAULT_ADDR, DEFAULT_ALPN_H3,
|
||||
DEFAULT_KEYS, DEFAULT_SERVER_NAME,
|
||||
};
|
||||
|
||||
|
@ -38,8 +38,8 @@ pub fn default_http3_client(client_params: Http3Parameters) -> Http3Client {
|
|||
Http3Client::new(
|
||||
DEFAULT_SERVER_NAME,
|
||||
Rc::new(RefCell::new(CountingConnectionIdGenerator::default())),
|
||||
addr(),
|
||||
addr(),
|
||||
DEFAULT_ADDR,
|
||||
DEFAULT_ADDR,
|
||||
client_params,
|
||||
now(),
|
||||
)
|
||||
|
|
|
@ -74,10 +74,7 @@ impl HFrame {
|
|||
Self::MaxPushId { .. } => H3_FRAME_TYPE_MAX_PUSH_ID,
|
||||
Self::PriorityUpdateRequest { .. } => H3_FRAME_TYPE_PRIORITY_UPDATE_REQUEST,
|
||||
Self::PriorityUpdatePush { .. } => H3_FRAME_TYPE_PRIORITY_UPDATE_PUSH,
|
||||
Self::Grease => {
|
||||
let r = random(7);
|
||||
Decoder::from(&r).decode_uint(7).unwrap() * 0x1f + 0x21
|
||||
}
|
||||
Self::Grease => Decoder::from(&random::<7>()).decode_uint(7).unwrap() * 0x1f + 0x21,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -120,7 +117,7 @@ impl HFrame {
|
|||
}
|
||||
Self::Grease => {
|
||||
// Encode some number of random bytes.
|
||||
let r = random(8);
|
||||
let r = random::<8>();
|
||||
enc.encode_vvec(&r[1..usize::from(1 + (r[0] & 0x7))]);
|
||||
}
|
||||
Self::PriorityUpdateRequest {
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use neqo_common::event::Provider;
|
||||
use neqo_crypto::AuthenticationStatus;
|
||||
use neqo_http3::{
|
||||
|
@ -15,14 +16,14 @@ use test_fixture::*;
|
|||
|
||||
const RESPONSE_DATA: &[u8] = &[0x61, 0x62, 0x63];
|
||||
|
||||
lazy_static! {
|
||||
static ref RESPONSE_HEADER_NO_DATA: Vec<Header> =
|
||||
vec![Header::new(":status", "200"), Header::new("something", "3")];
|
||||
fn response_header_no_data() -> &'static Vec<Header> {
|
||||
static HEADERS: OnceLock<Vec<Header>> = OnceLock::new();
|
||||
HEADERS.get_or_init(|| vec![Header::new(":status", "200"), Header::new("something", "3")])
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref RESPONSE_HEADER_103: Vec<Header> =
|
||||
vec![Header::new(":status", "103"), Header::new("link", "...")];
|
||||
fn response_header_103() -> &'static Vec<Header> {
|
||||
static HEADERS: OnceLock<Vec<Header>> = OnceLock::new();
|
||||
HEADERS.get_or_init(|| vec![Header::new(":status", "103"), Header::new("link", "...")])
|
||||
}
|
||||
|
||||
fn exchange_packets(client: &mut Http3Client, server: &mut Http3Server) {
|
||||
|
@ -68,7 +69,7 @@ fn send_trailers(request: &mut Http3OrWebTransportStream) -> Result<(), Error> {
|
|||
}
|
||||
|
||||
fn send_informational_headers(request: &mut Http3OrWebTransportStream) -> Result<(), Error> {
|
||||
request.send_headers(&RESPONSE_HEADER_103)
|
||||
request.send_headers(response_header_103())
|
||||
}
|
||||
|
||||
fn send_headers(request: &mut Http3OrWebTransportStream) -> Result<(), Error> {
|
||||
|
@ -90,7 +91,7 @@ fn process_client_events(conn: &mut Http3Client) {
|
|||
Header::new(":status", "200"),
|
||||
Header::new("content-length", "3"),
|
||||
])
|
||||
|| (headers.as_ref() == *RESPONSE_HEADER_103)
|
||||
|| (headers.as_ref() == *response_header_103())
|
||||
);
|
||||
assert!(!fin);
|
||||
response_header_found = true;
|
||||
|
@ -116,7 +117,7 @@ fn process_client_events_no_data(conn: &mut Http3Client) {
|
|||
while let Some(event) = conn.next_event() {
|
||||
match event {
|
||||
Http3ClientEvent::HeaderReady { headers, fin, .. } => {
|
||||
assert_eq!(headers.as_ref(), *RESPONSE_HEADER_NO_DATA);
|
||||
assert_eq!(headers.as_ref(), *response_header_no_data());
|
||||
fin_received = fin;
|
||||
response_header_found = true;
|
||||
}
|
||||
|
@ -201,7 +202,7 @@ fn response_trailers3() {
|
|||
#[test]
|
||||
fn response_trailers_no_data() {
|
||||
let (mut hconn_c, mut hconn_s, mut request) = connect_send_and_receive_request();
|
||||
request.send_headers(&RESPONSE_HEADER_NO_DATA).unwrap();
|
||||
request.send_headers(response_header_no_data()).unwrap();
|
||||
exchange_packets(&mut hconn_c, &mut hconn_s);
|
||||
send_trailers(&mut request).unwrap();
|
||||
exchange_packets(&mut hconn_c, &mut hconn_s);
|
||||
|
@ -258,10 +259,10 @@ fn trailers_after_close() {
|
|||
#[test]
|
||||
fn multiple_response_headers() {
|
||||
let (mut hconn_c, mut hconn_s, mut request) = connect_send_and_receive_request();
|
||||
request.send_headers(&RESPONSE_HEADER_NO_DATA).unwrap();
|
||||
request.send_headers(response_header_no_data()).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
request.send_headers(&RESPONSE_HEADER_NO_DATA),
|
||||
request.send_headers(response_header_no_data()),
|
||||
Err(Error::InvalidHeader)
|
||||
);
|
||||
|
||||
|
@ -273,7 +274,7 @@ fn multiple_response_headers() {
|
|||
#[test]
|
||||
fn informational_after_response_headers() {
|
||||
let (mut hconn_c, mut hconn_s, mut request) = connect_send_and_receive_request();
|
||||
request.send_headers(&RESPONSE_HEADER_NO_DATA).unwrap();
|
||||
request.send_headers(response_header_no_data()).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
send_informational_headers(&mut request),
|
||||
|
@ -307,7 +308,7 @@ fn non_trailers_headers_after_data() {
|
|||
exchange_packets(&mut hconn_c, &mut hconn_s);
|
||||
|
||||
assert_eq!(
|
||||
request.send_headers(&RESPONSE_HEADER_NO_DATA),
|
||||
request.send_headers(response_header_no_data()),
|
||||
Err(Error::InvalidHeader)
|
||||
);
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ use neqo_http3::{
|
|||
};
|
||||
use neqo_transport::{StreamId, StreamType};
|
||||
use test_fixture::{
|
||||
addr, anti_replay, fixture_init, now, CountingConnectionIdGenerator, DEFAULT_ALPN_H3,
|
||||
anti_replay, fixture_init, now, CountingConnectionIdGenerator, DEFAULT_ADDR, DEFAULT_ALPN_H3,
|
||||
DEFAULT_KEYS, DEFAULT_SERVER_NAME,
|
||||
};
|
||||
|
||||
|
@ -24,8 +24,8 @@ fn connect() -> (Http3Client, Http3Server) {
|
|||
let mut client = Http3Client::new(
|
||||
DEFAULT_SERVER_NAME,
|
||||
Rc::new(RefCell::new(CountingConnectionIdGenerator::default())),
|
||||
addr(),
|
||||
addr(),
|
||||
DEFAULT_ADDR,
|
||||
DEFAULT_ADDR,
|
||||
Http3Parameters::default().webtransport(true),
|
||||
now(),
|
||||
)
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"2eabb2ad2846a08b51b306634ed75dc14ab3a43738b1190e3b4c4f2beb00b8e2","src/decoder.rs":"7e468d59adff1fa9373cbb703d13a7503f721a89bebafd049feaf0308a39b606","src/decoder_instructions.rs":"d991d70e51f079bc5b30d3982fd0176edfa9bb7ba14c17a20ec3eea878c56206","src/encoder.rs":"e026da38c2c3410a4e9aa330cda09ac411008772dd66d262d6c375601cebf481","src/encoder_instructions.rs":"86e3abbd9cf94332041326ac6cf806ed64623e3fd38dbc0385b1f63c37e73fd9","src/header_block.rs":"3925476df69b90d950594faadc5cb24c374d46de8c75a374a235f0d27323a7d8","src/huffman.rs":"8b0b2ea069c2a6eb6677b076b99b08ac0d29eabe1f2bbbab37f18f49187ef276","src/huffman_decode_helper.rs":"81309e27ff8f120a10c0b1598888ded21b76e297dc02cea8c7378d6a6627d62a","src/huffman_table.rs":"06fea766a6276ac56c7ee0326faed800a742c15fda1f33bf2513e6cc6a5e6d27","src/lib.rs":"196114397c2b1bf6ef154206018f519b012789cf712e89b069a7616d7278ef3a","src/prefix.rs":"fb4a9acbcf6fd3178f4474404cd3d3b131abca934f69fe14a9d744bc7e636dc5","src/qlog.rs":"e320007ea8309546b26f9c0019ab8722da80dbd38fa976233fd8ae19a0af637c","src/qpack_send_buf.rs":"14d71310c260ee15ea40a783998b507c968eef12db2892b47c689e872b5242a5","src/reader.rs":"b9a7dccd726f471fc24f1d3304f03ac0a039c0828aac7b33c927be07d395c655","src/static_table.rs":"fda9d5c6f38f94b0bf92d3afdf8432dce6e27e189736596e16727090c77b78ec","src/stats.rs":"624dfa3b40858c304097bb0ce5b1be1bb4d7916b1abfc222f1aa705907009730","src/table.rs":"05dbec6483bb24c9fc8d721b70fdfefc2df53b458488b55104147f13c386a47d"},"package":null}
|
||||
{"files":{"Cargo.toml":"385fb0450ea02925108585a1d4e48ad6ef304a5f3c5f4385ee5f8b829bc85393","src/decoder.rs":"7e468d59adff1fa9373cbb703d13a7503f721a89bebafd049feaf0308a39b606","src/decoder_instructions.rs":"d991d70e51f079bc5b30d3982fd0176edfa9bb7ba14c17a20ec3eea878c56206","src/encoder.rs":"e026da38c2c3410a4e9aa330cda09ac411008772dd66d262d6c375601cebf481","src/encoder_instructions.rs":"86e3abbd9cf94332041326ac6cf806ed64623e3fd38dbc0385b1f63c37e73fd9","src/header_block.rs":"3925476df69b90d950594faadc5cb24c374d46de8c75a374a235f0d27323a7d8","src/huffman.rs":"4b3308ed71da233181d99101bc2eeb30eca7ec30ba776f055f01e94f10974a24","src/huffman_decode_helper.rs":"e9d405f00327a124d1751481077c31fb6348daeee7032ea3b0fa5cf6d5d8add1","src/huffman_table.rs":"06fea766a6276ac56c7ee0326faed800a742c15fda1f33bf2513e6cc6a5e6d27","src/lib.rs":"196114397c2b1bf6ef154206018f519b012789cf712e89b069a7616d7278ef3a","src/prefix.rs":"fb4a9acbcf6fd3178f4474404cd3d3b131abca934f69fe14a9d744bc7e636dc5","src/qlog.rs":"e320007ea8309546b26f9c0019ab8722da80dbd38fa976233fd8ae19a0af637c","src/qpack_send_buf.rs":"14d71310c260ee15ea40a783998b507c968eef12db2892b47c689e872b5242a5","src/reader.rs":"b9a7dccd726f471fc24f1d3304f03ac0a039c0828aac7b33c927be07d395c655","src/static_table.rs":"fda9d5c6f38f94b0bf92d3afdf8432dce6e27e189736596e16727090c77b78ec","src/stats.rs":"624dfa3b40858c304097bb0ce5b1be1bb4d7916b1abfc222f1aa705907009730","src/table.rs":"05dbec6483bb24c9fc8d721b70fdfefc2df53b458488b55104147f13c386a47d"},"package":null}
|
|
@ -11,18 +11,20 @@
|
|||
|
||||
[package]
|
||||
edition = "2018"
|
||||
rust-version = "1.70.0"
|
||||
rust-version = "1.74.0"
|
||||
name = "neqo-qpack"
|
||||
version = "0.7.0"
|
||||
authors = ["Dragana Damjanovic <dragana.damjano@gmail.com>"]
|
||||
version = "0.7.1"
|
||||
authors = ["The Neqo Authors <necko@mozilla.com>"]
|
||||
homepage = "https://github.com/mozilla/neqo/"
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/mozilla/neqo/"
|
||||
|
||||
[dependencies]
|
||||
lazy_static = "~1.4.0"
|
||||
static_assertions = "~1.1.0"
|
||||
qlog = "0.12"
|
||||
static_assertions = "1.1"
|
||||
|
||||
[dependencies.log]
|
||||
version = "~0.4.17"
|
||||
version = "0.4"
|
||||
default-features = false
|
||||
|
||||
[dependencies.neqo-common]
|
||||
|
@ -34,10 +36,6 @@ path = "./../neqo-crypto"
|
|||
[dependencies.neqo-transport]
|
||||
path = "./../neqo-transport"
|
||||
|
||||
[dependencies.qlog]
|
||||
git = "https://github.com/cloudflare/quiche"
|
||||
rev = "09ea4b244096a013071cfe2175bbf2945fb7f8d1"
|
||||
|
||||
[dev-dependencies.test-fixture]
|
||||
path = "../test-fixture"
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
use std::convert::TryFrom;
|
||||
|
||||
use crate::{
|
||||
huffman_decode_helper::{HuffmanDecoderNode, HUFFMAN_DECODE_ROOT},
|
||||
huffman_decode_helper::{huffman_decoder_root, HuffmanDecoderNode},
|
||||
huffman_table::HUFFMAN_TABLE,
|
||||
Error, Res,
|
||||
};
|
||||
|
@ -93,7 +93,7 @@ pub fn decode_huffman(input: &[u8]) -> Res<Vec<u8>> {
|
|||
}
|
||||
|
||||
fn decode_character(reader: &mut BitReader) -> Res<Option<u16>> {
|
||||
let mut node: &HuffmanDecoderNode = &HUFFMAN_DECODE_ROOT;
|
||||
let mut node: &HuffmanDecoderNode = huffman_decoder_root();
|
||||
let mut i = 0;
|
||||
while node.value.is_none() {
|
||||
match reader.read_bit() {
|
||||
|
|
|
@ -4,9 +4,7 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
use std::{convert::TryFrom, sync::OnceLock};
|
||||
|
||||
use crate::huffman_table::HUFFMAN_TABLE;
|
||||
|
||||
|
@ -15,8 +13,9 @@ pub struct HuffmanDecoderNode {
|
|||
pub value: Option<u16>,
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
pub static ref HUFFMAN_DECODE_ROOT: HuffmanDecoderNode = make_huffman_tree(0, 0);
|
||||
pub fn huffman_decoder_root() -> &'static HuffmanDecoderNode {
|
||||
static ROOT: OnceLock<HuffmanDecoderNode> = OnceLock::new();
|
||||
ROOT.get_or_init(|| make_huffman_tree(0, 0))
|
||||
}
|
||||
|
||||
fn make_huffman_tree(prefix: u32, len: u8) -> HuffmanDecoderNode {
|
||||
|
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -11,27 +11,36 @@
|
|||
|
||||
[package]
|
||||
edition = "2018"
|
||||
rust-version = "1.70.0"
|
||||
rust-version = "1.74.0"
|
||||
name = "neqo-transport"
|
||||
version = "0.7.0"
|
||||
authors = [
|
||||
"EKR <ekr@rtfm.com>",
|
||||
"Andy Grover <agrover@mozilla.com>",
|
||||
]
|
||||
version = "0.7.1"
|
||||
authors = ["The Neqo Authors <necko@mozilla.com>"]
|
||||
homepage = "https://github.com/mozilla/neqo/"
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/mozilla/neqo/"
|
||||
|
||||
[[bench]]
|
||||
name = "transfer"
|
||||
harness = false
|
||||
required-features = ["bench"]
|
||||
|
||||
[[bench]]
|
||||
name = "rx_stream_orderer"
|
||||
harness = false
|
||||
required-features = ["bench"]
|
||||
|
||||
[[bench]]
|
||||
name = "range_tracker"
|
||||
harness = false
|
||||
required-features = ["bench"]
|
||||
|
||||
[dependencies]
|
||||
indexmap = "1.9.3"
|
||||
lazy_static = "1.4"
|
||||
smallvec = "1.11.1"
|
||||
indexmap = "1.9"
|
||||
qlog = "0.12"
|
||||
smallvec = "1.11"
|
||||
|
||||
[dependencies.log]
|
||||
version = "0.4.17"
|
||||
version = "0.4"
|
||||
default-features = false
|
||||
|
||||
[dependencies.neqo-common]
|
||||
|
@ -40,14 +49,13 @@ path = "../neqo-common"
|
|||
[dependencies.neqo-crypto]
|
||||
path = "../neqo-crypto"
|
||||
|
||||
[dependencies.qlog]
|
||||
git = "https://github.com/cloudflare/quiche"
|
||||
rev = "09ea4b244096a013071cfe2175bbf2945fb7f8d1"
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = "0.5.1"
|
||||
enum-map = "2.7"
|
||||
|
||||
[dev-dependencies.criterion]
|
||||
version = "0.5"
|
||||
features = ["html_reports"]
|
||||
|
||||
[dev-dependencies.test-fixture]
|
||||
path = "../test-fixture"
|
||||
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
use criterion::{criterion_group, criterion_main, Criterion}; // black_box
|
||||
use neqo_transport::send_stream::RangeTracker;
|
||||
|
||||
const CHUNK: u64 = 1000;
|
||||
const END: u64 = 100_000;
|
||||
fn build_coalesce(len: u64) -> RangeTracker {
|
||||
let mut used = RangeTracker::default();
|
||||
used.mark_acked(0, CHUNK as usize);
|
||||
used.mark_sent(CHUNK, END as usize);
|
||||
// leave a gap or it will coalesce here
|
||||
for i in 2..=len {
|
||||
// These do not get immediately coalesced when marking since they're not at the end or start
|
||||
used.mark_acked(i * CHUNK, CHUNK as usize);
|
||||
}
|
||||
used
|
||||
}
|
||||
|
||||
fn coalesce(c: &mut Criterion, count: u64) {
|
||||
c.bench_function(
|
||||
&format!("coalesce_acked_from_zero {count}+1 entries"),
|
||||
|b| {
|
||||
b.iter_batched_ref(
|
||||
|| build_coalesce(count),
|
||||
|used| {
|
||||
used.mark_acked(CHUNK, CHUNK as usize);
|
||||
let tail = (count + 1) * CHUNK;
|
||||
used.mark_sent(tail, CHUNK as usize);
|
||||
used.mark_acked(tail, CHUNK as usize);
|
||||
},
|
||||
criterion::BatchSize::SmallInput,
|
||||
)
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn benchmark_coalesce(c: &mut Criterion) {
|
||||
coalesce(c, 1);
|
||||
coalesce(c, 3);
|
||||
coalesce(c, 10);
|
||||
coalesce(c, 1000);
|
||||
}
|
||||
|
||||
criterion_group!(benches, benchmark_coalesce);
|
||||
criterion_main!(benches);
|
|
@ -0,0 +1,64 @@
|
|||
use std::time::Duration;
|
||||
|
||||
use criterion::{criterion_group, criterion_main, BatchSize::SmallInput, Criterion};
|
||||
use test_fixture::{
|
||||
boxed,
|
||||
sim::{
|
||||
connection::{ConnectionNode, ReceiveData, SendData},
|
||||
network::{Delay, TailDrop},
|
||||
Simulator,
|
||||
},
|
||||
};
|
||||
|
||||
const ZERO: Duration = Duration::from_millis(0);
|
||||
const JITTER: Duration = Duration::from_millis(10);
|
||||
const TRANSFER_AMOUNT: usize = 1 << 22; // 4Mbyte
|
||||
|
||||
fn benchmark_transfer(c: &mut Criterion, label: &str, seed: Option<impl AsRef<str>>) {
|
||||
c.bench_function(label, |b| {
|
||||
b.iter_batched(
|
||||
|| {
|
||||
let nodes = boxed![
|
||||
ConnectionNode::default_client(boxed![SendData::new(TRANSFER_AMOUNT)]),
|
||||
TailDrop::dsl_uplink(),
|
||||
Delay::new(ZERO..JITTER),
|
||||
ConnectionNode::default_server(boxed![ReceiveData::new(TRANSFER_AMOUNT)]),
|
||||
TailDrop::dsl_downlink(),
|
||||
Delay::new(ZERO..JITTER),
|
||||
];
|
||||
let mut sim = Simulator::new(label, nodes);
|
||||
if let Some(seed) = &seed {
|
||||
sim.seed_str(seed);
|
||||
}
|
||||
sim.setup()
|
||||
},
|
||||
|sim| {
|
||||
sim.run();
|
||||
},
|
||||
SmallInput,
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
fn benchmark_transfer_variable(c: &mut Criterion) {
|
||||
benchmark_transfer(
|
||||
c,
|
||||
"Run multiple transfers with varying seeds",
|
||||
std::env::var("SIMULATION_SEED").ok(),
|
||||
);
|
||||
}
|
||||
|
||||
fn benchmark_transfer_fixed(c: &mut Criterion) {
|
||||
benchmark_transfer(
|
||||
c,
|
||||
"Run multiple transfers with the same seed",
|
||||
Some("62df6933ba1f543cece01db8f27fb2025529b27f93df39e19f006e1db3b8c843"),
|
||||
);
|
||||
}
|
||||
|
||||
criterion_group! {
|
||||
name = transfer;
|
||||
config = Criterion::default().warm_up_time(Duration::from_secs(5)).measurement_time(Duration::from_secs(15));
|
||||
targets = benchmark_transfer_variable, benchmark_transfer_fixed
|
||||
}
|
||||
criterion_main!(transfer);
|
|
@ -16,8 +16,8 @@ use std::{
|
|||
};
|
||||
|
||||
use neqo_common::{hex, hex_with_len, qinfo, Decoder, Encoder};
|
||||
use neqo_crypto::random;
|
||||
use smallvec::SmallVec;
|
||||
use neqo_crypto::{random, randomize};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
|
||||
use crate::{
|
||||
frame::FRAME_TYPE_NEW_CONNECTION_ID, packet::PacketBuilder, recovery::RecoveryToken,
|
||||
|
@ -41,14 +41,16 @@ pub struct ConnectionId {
|
|||
impl ConnectionId {
|
||||
pub fn generate(len: usize) -> Self {
|
||||
assert!(matches!(len, 0..=MAX_CONNECTION_ID_LEN));
|
||||
Self::from(random(len))
|
||||
let mut cid = smallvec![0; len];
|
||||
randomize(&mut cid);
|
||||
Self { cid }
|
||||
}
|
||||
|
||||
// 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);
|
||||
let v = random::<1>()[0];
|
||||
// Bias selection toward picking 8 (>50% of the time).
|
||||
let len: usize = max(8, 5 + (v[0] & (v[0] >> 4))).into();
|
||||
let len: usize = max(8, 5 + (v & (v >> 4))).into();
|
||||
Self::generate(len)
|
||||
}
|
||||
|
||||
|
@ -75,12 +77,6 @@ impl From<SmallVec<[u8; MAX_CONNECTION_ID_LEN]>> for ConnectionId {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for ConnectionId {
|
||||
fn from(cid: Vec<u8>) -> Self {
|
||||
Self::from(SmallVec::from(cid))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsRef<[u8]> + ?Sized> From<&T> for ConnectionId {
|
||||
fn from(buf: &T) -> Self {
|
||||
Self::from(SmallVec::from(buf.as_ref()))
|
||||
|
@ -222,7 +218,9 @@ impl ConnectionIdDecoder for RandomConnectionIdGenerator {
|
|||
|
||||
impl ConnectionIdGenerator for RandomConnectionIdGenerator {
|
||||
fn generate_cid(&mut self) -> Option<ConnectionId> {
|
||||
Some(ConnectionId::from(&random(self.len)))
|
||||
let mut buf = smallvec![0; self.len];
|
||||
randomize(&mut buf);
|
||||
Some(ConnectionId::from(buf))
|
||||
}
|
||||
|
||||
fn as_decoder(&self) -> &dyn ConnectionIdDecoder {
|
||||
|
@ -250,8 +248,8 @@ pub struct ConnectionIdEntry<SRT: Clone + PartialEq> {
|
|||
impl ConnectionIdEntry<[u8; 16]> {
|
||||
/// Create a random stateless reset token so that it is hard to guess the correct
|
||||
/// value and reset the connection.
|
||||
fn random_srt() -> [u8; 16] {
|
||||
<[u8; 16]>::try_from(&random(16)[..]).unwrap()
|
||||
pub fn random_srt() -> [u8; 16] {
|
||||
random::<16>()
|
||||
}
|
||||
|
||||
/// Create the first entry, which won't have a stateless reset token.
|
||||
|
@ -476,7 +474,7 @@ impl ConnectionIdManager {
|
|||
.add_local(ConnectionIdEntry::new(self.next_seqno, cid.clone(), ()));
|
||||
self.next_seqno += 1;
|
||||
|
||||
let srt = <[u8; 16]>::try_from(&random(16)[..]).unwrap();
|
||||
let srt = ConnectionIdEntry::random_srt();
|
||||
Ok((cid, srt))
|
||||
} else {
|
||||
Err(Error::ConnectionIdsExhausted)
|
||||
|
@ -565,7 +563,7 @@ impl ConnectionIdManager {
|
|||
if let Some(cid) = maybe_cid {
|
||||
assert_ne!(cid.len(), 0);
|
||||
// TODO: generate the stateless reset tokens from the connection ID and a key.
|
||||
let srt = <[u8; 16]>::try_from(&random(16)[..]).unwrap();
|
||||
let srt = ConnectionIdEntry::random_srt();
|
||||
|
||||
let seqno = self.next_seqno;
|
||||
self.next_seqno += 1;
|
||||
|
@ -573,7 +571,7 @@ impl ConnectionIdManager {
|
|||
.add_local(ConnectionIdEntry::new(seqno, cid.clone(), ()));
|
||||
|
||||
let entry = ConnectionIdEntry::new(seqno, cid, srt);
|
||||
debug_assert!(self.write_entry(&entry, builder, stats)?);
|
||||
self.write_entry(&entry, builder, stats)?;
|
||||
tokens.push(RecoveryToken::NewConnectionId(entry));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ use neqo_common::{
|
|||
qlog::NeqoQlog, qtrace, qwarn, Datagram, Decoder, Encoder, Role,
|
||||
};
|
||||
use neqo_crypto::{
|
||||
agent::CertificateInfo, random, Agent, AntiReplay, AuthenticationStatus, Cipher, Client, Group,
|
||||
agent::CertificateInfo, Agent, AntiReplay, AuthenticationStatus, Cipher, Client, Group,
|
||||
HandshakeState, PrivateKey, PublicKey, ResumptionToken, SecretAgentInfo, SecretAgentPreInfo,
|
||||
Server, ZeroRttChecker,
|
||||
};
|
||||
|
@ -59,6 +59,7 @@ use crate::{
|
|||
version::{Version, WireVersion},
|
||||
AppError, ConnectionError, Error, Res, StreamId,
|
||||
};
|
||||
|
||||
mod dump;
|
||||
mod idle;
|
||||
pub mod params;
|
||||
|
@ -66,6 +67,7 @@ mod saved;
|
|||
mod state;
|
||||
#[cfg(test)]
|
||||
pub mod test_internal;
|
||||
|
||||
use dump::dump_packet;
|
||||
use idle::IdleTimeout;
|
||||
pub use params::ConnectionParameters;
|
||||
|
@ -78,9 +80,6 @@ pub use state::{ClosingFrame, State};
|
|||
|
||||
pub use crate::send_stream::{RetransmissionPriority, SendStreamStats, TransmissionPriority};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct Packet(Vec<u8>);
|
||||
|
||||
/// The number of Initial packets that the client will send in response
|
||||
/// to receiving an undecryptable packet during the early part of the
|
||||
/// handshake. This is a hack, but a useful one.
|
||||
|
@ -1576,7 +1575,6 @@ impl Connection {
|
|||
/// During connection setup, the first path needs to be setup.
|
||||
/// This uses the connection IDs that were provided during the handshake
|
||||
/// to setup that path.
|
||||
#[allow(clippy::or_fun_call)] // Remove when MSRV >= 1.59
|
||||
fn setup_handshake_path(&mut self, path: &PathRef, now: Instant) {
|
||||
self.paths.make_permanent(
|
||||
path,
|
||||
|
@ -2404,7 +2402,7 @@ impl Connection {
|
|||
} else {
|
||||
// The other side didn't provide a stateless reset token.
|
||||
// That's OK, they can try guessing this.
|
||||
<[u8; 16]>::try_from(&random(16)[..]).unwrap()
|
||||
ConnectionIdEntry::random_srt()
|
||||
};
|
||||
self.paths
|
||||
.primary()
|
||||
|
@ -2585,10 +2583,16 @@ impl Connection {
|
|||
) -> Res<()> {
|
||||
qtrace!([self], "Handshake space={} data={:0x?}", space, data);
|
||||
|
||||
let was_authentication_pending =
|
||||
*self.crypto.tls.state() == HandshakeState::AuthenticationPending;
|
||||
let try_update = data.is_some();
|
||||
match self.crypto.handshake(now, space, data)? {
|
||||
HandshakeState::Authenticated(_) | HandshakeState::InProgress => (),
|
||||
HandshakeState::AuthenticationPending => self.events.authentication_needed(),
|
||||
HandshakeState::AuthenticationPending => {
|
||||
if !was_authentication_pending {
|
||||
self.events.authentication_needed()
|
||||
}
|
||||
}
|
||||
HandshakeState::EchFallbackAuthenticationPending(public_name) => self
|
||||
.events
|
||||
.ech_fallback_authentication_needed(public_name.clone()),
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
use std::{mem, time::Duration};
|
||||
|
||||
use test_fixture::{addr_v4, assertions};
|
||||
use test_fixture::{assertions, DEFAULT_ADDR_V4};
|
||||
|
||||
use super::{
|
||||
super::{ConnectionParameters, ACK_RATIO_SCALE},
|
||||
|
@ -164,7 +164,7 @@ fn migrate_ack_delay() {
|
|||
let mut now = connect_rtt_idle(&mut client, &mut server, DEFAULT_RTT);
|
||||
|
||||
client
|
||||
.migrate(Some(addr_v4()), Some(addr_v4()), true, now)
|
||||
.migrate(Some(DEFAULT_ADDR_V4), Some(DEFAULT_ADDR_V4), true, now)
|
||||
.unwrap();
|
||||
|
||||
let client1 = send_something(&mut client, now);
|
||||
|
|
|
@ -18,8 +18,8 @@ use neqo_crypto::{
|
|||
constants::TLS_CHACHA20_POLY1305_SHA256, generate_ech_keys, AuthenticationStatus,
|
||||
};
|
||||
use test_fixture::{
|
||||
self, addr, assertions, assertions::assert_coalesced_0rtt, datagram, fixture_init, now,
|
||||
split_datagram,
|
||||
self, assertions, assertions::assert_coalesced_0rtt, datagram, fixture_init, now,
|
||||
split_datagram, DEFAULT_ADDR,
|
||||
};
|
||||
|
||||
use super::{
|
||||
|
@ -122,8 +122,8 @@ fn no_alpn() {
|
|||
"example.com",
|
||||
&["bad-alpn"],
|
||||
Rc::new(RefCell::new(CountingConnectionIdGenerator::default())),
|
||||
addr(),
|
||||
addr(),
|
||||
DEFAULT_ADDR,
|
||||
DEFAULT_ADDR,
|
||||
ConnectionParameters::default(),
|
||||
now(),
|
||||
)
|
||||
|
@ -251,8 +251,8 @@ fn chacha20poly1305() {
|
|||
test_fixture::DEFAULT_SERVER_NAME,
|
||||
test_fixture::DEFAULT_ALPN,
|
||||
Rc::new(RefCell::new(EmptyConnectionIdGenerator::default())),
|
||||
addr(),
|
||||
addr(),
|
||||
DEFAULT_ADDR,
|
||||
DEFAULT_ADDR,
|
||||
ConnectionParameters::default(),
|
||||
now(),
|
||||
)
|
||||
|
@ -730,8 +730,8 @@ fn connect_one_version() {
|
|||
test_fixture::DEFAULT_SERVER_NAME,
|
||||
test_fixture::DEFAULT_ALPN,
|
||||
Rc::new(RefCell::new(CountingConnectionIdGenerator::default())),
|
||||
addr(),
|
||||
addr(),
|
||||
DEFAULT_ADDR,
|
||||
DEFAULT_ADDR,
|
||||
ConnectionParameters::default().versions(version, vec![version]),
|
||||
now(),
|
||||
)
|
||||
|
@ -1135,3 +1135,54 @@ fn implicit_rtt_server() {
|
|||
// an RTT estimate from having discarded the Initial packet number space.
|
||||
assert_eq!(server.stats().rtt, RTT);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn emit_authentication_needed_once() {
|
||||
let mut client = default_client();
|
||||
|
||||
let mut server = Connection::new_server(
|
||||
test_fixture::LONG_CERT_KEYS,
|
||||
test_fixture::DEFAULT_ALPN,
|
||||
Rc::new(RefCell::new(CountingConnectionIdGenerator::default())),
|
||||
ConnectionParameters::default(),
|
||||
)
|
||||
.expect("create a server");
|
||||
|
||||
let client1 = client.process(None, now());
|
||||
assert!(client1.as_dgram_ref().is_some());
|
||||
|
||||
// The entire server flight doesn't fit in a single packet because the
|
||||
// certificate is large, therefore the server will produce 2 packets.
|
||||
let server1 = server.process(client1.as_dgram_ref(), now());
|
||||
assert!(server1.as_dgram_ref().is_some());
|
||||
let server2 = server.process(None, now());
|
||||
assert!(server2.as_dgram_ref().is_some());
|
||||
|
||||
let authentication_needed_count = |client: &mut Connection| {
|
||||
client
|
||||
.events()
|
||||
.filter(|e| matches!(e, ConnectionEvent::AuthenticationNeeded))
|
||||
.count()
|
||||
};
|
||||
|
||||
// Upon receiving the first packet, the client has the server certificate,
|
||||
// but not yet all required handshake data. It moves to
|
||||
// `HandshakeState::AuthenticationPending` and emits a
|
||||
// `ConnectionEvent::AuthenticationNeeded` event.
|
||||
//
|
||||
// Note that this is a tiny bit fragile in that it depends on having a certificate
|
||||
// that is within a fairly narrow range of sizes. It has to fit in a single
|
||||
// packet, but be large enough that the CertificateVerify message does not
|
||||
// also fit in the same packet. Our default test setup achieves this, but
|
||||
// changes to the setup might invalidate this test.
|
||||
let _ = client.process(server1.as_dgram_ref(), now());
|
||||
assert_eq!(1, authentication_needed_count(&mut client));
|
||||
assert!(client.peer_certificate().is_some());
|
||||
|
||||
// The `AuthenticationNeeded` event is still pending a call to
|
||||
// `Connection::authenticated`. On receiving the second packet from the
|
||||
// server, the client must not emit a another
|
||||
// `ConnectionEvent::AuthenticationNeeded`.
|
||||
let _ = client.process(server2.as_dgram_ref(), now());
|
||||
assert_eq!(0, authentication_needed_count(&mut client));
|
||||
}
|
||||
|
|
|
@ -13,9 +13,9 @@ use std::{
|
|||
|
||||
use neqo_common::{Datagram, Decoder};
|
||||
use test_fixture::{
|
||||
self, addr, addr_v4,
|
||||
self,
|
||||
assertions::{assert_v4_path, assert_v6_path},
|
||||
fixture_init, new_neqo_qlog, now,
|
||||
fixture_init, new_neqo_qlog, now, DEFAULT_ADDR, DEFAULT_ADDR_V4,
|
||||
};
|
||||
|
||||
use super::{
|
||||
|
@ -94,8 +94,8 @@ fn rebinding_port() {
|
|||
server.stream_close_send(stream_id).unwrap();
|
||||
let dgram = server.process_output(now()).dgram();
|
||||
let dgram = dgram.unwrap();
|
||||
assert_eq!(dgram.source(), addr());
|
||||
assert_eq!(dgram.destination(), new_port(addr()));
|
||||
assert_eq!(dgram.source(), DEFAULT_ADDR);
|
||||
assert_eq!(dgram.destination(), new_port(DEFAULT_ADDR));
|
||||
}
|
||||
|
||||
/// This simulates an attack where a valid packet is forwarded on
|
||||
|
@ -109,7 +109,7 @@ fn path_forwarding_attack() {
|
|||
let mut now = now();
|
||||
|
||||
let dgram = send_something(&mut client, now);
|
||||
let dgram = change_path(&dgram, addr_v4());
|
||||
let dgram = change_path(&dgram, DEFAULT_ADDR_V4);
|
||||
server.process_input(&dgram, now);
|
||||
|
||||
// The server now probes the new (primary) path.
|
||||
|
@ -188,7 +188,7 @@ fn migrate_immediate() {
|
|||
let now = now();
|
||||
|
||||
client
|
||||
.migrate(Some(addr_v4()), Some(addr_v4()), true, now)
|
||||
.migrate(Some(DEFAULT_ADDR_V4), Some(DEFAULT_ADDR_V4), true, now)
|
||||
.unwrap();
|
||||
|
||||
let client1 = send_something(&mut client, now);
|
||||
|
@ -229,7 +229,7 @@ fn migrate_rtt() {
|
|||
let now = connect_rtt_idle(&mut client, &mut server, RTT);
|
||||
|
||||
client
|
||||
.migrate(Some(addr_v4()), Some(addr_v4()), true, now)
|
||||
.migrate(Some(DEFAULT_ADDR_V4), Some(DEFAULT_ADDR_V4), true, now)
|
||||
.unwrap();
|
||||
// The RTT might be increased for the new path, so allow a little flexibility.
|
||||
let rtt = client.paths.rtt();
|
||||
|
@ -245,7 +245,7 @@ fn migrate_immediate_fail() {
|
|||
let mut now = now();
|
||||
|
||||
client
|
||||
.migrate(Some(addr_v4()), Some(addr_v4()), true, now)
|
||||
.migrate(Some(DEFAULT_ADDR_V4), Some(DEFAULT_ADDR_V4), true, now)
|
||||
.unwrap();
|
||||
|
||||
let probe = client.process_output(now).dgram().unwrap();
|
||||
|
@ -293,7 +293,7 @@ fn migrate_same() {
|
|||
let now = now();
|
||||
|
||||
client
|
||||
.migrate(Some(addr()), Some(addr()), true, now)
|
||||
.migrate(Some(DEFAULT_ADDR), Some(DEFAULT_ADDR), true, now)
|
||||
.unwrap();
|
||||
|
||||
let probe = client.process_output(now).dgram().unwrap();
|
||||
|
@ -320,7 +320,7 @@ fn migrate_same_fail() {
|
|||
let mut now = now();
|
||||
|
||||
client
|
||||
.migrate(Some(addr()), Some(addr()), true, now)
|
||||
.migrate(Some(DEFAULT_ADDR), Some(DEFAULT_ADDR), true, now)
|
||||
.unwrap();
|
||||
|
||||
let probe = client.process_output(now).dgram().unwrap();
|
||||
|
@ -375,7 +375,7 @@ fn migration(mut client: Connection) {
|
|||
let now = now();
|
||||
|
||||
client
|
||||
.migrate(Some(addr_v4()), Some(addr_v4()), false, now)
|
||||
.migrate(Some(DEFAULT_ADDR_V4), Some(DEFAULT_ADDR_V4), false, now)
|
||||
.unwrap();
|
||||
|
||||
let probe = client.process_output(now).dgram().unwrap();
|
||||
|
@ -449,8 +449,8 @@ fn migration_client_empty_cid() {
|
|||
test_fixture::DEFAULT_SERVER_NAME,
|
||||
test_fixture::DEFAULT_ALPN,
|
||||
Rc::new(RefCell::new(EmptyConnectionIdGenerator::default())),
|
||||
addr(),
|
||||
addr(),
|
||||
DEFAULT_ADDR,
|
||||
DEFAULT_ADDR,
|
||||
ConnectionParameters::default(),
|
||||
now(),
|
||||
)
|
||||
|
@ -568,22 +568,22 @@ fn preferred_address(hs_client: SocketAddr, hs_server: SocketAddr, preferred: So
|
|||
/// Migration works for a new port number.
|
||||
#[test]
|
||||
fn preferred_address_new_port() {
|
||||
let a = addr();
|
||||
let a = DEFAULT_ADDR;
|
||||
preferred_address(a, a, new_port(a));
|
||||
}
|
||||
|
||||
/// Migration works for a new address too.
|
||||
#[test]
|
||||
fn preferred_address_new_address() {
|
||||
let mut preferred = addr();
|
||||
let mut preferred = DEFAULT_ADDR;
|
||||
preferred.set_ip(IpAddr::V6(Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 2)));
|
||||
preferred_address(addr(), addr(), preferred);
|
||||
preferred_address(DEFAULT_ADDR, DEFAULT_ADDR, preferred);
|
||||
}
|
||||
|
||||
/// Migration works for IPv4 addresses.
|
||||
#[test]
|
||||
fn preferred_address_new_port_v4() {
|
||||
let a = addr_v4();
|
||||
let a = DEFAULT_ADDR_V4;
|
||||
preferred_address(a, a, new_port(a));
|
||||
}
|
||||
|
||||
|
@ -623,7 +623,7 @@ fn preferred_address_ignore_loopback() {
|
|||
/// A preferred address in the wrong address family is ignored.
|
||||
#[test]
|
||||
fn preferred_address_ignore_different_family() {
|
||||
preferred_address_ignored(PreferredAddress::new_any(Some(addr_v4()), None));
|
||||
preferred_address_ignored(PreferredAddress::new_any(Some(DEFAULT_ADDR_V4), None));
|
||||
}
|
||||
|
||||
/// Disabling preferred addresses at the client means that it ignores a perfectly
|
||||
|
@ -631,7 +631,7 @@ fn preferred_address_ignore_different_family() {
|
|||
#[test]
|
||||
fn preferred_address_disabled_client() {
|
||||
let mut client = new_client(ConnectionParameters::default().disable_preferred_address());
|
||||
let mut preferred = addr();
|
||||
let mut preferred = DEFAULT_ADDR;
|
||||
preferred.set_ip(IpAddr::V6(Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 2)));
|
||||
let spa = PreferredAddress::new_any(None, Some(preferred));
|
||||
let mut server = new_server(ConnectionParameters::default().preferred_address(spa));
|
||||
|
@ -643,7 +643,7 @@ fn preferred_address_disabled_client() {
|
|||
fn preferred_address_empty_cid() {
|
||||
fixture_init();
|
||||
|
||||
let spa = PreferredAddress::new_any(None, Some(new_port(addr())));
|
||||
let spa = PreferredAddress::new_any(None, Some(new_port(DEFAULT_ADDR)));
|
||||
let res = Connection::new_server(
|
||||
test_fixture::DEFAULT_KEYS,
|
||||
test_fixture::DEFAULT_ALPN,
|
||||
|
@ -706,33 +706,33 @@ fn preferred_address_client() {
|
|||
fn migration_invalid_state() {
|
||||
let mut client = default_client();
|
||||
assert!(client
|
||||
.migrate(Some(addr()), Some(addr()), false, now())
|
||||
.migrate(Some(DEFAULT_ADDR), Some(DEFAULT_ADDR), false, now())
|
||||
.is_err());
|
||||
|
||||
let mut server = default_server();
|
||||
assert!(server
|
||||
.migrate(Some(addr()), Some(addr()), false, now())
|
||||
.migrate(Some(DEFAULT_ADDR), Some(DEFAULT_ADDR), false, now())
|
||||
.is_err());
|
||||
connect_force_idle(&mut client, &mut server);
|
||||
|
||||
assert!(server
|
||||
.migrate(Some(addr()), Some(addr()), false, now())
|
||||
.migrate(Some(DEFAULT_ADDR), Some(DEFAULT_ADDR), false, now())
|
||||
.is_err());
|
||||
|
||||
client.close(now(), 0, "closing");
|
||||
assert!(client
|
||||
.migrate(Some(addr()), Some(addr()), false, now())
|
||||
.migrate(Some(DEFAULT_ADDR), Some(DEFAULT_ADDR), false, now())
|
||||
.is_err());
|
||||
let close = client.process(None, now()).dgram();
|
||||
|
||||
let dgram = server.process(close.as_ref(), now()).dgram();
|
||||
assert!(server
|
||||
.migrate(Some(addr()), Some(addr()), false, now())
|
||||
.migrate(Some(DEFAULT_ADDR), Some(DEFAULT_ADDR), false, now())
|
||||
.is_err());
|
||||
|
||||
client.process_input(&dgram.unwrap(), now());
|
||||
assert!(client
|
||||
.migrate(Some(addr()), Some(addr()), false, now())
|
||||
.migrate(Some(DEFAULT_ADDR), Some(DEFAULT_ADDR), false, now())
|
||||
.is_err());
|
||||
}
|
||||
|
||||
|
@ -753,32 +753,32 @@ fn migration_invalid_address() {
|
|||
cant_migrate(None, None);
|
||||
|
||||
// Providing a zero port number isn't valid.
|
||||
let mut zero_port = addr();
|
||||
let mut zero_port = DEFAULT_ADDR;
|
||||
zero_port.set_port(0);
|
||||
cant_migrate(None, Some(zero_port));
|
||||
cant_migrate(Some(zero_port), None);
|
||||
|
||||
// An unspecified remote address is bad.
|
||||
let mut remote_unspecified = addr();
|
||||
let mut remote_unspecified = DEFAULT_ADDR;
|
||||
remote_unspecified.set_ip(IpAddr::V6(Ipv6Addr::from(0)));
|
||||
cant_migrate(None, Some(remote_unspecified));
|
||||
|
||||
// Mixed address families is bad.
|
||||
cant_migrate(Some(addr()), Some(addr_v4()));
|
||||
cant_migrate(Some(addr_v4()), Some(addr()));
|
||||
cant_migrate(Some(DEFAULT_ADDR), Some(DEFAULT_ADDR_V4));
|
||||
cant_migrate(Some(DEFAULT_ADDR_V4), Some(DEFAULT_ADDR));
|
||||
|
||||
// Loopback to non-loopback is bad.
|
||||
cant_migrate(Some(addr()), Some(loopback()));
|
||||
cant_migrate(Some(loopback()), Some(addr()));
|
||||
cant_migrate(Some(DEFAULT_ADDR), Some(loopback()));
|
||||
cant_migrate(Some(loopback()), Some(DEFAULT_ADDR));
|
||||
assert_eq!(
|
||||
client
|
||||
.migrate(Some(addr()), Some(loopback()), true, now())
|
||||
.migrate(Some(DEFAULT_ADDR), Some(loopback()), true, now())
|
||||
.unwrap_err(),
|
||||
Error::InvalidMigration
|
||||
);
|
||||
assert_eq!(
|
||||
client
|
||||
.migrate(Some(loopback()), Some(addr()), true, now())
|
||||
.migrate(Some(loopback()), Some(DEFAULT_ADDR), true, now())
|
||||
.unwrap_err(),
|
||||
Error::InvalidMigration
|
||||
);
|
||||
|
@ -864,7 +864,7 @@ fn retire_prior_to_migration_failure() {
|
|||
let original_cid = ConnectionId::from(get_cid(&send_something(&mut client, now())));
|
||||
|
||||
client
|
||||
.migrate(Some(addr_v4()), Some(addr_v4()), false, now())
|
||||
.migrate(Some(DEFAULT_ADDR_V4), Some(DEFAULT_ADDR_V4), false, now())
|
||||
.unwrap();
|
||||
|
||||
// The client now probes the new path.
|
||||
|
@ -919,7 +919,7 @@ fn retire_prior_to_migration_success() {
|
|||
let original_cid = ConnectionId::from(get_cid(&send_something(&mut client, now())));
|
||||
|
||||
client
|
||||
.migrate(Some(addr_v4()), Some(addr_v4()), false, now())
|
||||
.migrate(Some(DEFAULT_ADDR_V4), Some(DEFAULT_ADDR_V4), false, now())
|
||||
.unwrap();
|
||||
|
||||
// The client now probes the new path.
|
||||
|
|
|
@ -18,7 +18,7 @@ use std::{
|
|||
use enum_map::enum_map;
|
||||
use neqo_common::{event::Provider, qdebug, qtrace, Datagram, Decoder, Role};
|
||||
use neqo_crypto::{random, AllowZeroRtt, AuthenticationStatus, ResumptionToken};
|
||||
use test_fixture::{self, addr, fixture_init, new_neqo_qlog, now};
|
||||
use test_fixture::{self, fixture_init, new_neqo_qlog, now, DEFAULT_ADDR};
|
||||
|
||||
use super::{Connection, ConnectionError, ConnectionId, Output, State};
|
||||
use crate::{
|
||||
|
@ -79,7 +79,7 @@ impl ConnectionIdDecoder for CountingConnectionIdGenerator {
|
|||
|
||||
impl ConnectionIdGenerator for CountingConnectionIdGenerator {
|
||||
fn generate_cid(&mut self) -> Option<ConnectionId> {
|
||||
let mut r = random(20);
|
||||
let mut r = random::<20>();
|
||||
r[0] = 8;
|
||||
r[1] = u8::try_from(self.counter >> 24).unwrap();
|
||||
r[2] = u8::try_from((self.counter >> 16) & 0xff).unwrap();
|
||||
|
@ -107,8 +107,8 @@ pub fn new_client(params: ConnectionParameters) -> Connection {
|
|||
test_fixture::DEFAULT_SERVER_NAME,
|
||||
test_fixture::DEFAULT_ALPN,
|
||||
Rc::new(RefCell::new(CountingConnectionIdGenerator::default())),
|
||||
addr(),
|
||||
addr(),
|
||||
DEFAULT_ADDR,
|
||||
DEFAULT_ADDR,
|
||||
params,
|
||||
now(),
|
||||
)
|
||||
|
|
|
@ -30,6 +30,9 @@ pub mod recv_stream;
|
|||
#[cfg(not(feature = "bench"))]
|
||||
mod recv_stream;
|
||||
mod rtt;
|
||||
#[cfg(feature = "bench")]
|
||||
pub mod send_stream;
|
||||
#[cfg(not(feature = "bench"))]
|
||||
mod send_stream;
|
||||
mod sender;
|
||||
pub mod server;
|
||||
|
|
|
@ -271,7 +271,7 @@ impl PacketBuilder {
|
|||
let mask = if quic_bit { PACKET_BIT_FIXED_QUIC } else { 0 }
|
||||
| if self.is_long() { 0 } else { PACKET_BIT_SPIN };
|
||||
let first = self.header.start;
|
||||
self.encoder.as_mut()[first] ^= random(1)[0] & mask;
|
||||
self.encoder.as_mut()[first] ^= random::<1>()[0] & mask;
|
||||
}
|
||||
|
||||
/// For an Initial packet, encode the token.
|
||||
|
@ -424,7 +424,7 @@ impl PacketBuilder {
|
|||
PACKET_BIT_LONG
|
||||
| PACKET_BIT_FIXED_QUIC
|
||||
| (PacketType::Retry.to_byte(version) << 4)
|
||||
| (random(1)[0] & 0xf),
|
||||
| (random::<1>()[0] & 0xf),
|
||||
);
|
||||
encoder.encode_uint(4, version.wire_version());
|
||||
encoder.encode_vec(1, dcid);
|
||||
|
@ -448,7 +448,7 @@ impl PacketBuilder {
|
|||
versions: &[Version],
|
||||
) -> Vec<u8> {
|
||||
let mut encoder = Encoder::default();
|
||||
let mut grease = random(4);
|
||||
let mut grease = random::<4>();
|
||||
// This will not include the "QUIC bit" sometimes. Intentionally.
|
||||
encoder.encode_byte(PACKET_BIT_LONG | (grease[3] & 0x7f));
|
||||
encoder.encode(&[0; 4]); // Zero version == VN.
|
||||
|
|
|
@ -796,7 +796,7 @@ impl Path {
|
|||
// Send PATH_CHALLENGE.
|
||||
if let ProbeState::ProbeNeeded { probe_count } = self.state {
|
||||
qtrace!([self], "Initiating path challenge {}", probe_count);
|
||||
let data = <[u8; 8]>::try_from(&random(8)[..]).unwrap();
|
||||
let data = random::<8>();
|
||||
builder.encode_varint(FRAME_TYPE_PATH_CHALLENGE);
|
||||
builder.encode(&data);
|
||||
|
||||
|
|
|
@ -1027,7 +1027,7 @@ mod tests {
|
|||
};
|
||||
|
||||
use neqo_common::qlog::NeqoQlog;
|
||||
use test_fixture::{addr, now};
|
||||
use test_fixture::{now, DEFAULT_ADDR};
|
||||
|
||||
use super::{
|
||||
LossRecovery, LossRecoverySpace, PacketNumberSpace, SendProfile, SentPacket, FAST_PTO_SCALE,
|
||||
|
@ -1105,7 +1105,14 @@ mod tests {
|
|||
impl Default for Fixture {
|
||||
fn default() -> Self {
|
||||
const CC: CongestionControlAlgorithm = CongestionControlAlgorithm::NewReno;
|
||||
let mut path = Path::temporary(addr(), addr(), CC, true, NeqoQlog::default(), now());
|
||||
let mut path = Path::temporary(
|
||||
DEFAULT_ADDR,
|
||||
DEFAULT_ADDR,
|
||||
CC,
|
||||
true,
|
||||
NeqoQlog::default(),
|
||||
now(),
|
||||
);
|
||||
path.make_permanent(
|
||||
None,
|
||||
ConnectionIdEntry::new(0, ConnectionId::from(&[1, 2, 3]), [0; 16]),
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -438,7 +438,7 @@ impl TransportParameters {
|
|||
|
||||
/// Set version information.
|
||||
pub fn set_versions(&mut self, role: Role, versions: &VersionConfig) {
|
||||
let rbuf = random(4);
|
||||
let rbuf = random::<4>();
|
||||
let mut other = Vec::with_capacity(versions.all().len() + 1);
|
||||
let mut dec = Decoder::new(&rbuf);
|
||||
let grease = (dec.decode_uint(4).unwrap() as u32) & 0xf0f0_f0f0 | 0x0a0a_0a0a;
|
||||
|
|
|
@ -746,8 +746,8 @@ impl Default for AckTracker {
|
|||
mod tests {
|
||||
use std::collections::HashSet;
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
use neqo_common::Encoder;
|
||||
use test_fixture::now;
|
||||
|
||||
use super::{
|
||||
AckTracker, Duration, Instant, PacketNumberSpace, PacketNumberSpaceSet, RecoveryToken,
|
||||
|
@ -760,16 +760,13 @@ mod tests {
|
|||
};
|
||||
|
||||
const RTT: Duration = Duration::from_millis(100);
|
||||
lazy_static! {
|
||||
static ref NOW: Instant = Instant::now();
|
||||
}
|
||||
|
||||
fn test_ack_range(pns: &[PacketNumber], nranges: usize) {
|
||||
let mut rp = RecvdPackets::new(PacketNumberSpace::Initial); // Any space will do.
|
||||
let mut packets = HashSet::new();
|
||||
|
||||
for pn in pns {
|
||||
rp.set_received(*NOW, *pn, true);
|
||||
rp.set_received(now(), *pn, true);
|
||||
packets.insert(*pn);
|
||||
}
|
||||
|
||||
|
@ -824,7 +821,7 @@ mod tests {
|
|||
|
||||
// This will add one too many disjoint ranges.
|
||||
for i in 0..=MAX_TRACKED_RANGES {
|
||||
rp.set_received(*NOW, (i * 2) as u64, true);
|
||||
rp.set_received(now(), (i * 2) as u64, true);
|
||||
}
|
||||
|
||||
assert_eq!(rp.ranges.len(), MAX_TRACKED_RANGES);
|
||||
|
@ -843,22 +840,22 @@ mod tests {
|
|||
// Only application data packets are delayed.
|
||||
let mut rp = RecvdPackets::new(PacketNumberSpace::ApplicationData);
|
||||
assert!(rp.ack_time().is_none());
|
||||
assert!(!rp.ack_now(*NOW, RTT));
|
||||
assert!(!rp.ack_now(now(), RTT));
|
||||
|
||||
rp.ack_freq(0, COUNT, DELAY, false);
|
||||
|
||||
// Some packets won't cause an ACK to be needed.
|
||||
for i in 0..COUNT {
|
||||
rp.set_received(*NOW, i, true);
|
||||
assert_eq!(Some(*NOW + DELAY), rp.ack_time());
|
||||
assert!(!rp.ack_now(*NOW, RTT));
|
||||
assert!(rp.ack_now(*NOW + DELAY, RTT));
|
||||
rp.set_received(now(), i, true);
|
||||
assert_eq!(Some(now() + DELAY), rp.ack_time());
|
||||
assert!(!rp.ack_now(now(), RTT));
|
||||
assert!(rp.ack_now(now() + DELAY, RTT));
|
||||
}
|
||||
|
||||
// Exceeding COUNT will move the ACK time to now.
|
||||
rp.set_received(*NOW, COUNT, true);
|
||||
assert_eq!(Some(*NOW), rp.ack_time());
|
||||
assert!(rp.ack_now(*NOW, RTT));
|
||||
rp.set_received(now(), COUNT, true);
|
||||
assert_eq!(Some(now()), rp.ack_time());
|
||||
assert!(rp.ack_now(now(), RTT));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -866,12 +863,12 @@ mod tests {
|
|||
for space in &[PacketNumberSpace::Initial, PacketNumberSpace::Handshake] {
|
||||
let mut rp = RecvdPackets::new(*space);
|
||||
assert!(rp.ack_time().is_none());
|
||||
assert!(!rp.ack_now(*NOW, RTT));
|
||||
assert!(!rp.ack_now(now(), RTT));
|
||||
|
||||
// Any packet in these spaces is acknowledged straight away.
|
||||
rp.set_received(*NOW, 0, true);
|
||||
assert_eq!(Some(*NOW), rp.ack_time());
|
||||
assert!(rp.ack_now(*NOW, RTT));
|
||||
rp.set_received(now(), 0, true);
|
||||
assert_eq!(Some(now()), rp.ack_time());
|
||||
assert!(rp.ack_now(now(), RTT));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -879,12 +876,12 @@ mod tests {
|
|||
fn ooo_no_ack_delay_new() {
|
||||
let mut rp = RecvdPackets::new(PacketNumberSpace::ApplicationData);
|
||||
assert!(rp.ack_time().is_none());
|
||||
assert!(!rp.ack_now(*NOW, RTT));
|
||||
assert!(!rp.ack_now(now(), RTT));
|
||||
|
||||
// Anything other than packet 0 is acknowledged immediately.
|
||||
rp.set_received(*NOW, 1, true);
|
||||
assert_eq!(Some(*NOW), rp.ack_time());
|
||||
assert!(rp.ack_now(*NOW, RTT));
|
||||
rp.set_received(now(), 1, true);
|
||||
assert_eq!(Some(now()), rp.ack_time());
|
||||
assert!(rp.ack_now(now(), RTT));
|
||||
}
|
||||
|
||||
fn write_frame_at(rp: &mut RecvdPackets, now: Instant) {
|
||||
|
@ -897,37 +894,37 @@ mod tests {
|
|||
}
|
||||
|
||||
fn write_frame(rp: &mut RecvdPackets) {
|
||||
write_frame_at(rp, *NOW);
|
||||
write_frame_at(rp, now());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ooo_no_ack_delay_fill() {
|
||||
let mut rp = RecvdPackets::new(PacketNumberSpace::ApplicationData);
|
||||
rp.set_received(*NOW, 1, true);
|
||||
rp.set_received(now(), 1, true);
|
||||
write_frame(&mut rp);
|
||||
|
||||
// Filling in behind the largest acknowledged causes immediate ACK.
|
||||
rp.set_received(*NOW, 0, true);
|
||||
rp.set_received(now(), 0, true);
|
||||
write_frame(&mut rp);
|
||||
|
||||
// Receiving the next packet won't elicit an ACK.
|
||||
rp.set_received(*NOW, 2, true);
|
||||
assert!(!rp.ack_now(*NOW, RTT));
|
||||
rp.set_received(now(), 2, true);
|
||||
assert!(!rp.ack_now(now(), RTT));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn immediate_ack_after_rtt() {
|
||||
let mut rp = RecvdPackets::new(PacketNumberSpace::ApplicationData);
|
||||
rp.set_received(*NOW, 1, true);
|
||||
rp.set_received(now(), 1, true);
|
||||
write_frame(&mut rp);
|
||||
|
||||
// Filling in behind the largest acknowledged causes immediate ACK.
|
||||
rp.set_received(*NOW, 0, true);
|
||||
rp.set_received(now(), 0, true);
|
||||
write_frame(&mut rp);
|
||||
|
||||
// A new packet ordinarily doesn't result in an ACK, but this time it does.
|
||||
rp.set_received(*NOW + RTT, 2, true);
|
||||
write_frame_at(&mut rp, *NOW + RTT);
|
||||
rp.set_received(now() + RTT, 2, true);
|
||||
write_frame_at(&mut rp, now() + RTT);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -937,29 +934,29 @@ mod tests {
|
|||
// Set tolerance to 2 and then it takes three packets.
|
||||
rp.ack_freq(0, 2, Duration::from_millis(10), true);
|
||||
|
||||
rp.set_received(*NOW, 1, true);
|
||||
assert_ne!(Some(*NOW), rp.ack_time());
|
||||
rp.set_received(*NOW, 2, true);
|
||||
assert_ne!(Some(*NOW), rp.ack_time());
|
||||
rp.set_received(*NOW, 3, true);
|
||||
assert_eq!(Some(*NOW), rp.ack_time());
|
||||
rp.set_received(now(), 1, true);
|
||||
assert_ne!(Some(now()), rp.ack_time());
|
||||
rp.set_received(now(), 2, true);
|
||||
assert_ne!(Some(now()), rp.ack_time());
|
||||
rp.set_received(now(), 3, true);
|
||||
assert_eq!(Some(now()), rp.ack_time());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ooo_no_ack_delay_threshold_gap() {
|
||||
let mut rp = RecvdPackets::new(PacketNumberSpace::ApplicationData);
|
||||
rp.set_received(*NOW, 1, true);
|
||||
rp.set_received(now(), 1, true);
|
||||
write_frame(&mut rp);
|
||||
|
||||
// Set tolerance to 2 and then it takes three packets.
|
||||
rp.ack_freq(0, 2, Duration::from_millis(10), true);
|
||||
|
||||
rp.set_received(*NOW, 3, true);
|
||||
assert_ne!(Some(*NOW), rp.ack_time());
|
||||
rp.set_received(*NOW, 4, true);
|
||||
assert_ne!(Some(*NOW), rp.ack_time());
|
||||
rp.set_received(*NOW, 5, true);
|
||||
assert_eq!(Some(*NOW), rp.ack_time());
|
||||
rp.set_received(now(), 3, true);
|
||||
assert_ne!(Some(now()), rp.ack_time());
|
||||
rp.set_received(now(), 4, true);
|
||||
assert_ne!(Some(now()), rp.ack_time());
|
||||
rp.set_received(now(), 5, true);
|
||||
assert_eq!(Some(now()), rp.ack_time());
|
||||
}
|
||||
|
||||
/// Test that an in-order packet that is not ack-eliciting doesn't
|
||||
|
@ -970,13 +967,13 @@ mod tests {
|
|||
rp.ack_freq(0, 1, Duration::from_millis(10), true);
|
||||
|
||||
// This should be ignored.
|
||||
rp.set_received(*NOW, 0, false);
|
||||
assert_ne!(Some(*NOW), rp.ack_time());
|
||||
rp.set_received(now(), 0, false);
|
||||
assert_ne!(Some(now()), rp.ack_time());
|
||||
// Skip 1 (it has no effect).
|
||||
rp.set_received(*NOW, 2, true);
|
||||
assert_ne!(Some(*NOW), rp.ack_time());
|
||||
rp.set_received(*NOW, 3, true);
|
||||
assert_eq!(Some(*NOW), rp.ack_time());
|
||||
rp.set_received(now(), 2, true);
|
||||
assert_ne!(Some(now()), rp.ack_time());
|
||||
rp.set_received(now(), 3, true);
|
||||
assert_eq!(Some(now()), rp.ack_time());
|
||||
}
|
||||
|
||||
/// If a packet that is not ack-eliciting is reordered, that's fine too.
|
||||
|
@ -986,16 +983,16 @@ mod tests {
|
|||
rp.ack_freq(0, 1, Duration::from_millis(10), false);
|
||||
|
||||
// These are out of order, but they are not ack-eliciting.
|
||||
rp.set_received(*NOW, 1, false);
|
||||
assert_ne!(Some(*NOW), rp.ack_time());
|
||||
rp.set_received(*NOW, 0, false);
|
||||
assert_ne!(Some(*NOW), rp.ack_time());
|
||||
rp.set_received(now(), 1, false);
|
||||
assert_ne!(Some(now()), rp.ack_time());
|
||||
rp.set_received(now(), 0, false);
|
||||
assert_ne!(Some(now()), rp.ack_time());
|
||||
|
||||
// These are in order.
|
||||
rp.set_received(*NOW, 2, true);
|
||||
assert_ne!(Some(*NOW), rp.ack_time());
|
||||
rp.set_received(*NOW, 3, true);
|
||||
assert_eq!(Some(*NOW), rp.ack_time());
|
||||
rp.set_received(now(), 2, true);
|
||||
assert_ne!(Some(now()), rp.ack_time());
|
||||
rp.set_received(now(), 3, true);
|
||||
assert_eq!(Some(now()), rp.ack_time());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1007,23 +1004,23 @@ mod tests {
|
|||
tracker
|
||||
.get_mut(PacketNumberSpace::Handshake)
|
||||
.unwrap()
|
||||
.set_received(*NOW, 0, false);
|
||||
assert_eq!(None, tracker.ack_time(*NOW));
|
||||
.set_received(now(), 0, false);
|
||||
assert_eq!(None, tracker.ack_time(now()));
|
||||
|
||||
// This should be delayed.
|
||||
tracker
|
||||
.get_mut(PacketNumberSpace::ApplicationData)
|
||||
.unwrap()
|
||||
.set_received(*NOW, 0, true);
|
||||
assert_eq!(Some(*NOW + DELAY), tracker.ack_time(*NOW));
|
||||
.set_received(now(), 0, true);
|
||||
assert_eq!(Some(now() + DELAY), tracker.ack_time(now()));
|
||||
|
||||
// This should move the time forward.
|
||||
let later = *NOW + (DELAY / 2);
|
||||
let later = now() + (DELAY / 2);
|
||||
tracker
|
||||
.get_mut(PacketNumberSpace::Initial)
|
||||
.unwrap()
|
||||
.set_received(later, 0, true);
|
||||
assert_eq!(Some(later), tracker.ack_time(*NOW));
|
||||
assert_eq!(Some(later), tracker.ack_time(now()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1047,17 +1044,17 @@ mod tests {
|
|||
tracker
|
||||
.get_mut(PacketNumberSpace::Initial)
|
||||
.unwrap()
|
||||
.set_received(*NOW, 0, true);
|
||||
.set_received(now(), 0, true);
|
||||
// The reference time for `ack_time` has to be in the past or we filter out the timer.
|
||||
assert!(tracker
|
||||
.ack_time(NOW.checked_sub(Duration::from_millis(1)).unwrap())
|
||||
.ack_time(now().checked_sub(Duration::from_millis(1)).unwrap())
|
||||
.is_some());
|
||||
|
||||
let mut tokens = Vec::new();
|
||||
let mut stats = FrameStats::default();
|
||||
tracker.write_frame(
|
||||
PacketNumberSpace::Initial,
|
||||
*NOW,
|
||||
now(),
|
||||
RTT,
|
||||
&mut builder,
|
||||
&mut tokens,
|
||||
|
@ -1069,9 +1066,9 @@ mod tests {
|
|||
tracker
|
||||
.get_mut(PacketNumberSpace::Initial)
|
||||
.unwrap()
|
||||
.set_received(*NOW, 1, true);
|
||||
.set_received(now(), 1, true);
|
||||
assert!(tracker
|
||||
.ack_time(NOW.checked_sub(Duration::from_millis(1)).unwrap())
|
||||
.ack_time(now().checked_sub(Duration::from_millis(1)).unwrap())
|
||||
.is_some());
|
||||
|
||||
// Now drop that space.
|
||||
|
@ -1079,11 +1076,11 @@ mod tests {
|
|||
|
||||
assert!(tracker.get_mut(PacketNumberSpace::Initial).is_none());
|
||||
assert!(tracker
|
||||
.ack_time(NOW.checked_sub(Duration::from_millis(1)).unwrap())
|
||||
.ack_time(now().checked_sub(Duration::from_millis(1)).unwrap())
|
||||
.is_none());
|
||||
tracker.write_frame(
|
||||
PacketNumberSpace::Initial,
|
||||
*NOW,
|
||||
now(),
|
||||
RTT,
|
||||
&mut builder,
|
||||
&mut tokens,
|
||||
|
@ -1103,9 +1100,9 @@ mod tests {
|
|||
tracker
|
||||
.get_mut(PacketNumberSpace::Initial)
|
||||
.unwrap()
|
||||
.set_received(*NOW, 0, true);
|
||||
.set_received(now(), 0, true);
|
||||
assert!(tracker
|
||||
.ack_time(NOW.checked_sub(Duration::from_millis(1)).unwrap())
|
||||
.ack_time(now().checked_sub(Duration::from_millis(1)).unwrap())
|
||||
.is_some());
|
||||
|
||||
let mut builder = PacketBuilder::short(Encoder::new(), false, []);
|
||||
|
@ -1114,7 +1111,7 @@ mod tests {
|
|||
let mut stats = FrameStats::default();
|
||||
tracker.write_frame(
|
||||
PacketNumberSpace::Initial,
|
||||
*NOW,
|
||||
now(),
|
||||
RTT,
|
||||
&mut builder,
|
||||
&mut Vec::new(),
|
||||
|
@ -1130,13 +1127,13 @@ mod tests {
|
|||
tracker
|
||||
.get_mut(PacketNumberSpace::Initial)
|
||||
.unwrap()
|
||||
.set_received(*NOW, 0, true);
|
||||
.set_received(now(), 0, true);
|
||||
tracker
|
||||
.get_mut(PacketNumberSpace::Initial)
|
||||
.unwrap()
|
||||
.set_received(*NOW, 2, true);
|
||||
.set_received(now(), 2, true);
|
||||
assert!(tracker
|
||||
.ack_time(NOW.checked_sub(Duration::from_millis(1)).unwrap())
|
||||
.ack_time(now().checked_sub(Duration::from_millis(1)).unwrap())
|
||||
.is_some());
|
||||
|
||||
let mut builder = PacketBuilder::short(Encoder::new(), false, []);
|
||||
|
@ -1145,7 +1142,7 @@ mod tests {
|
|||
let mut stats = FrameStats::default();
|
||||
tracker.write_frame(
|
||||
PacketNumberSpace::Initial,
|
||||
*NOW,
|
||||
now(),
|
||||
RTT,
|
||||
&mut builder,
|
||||
&mut Vec::new(),
|
||||
|
@ -1168,19 +1165,19 @@ mod tests {
|
|||
let mut tracker = AckTracker::default();
|
||||
|
||||
// While we have multiple PN spaces, we ignore ACK timers from the past.
|
||||
// Send out of order to cause the delayed ack timer to be set to `*NOW`.
|
||||
// Send out of order to cause the delayed ack timer to be set to `now()`.
|
||||
tracker
|
||||
.get_mut(PacketNumberSpace::ApplicationData)
|
||||
.unwrap()
|
||||
.set_received(*NOW, 3, true);
|
||||
assert!(tracker.ack_time(*NOW + Duration::from_millis(1)).is_none());
|
||||
.set_received(now(), 3, true);
|
||||
assert!(tracker.ack_time(now() + Duration::from_millis(1)).is_none());
|
||||
|
||||
// When we are reduced to one space, that filter is off.
|
||||
tracker.drop_space(PacketNumberSpace::Initial);
|
||||
tracker.drop_space(PacketNumberSpace::Handshake);
|
||||
assert_eq!(
|
||||
tracker.ack_time(*NOW + Duration::from_millis(1)),
|
||||
Some(*NOW)
|
||||
tracker.ack_time(now() + Duration::from_millis(1)),
|
||||
Some(now())
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,15 +7,17 @@
|
|||
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
|
||||
#![warn(clippy::pedantic)]
|
||||
|
||||
mod sim;
|
||||
|
||||
use std::{ops::Range, time::Duration};
|
||||
|
||||
use neqo_transport::{ConnectionError, ConnectionParameters, Error, State};
|
||||
use sim::{
|
||||
connection::{ConnectionNode, ReachState, ReceiveData, SendData},
|
||||
network::{Delay, Drop, TailDrop},
|
||||
Simulator,
|
||||
use test_fixture::{
|
||||
boxed,
|
||||
sim::{
|
||||
connection::{ConnectionNode, ReachState, ReceiveData, SendData},
|
||||
network::{Delay, Drop, TailDrop},
|
||||
Simulator,
|
||||
},
|
||||
simulate,
|
||||
};
|
||||
|
||||
/// The amount of transfer. Much more than this takes a surprising amount of time.
|
||||
|
@ -32,26 +34,28 @@ const fn weeks(m: u32) -> Duration {
|
|||
simulate!(
|
||||
connect_direct,
|
||||
[
|
||||
ConnectionNode::default_client(boxed![ReachState::new(State::Confirmed)]),
|
||||
ConnectionNode::default_server(boxed![ReachState::new(State::Confirmed)]),
|
||||
ConnectionNode::new_client(
|
||||
ConnectionParameters::default(),
|
||||
[],
|
||||
boxed![ReachState::new(State::Confirmed)]
|
||||
),
|
||||
ConnectionNode::new_server(
|
||||
ConnectionParameters::default(),
|
||||
[],
|
||||
boxed![ReachState::new(State::Confirmed)]
|
||||
),
|
||||
]
|
||||
);
|
||||
|
||||
simulate!(
|
||||
idle_timeout,
|
||||
[
|
||||
ConnectionNode::default_client(boxed![
|
||||
ReachState::new(State::Confirmed),
|
||||
ReachState::new(State::Closed(ConnectionError::Transport(
|
||||
Error::IdleTimeout
|
||||
)))
|
||||
]),
|
||||
ConnectionNode::default_server(boxed![
|
||||
ReachState::new(State::Confirmed),
|
||||
ReachState::new(State::Closed(ConnectionError::Transport(
|
||||
Error::IdleTimeout
|
||||
)))
|
||||
]),
|
||||
ConnectionNode::default_client(boxed![ReachState::new(State::Closed(
|
||||
ConnectionError::Transport(Error::IdleTimeout)
|
||||
))]),
|
||||
ConnectionNode::default_server(boxed![ReachState::new(State::Closed(
|
||||
ConnectionError::Transport(Error::IdleTimeout)
|
||||
))]),
|
||||
]
|
||||
);
|
||||
|
||||
|
@ -60,23 +64,19 @@ simulate!(
|
|||
[
|
||||
ConnectionNode::new_client(
|
||||
ConnectionParameters::default().idle_timeout(weeks(1000)),
|
||||
boxed![
|
||||
ReachState::new(State::Confirmed),
|
||||
ReachState::new(State::Closed(ConnectionError::Transport(
|
||||
Error::IdleTimeout
|
||||
)))
|
||||
]
|
||||
boxed![ReachState::new(State::Confirmed),],
|
||||
boxed![ReachState::new(State::Closed(ConnectionError::Transport(
|
||||
Error::IdleTimeout
|
||||
)))]
|
||||
),
|
||||
Delay::new(weeks(6)..weeks(6)),
|
||||
Drop::percentage(10),
|
||||
ConnectionNode::new_server(
|
||||
ConnectionParameters::default().idle_timeout(weeks(1000)),
|
||||
boxed![
|
||||
ReachState::new(State::Confirmed),
|
||||
ReachState::new(State::Closed(ConnectionError::Transport(
|
||||
Error::IdleTimeout
|
||||
)))
|
||||
]
|
||||
boxed![ReachState::new(State::Confirmed),],
|
||||
boxed![ReachState::new(State::Closed(ConnectionError::Transport(
|
||||
Error::IdleTimeout
|
||||
)))]
|
||||
),
|
||||
Delay::new(weeks(8)..weeks(8)),
|
||||
Drop::percentage(10),
|
||||
|
@ -94,9 +94,17 @@ simulate!(
|
|||
simulate!(
|
||||
connect_fixed_rtt,
|
||||
[
|
||||
ConnectionNode::default_client(boxed![ReachState::new(State::Confirmed)]),
|
||||
ConnectionNode::new_client(
|
||||
ConnectionParameters::default(),
|
||||
[],
|
||||
boxed![ReachState::new(State::Confirmed)]
|
||||
),
|
||||
Delay::new(DELAY..DELAY),
|
||||
ConnectionNode::default_server(boxed![ReachState::new(State::Confirmed)]),
|
||||
ConnectionNode::new_server(
|
||||
ConnectionParameters::default(),
|
||||
[],
|
||||
boxed![ReachState::new(State::Confirmed)]
|
||||
),
|
||||
Delay::new(DELAY..DELAY),
|
||||
],
|
||||
);
|
||||
|
@ -104,22 +112,38 @@ simulate!(
|
|||
simulate!(
|
||||
connect_taildrop_jitter,
|
||||
[
|
||||
ConnectionNode::default_client(boxed![ReachState::new(State::Confirmed)]),
|
||||
TailDrop::dsl_uplink(),
|
||||
Delay::new(ZERO..JITTER),
|
||||
ConnectionNode::default_server(boxed![ReachState::new(State::Confirmed)]),
|
||||
ConnectionNode::new_client(
|
||||
ConnectionParameters::default(),
|
||||
[],
|
||||
boxed![ReachState::new(State::Confirmed)]
|
||||
),
|
||||
TailDrop::dsl_downlink(),
|
||||
Delay::new(ZERO..JITTER),
|
||||
ConnectionNode::new_server(
|
||||
ConnectionParameters::default(),
|
||||
[],
|
||||
boxed![ReachState::new(State::Confirmed)]
|
||||
),
|
||||
TailDrop::dsl_uplink(),
|
||||
Delay::new(ZERO..JITTER),
|
||||
],
|
||||
);
|
||||
|
||||
simulate!(
|
||||
connect_taildrop,
|
||||
[
|
||||
ConnectionNode::default_client(boxed![ReachState::new(State::Confirmed)]),
|
||||
TailDrop::dsl_uplink(),
|
||||
ConnectionNode::default_server(boxed![ReachState::new(State::Confirmed)]),
|
||||
ConnectionNode::new_client(
|
||||
ConnectionParameters::default(),
|
||||
[],
|
||||
boxed![ReachState::new(State::Confirmed)]
|
||||
),
|
||||
TailDrop::dsl_downlink(),
|
||||
ConnectionNode::new_server(
|
||||
ConnectionParameters::default(),
|
||||
[],
|
||||
boxed![ReachState::new(State::Confirmed)]
|
||||
),
|
||||
TailDrop::dsl_uplink(),
|
||||
],
|
||||
);
|
||||
|
||||
|
@ -139,9 +163,9 @@ simulate!(
|
|||
transfer_taildrop,
|
||||
[
|
||||
ConnectionNode::default_client(boxed![SendData::new(TRANSFER_AMOUNT)]),
|
||||
TailDrop::dsl_uplink(),
|
||||
ConnectionNode::default_server(boxed![ReceiveData::new(TRANSFER_AMOUNT)]),
|
||||
TailDrop::dsl_downlink(),
|
||||
ConnectionNode::default_server(boxed![ReceiveData::new(TRANSFER_AMOUNT)]),
|
||||
TailDrop::dsl_uplink(),
|
||||
],
|
||||
);
|
||||
|
||||
|
@ -149,10 +173,10 @@ simulate!(
|
|||
transfer_taildrop_jitter,
|
||||
[
|
||||
ConnectionNode::default_client(boxed![SendData::new(TRANSFER_AMOUNT)]),
|
||||
TailDrop::dsl_uplink(),
|
||||
TailDrop::dsl_downlink(),
|
||||
Delay::new(ZERO..JITTER),
|
||||
ConnectionNode::default_server(boxed![ReceiveData::new(TRANSFER_AMOUNT)]),
|
||||
TailDrop::dsl_downlink(),
|
||||
TailDrop::dsl_uplink(),
|
||||
Delay::new(ZERO..JITTER),
|
||||
],
|
||||
);
|
||||
|
|
|
@ -1,315 +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.
|
||||
|
||||
#![allow(clippy::module_name_repetitions)]
|
||||
|
||||
use std::{
|
||||
cmp::min,
|
||||
fmt::{self, Debug},
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
use neqo_common::{event::Provider, qdebug, qtrace, Datagram};
|
||||
use neqo_crypto::AuthenticationStatus;
|
||||
use neqo_transport::{
|
||||
Connection, ConnectionEvent, ConnectionParameters, Output, State, StreamId, StreamType,
|
||||
};
|
||||
|
||||
use super::{Node, Rng};
|
||||
|
||||
/// The status of the processing of an event.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum GoalStatus {
|
||||
/// The event didn't result in doing anything; the goal is waiting for something.
|
||||
Waiting,
|
||||
/// An action was taken as a result of the event.
|
||||
Active,
|
||||
/// The goal was accomplished.
|
||||
Done,
|
||||
}
|
||||
|
||||
/// A goal for the connection.
|
||||
/// Goals can be accomplished in any order.
|
||||
pub trait ConnectionGoal {
|
||||
fn init(&mut self, _c: &mut Connection, _now: Instant) {}
|
||||
/// Perform some processing.
|
||||
fn process(&mut self, _c: &mut Connection, _now: Instant) -> GoalStatus {
|
||||
GoalStatus::Waiting
|
||||
}
|
||||
/// Handle an event from the provided connection, returning `true` when the
|
||||
/// goal is achieved.
|
||||
fn handle_event(&mut self, c: &mut Connection, e: &ConnectionEvent, now: Instant)
|
||||
-> GoalStatus;
|
||||
}
|
||||
|
||||
pub struct ConnectionNode {
|
||||
c: Connection,
|
||||
goals: Vec<Box<dyn ConnectionGoal>>,
|
||||
}
|
||||
|
||||
impl ConnectionNode {
|
||||
pub fn new_client(
|
||||
params: ConnectionParameters,
|
||||
goals: impl IntoIterator<Item = Box<dyn ConnectionGoal>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
c: test_fixture::new_client(params),
|
||||
goals: goals.into_iter().collect(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_server(
|
||||
params: ConnectionParameters,
|
||||
goals: impl IntoIterator<Item = Box<dyn ConnectionGoal>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
c: test_fixture::new_server(test_fixture::DEFAULT_ALPN, params),
|
||||
goals: goals.into_iter().collect(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default_client(goals: impl IntoIterator<Item = Box<dyn ConnectionGoal>>) -> Self {
|
||||
Self::new_client(ConnectionParameters::default(), goals)
|
||||
}
|
||||
|
||||
pub fn default_server(goals: impl IntoIterator<Item = Box<dyn ConnectionGoal>>) -> Self {
|
||||
Self::new_server(ConnectionParameters::default(), goals)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn clear_goals(&mut self) {
|
||||
self.goals.clear();
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn add_goal(&mut self, goal: Box<dyn ConnectionGoal>) {
|
||||
self.goals.push(goal);
|
||||
}
|
||||
|
||||
/// Process all goals using the given closure and return whether any were active.
|
||||
fn process_goals<F>(&mut self, mut f: F) -> bool
|
||||
where
|
||||
F: FnMut(&mut Box<dyn ConnectionGoal>, &mut Connection) -> GoalStatus,
|
||||
{
|
||||
// Waiting on drain_filter...
|
||||
// self.goals.drain_filter(|g| f(g, &mut self.c, &e)).count();
|
||||
let mut active = false;
|
||||
let mut i = 0;
|
||||
while i < self.goals.len() {
|
||||
let status = f(&mut self.goals[i], &mut self.c);
|
||||
if status == GoalStatus::Done {
|
||||
self.goals.remove(i);
|
||||
active = true;
|
||||
} else {
|
||||
active |= status == GoalStatus::Active;
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
active
|
||||
}
|
||||
}
|
||||
|
||||
impl Node for ConnectionNode {
|
||||
fn init(&mut self, _rng: Rng, now: Instant) {
|
||||
for g in &mut self.goals {
|
||||
g.init(&mut self.c, now);
|
||||
}
|
||||
}
|
||||
|
||||
fn process(&mut self, mut d: Option<Datagram>, now: Instant) -> Output {
|
||||
_ = self.process_goals(|goal, c| goal.process(c, now));
|
||||
loop {
|
||||
let res = self.c.process(d.take().as_ref(), now);
|
||||
|
||||
let mut active = false;
|
||||
while let Some(e) = self.c.next_event() {
|
||||
qtrace!([self.c], "received event {:?}", e);
|
||||
|
||||
// Perform authentication automatically.
|
||||
if matches!(e, ConnectionEvent::AuthenticationNeeded) {
|
||||
self.c.authenticated(AuthenticationStatus::Ok, now);
|
||||
}
|
||||
|
||||
active |= self.process_goals(|goal, c| goal.handle_event(c, &e, now));
|
||||
}
|
||||
// Exit at this point if the connection produced a datagram.
|
||||
// We also exit if none of the goals were active, as there is
|
||||
// no point trying again if they did nothing.
|
||||
if matches!(res, Output::Datagram(_)) || !active {
|
||||
return res;
|
||||
}
|
||||
qdebug!([self.c], "no datagram and goal activity, looping");
|
||||
}
|
||||
}
|
||||
|
||||
fn done(&self) -> bool {
|
||||
self.goals.is_empty()
|
||||
}
|
||||
|
||||
fn print_summary(&self, test_name: &str) {
|
||||
println!("{}: {:?}", test_name, self.c.stats());
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for ConnectionNode {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt::Display::fmt(&self.c, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ReachState {
|
||||
target: State,
|
||||
}
|
||||
|
||||
impl ReachState {
|
||||
pub fn new(target: State) -> Self {
|
||||
Self { target }
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectionGoal for ReachState {
|
||||
fn handle_event(
|
||||
&mut self,
|
||||
_c: &mut Connection,
|
||||
e: &ConnectionEvent,
|
||||
_now: Instant,
|
||||
) -> GoalStatus {
|
||||
if matches!(e, ConnectionEvent::StateChange(state) if *state == self.target) {
|
||||
GoalStatus::Done
|
||||
} else {
|
||||
GoalStatus::Waiting
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SendData {
|
||||
remaining: usize,
|
||||
stream_id: Option<StreamId>,
|
||||
}
|
||||
|
||||
impl SendData {
|
||||
pub fn new(amount: usize) -> Self {
|
||||
Self {
|
||||
remaining: amount,
|
||||
stream_id: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn make_stream(&mut self, c: &mut Connection) {
|
||||
if self.stream_id.is_none() {
|
||||
if let Ok(stream_id) = c.stream_create(StreamType::UniDi) {
|
||||
qdebug!([c], "made stream {} for sending", stream_id);
|
||||
self.stream_id = Some(stream_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn send(&mut self, c: &mut Connection, stream_id: StreamId) -> GoalStatus {
|
||||
const DATA: &[u8] = &[0; 4096];
|
||||
let mut status = GoalStatus::Waiting;
|
||||
loop {
|
||||
let end = min(self.remaining, DATA.len());
|
||||
let sent = c.stream_send(stream_id, &DATA[..end]).unwrap();
|
||||
if sent == 0 {
|
||||
return status;
|
||||
}
|
||||
self.remaining -= sent;
|
||||
qtrace!("sent {} remaining {}", sent, self.remaining);
|
||||
if self.remaining == 0 {
|
||||
c.stream_close_send(stream_id).unwrap();
|
||||
return GoalStatus::Done;
|
||||
}
|
||||
status = GoalStatus::Active;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectionGoal for SendData {
|
||||
fn init(&mut self, c: &mut Connection, _now: Instant) {
|
||||
self.make_stream(c);
|
||||
}
|
||||
|
||||
fn process(&mut self, c: &mut Connection, _now: Instant) -> GoalStatus {
|
||||
self.stream_id
|
||||
.map_or(GoalStatus::Waiting, |stream_id| self.send(c, stream_id))
|
||||
}
|
||||
|
||||
fn handle_event(
|
||||
&mut self,
|
||||
c: &mut Connection,
|
||||
e: &ConnectionEvent,
|
||||
_now: Instant,
|
||||
) -> GoalStatus {
|
||||
match e {
|
||||
ConnectionEvent::SendStreamCreatable {
|
||||
stream_type: StreamType::UniDi,
|
||||
}
|
||||
// TODO(mt): remove the second condition when #842 is fixed.
|
||||
| ConnectionEvent::StateChange(_) => {
|
||||
self.make_stream(c);
|
||||
GoalStatus::Active
|
||||
}
|
||||
|
||||
ConnectionEvent::SendStreamWritable { stream_id }
|
||||
if Some(*stream_id) == self.stream_id =>
|
||||
{
|
||||
self.send(c, *stream_id)
|
||||
}
|
||||
|
||||
// If we sent data in 0-RTT, then we didn't track how much we should
|
||||
// have sent. This is trivial to fix if 0-RTT testing is ever needed.
|
||||
ConnectionEvent::ZeroRttRejected => panic!("not supported"),
|
||||
_ => GoalStatus::Waiting,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Receive a prescribed amount of data from any stream.
|
||||
#[derive(Debug)]
|
||||
pub struct ReceiveData {
|
||||
remaining: usize,
|
||||
}
|
||||
|
||||
impl ReceiveData {
|
||||
pub fn new(amount: usize) -> Self {
|
||||
Self { remaining: amount }
|
||||
}
|
||||
|
||||
fn recv(&mut self, c: &mut Connection, stream_id: StreamId) -> GoalStatus {
|
||||
let mut buf = vec![0; 4096];
|
||||
let mut status = GoalStatus::Waiting;
|
||||
loop {
|
||||
let end = min(self.remaining, buf.len());
|
||||
let (recvd, _) = c.stream_recv(stream_id, &mut buf[..end]).unwrap();
|
||||
qtrace!("received {} remaining {}", recvd, self.remaining);
|
||||
if recvd == 0 {
|
||||
return status;
|
||||
}
|
||||
self.remaining -= recvd;
|
||||
if self.remaining == 0 {
|
||||
return GoalStatus::Done;
|
||||
}
|
||||
status = GoalStatus::Active;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectionGoal for ReceiveData {
|
||||
fn handle_event(
|
||||
&mut self,
|
||||
c: &mut Connection,
|
||||
e: &ConnectionEvent,
|
||||
_now: Instant,
|
||||
) -> GoalStatus {
|
||||
if let ConnectionEvent::RecvStreamReadable { stream_id } = e {
|
||||
self.recv(c, *stream_id)
|
||||
} else {
|
||||
GoalStatus::Waiting
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,102 +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.
|
||||
|
||||
#![allow(clippy::module_name_repetitions)]
|
||||
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
convert::TryFrom,
|
||||
fmt::{self, Debug},
|
||||
ops::Range,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use neqo_common::Datagram;
|
||||
use neqo_transport::Output;
|
||||
|
||||
use super::{Node, Rng};
|
||||
|
||||
/// An iterator that shares a `Random` instance and produces uniformly
|
||||
/// random `Duration`s within a specified range.
|
||||
pub struct RandomDelay {
|
||||
start: Duration,
|
||||
max: u64,
|
||||
rng: Option<Rng>,
|
||||
}
|
||||
|
||||
impl RandomDelay {
|
||||
/// Make a new random `Duration` generator. This panics if the range provided
|
||||
/// is inverted (i.e., `bounds.start > bounds.end`), or spans 2^64
|
||||
/// or more nanoseconds.
|
||||
/// A zero-length range means that random values won't be taken from the Rng
|
||||
pub fn new(bounds: Range<Duration>) -> Self {
|
||||
let max = u64::try_from((bounds.end - bounds.start).as_nanos()).unwrap();
|
||||
Self {
|
||||
start: bounds.start,
|
||||
max,
|
||||
rng: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_rng(&mut self, rng: Rng) {
|
||||
self.rng = Some(rng);
|
||||
}
|
||||
|
||||
pub fn next(&mut self) -> Duration {
|
||||
let mut rng = self.rng.as_ref().unwrap().borrow_mut();
|
||||
let r = rng.random_from(0..self.max);
|
||||
self.start + Duration::from_nanos(r)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Delay {
|
||||
random: RandomDelay,
|
||||
queue: BTreeMap<Instant, Datagram>,
|
||||
}
|
||||
|
||||
impl Delay {
|
||||
pub fn new(bounds: Range<Duration>) -> Self {
|
||||
Self {
|
||||
random: RandomDelay::new(bounds),
|
||||
queue: BTreeMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn insert(&mut self, d: Datagram, now: Instant) {
|
||||
let mut t = now + self.random.next();
|
||||
while self.queue.contains_key(&t) {
|
||||
// This is a little inefficient, but it avoids drops on collisions,
|
||||
// which are super-common for a fixed delay.
|
||||
t += Duration::from_nanos(1);
|
||||
}
|
||||
self.queue.insert(t, d);
|
||||
}
|
||||
}
|
||||
|
||||
impl Node for Delay {
|
||||
fn init(&mut self, rng: Rng, _now: Instant) {
|
||||
self.random.set_rng(rng);
|
||||
}
|
||||
|
||||
fn process(&mut self, d: Option<Datagram>, now: Instant) -> Output {
|
||||
if let Some(dgram) = d {
|
||||
self.insert(dgram, now);
|
||||
}
|
||||
if let Some((&k, _)) = self.queue.range(..=now).next() {
|
||||
Output::Datagram(self.queue.remove(&k).unwrap())
|
||||
} else if let Some(&t) = self.queue.keys().next() {
|
||||
Output::Callback(t - now)
|
||||
} else {
|
||||
Output::None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Delay {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str("delay")
|
||||
}
|
||||
}
|
|
@ -1,75 +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.
|
||||
|
||||
#![allow(clippy::module_name_repetitions)]
|
||||
|
||||
use std::{
|
||||
fmt::{self, Debug},
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
use neqo_common::{qtrace, Datagram};
|
||||
use neqo_transport::Output;
|
||||
|
||||
use super::{Node, Rng};
|
||||
|
||||
/// A random dropper.
|
||||
pub struct Drop {
|
||||
threshold: u64,
|
||||
max: u64,
|
||||
rng: Option<Rng>,
|
||||
}
|
||||
|
||||
impl Drop {
|
||||
/// Make a new random drop generator. Each `drop` is called, this generates a
|
||||
/// random value between 0 and `max` (exclusive). If this value is less than
|
||||
/// `threshold` a value of `true` is returned.
|
||||
pub fn new(threshold: u64, max: u64) -> Self {
|
||||
Self {
|
||||
threshold,
|
||||
max,
|
||||
rng: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate random drops with the given percentage.
|
||||
pub fn percentage(pct: u8) -> Self {
|
||||
// Multiply by 10 so that the random number generator works more efficiently.
|
||||
Self::new(u64::from(pct) * 10, 1000)
|
||||
}
|
||||
|
||||
pub fn drop(&mut self) -> bool {
|
||||
let mut rng = self.rng.as_ref().unwrap().borrow_mut();
|
||||
let r = rng.random_from(0..self.max);
|
||||
r < self.threshold
|
||||
}
|
||||
}
|
||||
|
||||
impl Node for Drop {
|
||||
fn init(&mut self, rng: Rng, _now: Instant) {
|
||||
self.rng = Some(rng);
|
||||
}
|
||||
|
||||
// Pass any datagram provided directly out, but drop some of them.
|
||||
fn process(&mut self, d: Option<Datagram>, _now: Instant) -> Output {
|
||||
if let Some(dgram) = d {
|
||||
if self.drop() {
|
||||
qtrace!("drop {}", dgram.len());
|
||||
Output::None
|
||||
} else {
|
||||
Output::Datagram(dgram)
|
||||
}
|
||||
} else {
|
||||
Output::None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Drop {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str("drop")
|
||||
}
|
||||
}
|
|
@ -1,232 +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.
|
||||
|
||||
// Tests with simulated network
|
||||
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
|
||||
#![warn(clippy::pedantic)]
|
||||
|
||||
pub mod connection;
|
||||
mod delay;
|
||||
mod drop;
|
||||
pub mod rng;
|
||||
mod taildrop;
|
||||
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
cmp::min,
|
||||
convert::TryFrom,
|
||||
fmt::Debug,
|
||||
rc::Rc,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use neqo_common::{qdebug, qinfo, qtrace, Datagram, Encoder};
|
||||
use neqo_transport::Output;
|
||||
use rng::Random;
|
||||
use test_fixture::{self, now};
|
||||
use NodeState::{Active, Idle, Waiting};
|
||||
|
||||
pub mod network {
|
||||
pub use super::{delay::Delay, drop::Drop, taildrop::TailDrop};
|
||||
}
|
||||
|
||||
type Rng = Rc<RefCell<Random>>;
|
||||
|
||||
/// A macro that turns a list of values into boxed versions of the same.
|
||||
#[macro_export]
|
||||
macro_rules! boxed {
|
||||
[$($v:expr),+ $(,)?] => {
|
||||
vec![ $( Box::new($v) as _ ),+ ]
|
||||
};
|
||||
}
|
||||
|
||||
/// Create a simulation test case. This takes either two or three arguments.
|
||||
/// The two argument form takes a bare name (`ident`), a comma, and an array of
|
||||
/// items that implement `Node`.
|
||||
/// The three argument form adds a setup block that can be used to construct a
|
||||
/// complex value that is then shared between all nodes. The values in the
|
||||
/// three-argument form have to be closures (or functions) that accept a reference
|
||||
/// to the value returned by the setup.
|
||||
#[macro_export]
|
||||
macro_rules! simulate {
|
||||
($n:ident, [ $($v:expr),+ $(,)? ] $(,)?) => {
|
||||
simulate!($n, (), [ $(|_| $v),+ ]);
|
||||
};
|
||||
($n:ident, $setup:expr, [ $( $v:expr ),+ $(,)? ] $(,)?) => {
|
||||
#[test]
|
||||
fn $n() {
|
||||
let fixture = $setup;
|
||||
let mut nodes: Vec<Box<dyn $crate::sim::Node>> = Vec::new();
|
||||
$(
|
||||
let f: Box<dyn FnOnce(&_) -> _> = Box::new($v);
|
||||
nodes.push(Box::new(f(&fixture)));
|
||||
)*
|
||||
let mut sim = Simulator::new(stringify!($n), nodes);
|
||||
if let Ok(seed) = std::env::var("SIMULATION_SEED") {
|
||||
sim.seed_str(seed);
|
||||
}
|
||||
sim.run();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub trait Node: Debug {
|
||||
fn init(&mut self, _rng: Rng, _now: Instant) {}
|
||||
/// Perform processing. This optionally takes a datagram and produces either
|
||||
/// another data, a time that the simulator needs to wait, or nothing.
|
||||
fn process(&mut self, d: Option<Datagram>, now: Instant) -> Output;
|
||||
/// An node can report when it considers itself "done".
|
||||
fn done(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn print_summary(&self, _test_name: &str) {}
|
||||
}
|
||||
|
||||
/// The state of a single node. Nodes will be activated if they are `Active`
|
||||
/// or if the previous node in the loop generated a datagram. Nodes that return
|
||||
/// `true` from `Node::done` will be activated as normal.
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum NodeState {
|
||||
/// The node just produced a datagram. It should be activated again as soon as possible.
|
||||
Active,
|
||||
/// The node is waiting.
|
||||
Waiting(Instant),
|
||||
/// The node became idle.
|
||||
Idle,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct NodeHolder {
|
||||
node: Box<dyn Node>,
|
||||
state: NodeState,
|
||||
}
|
||||
|
||||
impl NodeHolder {
|
||||
fn ready(&self, now: Instant) -> bool {
|
||||
match self.state {
|
||||
Active => true,
|
||||
Waiting(t) => t >= now,
|
||||
Idle => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Simulator {
|
||||
name: String,
|
||||
nodes: Vec<NodeHolder>,
|
||||
rng: Rng,
|
||||
}
|
||||
|
||||
impl Simulator {
|
||||
pub fn new(name: impl AsRef<str>, nodes: impl IntoIterator<Item = Box<dyn Node>>) -> Self {
|
||||
let name = String::from(name.as_ref());
|
||||
// The first node is marked as Active, the rest are idle.
|
||||
let mut it = nodes.into_iter();
|
||||
let nodes = it
|
||||
.next()
|
||||
.map(|node| NodeHolder {
|
||||
node,
|
||||
state: Active,
|
||||
})
|
||||
.into_iter()
|
||||
.chain(it.map(|node| NodeHolder { node, state: Idle }))
|
||||
.collect::<Vec<_>>();
|
||||
Self {
|
||||
name,
|
||||
nodes,
|
||||
rng: Rc::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn seed(&mut self, seed: [u8; 32]) {
|
||||
self.rng = Rc::new(RefCell::new(Random::new(seed)));
|
||||
}
|
||||
|
||||
/// Seed from a hex string.
|
||||
/// Though this is convenient, it panics if this isn't a 64 character hex string.
|
||||
pub fn seed_str(&mut self, seed: impl AsRef<str>) {
|
||||
let seed = Encoder::from_hex(seed);
|
||||
self.seed(<[u8; 32]>::try_from(seed.as_ref()).unwrap());
|
||||
}
|
||||
|
||||
fn next_time(&self, now: Instant) -> Instant {
|
||||
let mut next = None;
|
||||
for n in &self.nodes {
|
||||
match n.state {
|
||||
Idle => continue,
|
||||
Active => return now,
|
||||
Waiting(a) => next = Some(next.map_or(a, |b| min(a, b))),
|
||||
}
|
||||
}
|
||||
next.expect("a node cannot be idle and not done")
|
||||
}
|
||||
|
||||
/// Runs the simulation.
|
||||
pub fn run(mut self) -> Duration {
|
||||
let start = now();
|
||||
let mut now = start;
|
||||
let mut dgram = None;
|
||||
|
||||
for n in &mut self.nodes {
|
||||
n.node.init(self.rng.clone(), now);
|
||||
}
|
||||
println!("{}: seed {}", self.name, self.rng.borrow().seed_str());
|
||||
|
||||
let real_start = Instant::now();
|
||||
loop {
|
||||
for n in &mut self.nodes {
|
||||
if dgram.is_none() && !n.ready(now) {
|
||||
qdebug!([self.name], "skipping {:?}", n.node);
|
||||
continue;
|
||||
}
|
||||
|
||||
qdebug!([self.name], "processing {:?}", n.node);
|
||||
let res = n.node.process(dgram.take(), now);
|
||||
n.state = match res {
|
||||
Output::Datagram(d) => {
|
||||
qtrace!([self.name], " => datagram {}", d.len());
|
||||
dgram = Some(d);
|
||||
Active
|
||||
}
|
||||
Output::Callback(delay) => {
|
||||
qtrace!([self.name], " => callback {:?}", delay);
|
||||
assert_ne!(delay, Duration::new(0, 0));
|
||||
Waiting(now + delay)
|
||||
}
|
||||
Output::None => {
|
||||
qtrace!([self.name], " => nothing");
|
||||
assert!(n.node.done(), "nodes have to be done when they go idle");
|
||||
Idle
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if self.nodes.iter().all(|n| n.node.done()) {
|
||||
let real_elapsed = real_start.elapsed();
|
||||
println!("{}: real elapsed time: {:?}", self.name, real_elapsed);
|
||||
let elapsed = now - start;
|
||||
println!("{}: simulated elapsed time: {:?}", self.name, elapsed);
|
||||
for n in &self.nodes {
|
||||
n.node.print_summary(&self.name);
|
||||
}
|
||||
return elapsed;
|
||||
}
|
||||
|
||||
if dgram.is_none() {
|
||||
let next = self.next_time(now);
|
||||
if next > now {
|
||||
qinfo!(
|
||||
[self.name],
|
||||
"advancing time by {:?} to {:?}",
|
||||
next - now,
|
||||
next - start
|
||||
);
|
||||
now = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,111 +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.
|
||||
|
||||
use super::rng::RandomDuration;
|
||||
use super::{Node, Rng};
|
||||
use neqo_common::Datagram;
|
||||
use neqo_transport::Output;
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt::{self, Debug};
|
||||
use std::iter;
|
||||
use std::ops::Range;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
///
|
||||
pub struct RandomDrop {
|
||||
threshold: u64,
|
||||
max: u64,
|
||||
rng: Rng,
|
||||
}
|
||||
|
||||
impl RandomDuration {
|
||||
/// Make a new random `Duration` generator. This asserts if the range provided
|
||||
/// is inverted (i.e., `bounds.start > bounds.end`), or spans 2^64
|
||||
/// or more nanoseconds.
|
||||
/// A zero-length range means that random values won't be taken from the Rng
|
||||
pub fn new(bounds: Range<Duration>, rng: Rng) -> Self {
|
||||
let max = u64::try_from((bounds.end - bounds.start).as_nanos()).unwrap();
|
||||
Self {
|
||||
start: bounds.start,
|
||||
max,
|
||||
rng,
|
||||
}
|
||||
}
|
||||
|
||||
fn next(&mut self) -> Duration {
|
||||
let r = if self.max == 0 {
|
||||
Duration::new(0, 0)
|
||||
} else {
|
||||
self.rng.borrow_mut().random_from(0..self.max)
|
||||
}
|
||||
self.start + Duration::from_nanos(r)
|
||||
}
|
||||
}
|
||||
|
||||
enum DelayState {
|
||||
New(Range<Duration>),
|
||||
Ready(RandomDuration),
|
||||
}
|
||||
|
||||
pub struct Delay {
|
||||
state: DelayState,
|
||||
queue: BTreeMap<Instant, Datagram>,
|
||||
}
|
||||
|
||||
impl Delay
|
||||
{
|
||||
pub fn new(bounds: Range<Duration>) -> Self
|
||||
{
|
||||
Self {
|
||||
State: DelayState::New(bounds),
|
||||
queue: BTreeMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn insert(&mut self, d: Datagram, now: Instant) {
|
||||
let mut t = if let State::Ready(r) = self.state {
|
||||
now + self.source.next()
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
while self.queue.contains_key(&t) {
|
||||
// This is a little inefficient, but it avoids drops on collisions,
|
||||
// which are super-common for a fixed delay.
|
||||
t += Duration::from_nanos(1);
|
||||
}
|
||||
self.queue.insert(t, d);
|
||||
}
|
||||
}
|
||||
|
||||
impl Node for Delay
|
||||
{
|
||||
fn init(&mut self, rng: Rng, now: Instant) {
|
||||
if let DelayState::New(bounds) = self.state {
|
||||
self.state = RandomDuration::new(bounds);
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
fn process(&mut self, d: Option<Datagram>, now: Instant) -> Output {
|
||||
if let Some(dgram) = d {
|
||||
self.insert(dgram, now);
|
||||
}
|
||||
if let Some((&k, _)) = self.queue.range(..now).nth(0) {
|
||||
Output::Datagram(self.queue.remove(&k).unwrap())
|
||||
} else if let Some(&t) = self.queue.keys().nth(0) {
|
||||
Output::Callback(t - now)
|
||||
} else {
|
||||
Output::None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Debug for Delay<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str("delay")
|
||||
}
|
||||
}
|
|
@ -1,81 +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.
|
||||
|
||||
use std::{convert::TryFrom, ops::Range};
|
||||
|
||||
use neqo_common::Decoder;
|
||||
|
||||
/// An implementation of a xoshiro256** pseudorandom generator.
|
||||
pub struct Random {
|
||||
state: [u64; 4],
|
||||
}
|
||||
|
||||
impl Random {
|
||||
pub fn new(seed: [u8; 32]) -> Self {
|
||||
assert!(seed.iter().any(|&x| x != 0));
|
||||
let mut dec = Decoder::from(&seed);
|
||||
Self {
|
||||
state: [
|
||||
dec.decode_uint(8).unwrap(),
|
||||
dec.decode_uint(8).unwrap(),
|
||||
dec.decode_uint(8).unwrap(),
|
||||
dec.decode_uint(8).unwrap(),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn random(&mut self) -> u64 {
|
||||
let result = (self.state[1].overflowing_mul(5).0)
|
||||
.rotate_right(7)
|
||||
.overflowing_mul(9)
|
||||
.0;
|
||||
let t = self.state[1] << 17;
|
||||
|
||||
self.state[2] ^= self.state[0];
|
||||
self.state[3] ^= self.state[1];
|
||||
self.state[1] ^= self.state[2];
|
||||
self.state[0] ^= self.state[3];
|
||||
|
||||
self.state[2] ^= t;
|
||||
self.state[3] = self.state[3].rotate_right(45);
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// Generate a random value from the range.
|
||||
/// If the range is empty or inverted (`range.start > range.end`), then
|
||||
/// this returns the value of `range.start` without generating any random values.
|
||||
pub fn random_from(&mut self, range: Range<u64>) -> u64 {
|
||||
let max = range.end.saturating_sub(range.start);
|
||||
if max == 0 {
|
||||
return range.start;
|
||||
}
|
||||
|
||||
let shift = (max - 1).leading_zeros();
|
||||
assert_ne!(max, 0);
|
||||
loop {
|
||||
let r = self.random() >> shift;
|
||||
if r < max {
|
||||
return range.start + r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the seed necessary to continue from this point.
|
||||
pub fn seed_str(&self) -> String {
|
||||
format!(
|
||||
"{:8x}{:8x}{:8x}{:8x}",
|
||||
self.state[0], self.state[1], self.state[2], self.state[3],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Random {
|
||||
fn default() -> Self {
|
||||
let buf = neqo_crypto::random(32);
|
||||
Random::new(<[u8; 32]>::try_from(&buf[..]).unwrap())
|
||||
}
|
||||
}
|
|
@ -1,188 +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.
|
||||
|
||||
#![allow(clippy::module_name_repetitions)]
|
||||
|
||||
use std::{
|
||||
cmp::max,
|
||||
collections::VecDeque,
|
||||
convert::TryFrom,
|
||||
fmt::{self, Debug},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use neqo_common::{qtrace, Datagram};
|
||||
use neqo_transport::Output;
|
||||
|
||||
use super::Node;
|
||||
|
||||
/// One second in nanoseconds.
|
||||
const ONE_SECOND_NS: u128 = 1_000_000_000;
|
||||
|
||||
/// This models a link with a tail drop router at the front of it.
|
||||
pub struct TailDrop {
|
||||
/// An overhead associated with each entry. This accounts for
|
||||
/// layer 2, IP, and UDP overheads.
|
||||
overhead: usize,
|
||||
/// The rate at which bytes egress the link, in bytes per second.
|
||||
rate: usize,
|
||||
/// The depth of the queue, in bytes.
|
||||
capacity: usize,
|
||||
|
||||
/// A counter for how many bytes are enqueued.
|
||||
used: usize,
|
||||
/// A queue of unsent bytes.
|
||||
queue: VecDeque<Datagram>,
|
||||
/// The time that the next datagram can enter the link.
|
||||
next_deque: Option<Instant>,
|
||||
|
||||
/// Any sub-ns delay from the last enqueue.
|
||||
sub_ns_delay: u32,
|
||||
/// The time it takes a byte to exit the other end of the link.
|
||||
delay: Duration,
|
||||
/// The packets that are on the link and when they can be delivered.
|
||||
on_link: VecDeque<(Instant, Datagram)>,
|
||||
|
||||
/// The number of packets received.
|
||||
received: usize,
|
||||
/// The number of packets dropped.
|
||||
dropped: usize,
|
||||
/// The number of packets delivered.
|
||||
delivered: usize,
|
||||
/// The maximum amount of queue capacity ever used.
|
||||
/// As packets leave the queue as soon as they start being used, this doesn't
|
||||
/// count them.
|
||||
maxq: usize,
|
||||
}
|
||||
|
||||
impl TailDrop {
|
||||
/// Make a new taildrop node with the given rate, queue capacity, and link delay.
|
||||
pub fn new(rate: usize, capacity: usize, delay: Duration) -> Self {
|
||||
Self {
|
||||
overhead: 64,
|
||||
rate,
|
||||
capacity,
|
||||
used: 0,
|
||||
queue: VecDeque::new(),
|
||||
next_deque: None,
|
||||
sub_ns_delay: 0,
|
||||
delay,
|
||||
on_link: VecDeque::new(),
|
||||
received: 0,
|
||||
dropped: 0,
|
||||
delivered: 0,
|
||||
maxq: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// A tail drop queue on a 10Mbps link (approximated to 1 million bytes per second)
|
||||
/// with a fat 32k buffer (about 30ms), and the default forward delay of 50ms.
|
||||
pub fn dsl_uplink() -> Self {
|
||||
TailDrop::new(1_000_000, 32_768, Duration::from_millis(50))
|
||||
}
|
||||
|
||||
/// Cut downlink to one fifth of the uplink (2Mbps), and reduce the buffer to 1/4.
|
||||
pub fn dsl_downlink() -> Self {
|
||||
TailDrop::new(200_000, 8_192, Duration::from_millis(50))
|
||||
}
|
||||
|
||||
/// How "big" is this datagram, accounting for overheads.
|
||||
/// This approximates by using the same overhead for storing in the queue
|
||||
/// and for sending on the wire.
|
||||
fn size(&self, d: &Datagram) -> usize {
|
||||
d.len() + self.overhead
|
||||
}
|
||||
|
||||
/// Start sending a datagram.
|
||||
fn send(&mut self, d: Datagram, now: Instant) {
|
||||
// How many bytes are we "transmitting"?
|
||||
let sz = u128::try_from(self.size(&d)).unwrap();
|
||||
|
||||
// Calculate how long it takes to put the packet on the link.
|
||||
// Perform the calculation based on 2^32 seconds and save any remainder.
|
||||
// This ensures that high rates and small packets don't result in rounding
|
||||
// down times too badly.
|
||||
// Duration consists of a u64 and a u32, so we have 32 high bits to spare.
|
||||
let t = sz * (ONE_SECOND_NS << 32) / u128::try_from(self.rate).unwrap()
|
||||
+ u128::from(self.sub_ns_delay);
|
||||
let send_ns = u64::try_from(t >> 32).unwrap();
|
||||
assert_ne!(send_ns, 0, "sending a packet takes <1ns");
|
||||
self.sub_ns_delay = u32::try_from(t & u128::from(u32::MAX)).unwrap();
|
||||
let deque_time = now + Duration::from_nanos(send_ns);
|
||||
self.next_deque = Some(deque_time);
|
||||
|
||||
// Now work out when the packet is fully received at the other end of
|
||||
// the link. Setup to deliver the packet then.
|
||||
let delivery_time = deque_time + self.delay;
|
||||
self.on_link.push_back((delivery_time, d));
|
||||
}
|
||||
|
||||
/// Enqueue for sending. Maybe. If this overflows the queue, drop it instead.
|
||||
fn maybe_enqueue(&mut self, d: Datagram, now: Instant) {
|
||||
self.received += 1;
|
||||
if self.next_deque.is_none() {
|
||||
// Nothing in the queue and nothing still sending.
|
||||
debug_assert!(self.queue.is_empty());
|
||||
self.send(d, now);
|
||||
} else if self.used + self.size(&d) <= self.capacity {
|
||||
self.used += self.size(&d);
|
||||
self.maxq = max(self.maxq, self.used);
|
||||
self.queue.push_back(d);
|
||||
} else {
|
||||
qtrace!("taildrop dropping {} bytes", d.len());
|
||||
self.dropped += 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// If the last packet that was sending has been sent, start sending
|
||||
/// the next one.
|
||||
fn maybe_send(&mut self, now: Instant) {
|
||||
if self.next_deque.as_ref().map_or(false, |t| *t <= now) {
|
||||
if let Some(d) = self.queue.pop_front() {
|
||||
self.used -= self.size(&d);
|
||||
self.send(d, now);
|
||||
} else {
|
||||
self.next_deque = None;
|
||||
self.sub_ns_delay = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Node for TailDrop {
|
||||
fn process(&mut self, d: Option<Datagram>, now: Instant) -> Output {
|
||||
if let Some(dgram) = d {
|
||||
self.maybe_enqueue(dgram, now);
|
||||
}
|
||||
|
||||
self.maybe_send(now);
|
||||
|
||||
if let Some((t, _)) = self.on_link.front() {
|
||||
if *t <= now {
|
||||
let (_, d) = self.on_link.pop_front().unwrap();
|
||||
self.delivered += 1;
|
||||
Output::Datagram(d)
|
||||
} else {
|
||||
Output::Callback(*t - now)
|
||||
}
|
||||
} else {
|
||||
Output::None
|
||||
}
|
||||
}
|
||||
|
||||
fn print_summary(&self, test_name: &str) {
|
||||
println!(
|
||||
"{}: taildrop: rx {} drop {} tx {} maxq {}",
|
||||
test_name, self.received, self.dropped, self.delivered, self.maxq,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for TailDrop {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str("taildrop")
|
||||
}
|
||||
}
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"15c2606defff66515f2ded1ace2aeb729229a3558d9a026c058e51a7518e6859","README.md":"597691eb766c2cbd7a6591bda56d3e70e6836b62b6327fb73497523eabd5b53d","src/events/connectivity.rs":"116993412e200e375c97762980ffb638d2244197fd752b9569b5b20baf574308","src/events/h3.rs":"45dfa1dea722f3c8adb989f04ff24e8c39550a65a35325885b3a915cafd3a550","src/events/mod.rs":"75f57b4717fa9777e19d61b99b6a79164f0e8bca9b4681c3ab11b204320c8c55","src/events/qpack.rs":"5c7267c45e3fb947cdfa946f9f9692d3e3e36a166f70124ba293dc27534267d0","src/events/quic.rs":"88b884f5788c671ffee79a3448f367c18f95ee30531262fcc14310d80e662f4a","src/events/security.rs":"e9852d7de16851b62c3e0a886a2c1a31d237e62574ef88428ef62dd179b0b008","src/lib.rs":"bbc190a6d0f484fd723f9df6c1b2a4596f826e0282ad40ee17a0822ea28a5626","src/reader.rs":"4e0069c24aca9cb99d75075c9b784fa02855ea449d2f1528bea944a4e02a9af5","src/streamer.rs":"4774c2abde1a5b0f4448aac06c62c7927208c12f338c46981f80c98703b54074"},"package":null}
|
||||
{"files":{"Cargo.toml":"66c6d85a84c84711f3abddaafb163bc6025a8505701fea8b61bfa961851be757","README.md":"597691eb766c2cbd7a6591bda56d3e70e6836b62b6327fb73497523eabd5b53d","src/events/connectivity.rs":"116993412e200e375c97762980ffb638d2244197fd752b9569b5b20baf574308","src/events/h3.rs":"4799179c4d18403a3936a327c483d110bd058d42061f9cdd39601b3e91500c1b","src/events/mod.rs":"75f57b4717fa9777e19d61b99b6a79164f0e8bca9b4681c3ab11b204320c8c55","src/events/qpack.rs":"5c7267c45e3fb947cdfa946f9f9692d3e3e36a166f70124ba293dc27534267d0","src/events/quic.rs":"88b884f5788c671ffee79a3448f367c18f95ee30531262fcc14310d80e662f4a","src/events/security.rs":"e9852d7de16851b62c3e0a886a2c1a31d237e62574ef88428ef62dd179b0b008","src/lib.rs":"bbc190a6d0f484fd723f9df6c1b2a4596f826e0282ad40ee17a0822ea28a5626","src/reader.rs":"4e0069c24aca9cb99d75075c9b784fa02855ea449d2f1528bea944a4e02a9af5","src/streamer.rs":"4774c2abde1a5b0f4448aac06c62c7927208c12f338c46981f80c98703b54074"},"package":"9c0407438c69b3d99714a796a135cbfb2d60744e4747fb2b46a87acd1c1fcd0e"}
|
|
@ -12,7 +12,7 @@
|
|||
[package]
|
||||
edition = "2018"
|
||||
name = "qlog"
|
||||
version = "0.11.0"
|
||||
version = "0.12.0"
|
||||
authors = ["Lucas Pardue <lucaspardue.24.7@gmail.com>"]
|
||||
description = "qlog data model for QUIC and HTTP/3"
|
||||
readme = "README.md"
|
||||
|
@ -25,13 +25,13 @@ categories = ["network-programming"]
|
|||
license = "BSD-2-Clause"
|
||||
repository = "https://github.com/cloudflare/quiche"
|
||||
|
||||
[dependencies]
|
||||
serde_derive = "1.0"
|
||||
|
||||
[dependencies.serde]
|
||||
version = "1.0.139"
|
||||
features = ["derive"]
|
||||
|
||||
[dependencies.serde_derive]
|
||||
version = "1.0"
|
||||
|
||||
[dependencies.serde_json]
|
||||
version = "1.0"
|
||||
features = ["preserve_order"]
|
||||
|
|
|
@ -209,9 +209,8 @@ pub struct H3ParametersRestored {
|
|||
pub struct H3StreamTypeSet {
|
||||
pub owner: Option<H3Owner>,
|
||||
pub stream_id: u64,
|
||||
|
||||
pub stream_type: H3StreamType,
|
||||
|
||||
pub stream_type_value: Option<u64>,
|
||||
pub associated_push_id: Option<u64>,
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче