Bug 1698438 - Neqo version 0.4.22 r=necko-reviewers,dragana

Differential Revision: https://phabricator.services.mozilla.com/D108750
This commit is contained in:
Kershaw Chang 2021-03-17 12:15:54 +00:00
parent de0da5772d
commit cb78102ea6
67 changed files with 4681 additions and 1937 deletions

View File

@ -15,7 +15,7 @@ rev = "01c7a0da8d34059f7dae8ab9e7512529ff16347a"
[source."https://github.com/mozilla/neqo"]
git = "https://github.com/mozilla/neqo"
replace-with = "vendored-sources"
tag = "v0.4.21"
tag = "v0.4.22"
[source."https://github.com/mozilla/mp4parse-rust"]
git = "https://github.com/mozilla/mp4parse-rust"

20
Cargo.lock generated
View File

@ -3336,8 +3336,8 @@ dependencies = [
[[package]]
name = "neqo-common"
version = "0.4.21"
source = "git+https://github.com/mozilla/neqo?tag=v0.4.21#0e6040cd6385c038f8b7f604bcb09ad74d29e0d6"
version = "0.4.22"
source = "git+https://github.com/mozilla/neqo?tag=v0.4.22#33a8796b2606b5c37430a8804cae17e374f6dbfe"
dependencies = [
"chrono",
"env_logger",
@ -3348,8 +3348,8 @@ dependencies = [
[[package]]
name = "neqo-crypto"
version = "0.4.21"
source = "git+https://github.com/mozilla/neqo?tag=v0.4.21#0e6040cd6385c038f8b7f604bcb09ad74d29e0d6"
version = "0.4.22"
source = "git+https://github.com/mozilla/neqo?tag=v0.4.22#33a8796b2606b5c37430a8804cae17e374f6dbfe"
dependencies = [
"bindgen",
"log",
@ -3361,8 +3361,8 @@ dependencies = [
[[package]]
name = "neqo-http3"
version = "0.4.21"
source = "git+https://github.com/mozilla/neqo?tag=v0.4.21#0e6040cd6385c038f8b7f604bcb09ad74d29e0d6"
version = "0.4.22"
source = "git+https://github.com/mozilla/neqo?tag=v0.4.22#33a8796b2606b5c37430a8804cae17e374f6dbfe"
dependencies = [
"log",
"neqo-common",
@ -3375,8 +3375,8 @@ dependencies = [
[[package]]
name = "neqo-qpack"
version = "0.4.21"
source = "git+https://github.com/mozilla/neqo?tag=v0.4.21#0e6040cd6385c038f8b7f604bcb09ad74d29e0d6"
version = "0.4.22"
source = "git+https://github.com/mozilla/neqo?tag=v0.4.22#33a8796b2606b5c37430a8804cae17e374f6dbfe"
dependencies = [
"lazy_static",
"log",
@ -3389,8 +3389,8 @@ dependencies = [
[[package]]
name = "neqo-transport"
version = "0.4.21"
source = "git+https://github.com/mozilla/neqo?tag=v0.4.21#0e6040cd6385c038f8b7f604bcb09ad74d29e0d6"
version = "0.4.22"
source = "git+https://github.com/mozilla/neqo?tag=v0.4.22#33a8796b2606b5c37430a8804cae17e374f6dbfe"
dependencies = [
"indexmap",
"lazy_static",

View File

@ -8,10 +8,10 @@ edition = "2018"
name = "neqo_glue"
[dependencies]
neqo-http3 = { tag = "v0.4.21", git = "https://github.com/mozilla/neqo" }
neqo-transport = { tag = "v0.4.21", git = "https://github.com/mozilla/neqo" }
neqo-common = { tag = "v0.4.21", git = "https://github.com/mozilla/neqo" }
neqo-qpack = { tag = "v0.4.21", git = "https://github.com/mozilla/neqo" }
neqo-http3 = { tag = "v0.4.22", git = "https://github.com/mozilla/neqo" }
neqo-transport = { tag = "v0.4.22", git = "https://github.com/mozilla/neqo" }
neqo-common = { tag = "v0.4.22", git = "https://github.com/mozilla/neqo" }
neqo-qpack = { tag = "v0.4.22", git = "https://github.com/mozilla/neqo" }
nserror = { path = "../../../xpcom/rust/nserror" }
nsstring = { path = "../../../xpcom/rust/nsstring" }
xpcom = { path = "../../../xpcom/rust/xpcom" }
@ -20,7 +20,7 @@ log = "0.4.0"
qlog = "0.4.0"
[dependencies.neqo-crypto]
tag = "v0.4.21"
tag = "v0.4.22"
git = "https://github.com/mozilla/neqo"
default-features = false
features = ["gecko"]

View File

@ -5,16 +5,16 @@ authors = ["Dragana Damjanovic <dragana.damjano@gmail.com>"]
edition = "2018"
[dependencies]
neqo-transport = { tag = "v0.4.21", git = "https://github.com/mozilla/neqo" }
neqo-common = { tag = "v0.4.21", git = "https://github.com/mozilla/neqo" }
neqo-http3 = { tag = "v0.4.21", git = "https://github.com/mozilla/neqo" }
neqo-qpack = { tag = "v0.4.21", git = "https://github.com/mozilla/neqo" }
neqo-transport = { tag = "v0.4.22", git = "https://github.com/mozilla/neqo" }
neqo-common = { tag = "v0.4.22", git = "https://github.com/mozilla/neqo" }
neqo-http3 = { tag = "v0.4.22", git = "https://github.com/mozilla/neqo" }
neqo-qpack = { tag = "v0.4.22", git = "https://github.com/mozilla/neqo" }
mio = "0.6.17"
mio-extras = "2.0.5"
log = "0.4.0"
[dependencies.neqo-crypto]
tag = "v0.4.21"
tag = "v0.4.22"
git = "https://github.com/mozilla/neqo"
default-features = false
features = ["gecko"]

View File

@ -1 +1 @@
{"files":{"Cargo.toml":"1c3a6822f7e9c513214ad1f0be4eafc0aa17cb6272ef38dfd716b37c93ef74f8","src/codec.rs":"1cba28857d97c1f9021941f1220cecaba736bd5d8907cee6e9a3055735c06568","src/datagram.rs":"569f8d9e34d7ee17144bf63d34136ecd9778da0d337e513f338738c50284615e","src/event.rs":"f60fee9f4b09ef47ff5e4bfa21c07e45ffd5873c292f2605f24d834070127d62","src/incrdecoder.rs":"b97a40f89da6832ad92bd652cb6ceac82a0a5cc68a9b3d0c96f89d02e1ee9902","src/lib.rs":"5af4f0e7284b49d1b03b5eee78f2b814e5fa6eb9424507291e25b1955aebe007","src/log.rs":"b69e492af85e65866cb6588138e8a337dd897d3ce399cb4e9fb8cc04ac042b7f","src/qlog.rs":"a8aa4f1f0110076b401f6e5a7057ec154c7ad3677374a21ceca1209469b9c07d","src/timer.rs":"66886b3697e1b4232d9d9892a12d93afd3381812a8ff901bceac4bb48b264607","tests/log.rs":"480b165b7907ec642c508b303d63005eee1427115d6973a349eaf6b2242ed18d"},"package":null}
{"files":{"Cargo.toml":"3a8d7b338de5c01cacba0ada4b8bda9ba4ac8ed689a99727734ff6b477d492a9","build.rs":"f6d7d76e8fbe697bd094345d14075758fc011464a5fea1521376faf361e0fb7d","src/codec.rs":"32ffe9fc505637c469c3a84dec8035e245be93b8092594b80908987b9dd40534","src/datagram.rs":"569f8d9e34d7ee17144bf63d34136ecd9778da0d337e513f338738c50284615e","src/event.rs":"f60fee9f4b09ef47ff5e4bfa21c07e45ffd5873c292f2605f24d834070127d62","src/hrtime.rs":"609b38a466f0743617e8772e661e312089eb17a8df827b1893e6aadc8347e5b3","src/incrdecoder.rs":"b97a40f89da6832ad92bd652cb6ceac82a0a5cc68a9b3d0c96f89d02e1ee9902","src/lib.rs":"659d5397175c1645eab07a999b6ccf0730d02d9614b58720955af83afef0f0e4","src/log.rs":"b69e492af85e65866cb6588138e8a337dd897d3ce399cb4e9fb8cc04ac042b7f","src/qlog.rs":"e59c4e6dcf9c70553dd6f58da41ff2053ea67b008cac186742140352f5044130","src/timer.rs":"66886b3697e1b4232d9d9892a12d93afd3381812a8ff901bceac4bb48b264607","tests/log.rs":"480b165b7907ec642c508b303d63005eee1427115d6973a349eaf6b2242ed18d"},"package":null}

View File

@ -1,9 +1,10 @@
[package]
name = "neqo-common"
version = "0.4.21"
version = "0.4.22"
authors = ["Bobby Holley <bobbyholley@gmail.com>"]
edition = "2018"
license = "MIT/Apache-2.0"
build = "build.rs"
[dependencies]
log = {version = "0.4.0", default-features = false}

4
third_party/rust/neqo-common/build.rs vendored Normal file
View File

@ -0,0 +1,4 @@
fn main() {
#[cfg(windows)]
println!("cargo:rustc-link-lib=winmm");
}

View File

@ -764,9 +764,8 @@ mod tests {
enc.encode_vvec_with(|enc_inner| {
enc_inner.encode(&[0xa5; 65]);
});
let mut v: Vec<u8> = enc.into();
let _ = v.split_off(3);
assert_eq!(v, vec![0x40, 0x41, 0xa5]);
let v: Vec<u8> = enc.into();
assert_eq!(&v[..3], &[0x40, 0x41, 0xa5]);
}
// Test that Deref to &[u8] works for Encoder.

View File

@ -0,0 +1,484 @@
// 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::cell::RefCell;
use std::cmp::{max, min};
use std::convert::TryFrom;
use std::rc::{Rc, Weak};
use std::time::Duration;
/// A quantized `Duration`. This currently just produces 16 discrete values
/// corresponding to whole milliseconds. Future implementations might choose
/// a different allocation, such as a logarithmic scale.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
struct Period(u8);
impl Period {
const MAX: Period = Period(16);
const MIN: Period = Period(1);
#[cfg(windows)]
fn as_uint(&self) -> win::UINT {
win::UINT::from(self.0)
}
#[cfg(target_os = "macos")]
fn scaled(&self, scale: f64) -> f64 {
scale * f64::from(self.0)
}
}
impl From<Duration> for Period {
fn from(p: Duration) -> Self {
let rounded = u8::try_from(p.as_millis()).unwrap_or(Self::MAX.0);
Self(max(Self::MIN.0, min(rounded, Self::MAX.0)))
}
}
/// This counts instances of `Period`, except those of `Period::MAX`.
#[derive(Default)]
struct PeriodSet {
counts: [usize; (Period::MAX.0 - Period::MIN.0) as usize],
}
impl PeriodSet {
fn idx(&mut self, p: Period) -> &mut usize {
debug_assert!(p >= Period::MIN);
&mut self.counts[usize::from(p.0 - Period::MIN.0)]
}
fn add(&mut self, p: Period) {
if p != Period::MAX {
*self.idx(p) += 1;
}
}
fn remove(&mut self, p: Period) {
if p != Period::MAX {
debug_assert_ne!(*self.idx(p), 0);
*self.idx(p) -= 1;
}
}
fn min(&self) -> Option<Period> {
for (i, v) in self.counts.iter().enumerate() {
if *v > 0 {
return Some(Period(u8::try_from(i).unwrap() + Period::MIN.0));
}
}
None
}
}
#[cfg(windows)]
mod win {
// These are manually extracted from the 10Mb bindings generated
// by bindgen when provided with the simple header:
// #include <windows.h>
// #include <timeapi.h>
// The complete bindings don't compile and filtering them is work.
pub type UINT = ::std::os::raw::c_uint;
pub type MMRESULT = UINT;
extern "C" {
pub fn timeBeginPeriod(uPeriod: UINT) -> MMRESULT;
pub fn timeEndPeriod(uPeriod: UINT) -> MMRESULT;
}
}
#[cfg(target_os = "macos")]
#[allow(non_camel_case_types)]
mod mac {
use std::mem::size_of;
// These are manually extracted from the many bindings generated
// by bindgen when provided with the simple header:
// #include <mach/mach_init.h>
// #include <mach/mach_time.h>
// #include <mach/thread_policy.h>
// #include <pthread.h>
type __darwin_natural_t = ::std::os::raw::c_uint;
type __darwin_mach_port_name_t = __darwin_natural_t;
type __darwin_mach_port_t = __darwin_mach_port_name_t;
type mach_port_t = __darwin_mach_port_t;
type thread_t = mach_port_t;
type natural_t = __darwin_natural_t;
type thread_policy_flavor_t = natural_t;
type integer_t = ::std::os::raw::c_int;
type thread_policy_t = *mut integer_t;
type mach_msg_type_number_t = natural_t;
type boolean_t = ::std::os::raw::c_uint;
type kern_return_t = ::std::os::raw::c_int;
#[repr(C)]
#[derive(Debug, Copy, Clone, Default)]
struct mach_timebase_info {
numer: u32,
denom: u32,
}
type mach_timebase_info_t = *mut mach_timebase_info;
type mach_timebase_info_data_t = mach_timebase_info;
extern "C" {
fn mach_timebase_info(info: mach_timebase_info_t) -> kern_return_t;
}
#[repr(C)]
#[derive(Debug, Copy, Clone, Default)]
pub struct thread_time_constraint_policy {
period: u32,
computation: u32,
constraint: u32,
preemptible: boolean_t,
}
const THREAD_TIME_CONSTRAINT_POLICY: thread_policy_flavor_t = 2;
const THREAD_TIME_CONSTRAINT_POLICY_COUNT: mach_msg_type_number_t =
(size_of::<thread_time_constraint_policy>() / size_of::<integer_t>())
as mach_msg_type_number_t;
// These function definitions are taken from a comment in <thread_policy.h>.
// Why they are inaccessible is unknown, but they work as declared.
extern "C" {
fn thread_policy_set(
thread: thread_t,
flavor: thread_policy_flavor_t,
policy_info: thread_policy_t,
count: mach_msg_type_number_t,
) -> kern_return_t;
fn thread_policy_get(
thread: thread_t,
flavor: thread_policy_flavor_t,
policy_info: thread_policy_t,
count: *mut mach_msg_type_number_t,
get_default: *mut boolean_t,
) -> kern_return_t;
}
enum _opaque_pthread_t {} // An opaque type is fine here.
type __darwin_pthread_t = *mut _opaque_pthread_t;
type pthread_t = __darwin_pthread_t;
extern "C" {
fn pthread_self() -> pthread_t;
fn pthread_mach_thread_np(thread: pthread_t) -> mach_port_t;
}
/// Set a thread time policy.
pub fn set_thread_policy(mut policy: thread_time_constraint_policy) {
let _ = unsafe {
thread_policy_set(
pthread_mach_thread_np(pthread_self()),
THREAD_TIME_CONSTRAINT_POLICY,
&mut policy as *mut thread_time_constraint_policy as *mut _,
THREAD_TIME_CONSTRAINT_POLICY_COUNT,
)
};
}
pub fn get_scale() -> f64 {
const NANOS_PER_MSEC: f64 = 1_000_000.0;
let mut timebase_info = mach_timebase_info_data_t::default();
unsafe {
mach_timebase_info(&mut timebase_info);
}
f64::from(timebase_info.denom) * NANOS_PER_MSEC / f64::from(timebase_info.numer)
}
/// Create a realtime policy and set it.
pub fn set_realtime(base: f64) {
let policy = thread_time_constraint_policy {
period: base as u32, // Base interval
computation: (base * 0.5) as u32,
constraint: (base * 1.0) as u32,
preemptible: 1,
};
set_thread_policy(policy);
}
/// Get the default policy.
pub fn get_default_policy() -> thread_time_constraint_policy {
let mut policy = thread_time_constraint_policy::default();
let mut count = THREAD_TIME_CONSTRAINT_POLICY_COUNT;
let mut get_default = 0;
let _ = unsafe {
thread_policy_get(
pthread_mach_thread_np(pthread_self()),
THREAD_TIME_CONSTRAINT_POLICY,
&mut policy as *mut thread_time_constraint_policy as *mut _,
&mut count,
&mut get_default,
)
};
policy
}
}
/// A handle for a high-resolution timer of a specific period.
pub struct Handle {
hrt: Rc<RefCell<Time>>,
active: Period,
hysteresis: [Period; Self::HISTORY],
hysteresis_index: usize,
}
impl Handle {
const HISTORY: usize = 8;
fn new(hrt: Rc<RefCell<Time>>, active: Period) -> Self {
Self {
hrt,
active,
hysteresis: [Period::MAX; Self::HISTORY],
hysteresis_index: 0,
}
}
/// Update shortcut. Equivalent to dropping the current reference and
/// calling `HrTime::get` again with the new period, except that this applies
/// a little hysteresis that smoothes out fluctuations.
pub fn update(&mut self, period: Duration) {
self.hysteresis[self.hysteresis_index] = Period::from(period);
self.hysteresis_index += 1;
self.hysteresis_index %= self.hysteresis.len();
let mut first = Period::MAX;
let mut second = Period::MAX;
for i in &self.hysteresis {
if *i < first {
second = first;
first = *i;
} else if *i < second {
second = *i;
}
}
if second != self.active {
let mut b = self.hrt.borrow_mut();
b.periods.remove(self.active);
self.active = second;
b.periods.add(self.active);
b.update();
}
}
}
impl Drop for Handle {
fn drop(&mut self) {
self.hrt.borrow_mut().remove(self.active);
}
}
/// Holding an instance of this indicates that high resolution timers are enabled.
pub struct Time {
periods: PeriodSet,
active: Option<Period>,
#[cfg(target_os = "macos")]
scale: f64,
#[cfg(target_os = "macos")]
deflt: mac::thread_time_constraint_policy,
}
impl Time {
fn new() -> Self {
Self {
periods: PeriodSet::default(),
active: None,
#[cfg(target_os = "macos")]
scale: mac::get_scale(),
#[cfg(target_os = "macos")]
deflt: mac::get_default_policy(),
}
}
#[allow(clippy::unused_self)] // Only on some platforms is it unused.
fn start(&self) {
#[cfg(target_os = "macos")]
{
if let Some(p) = self.active {
mac::set_realtime(p.scaled(self.scale));
} else {
mac::set_thread_policy(self.deflt.clone());
}
}
#[cfg(windows)]
{
if let Some(p) = self.active {
assert_eq!(0, unsafe { win::timeBeginPeriod(p.as_uint()) });
}
}
}
#[allow(clippy::unused_self)] // Only on some platforms is it unused.
fn stop(&self) {
#[cfg(windows)]
{
if let Some(p) = self.active {
assert_eq!(0, unsafe { win::timeEndPeriod(p.as_uint()) });
}
}
}
fn update(&mut self) {
let next = self.periods.min();
if next != self.active {
self.stop();
self.active = next;
self.start();
}
}
fn add(&mut self, p: Period) {
self.periods.add(p);
self.update();
}
fn remove(&mut self, p: Period) {
self.periods.remove(p);
self.update();
}
/// Enable high resolution time. Returns a thread-bound handle that
/// needs to be held until the high resolution time is no longer needed.
/// The handle can also be used to update the resolution.
#[must_use]
pub fn get(period: Duration) -> Handle {
thread_local! {
static HR_TIME: RefCell<Weak<RefCell<Time>>> = RefCell::default();
}
HR_TIME.with(|r| {
let mut b = r.borrow_mut();
let hrt = b.upgrade().unwrap_or_else(|| {
let hrt = Rc::new(RefCell::new(Time::new()));
*b = Rc::downgrade(&hrt);
hrt
});
let p = Period::from(period);
hrt.borrow_mut().add(p);
Handle::new(hrt, p)
})
}
}
impl Drop for Time {
fn drop(&mut self) {
self.stop();
#[cfg(target_os = "macos")]
{
if self.active.is_some() {
mac::set_thread_policy(self.deflt);
}
}
}
}
#[cfg(test)]
mod test {
use super::Time;
use std::thread::{sleep, spawn};
use std::time::{Duration, Instant};
const ONE: Duration = Duration::from_millis(1);
const ONE_AND_A_BIT: Duration = Duration::from_micros(1500);
/// A limit for when high resolution timers are disabled.
const GENEROUS: Duration = Duration::from_millis(30);
fn validate_delays(max_lag: Duration) -> Result<(), ()> {
const DELAYS: &[u64] = &[1, 2, 3, 5, 8, 10, 12, 15, 20, 25, 30];
let durations = DELAYS.iter().map(|&d| Duration::from_millis(d));
let mut s = Instant::now();
for d in durations {
sleep(d);
let e = Instant::now();
let actual = e - s;
let lag = actual - d;
println!("sleep({:?}) \u{2192} {:?} \u{394}{:?}", d, actual, lag);
if lag > max_lag {
return Err(());
}
s = Instant::now();
}
Ok(())
}
/// Validate the delays twice. Sometimes the first run can stall.
/// Reliability in CI is more important than reliable timers.
fn check_delays(max_lag: Duration) {
if validate_delays(max_lag).is_err() {
sleep(Duration::from_millis(50));
validate_delays(max_lag).unwrap();
}
}
/// Note that you have to run this test alone or other tests will
/// grab the high resolution timer and this will run faster.
#[test]
fn baseline() {
check_delays(GENEROUS);
}
#[test]
fn one_ms() {
let _hrt = Time::get(ONE);
check_delays(ONE_AND_A_BIT);
}
#[test]
fn multithread_baseline() {
let thr = spawn(move || {
baseline();
});
baseline();
thr.join().unwrap();
}
#[test]
fn one_ms_multi() {
let thr = spawn(move || {
one_ms();
});
one_ms();
thr.join().unwrap();
}
#[test]
fn mixed_multi() {
let thr = spawn(move || {
one_ms();
});
let _hrt = Time::get(Duration::from_millis(4));
check_delays(Duration::from_millis(5));
thr.join().unwrap();
}
#[test]
fn update() {
let mut hrt = Time::get(Duration::from_millis(4));
check_delays(Duration::from_millis(5));
hrt.update(ONE);
check_delays(ONE_AND_A_BIT);
}
#[test]
fn update_multi() {
let thr = spawn(move || {
update();
});
update();
thr.join().unwrap();
}
#[test]
fn max() {
let _hrt = Time::get(Duration::from_secs(1));
check_delays(GENEROUS);
}
}

View File

@ -10,6 +10,7 @@
mod codec;
mod datagram;
pub mod event;
pub mod hrtime;
mod incrdecoder;
pub mod log;
pub mod qlog;

View File

@ -19,7 +19,7 @@ use qlog::{
use crate::Role;
#[allow(clippy::module_name_repetitions)]
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Default)]
pub struct NeqoQlog {
inner: Rc<RefCell<Option<NeqoQlogShared>>>,
}
@ -51,9 +51,7 @@ impl NeqoQlog {
/// Create a disabled `NeqoQlog` configuration.
#[must_use]
pub fn disabled() -> Self {
Self {
inner: Rc::new(RefCell::new(None)),
}
Self::default()
}
/// If logging enabled, closure may generate an event to be logged.
@ -88,12 +86,6 @@ impl NeqoQlog {
}
}
impl Default for NeqoQlog {
fn default() -> Self {
Self::disabled()
}
}
impl fmt::Debug for NeqoQlogShared {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "NeqoQlog writing to {}", self.qlog_path.display())

View File

@ -1 +1 @@
{"files":{"Cargo.toml":"7d7c790d2c56b305869e1c9ef96435102b2b43102a45659c2e65d3223c3d5885","TODO":"ac0f1c2ebcca03f5b3c0cc56c5aedbb030a4b511e438bc07a57361c789f91e9f","bindings/bindings.toml":"a896b4accf5fbaf146a45a060142974bfa2f59d6a5ab18c5080753078ae39474","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":"5e7fa86d565707908611b37f9ec679d29afa33cdc4d0c589e398d4240e236f68","src/aead.rs":"e26ad34f7168f42aa87eb3a6455af3191a0e8555782b1d70032fe1d248922f98","src/agent.rs":"9dae2fa87a5a7b65dfc7fdd1b2ab1be0f75f1d9ee6704b44edd9bd406252b10a","src/agentio.rs":"cc562d09a09719b90b4e1d147fd579e3e89b683448709e920033bceaea108a61","src/auth.rs":"71ac7e297a5f872d26cf67b6bbd96e4548ea38374bdd84c1094f76a5de4ed1cb","src/cert.rs":"fd3fd2bbb38754bdcee3898549feae412943c9f719032531c1ad6e61783b5394","src/constants.rs":"c39ee506a10d685fda77c1d2ddf691b595b067b4e1044ac7a21e360119d6002b","src/err.rs":"04f38831ca62d29d8aadfe9daf95fd29e68ece184e6d3e00bfb9ee1d12744033","src/exp.rs":"61586662407359c1ecb8ed4987bc3c702f26ba2e203a091a51b6d6363cbd510f","src/ext.rs":"97cba23247e5f9656f27587214f7d7370a69174bae5960a012ce3e6fc99f9116","src/hkdf.rs":"40e44f4280497ef525c2b4c465f14f06d241150851668b264ee958f74321cfbe","src/hp.rs":"974844d885d23c480d5256621053d590b331067b43ee4061e519e58487f5852b","src/lib.rs":"3b22108a069c8c9f1b78e94d48c8759b17e0941b28e2def3fa343d6acace4b6d","src/once.rs":"b9850384899a1a016e839743d3489c0d4d916e1973746ef8c89872105d7d9736","src/p11.rs":"0b62ee5938aefb82e8faee5aa14e990a00442cc9744e8ba22eda80b32030c42c","src/prio.rs":"2f0d86385941aaed7c710e6b82aa1f7adc6ded74b90efbbbcafba6dd9ea1ccdb","src/replay.rs":"40924865994396441a68e6009ecbdf352d6a02fdf539aa65604124e26bffb4d3","src/result.rs":"cef34dfcb907723e195b56501132e4560e250b327783cb5e41201da5b63e9b5c","src/secrets.rs":"acb5befa74e06281c6f80d7298efc58f568bb4e6d949b4225c335e3f392be741","src/selfencrypt.rs":"429cb889a4e9e2345888cc033115c0aa306d2ff90bdfe22b3067700eb1426c37","src/ssl.rs":"3e3a4f539f3c4d18bd6e774dc34fca611db0c75bba00badcd2078c975db055bf","src/time.rs":"df2b14912f70b8262c76ec7907996da6993a31fbb1682fdf6fe51b421800dcfe","tests/aead.rs":"a1d8eb69f5672e064f84dce3d214b347a396718e3de56d57ccc108ee87f1cbc1","tests/agent.rs":"d43e5b05dcc845394d3c7312974faae0fdcbc325c07c970aeb7ef30c3ade652e","tests/ext.rs":"eba9f03accdd598e38292ac88263a81b367d60d5a736a43117a3663de105ec48","tests/handshake.rs":"93c478fcd07d29691007abd6dcfcd2014c10c23b0206ba2d97d01594e4d64397","tests/hkdf.rs":"539235e9dcf2a56b72961a9a04f0080409adf6bf465bfad7c30026421b2d4326","tests/hp.rs":"e52a7d2f4387f2dfe8bfe1da5867e8e0d3eb51e171c6904e18b18c4343536af8","tests/init.rs":"20aad800ac793aaf83059cf860593750509fdedeeff0c08a648e7a5cb398dae0","tests/selfencrypt.rs":"46e9a1a09c2ae577eb106d23a5cdacf762575c0dea1948aedab06ef7389ce713"},"package":null}
{"files":{"Cargo.toml":"ccdc371597cdac7816fc8da88046e56543339c88002efd10a071d72c1eb0b20b","TODO":"ac0f1c2ebcca03f5b3c0cc56c5aedbb030a4b511e438bc07a57361c789f91e9f","bindings/bindings.toml":"a896b4accf5fbaf146a45a060142974bfa2f59d6a5ab18c5080753078ae39474","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":"5e7fa86d565707908611b37f9ec679d29afa33cdc4d0c589e398d4240e236f68","src/aead.rs":"e26ad34f7168f42aa87eb3a6455af3191a0e8555782b1d70032fe1d248922f98","src/agent.rs":"9dae2fa87a5a7b65dfc7fdd1b2ab1be0f75f1d9ee6704b44edd9bd406252b10a","src/agentio.rs":"cc562d09a09719b90b4e1d147fd579e3e89b683448709e920033bceaea108a61","src/auth.rs":"71ac7e297a5f872d26cf67b6bbd96e4548ea38374bdd84c1094f76a5de4ed1cb","src/cert.rs":"fd3fd2bbb38754bdcee3898549feae412943c9f719032531c1ad6e61783b5394","src/constants.rs":"998e77bee88197a240032c1bfbddcff417a25ba82e576a0d2fe18ee9b63cefc7","src/err.rs":"04f38831ca62d29d8aadfe9daf95fd29e68ece184e6d3e00bfb9ee1d12744033","src/exp.rs":"61586662407359c1ecb8ed4987bc3c702f26ba2e203a091a51b6d6363cbd510f","src/ext.rs":"97cba23247e5f9656f27587214f7d7370a69174bae5960a012ce3e6fc99f9116","src/hkdf.rs":"40e44f4280497ef525c2b4c465f14f06d241150851668b264ee958f74321cfbe","src/hp.rs":"974844d885d23c480d5256621053d590b331067b43ee4061e519e58487f5852b","src/lib.rs":"3b22108a069c8c9f1b78e94d48c8759b17e0941b28e2def3fa343d6acace4b6d","src/once.rs":"b9850384899a1a016e839743d3489c0d4d916e1973746ef8c89872105d7d9736","src/p11.rs":"a24b87855cfee4583d16e319b6b8c67b7031dede3d74c833557f48607626ebd3","src/prio.rs":"2f0d86385941aaed7c710e6b82aa1f7adc6ded74b90efbbbcafba6dd9ea1ccdb","src/replay.rs":"40924865994396441a68e6009ecbdf352d6a02fdf539aa65604124e26bffb4d3","src/result.rs":"cef34dfcb907723e195b56501132e4560e250b327783cb5e41201da5b63e9b5c","src/secrets.rs":"acb5befa74e06281c6f80d7298efc58f568bb4e6d949b4225c335e3f392be741","src/selfencrypt.rs":"429cb889a4e9e2345888cc033115c0aa306d2ff90bdfe22b3067700eb1426c37","src/ssl.rs":"3e3a4f539f3c4d18bd6e774dc34fca611db0c75bba00badcd2078c975db055bf","src/time.rs":"6e04eb590b03d8340a2db04214fd4e57980f02f984769c5ec9393f9fb2a6d876","tests/aead.rs":"a1d8eb69f5672e064f84dce3d214b347a396718e3de56d57ccc108ee87f1cbc1","tests/agent.rs":"d43e5b05dcc845394d3c7312974faae0fdcbc325c07c970aeb7ef30c3ade652e","tests/ext.rs":"eba9f03accdd598e38292ac88263a81b367d60d5a736a43117a3663de105ec48","tests/handshake.rs":"93c478fcd07d29691007abd6dcfcd2014c10c23b0206ba2d97d01594e4d64397","tests/hkdf.rs":"539235e9dcf2a56b72961a9a04f0080409adf6bf465bfad7c30026421b2d4326","tests/hp.rs":"e52a7d2f4387f2dfe8bfe1da5867e8e0d3eb51e171c6904e18b18c4343536af8","tests/init.rs":"20aad800ac793aaf83059cf860593750509fdedeeff0c08a648e7a5cb398dae0","tests/selfencrypt.rs":"46e9a1a09c2ae577eb106d23a5cdacf762575c0dea1948aedab06ef7389ce713"},"package":null}

View File

@ -1,6 +1,6 @@
[package]
name = "neqo-crypto"
version = "0.4.21"
version = "0.4.22"
authors = ["Martin Thomson <mt@lowentropy.net>"]
edition = "2018"
build = "build.rs"

View File

@ -16,11 +16,11 @@ pub type Alert = u8;
pub type Epoch = u16;
// TLS doesn't really have an "initial" concept that maps to QUIC so directly,
// but this should be clear enough.
pub const TLS_EPOCH_INITIAL: Epoch = 0 as Epoch;
pub const TLS_EPOCH_ZERO_RTT: Epoch = 1 as Epoch;
pub const TLS_EPOCH_HANDSHAKE: Epoch = 2 as Epoch;
pub const TLS_EPOCH_INITIAL: Epoch = 0_u16;
pub const TLS_EPOCH_ZERO_RTT: Epoch = 1_u16;
pub const TLS_EPOCH_HANDSHAKE: Epoch = 2_u16;
// Also, we don't use TLS epochs > 3.
pub const TLS_EPOCH_APPLICATION_DATA: Epoch = 3 as Epoch;
pub const TLS_EPOCH_APPLICATION_DATA: Epoch = 3_u16;
/// Rather than defining a type alias and a bunch of constants, which leads to a ton of repetition,
/// use this macro.

View File

@ -70,7 +70,7 @@ impl SymKey {
///
/// # Errors
/// Internal errors in case of failures in NSS.
pub fn as_bytes<'a>(&'a self) -> Res<&'a [u8]> {
pub fn as_bytes(&self) -> Res<&[u8]> {
secstatus_to_res(unsafe { PK11_ExtractKeyValue(self.ptr) })?;
let key_item = unsafe { PK11_GetKeyData(self.ptr) };

View File

@ -120,8 +120,7 @@ impl TryInto<PRTime> for Time {
// TODO(mt) use checked_duration_since when that is available.
let delta = self.t.duration_since(base.instant);
if let Ok(d) = PRTime::try_from(delta.as_micros()) {
d.checked_add(base.prtime)
.map_or(Err(Error::TimeTravelError), Ok)
d.checked_add(base.prtime).ok_or(Error::TimeTravelError)
} else {
Err(Error::TimeTravelError)
}

View File

@ -1 +1 @@
{"files":{"Cargo.toml":"9e4aed1f000f437e21143b650ebf4aa31ddcd647ad471362febcf44e4579b78c","src/client_events.rs":"87e7c323ec2ceb759da7d6f2bf24e774f6f2e3833f63454d09cc09f47fccfa8e","src/connection.rs":"7ce294aa57c4ffbdf5f849ad28ca3e38a491abbd2ebbbada2a6f272356535ab9","src/connection_client.rs":"e35e38fb4a87d0eba1e327a3029cf75f8e53de0fa4754120210c265613b53155","src/connection_server.rs":"884016ac4a0e43e0b14146b057c4d8d906f5aac94d47e05312298f76b0818e85","src/control_stream_local.rs":"2e9483d79dc00a3e5ef51ea2b0f28fda2b67348996c47729947c70a8be3007ed","src/control_stream_remote.rs":"1dfac4956a7d6971e2cef2c83963d838e73aa3bf3286b7bde97099978c41d527","src/hframe.rs":"3620c6d114e6d5b98447a2dd2a7cb6872562ebda2cc6de828177b745c8dcbf4d","src/lib.rs":"f57ddf98d38c112c52e1682d1a4c8ef244f4f0c8c463fbf371b8dc4bf5cf40a8","src/push_controller.rs":"70811f87bf0562630a3a68dc0783fce3c4694ab12de9cac835a803e1115431a5","src/push_stream.rs":"5f3a5c6d72c0a8e4d1c1c5041125f7aff9a65950fa60c77f42cd4b35c768f585","src/qlog.rs":"29c0e3c4c9571eb7fe905967edeb1c4bc236b1e35a0e0f11a4a847f1d246681d","src/recv_message.rs":"975fe666de73493501987bdd285efbdef6efce8e721e3a3411af9baf9599c4aa","src/send_message.rs":"5a0214c728f181aaeaa99121fcda804e22866e5150d2f2a3aa8abeb006f1fe14","src/server.rs":"6979eb282f6a0b27439857cd3686c2f69859f87d6d362f5f921d29fcca1f97b1","src/server_connection_events.rs":"762ddb87f700abe91ae1ae78ebbb87a88da0c0a341748b0751b01099870e9985","src/server_events.rs":"c8cdd838129ef6b1e77ba63608d20f6d297bca4e67f26a077d1c015a5e2a3b82","src/settings.rs":"127a51fa7857b870718baa14340b0461d86a67e59bf1a8cb42d7bae0240c0ef1","src/stream_type_reader.rs":"aacb2e865f79b3ac55a887fd670f2286d8ffef94f7d8b3ecfa7e0cccbfa9ec04","tests/httpconn.rs":"1a97a80f7abe11c6ba0bd9b41003be6b293049164daa21e907365d93b00a782f"},"package":null}
{"files":{"Cargo.toml":"e1d462db91bf3876e821e168b9d45ecd64c4733a59d2fe955222d4e178cb8a86","src/client_events.rs":"87e7c323ec2ceb759da7d6f2bf24e774f6f2e3833f63454d09cc09f47fccfa8e","src/connection.rs":"bd486796ce71347aeb28f81d16a104dfc74236adf95c4ae448af3a21c52ae518","src/connection_client.rs":"111ae67649df554ef822305a65f6142eddb3c00f76599b9ad355f0524e3dc08c","src/connection_server.rs":"884016ac4a0e43e0b14146b057c4d8d906f5aac94d47e05312298f76b0818e85","src/control_stream_local.rs":"2e9483d79dc00a3e5ef51ea2b0f28fda2b67348996c47729947c70a8be3007ed","src/control_stream_remote.rs":"1dfac4956a7d6971e2cef2c83963d838e73aa3bf3286b7bde97099978c41d527","src/hframe.rs":"d6d8365a333e33d41ae37d2e774f5dffb7a9676ddfb50de56bb98a50bfe49cbb","src/lib.rs":"f57ddf98d38c112c52e1682d1a4c8ef244f4f0c8c463fbf371b8dc4bf5cf40a8","src/push_controller.rs":"fcf7873e8c41560b16d443e77f897e5e8b3fccb55f35f6d9997bac935e7744c3","src/push_stream.rs":"5f3a5c6d72c0a8e4d1c1c5041125f7aff9a65950fa60c77f42cd4b35c768f585","src/qlog.rs":"29c0e3c4c9571eb7fe905967edeb1c4bc236b1e35a0e0f11a4a847f1d246681d","src/recv_message.rs":"cb6e08fb2d479c68a21b452ba616bdb36dbc7579012b0705791d8f05550c76ba","src/send_message.rs":"7276268e5b190a1dc019d74034dad8a3c0c4b8cb409a135c733c038f9ccff6ff","src/server.rs":"5dda8eb6304be58b8b033b8a6e8f3da75d0b638c2a59c99b4e2b562cc9223137","src/server_connection_events.rs":"762ddb87f700abe91ae1ae78ebbb87a88da0c0a341748b0751b01099870e9985","src/server_events.rs":"c8cdd838129ef6b1e77ba63608d20f6d297bca4e67f26a077d1c015a5e2a3b82","src/settings.rs":"127a51fa7857b870718baa14340b0461d86a67e59bf1a8cb42d7bae0240c0ef1","src/stream_type_reader.rs":"aacb2e865f79b3ac55a887fd670f2286d8ffef94f7d8b3ecfa7e0cccbfa9ec04","tests/httpconn.rs":"1a97a80f7abe11c6ba0bd9b41003be6b293049164daa21e907365d93b00a782f"},"package":null}

View File

@ -1,6 +1,6 @@
[package]
name = "neqo-http3"
version = "0.4.21"
version = "0.4.22"
authors = ["Dragana Damjanovic <dragana.damjano@gmail.com>"]
edition = "2018"
license = "MIT/Apache-2.0"

View File

@ -54,7 +54,10 @@ pub enum Http3State {
impl Http3State {
#[must_use]
pub fn active(&self) -> bool {
matches!(self, Http3State::Connected | Http3State::GoingAway(_) | Http3State::ZeroRtt)
matches!(
self,
Http3State::Connected | Http3State::GoingAway(_) | Http3State::ZeroRtt
)
}
}
@ -398,7 +401,7 @@ impl Http3Connection {
Ok(true)
}
State::Closing { error, .. } | State::Draining { error, .. } => {
if matches!(self.state, Http3State::Closing(_)| Http3State::Closed(_)) {
if matches!(self.state, Http3State::Closing(_) | Http3State::Closed(_)) {
Ok(false)
} else {
self.state = Http3State::Closing(error.clone());
@ -569,7 +572,10 @@ impl Http3Connection {
fn handle_control_frame(&mut self, f: HFrame) -> Res<Option<HFrame>> {
qinfo!([self], "Handle a control frame {:?}", f);
if !matches!(f, HFrame::Settings { .. })
&& !matches!(self.settings_state, Http3RemoteSettingsState::Received{..})
&& !matches!(
self.settings_state,
Http3RemoteSettingsState::Received { .. }
)
{
return Err(Error::HttpMissingSettings);
}

View File

@ -59,6 +59,7 @@ where
fn alpn_from_quic_version(version: QuicVersion) -> &'static str {
match version {
QuicVersion::Version1 => "h3",
QuicVersion::Draft27 => "h3-27",
QuicVersion::Draft28 => "h3-28",
QuicVersion::Draft29 => "h3-29",
@ -97,6 +98,7 @@ impl Http3Client {
remote_addr: SocketAddr,
conn_params: ConnectionParameters,
http3_parameters: &Http3Parameters,
now: Instant,
) -> Res<Self> {
Ok(Self::new_with_conn(
Connection::new_client(
@ -106,6 +108,7 @@ impl Http3Client {
local_addr,
remote_addr,
conn_params,
now,
)?,
http3_parameters,
))
@ -223,7 +226,10 @@ impl Http3Client {
S: AsRef<str> + Display,
{
qinfo!([self], "Close the connection error={} msg={}.", error, msg);
if !matches!(self.base_handler.state, Http3State::Closing(_)| Http3State::Closed(_)) {
if !matches!(
self.base_handler.state,
Http3State::Closing(_) | Http3State::Closed(_)
) {
self.push_handler.borrow_mut().clear();
self.conn.close(now, error, msg);
self.base_handler.close(error);
@ -783,6 +789,7 @@ mod tests {
},
max_concurrent_push_streams: 5,
},
now(),
)
.expect("create a default client")
}
@ -4464,28 +4471,21 @@ mod tests {
let any_push_event = |e| {
matches!(
e,
Http3ClientEvent::PushPromise{..}
| Http3ClientEvent::PushHeaderReady{..}
| Http3ClientEvent::PushDataReadable{..})
Http3ClientEvent::PushPromise { .. }
| Http3ClientEvent::PushHeaderReady { .. }
| Http3ClientEvent::PushDataReadable { .. }
)
};
client.events().any(any_push_event)
}
fn check_data_readable(client: &mut Http3Client) -> bool {
let any_data_event = |e| {
matches!(
e,
Http3ClientEvent::DataReadable{..})
};
let any_data_event = |e| matches!(e, Http3ClientEvent::DataReadable { .. });
client.events().any(any_data_event)
}
fn check_header_ready(client: &mut Http3Client) -> bool {
let any_event = |e| {
matches!(
e,
Http3ClientEvent::HeaderReady{..})
};
let any_event = |e| matches!(e, Http3ClientEvent::HeaderReady { .. });
client.events().any(any_event)
}
@ -4493,8 +4493,8 @@ mod tests {
let any_event = |e| {
matches!(
e,
Http3ClientEvent::HeaderReady{..}
| Http3ClientEvent::PushPromise{..})
Http3ClientEvent::HeaderReady { .. } | Http3ClientEvent::PushPromise { .. }
)
};
client.events().any(any_event)
}
@ -4740,7 +4740,7 @@ mod tests {
send_push_promise_and_exchange_packets(&mut client, &mut server, request_stream_id_2, 5);
// Check that we do not have a Http3ClientEvent::PushPromise.
let push_event = |e| matches!(e, Http3ClientEvent::PushPromise{ .. });
let push_event = |e| matches!(e, Http3ClientEvent::PushPromise { .. });
assert!(!client.events().any(push_event));
}

View File

@ -214,9 +214,8 @@ impl HFrameReader {
if fin {
if self.decoding_in_progress() {
break Err(Error::HttpFrame);
} else {
break Ok((None, fin));
}
break Ok((None, fin));
}
if !read {
@ -266,12 +265,11 @@ impl HFrameReader {
| H3_FRAME_TYPE_HEADERS => {
if len == 0 {
return Ok(Some(self.get_frame()?));
} else {
HFrameReaderState::GetData {
decoder: IncrementalDecoderBuffer::new(
usize::try_from(len).or(Err(Error::HttpFrame))?,
),
}
}
HFrameReaderState::GetData {
decoder: IncrementalDecoderBuffer::new(
usize::try_from(len).or(Err(Error::HttpFrame))?,
),
}
}
_ => {

View File

@ -322,7 +322,7 @@ impl PushController {
pub fn close(&mut self, push_id: u64) {
qtrace!("Push stream has been closed.");
if let Some(push_state) = self.push_streams.close(push_id) {
debug_assert!(matches!(push_state, PushState::Active{..}));
debug_assert!(matches!(push_state, PushState::Active { .. }));
} else {
debug_assert!(false, "Closing non existing push stream!");
}

View File

@ -106,9 +106,8 @@ impl RecvMessage {
RecvMessageState::WaitingForResponseHeaders {..} => {
if header_block.is_empty() {
return Err(Error::HttpGeneralProtocolStream);
} else {
self.state = RecvMessageState::DecodingHeaders { header_block, fin };
}
self.state = RecvMessageState::DecodingHeaders { header_block, fin };
}
RecvMessageState::WaitingForData { ..} => {
// TODO implement trailers, for now just ignore them.
@ -268,7 +267,9 @@ impl RecvMessage {
if matches!(self.state, RecvMessageState::Closed) {
break Ok(());
}
if fin && !matches!(self.state, RecvMessageState::DecodingHeaders{..}) {
if fin
&& !matches!(self.state, RecvMessageState::DecodingHeaders { .. })
{
break self.set_state_to_close_pending(post_readable_event);
}
}
@ -334,7 +335,7 @@ impl RecvMessage {
if let Some((_name, value)) = status {
#[allow(clippy::map_err_ignore, clippy::unknown_clippy_lints)]
let status_code = value.parse::<i32>().map_err(|_| Error::InvalidHeader)?;
Ok(status_code >= 100 && status_code < 200)
Ok((100..200).contains(&status_code))
} else {
Err(Error::InvalidHeader)
}

View File

@ -271,7 +271,10 @@ impl SendMessage {
// This method returns if they're still being sent. Request body (if any) is sent by
// http client afterwards using `send_request_body` after receiving DataWritable event.
pub fn has_data_to_send(&self) -> bool {
matches!(self.state, SendMessageState::Initialized {..} | SendMessageState::SendingInitialMessage { .. } )
matches!(
self.state,
SendMessageState::Initialized { .. } | SendMessageState::SendingInitialMessage { .. }
)
}
pub fn close(&mut self, conn: &mut Connection) -> Res<()> {

View File

@ -283,15 +283,27 @@ mod tests {
}
fn assert_connected(hconn: &mut Http3Server) {
let connected =
|e| matches!(e, Http3ServerEvent::StateChange{ state: Http3State::Connected, ..} );
let connected = |e| {
matches!(
e,
Http3ServerEvent::StateChange {
state: Http3State::Connected,
..
}
)
};
assert!(hconn.events().any(connected));
}
fn assert_not_closed(hconn: &mut Http3Server) {
let closed = |e| {
matches!(e,
Http3ServerEvent::StateChange{ state: Http3State::Closing(..), .. })
matches!(
e,
Http3ServerEvent::StateChange {
state: Http3State::Closing(..),
..
}
)
};
assert!(!hconn.events().any(closed));
}

View File

@ -1 +1 @@
{"files":{"Cargo.toml":"5d4988576ac89bab7c1d2b1e521e67ce5062a68b395d553df64eeaee78ea9b29","src/decoder.rs":"1dd87823a8aeb8673d2521d2f2735cea5aaf45713a4fdc3a01b3803c7a7c30f7","src/decoder_instructions.rs":"a8e04dff5fc4c658322a10daadab947dc2e41932c00c3f8d387671a86d0516af","src/encoder.rs":"30442c457d7fb8a63bc54bd68928383adb9fdeda14d6b082c2035736389d96c5","src/encoder_instructions.rs":"1d4424bf21c0ac26b7c8fee6450b943346c5493ab86dd7ec2edc5f566454721e","src/header_block.rs":"8477281843fb9c927cbf9cda488d4586605a64157e582a71e405f2bf1852f43b","src/huffman.rs":"68fa0bada0c35d20f793980596accdcc548970214841f71789290fc334e51fc1","src/huffman_decode_helper.rs":"2970c57f052878b727c2f764490c54184f5c2608e1d6aa961c3b01509e290122","src/huffman_table.rs":"06fea766a6276ac56c7ee0326faed800a742c15fda1f33bf2513e6cc6a5e6d27","src/lib.rs":"bc9d936fe126d53ff752ae7fe17e8be59bb992f48b17e82d5811f274b492af3c","src/prefix.rs":"72c587c40aef4ed38cf13b2de91091d671611679be2a9da6f0b24abafaf50dc5","src/qlog.rs":"7618085e27bb3fb1f4d1c73ba501b9a293723293c4020b7cc4129676eb278131","src/qpack_send_buf.rs":"5170b93afaf0c1609463e6c5ae4dccb1a2ce4e4407296db1bcaf06c6b5bb97ab","src/reader.rs":"4bcea0de1d7dc09ec0cdff364d8f62da54bbbe1f6db55a495f943f31369b4074","src/static_table.rs":"fda9d5c6f38f94b0bf92d3afdf8432dce6e27e189736596e16727090c77b78ec","src/stats.rs":"624dfa3b40858c304097bb0ce5b1be1bb4d7916b1abfc222f1aa705907009730","src/table.rs":"f7091bdd9ad1f8fe3b2298a7dbfd3d285c212d69569cda54f9bcf251cb758a21"},"package":null}
{"files":{"Cargo.toml":"063b51b1dcd6e1cfac0226a05f20a5a55826c5b5fab90483b9516048d91f40f8","src/decoder.rs":"3a069d7d224a51e19690c024ccb66c3059151dbac22a0c909b195d712b307dfc","src/decoder_instructions.rs":"a8e04dff5fc4c658322a10daadab947dc2e41932c00c3f8d387671a86d0516af","src/encoder.rs":"208ff94030866c07151be2695da4e01269783ed979830112bf15502063486534","src/encoder_instructions.rs":"1d4424bf21c0ac26b7c8fee6450b943346c5493ab86dd7ec2edc5f566454721e","src/header_block.rs":"8bf9c4890dfa07c6a2f23fb1dda9446ec9c15838dd44dc133c31800f970d1d8d","src/huffman.rs":"68fa0bada0c35d20f793980596accdcc548970214841f71789290fc334e51fc1","src/huffman_decode_helper.rs":"2970c57f052878b727c2f764490c54184f5c2608e1d6aa961c3b01509e290122","src/huffman_table.rs":"06fea766a6276ac56c7ee0326faed800a742c15fda1f33bf2513e6cc6a5e6d27","src/lib.rs":"bc9d936fe126d53ff752ae7fe17e8be59bb992f48b17e82d5811f274b492af3c","src/prefix.rs":"72c587c40aef4ed38cf13b2de91091d671611679be2a9da6f0b24abafaf50dc5","src/qlog.rs":"7618085e27bb3fb1f4d1c73ba501b9a293723293c4020b7cc4129676eb278131","src/qpack_send_buf.rs":"5170b93afaf0c1609463e6c5ae4dccb1a2ce4e4407296db1bcaf06c6b5bb97ab","src/reader.rs":"8d006058e08efdd4a4b3d8c40da379e9b637aa337077bf2b74ea0a8a57c23c72","src/static_table.rs":"fda9d5c6f38f94b0bf92d3afdf8432dce6e27e189736596e16727090c77b78ec","src/stats.rs":"624dfa3b40858c304097bb0ce5b1be1bb4d7916b1abfc222f1aa705907009730","src/table.rs":"f7091bdd9ad1f8fe3b2298a7dbfd3d285c212d69569cda54f9bcf251cb758a21"},"package":null}

View File

@ -1,6 +1,6 @@
[package]
name = "neqo-qpack"
version = "0.4.21"
version = "0.4.22"
authors = ["Dragana Damjanovic <dragana.damjano@gmail.com>"]
edition = "2018"
license = "MIT/Apache-2.0"

View File

@ -160,7 +160,7 @@ impl QPackDecoder {
}
/// # Errors
/// May return an error in case of any transport error. TODO: define transport errors.
/// May return an error in case of any transport error. TODO: define transport errors.
#[allow(clippy::map_err_ignore, clippy::unknown_clippy_lints)]
pub fn send(&mut self, conn: &mut Connection) -> Res<()> {
// Encode increment instruction if needed.
@ -239,7 +239,7 @@ impl QPackDecoder {
}
/// # Errors
/// May return WrongStreamCount if Http3 has received multiple encoder streams.
/// May return `WrongStreamCount` if HTTP/3 has received multiple encoder streams.
pub fn add_recv_stream(&mut self, stream_id: u64) -> Res<()> {
if self.remote_stream_id.is_some() {
Err(Error::WrongStreamCount)

View File

@ -164,7 +164,7 @@ impl QPackEncoder {
Ok(())
}
fn header_ack(&mut self, stream_id: u64) -> Res<()> {
fn header_ack(&mut self, stream_id: u64) {
self.stats.header_acks_recv += 1;
let mut new_acked = self.table.get_acked_inserts_cnt();
if let Some(hb_list) = self.unacked_header_blocks.get_mut(&stream_id) {
@ -186,10 +186,9 @@ impl QPackEncoder {
self.insert_count_instruction(new_acked - self.table.get_acked_inserts_cnt())
.expect("This should neve happen");
}
Ok(())
}
fn stream_cancellation(&mut self, stream_id: u64) -> Res<()> {
fn stream_cancellation(&mut self, stream_id: u64) {
self.stats.stream_cancelled_recv += 1;
let mut was_blocker = false;
if let Some(mut hb_list) = self.unacked_header_blocks.remove(&stream_id) {
@ -205,7 +204,6 @@ impl QPackEncoder {
debug_assert!(self.blocked_stream_cnt > 0);
self.blocked_stream_cnt -= 1;
}
Ok(())
}
fn call_instruction(
@ -224,9 +222,13 @@ impl QPackEncoder {
self.insert_count_instruction(increment)
}
DecoderInstruction::HeaderAck { stream_id } => self.header_ack(stream_id),
DecoderInstruction::HeaderAck { stream_id } => {
self.header_ack(stream_id);
Ok(())
}
DecoderInstruction::StreamCancellation { stream_id } => {
self.stream_cancellation(stream_id)
self.stream_cancellation(stream_id);
Ok(())
}
_ => Ok(()),
}
@ -529,9 +531,8 @@ fn map_stream_send_atomic_error(err: &TransportError) -> Error {
mod tests {
use super::{Connection, Error, Header, QPackEncoder, Res};
use crate::QpackSettings;
use neqo_transport::tparams::{self, TransportParameter};
use neqo_transport::StreamType;
use test_fixture::{default_client, default_server, handshake, now};
use neqo_transport::{ConnectionParameters, StreamType};
use test_fixture::{configure_server, default_client, default_server, handshake, now};
struct TestEncoder {
encoder: QPackEncoder,
@ -584,13 +585,17 @@ mod tests {
}
}
fn connect_generic<F>(huffman: bool, f: F) -> TestEncoder
where
F: FnOnce(&mut Connection, &mut Connection),
{
fn connect_generic(huffman: bool, max_data: Option<u64>) -> TestEncoder {
let mut conn = default_client();
let mut peer_conn = default_server();
f(&mut conn, &mut peer_conn);
let mut peer_conn = max_data.map_or_else(default_server, |max| {
configure_server(
ConnectionParameters::default()
.max_stream_data(StreamType::UniDi, true, max)
.max_stream_data(StreamType::BiDi, true, max)
.max_stream_data(StreamType::BiDi, false, max),
)
});
handshake(&mut conn, &mut peer_conn);
// create a stream
let recv_stream_id = peer_conn.stream_create(StreamType::UniDi).unwrap();
@ -617,22 +622,11 @@ mod tests {
}
fn connect(huffman: bool) -> TestEncoder {
connect_generic(huffman, |client, server| {
handshake(client, server);
})
connect_generic(huffman, None)
}
fn connect_flow_control(max_data: u64) -> TestEncoder {
connect_generic(true, |client, server| {
server
.set_local_tparam(
tparams::INITIAL_MAX_DATA,
TransportParameter::Integer(max_data),
)
.unwrap();
handshake(client, server);
})
connect_generic(true, Some(max_data))
}
fn recv_instruction(encoder: &mut TestEncoder, decoder_instruction: &[u8]) {
@ -1584,17 +1578,14 @@ mod tests {
#[test]
fn encoder_flow_controlled_blocked() {
const SMALL_MAX_DATA: u64 = 900;
const STREAM_DATA_LEN: usize = 900 - 20;
const STREAM_DATA: &[u8] = &[0; STREAM_DATA_LEN];
const ONE_INSTRUCTION: &[u8] = &[
const SMALL_MAX_DATA: u64 = 20;
const ONE_INSTRUCTION_1: &[u8] = &[
0x67, 0x41, 0xe9, 0x2a, 0x67, 0x35, 0x53, 0x7f, 0x83, 0x8, 0x99, 0x6b,
];
const TWO_INSTRUCTION: &[u8] = &[
0x67, 0x41, 0xe9, 0x2a, 0x67, 0x35, 0x53, 0x37, 0x83, 0x8, 0x99, 0x6b, 0x67, 0x41,
0xe9, 0x2a, 0x67, 0x35, 0x53, 0x39, 0x88, 0x8, 0x99, 0x69, 0xb7, 0x1d, 0x79, 0xf0,
0x83,
const ONE_INSTRUCTION_2: &[u8] = &[
0x67, 0x41, 0xe9, 0x2a, 0x67, 0x35, 0x53, 0x37, 0x83, 0x8, 0x99, 0x6b,
];
let mut encoder = connect_flow_control(SMALL_MAX_DATA);
// change capacity to 1000 and max_block streams to 20.
@ -1602,13 +1593,6 @@ mod tests {
assert!(encoder.encoder.set_max_capacity(1000).is_ok());
encoder.send_instructions(CAP_INSTRUCTION_1000);
// Write some data to fill the flow control allowance.
let stream_id = encoder.conn.stream_create(StreamType::UniDi).unwrap();
assert_eq!(
encoder.conn.stream_send(stream_id, STREAM_DATA).unwrap(),
STREAM_DATA_LEN
);
// Encode a header block with 2 headers. The first header will be added to the dynamic table.
// The second will not be added to the dynamic table, because the corresponding instruction
// cannot be written immediately due to the flow control limit.
@ -1644,7 +1628,11 @@ mod tests {
assert_eq!(buf2[2] & 0xf0, 0x20);
// Ensure that we have sent only one instruction for (String::from("something"), String::from("1234"))
encoder.send_instructions(ONE_INSTRUCTION);
encoder.send_instructions(ONE_INSTRUCTION_1);
// exchange a flow control update.
let out = encoder.peer_conn.process(None, now());
let _ = encoder.conn.process(out.dgram(), now());
// Try writing a new header block. Now, headers will be added to the dynamic table again, because
// instructions can be sent.
@ -1659,12 +1647,13 @@ mod tests {
3,
)
.unwrap();
// Assert that both headers are encoded as an index to the dynamic table (a post form).
// Assert that the first header is encoded as an index to the dynamic table (a post form).
assert_eq!(buf3[2], 0x10);
assert_eq!(buf3[3], 0x11);
// Assert that the second header is encoded as a literal with a name literal
assert_eq!(buf3[3] & 0xf0, 0x20);
// Asset that 2 instruction has been sent
encoder.send_instructions(TWO_INSTRUCTION);
// Asset that one instruction has been sent
encoder.send_instructions(ONE_INSTRUCTION_2);
}
#[test]

View File

@ -316,9 +316,8 @@ impl<'a> HeaderDecoder<'a> {
if req_insert_cnt > max_value {
if req_insert_cnt < full_range {
return Err(Error::DecompressionFailed);
} else {
req_insert_cnt -= full_range;
}
req_insert_cnt -= full_range;
}
Ok(req_insert_cnt)
}

View File

@ -290,12 +290,10 @@ impl LiteralReader {
self.state = LiteralReaderState::Done;
if self.use_huffman {
break Ok(decode_huffman(&self.literal)?);
} else {
break Ok(mem::replace(&mut self.literal, Vec::new()));
}
} else {
break Err(Error::NeedMoreData);
break Ok(mem::replace(&mut self.literal, Vec::new()));
}
break Err(Error::NeedMoreData);
}
LiteralReaderState::Done => {
panic!("Should not call read() in this state.");

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,6 @@
[package]
name = "neqo-transport"
version = "0.4.21"
version = "0.4.22"
authors = ["EKR <ekr@rtfm.com>", "Andy Grover <agrover@mozilla.com>"]
edition = "2018"
license = "MIT/Apache-2.0"

View File

@ -246,7 +246,7 @@ impl<T: WindowAdjustment> CongestionControl for ClassicCongestionControl<T> {
return;
}
for pkt in lost_packets.iter().filter(|pkt| pkt.ack_eliciting()) {
for pkt in lost_packets.iter().filter(|pkt| pkt.cc_in_flight()) {
assert!(self.bytes_in_flight >= pkt.size);
self.bytes_in_flight -= pkt.size;
}
@ -278,6 +278,14 @@ impl<T: WindowAdjustment> CongestionControl for ClassicCongestionControl<T> {
}
}
fn discard_in_flight(&mut self) {
self.bytes_in_flight = 0;
qlog::metrics_updated(
&mut self.qlog,
&[QlogMetric::BytesInFlight(self.bytes_in_flight)],
);
}
fn on_packet_sent(&mut self, pkt: &SentPacket) {
// Record the recovery time and exit any transient state.
if self.state.transient() {
@ -285,7 +293,7 @@ impl<T: WindowAdjustment> CongestionControl for ClassicCongestionControl<T> {
self.state.update();
}
if !pkt.ack_eliciting() {
if !pkt.cc_in_flight() {
return;
}
@ -304,7 +312,6 @@ impl<T: WindowAdjustment> CongestionControl for ClassicCongestionControl<T> {
}
/// Whether a packet can be sent immediately as a result of entering recovery.
#[must_use]
fn recovery_packet(&self) -> bool {
self.state == State::RecoveryStart
}
@ -399,7 +406,7 @@ impl<T: WindowAdjustment> ClassicCongestionControl<T> {
start = None;
}
last_pn = p.pn;
if !p.ack_eliciting() {
if !p.cc_in_flight() {
// Not interesting, keep looking.
continue;
}
@ -482,10 +489,11 @@ mod tests {
};
use crate::cc::cubic::{Cubic, CUBIC_BETA_USIZE_DIVISOR, CUBIC_BETA_USIZE_QUOTIENT};
use crate::cc::new_reno::NewReno;
use crate::cc::{CongestionControl, CWND_INITIAL_PKTS, MAX_DATAGRAM_SIZE};
use crate::cc::{
CongestionControl, CongestionControlAlgorithm, CWND_INITIAL_PKTS, MAX_DATAGRAM_SIZE,
};
use crate::packet::{PacketNumber, PacketType};
use crate::tracking::SentPacket;
use crate::CongestionControlAlgorithm;
use std::convert::TryFrom;
use std::time::{Duration, Instant};
use test_fixture::now;

View File

@ -9,9 +9,11 @@
use crate::path::PATH_MTU_V6;
use crate::tracking::SentPacket;
use crate::Error;
use neqo_common::qlog::NeqoQlog;
use std::fmt::{Debug, Display};
use std::str::FromStr;
use std::time::{Duration, Instant};
mod classic_cc;
@ -29,10 +31,13 @@ pub const MAX_DATAGRAM_SIZE_F64: f64 = 1337.0;
pub trait CongestionControl: Display + Debug {
fn set_qlog(&mut self, qlog: NeqoQlog);
#[must_use]
fn cwnd(&self) -> usize;
#[must_use]
fn bytes_in_flight(&self) -> usize;
#[must_use]
fn cwnd_avail(&self) -> usize;
fn on_packets_acked(&mut self, acked_pkts: &[SentPacket], min_rtt: Duration, now: Instant);
@ -45,11 +50,14 @@ pub trait CongestionControl: Display + Debug {
lost_packets: &[SentPacket],
);
#[must_use]
fn recovery_packet(&self) -> bool;
fn discard(&mut self, pkt: &SentPacket);
fn on_packet_sent(&mut self, pkt: &SentPacket);
fn discard_in_flight(&mut self);
}
#[derive(Debug, Copy, Clone)]
@ -58,5 +66,18 @@ pub enum CongestionControlAlgorithm {
Cubic,
}
// A `FromStr` implementation so that this can be used in command-line interfaces.
impl FromStr for CongestionControlAlgorithm {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.trim().to_ascii_lowercase().as_str() {
"newreno" | "reno" => Ok(Self::NewReno),
"cubic" => Ok(Self::Cubic),
_ => Err(Error::InvalidInput),
}
}
}
#[cfg(test)]
mod tests;

File diff suppressed because it is too large Load Diff

View File

@ -4,10 +4,14 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use crate::connection::{ConnectionIdManager, Role, LOCAL_ACTIVE_CID_LIMIT, LOCAL_IDLE_TIMEOUT};
use crate::recv_stream::RECV_BUFFER_SIZE;
use crate::stream_id::{StreamIndex, StreamType};
use crate::tparams::PreferredAddress;
use crate::{CongestionControlAlgorithm, QuicVersion};
use crate::tparams::{self, PreferredAddress, TransportParameter, TransportParametersHandler};
use crate::{CongestionControlAlgorithm, QuicVersion, Res};
use std::convert::TryFrom;
const LOCAL_MAX_DATA: u64 = 0x3FFF_FFFF_FFFF_FFFF; // 2^62-1
const LOCAL_STREAM_LIMIT_BIDI: StreamIndex = StreamIndex::new(16);
const LOCAL_STREAM_LIMIT_UNI: StreamIndex = StreamIndex::new(16);
@ -23,12 +27,23 @@ pub enum PreferredAddressConfig {
}
/// ConnectionParameters use for setting intitial value for QUIC parameters.
/// This collect like initial limits, protocol version and congestion control.
/// This collects configuration like initial limits, protocol version, and
/// congestion control algorithm.
#[derive(Debug, Clone)]
pub struct ConnectionParameters {
quic_version: QuicVersion,
cc_algorithm: CongestionControlAlgorithm,
/// Initial connection-level flow control limit.
max_data: u64,
/// Initial flow control limit for receiving data on bidirectional streams that the peer creates.
max_stream_data_bidi_remote: u64,
/// Initial flow control limit for receiving data on bidirectional streams that this endpoint creates.
max_stream_data_bidi_local: u64,
/// Initial flow control limit for receiving data on unidirectional streams that the peer creates.
max_stream_data_uni: u64,
/// Initial limit on bidirectional streams that the peer creates.
max_streams_bidi: StreamIndex,
/// Initial limit on unidirectional streams that this endpoint creates.
max_streams_uni: StreamIndex,
preferred_address: PreferredAddressConfig,
}
@ -38,6 +53,10 @@ impl Default for ConnectionParameters {
Self {
quic_version: QuicVersion::default(),
cc_algorithm: CongestionControlAlgorithm::NewReno,
max_data: LOCAL_MAX_DATA,
max_stream_data_bidi_remote: u64::try_from(RECV_BUFFER_SIZE).unwrap(),
max_stream_data_bidi_local: u64::try_from(RECV_BUFFER_SIZE).unwrap(),
max_stream_data_uni: u64::try_from(RECV_BUFFER_SIZE).unwrap(),
max_streams_bidi: LOCAL_STREAM_LIMIT_BIDI,
max_streams_uni: LOCAL_STREAM_LIMIT_UNI,
preferred_address: PreferredAddressConfig::Default,
@ -64,6 +83,15 @@ impl ConnectionParameters {
self
}
pub fn get_max_data(&self) -> u64 {
self.max_data
}
pub fn max_data(mut self, v: u64) -> Self {
self.max_data = v;
self
}
pub fn get_max_streams(&self, stream_type: StreamType) -> StreamIndex {
match stream_type {
StreamType::BiDi => self.max_streams_bidi,
@ -84,6 +112,39 @@ impl ConnectionParameters {
self
}
/// Get the maximum stream data that we will accept on different types of streams.
/// Asserts if `StreamType::UniDi` and `false` are passed as that is not a valid combination.
pub fn get_max_stream_data(&self, stream_type: StreamType, remote: bool) -> u64 {
match (stream_type, remote) {
(StreamType::BiDi, false) => self.max_stream_data_bidi_local,
(StreamType::BiDi, true) => self.max_stream_data_bidi_remote,
(StreamType::UniDi, false) => {
panic!("Can't get receive limit on a stream that can only be sent.")
}
(StreamType::UniDi, true) => self.max_stream_data_uni,
}
}
/// Set the maximum stream data that we will accept on different types of streams.
/// Asserts if `StreamType::UniDi` and `false` are passed as that is not a valid combination.
pub fn max_stream_data(mut self, stream_type: StreamType, remote: bool, v: u64) -> Self {
match (stream_type, remote) {
(StreamType::BiDi, false) => {
self.max_stream_data_bidi_local = v;
}
(StreamType::BiDi, true) => {
self.max_stream_data_bidi_remote = v;
}
(StreamType::UniDi, false) => {
panic!("Can't set receive limit on a stream that can only be sent.")
}
(StreamType::UniDi, true) => {
self.max_stream_data_uni = v;
}
}
self
}
/// Set a preferred address (which only has an effect for a server).
pub fn preferred_address(mut self, preferred: PreferredAddress) -> Self {
self.preferred_address = PreferredAddressConfig::Address(preferred);
@ -99,4 +160,62 @@ impl ConnectionParameters {
pub fn get_preferred_address(&self) -> &PreferredAddressConfig {
&self.preferred_address
}
pub fn create_transport_parameter(
&self,
role: Role,
cid_manager: &mut ConnectionIdManager,
) -> Res<TransportParametersHandler> {
let mut tps = TransportParametersHandler::default();
// default parameters
tps.local.set_integer(
tparams::IDLE_TIMEOUT,
u64::try_from(LOCAL_IDLE_TIMEOUT.as_millis()).unwrap(),
);
tps.local.set_integer(
tparams::ACTIVE_CONNECTION_ID_LIMIT,
u64::try_from(LOCAL_ACTIVE_CID_LIMIT).unwrap(),
);
tps.local.set_empty(tparams::DISABLE_MIGRATION);
tps.local.set_empty(tparams::GREASE_QUIC_BIT);
// set configurable parameters
tps.local
.set_integer(tparams::INITIAL_MAX_DATA, self.max_data);
tps.local.set_integer(
tparams::INITIAL_MAX_STREAM_DATA_BIDI_LOCAL,
self.max_stream_data_bidi_local,
);
tps.local.set_integer(
tparams::INITIAL_MAX_STREAM_DATA_BIDI_REMOTE,
self.max_stream_data_bidi_remote,
);
tps.local.set_integer(
tparams::INITIAL_MAX_STREAM_DATA_UNI,
self.max_stream_data_uni,
);
tps.local.set_integer(
tparams::INITIAL_MAX_STREAMS_BIDI,
self.max_streams_bidi.as_u64(),
);
tps.local.set_integer(
tparams::INITIAL_MAX_STREAMS_UNI,
self.max_streams_uni.as_u64(),
);
if let PreferredAddressConfig::Address(preferred) = &self.preferred_address {
if role == Role::Server {
let (cid, srt) = cid_manager.preferred_address_cid()?;
tps.local.set(
tparams::PREFERRED_ADDRESS,
TransportParameter::PreferredAddress {
v4: preferred.ipv4(),
v6: preferred.ipv6(),
cid,
srt,
},
);
}
}
Ok(tps)
}
}

View File

@ -46,7 +46,10 @@ impl State {
#[must_use]
pub fn closed(&self) -> bool {
matches!(self, Self::Closing { .. } | Self::Draining { .. } | Self::Closed(_))
matches!(
self,
Self::Closing { .. } | Self::Draining { .. } | Self::Closed(_)
)
}
}

View File

@ -19,8 +19,17 @@ use crate::tracking::MAX_UNACKED_PKTS;
use neqo_common::{qdebug, qinfo, qtrace, Datagram};
use std::convert::TryFrom;
use std::mem;
use std::time::{Duration, Instant};
// Get the current congestion window for the connection.
fn cwnd(c: &Connection) -> usize {
c.paths.primary().borrow().sender().cwnd()
}
fn cwnd_avail(c: &Connection) -> usize {
c.paths.primary().borrow().sender().cwnd_avail()
}
fn induce_persistent_congestion(
client: &mut Connection,
server: &mut Connection,
@ -69,7 +78,7 @@ fn induce_persistent_congestion(
client.process_input(dgram, now);
}
assert_eq!(client.loss_recovery.cwnd(), CWND_MIN);
assert_eq!(cwnd(client), CWND_MIN);
now
}
@ -115,7 +124,7 @@ fn cc_slow_start() {
let stream_id = client.stream_create(StreamType::UniDi).unwrap();
let (c_tx_dgrams, _) = fill_cwnd(&mut client, stream_id, now);
assert_full_cwnd(&c_tx_dgrams, POST_HANDSHAKE_CWND);
assert!(client.loss_recovery.cwnd_avail() < ACK_ONLY_SIZE_LIMIT);
assert!(cwnd_avail(&client) < ACK_ONLY_SIZE_LIMIT);
}
#[test]
@ -206,7 +215,7 @@ fn cc_cong_avoidance_recovery_period_unchanged() {
client.process_input(dgram, now);
}
let cwnd1 = client.loss_recovery.cwnd();
let cwnd1 = cwnd(&client);
// Generate ACK for more received packets
let s_tx_dgram = ack_bytes(&mut server, 0, c_tx_dgrams2, now);
@ -218,7 +227,7 @@ fn cc_cong_avoidance_recovery_period_unchanged() {
// cwnd should not have changed since ACKed packets were sent before
// recovery period expired
let cwnd2 = client.loss_recovery.cwnd();
let cwnd2 = cwnd(&client);
assert_eq!(cwnd1, cwnd2);
}
@ -239,7 +248,7 @@ fn single_packet_on_recovery() {
// Now fill the congestion window.
assert_eq!(client.stream_create(StreamType::BiDi).unwrap(), 0);
let (_, now) = fill_cwnd(&mut client, 0, now);
assert!(client.loss_recovery.cwnd_avail() < ACK_ONLY_SIZE_LIMIT);
assert!(cwnd_avail(&client) < ACK_ONLY_SIZE_LIMIT);
// Acknowledge just one packet and cause one packet to be declared lost.
// The length is the amount of credit the client should have.
@ -249,7 +258,7 @@ fn single_packet_on_recovery() {
// The client should see the loss and enter recovery.
// As there are many outstanding packets, there should be no available cwnd.
client.process_input(ack.unwrap(), now);
assert_eq!(client.loss_recovery.cwnd_avail(), 0);
assert_eq!(cwnd_avail(&client), 0);
// The client should send one packet, ignoring the cwnd.
let dgram = client.process_output(now).dgram();
@ -285,15 +294,12 @@ fn cc_cong_avoidance_recovery_period_to_cong_avoidance() {
// Should be in CARP now.
now += DEFAULT_RTT / 2;
qinfo!(
"moving to congestion avoidance {}",
client.loss_recovery.cwnd()
);
qinfo!("moving to congestion avoidance {}", cwnd(&client));
// Now make sure that we increase congestion window according to the
// accurate byte counting version of congestion avoidance.
// Check over several increases to be sure.
let mut expected_cwnd = client.loss_recovery.cwnd();
let mut expected_cwnd = cwnd(&client);
// Fill cwnd.
let (mut c_tx_dgrams, next_now) = fill_cwnd(&mut client, 0, now);
now = next_now;
@ -304,7 +310,7 @@ fn cc_cong_avoidance_recovery_period_to_cong_avoidance() {
qinfo!(
"client sending {} bytes into cwnd of {}",
c_tx_size,
client.loss_recovery.cwnd()
cwnd(&client)
);
assert_eq!(c_tx_size, expected_cwnd);
@ -318,7 +324,7 @@ fn cc_cong_avoidance_recovery_period_to_cong_avoidance() {
let most = c_tx_dgrams.len() - MAX_UNACKED_PKTS - 1;
let s_tx_dgram = ack_bytes(&mut server, 0, c_tx_dgrams.drain(..most), now);
for dgram in s_tx_dgram {
assert_eq!(client.loss_recovery.cwnd(), expected_cwnd);
assert_eq!(cwnd(&client), expected_cwnd);
client.process_input(dgram, now);
// make sure to fill cwnd again.
let (mut new_pkts, next_now) = fill_cwnd(&mut client, 0, now);
@ -327,7 +333,7 @@ fn cc_cong_avoidance_recovery_period_to_cong_avoidance() {
}
let s_tx_dgram = ack_bytes(&mut server, 0, c_tx_dgrams, now);
for dgram in s_tx_dgram {
assert_eq!(client.loss_recovery.cwnd(), expected_cwnd);
assert_eq!(cwnd(&client), expected_cwnd);
client.process_input(dgram, now);
// make sure to fill cwnd again.
let (mut new_pkts, next_now) = fill_cwnd(&mut client, 0, now);
@ -335,7 +341,7 @@ fn cc_cong_avoidance_recovery_period_to_cong_avoidance() {
next_c_tx_dgrams.append(&mut new_pkts);
}
expected_cwnd += MAX_DATAGRAM_SIZE;
assert_eq!(client.loss_recovery.cwnd(), expected_cwnd);
assert_eq!(cwnd(&client), expected_cwnd);
c_tx_dgrams = next_c_tx_dgrams;
}
}
@ -356,7 +362,7 @@ fn cc_slow_start_to_persistent_congestion_no_acks() {
// Server: Receive and generate ack
now += DEFAULT_RTT / 2;
let _ = ack_bytes(&mut server, 0, c_tx_dgrams, now);
mem::drop(ack_bytes(&mut server, 0, c_tx_dgrams, now));
// ACK lost.
induce_persistent_congestion(&mut client, &mut server, now);
@ -409,7 +415,7 @@ fn cc_persistent_congestion_to_slow_start() {
// Server: Receive and generate ack
now += Duration::from_millis(10);
let _ = ack_bytes(&mut server, 0, c_tx_dgrams, now);
mem::drop(ack_bytes(&mut server, 0, c_tx_dgrams, now));
// ACK lost.

View File

@ -15,11 +15,15 @@ use crate::events::ConnectionEvent;
use crate::path::PATH_MTU_V6;
use crate::server::ValidateAddress;
use crate::tparams::TransportParameter;
use crate::{ConnectionError, ConnectionParameters, EmptyConnectionIdGenerator, Error, StreamType};
use crate::{
ConnectionError, ConnectionParameters, EmptyConnectionIdGenerator, Error, QuicVersion,
StreamType,
};
use neqo_common::{event::Provider, qdebug, Datagram};
use neqo_crypto::{constants::TLS_CHACHA20_POLY1305_SHA256, AuthenticationStatus};
use std::cell::RefCell;
use std::net::{IpAddr, Ipv6Addr, SocketAddr};
use std::rc::Rc;
use std::time::Duration;
use test_fixture::{self, addr, assertions, fixture_init, now, split_datagram};
@ -108,6 +112,7 @@ fn no_alpn() {
addr(),
addr(),
ConnectionParameters::default(),
now(),
)
.unwrap();
let mut server = default_server();
@ -236,6 +241,7 @@ fn chacha20poly1305() {
addr(),
addr(),
ConnectionParameters::default(),
now(),
)
.expect("create a default client");
client.set_ciphers(&[TLS_CHACHA20_POLY1305_SHA256]).unwrap();
@ -379,12 +385,12 @@ fn reorder_05rtt_with_0rtt() {
maybe_authenticate(&mut client);
let c4 = client.process(None, now).dgram();
assert_eq!(*client.state(), State::Connected);
assert_eq!(client.loss_recovery.rtt(), RTT);
assert_eq!(client.paths.rtt(), RTT);
now += RTT / 2;
server.process_input(c4.unwrap(), now);
assert_eq!(*server.state(), State::Confirmed);
assert_eq!(server.loss_recovery.rtt(), RTT);
assert_eq!(server.paths.rtt(), RTT);
}
/// Test that a server that coalesces 0.5 RTT with handshake packets
@ -508,12 +514,12 @@ fn reorder_handshake() {
now += RTT / 2;
let s3 = server.process(c3, now).dgram();
assert_eq!(*server.state(), State::Confirmed);
assert_eq!(server.loss_recovery.rtt(), RTT);
assert_eq!(server.paths.rtt(), RTT);
now += RTT / 2;
client.process_input(s3.unwrap(), now);
assert_eq!(*client.state(), State::Confirmed);
assert_eq!(client.loss_recovery.rtt(), RTT);
assert_eq!(client.paths.rtt(), RTT);
}
#[test]
@ -558,11 +564,11 @@ fn reorder_1rtt() {
assert_eq!(server.stats().saved_datagrams, PACKETS);
assert_eq!(server.stats().dropped_rx, 1);
assert_eq!(*server.state(), State::Confirmed);
assert_eq!(server.loss_recovery.rtt(), RTT);
assert_eq!(server.paths.rtt(), RTT);
now += RTT / 2;
client.process_input(s2.unwrap(), now);
assert_eq!(client.loss_recovery.rtt(), RTT);
assert_eq!(client.paths.rtt(), RTT);
// All the stream data that was sent should now be available.
let streams = server
@ -698,6 +704,63 @@ fn extra_initial_invalid_cid() {
assert!(nothing.is_none());
}
fn connect_version(version: QuicVersion) {
fixture_init();
let mut client = Connection::new_client(
test_fixture::DEFAULT_SERVER_NAME,
test_fixture::DEFAULT_ALPN,
Rc::new(RefCell::new(CountingConnectionIdGenerator::default())),
addr(),
addr(),
ConnectionParameters::default().quic_version(version),
now(),
)
.unwrap();
let mut server = Connection::new_server(
test_fixture::DEFAULT_KEYS,
test_fixture::DEFAULT_ALPN,
Rc::new(RefCell::new(CountingConnectionIdGenerator::default())),
ConnectionParameters::default().quic_version(version),
)
.unwrap();
connect_force_idle(&mut client, &mut server);
}
#[test]
fn connect_v1() {
connect_version(QuicVersion::Version1);
}
#[test]
fn connect_27() {
connect_version(QuicVersion::Draft27);
}
#[test]
fn connect_28() {
connect_version(QuicVersion::Draft28);
}
#[test]
fn connect_29() {
connect_version(QuicVersion::Draft29);
}
#[test]
fn connect_30() {
connect_version(QuicVersion::Draft30);
}
#[test]
fn connect_31() {
connect_version(QuicVersion::Draft31);
}
#[test]
fn connect_32() {
connect_version(QuicVersion::Draft32);
}
#[test]
fn anti_amplification() {
let mut client = default_client();
@ -715,6 +778,12 @@ fn anti_amplification() {
assert_eq!(s_init1.len(), PATH_MTU_V6);
let s_init2 = server.process_output(now).dgram().unwrap();
assert_eq!(s_init2.len(), PATH_MTU_V6);
// Skip the gap for pacing here.
let s_pacing = server.process_output(now).callback();
assert_ne!(s_pacing, Duration::new(0, 0));
now += s_pacing;
let s_init3 = server.process_output(now).dgram().unwrap();
assert_eq!(s_init3.len(), PATH_MTU_V6);
let cb = server.process_output(now).callback();
@ -746,3 +815,64 @@ fn anti_amplification() {
server.process_input(fin.unwrap(), now);
assert_eq!(*server.state(), State::Confirmed);
}
#[test]
fn garbage_initial() {
let mut client = default_client();
let mut server = default_server();
let dgram = client.process_output(now()).dgram().unwrap();
let (initial, rest) = split_datagram(&dgram);
let mut corrupted = Vec::from(&initial[..initial.len() - 1]);
corrupted.push(initial[initial.len() - 1] ^ 0xb7);
corrupted.extend_from_slice(rest.as_ref().map_or(&[], |r| &r[..]));
let garbage = Datagram::new(addr(), addr(), corrupted);
assert_eq!(Output::None, server.process(Some(garbage), now()));
}
#[test]
fn drop_initial_packet_from_wrong_address() {
let mut client = default_client();
let out = client.process(None, now());
assert!(out.as_dgram_ref().is_some());
let mut server = default_server();
let out = server.process(out.dgram(), now());
assert!(out.as_dgram_ref().is_some());
let p = out.dgram().unwrap();
let dgram = Datagram::new(
SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 2)), 443),
p.destination(),
&p[..],
);
let out = client.process(Some(dgram), now());
assert!(out.as_dgram_ref().is_none());
}
#[test]
fn drop_handshake_packet_from_wrong_address() {
let mut client = default_client();
let out = client.process(None, now());
assert!(out.as_dgram_ref().is_some());
let mut server = default_server();
let out = server.process(out.dgram(), now());
assert!(out.as_dgram_ref().is_some());
let (s_in, s_hs) = split_datagram(&out.dgram().unwrap());
// Pass the initial packet.
let _ = client.process(Some(s_in), now()).dgram();
let p = s_hs.unwrap();
let dgram = Datagram::new(
SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 2)), 443),
p.destination(),
&p[..],
);
let out = client.process(Some(dgram), now());
assert!(out.as_dgram_ref().is_none());
}

View File

@ -10,6 +10,7 @@ use super::{
maybe_authenticate, send_something, AT_LEAST_PTO,
};
use crate::packet::PacketBuilder;
use crate::stats::FrameStats;
use crate::tparams::{self, TransportParameter};
use crate::tracking::PNSpace;
use crate::StreamType;
@ -250,18 +251,30 @@ fn idle_caching() {
let ping_before_s = server.stats().frame_rx.ping;
server.process_input(dgram.unwrap(), middle);
assert_eq!(server.stats().frame_rx.ping, ping_before_s + 1);
let crypto = server
let mut tokens = Vec::new();
server
.crypto
.streams
.write_frame(PNSpace::Initial, &mut builder)
.write_frame(
PNSpace::Initial,
&mut builder,
&mut tokens,
&mut FrameStats::default(),
)
.unwrap();
assert!(crypto.is_some());
let crypto = server
assert_eq!(tokens.len(), 1);
tokens.clear();
server
.crypto
.streams
.write_frame(PNSpace::Initial, &mut builder)
.write_frame(
PNSpace::Initial,
&mut builder,
&mut tokens,
&mut FrameStats::default(),
)
.unwrap();
assert!(crypto.is_none());
assert!(tokens.is_empty());
let dgram = server.process_output(middle).dgram();
// Now only allow the Initial packet from the server through;

View File

@ -277,9 +277,13 @@ fn exhaust_read_keys() {
));
client.process_input(dgram.unwrap(), now());
assert!(matches!(client.state(), State::Draining {
error: ConnectionError::Transport(Error::PeerError(ERROR_AEAD_LIMIT_REACHED)), ..
}));
assert!(matches!(
client.state(),
State::Draining {
error: ConnectionError::Transport(Error::PeerError(ERROR_AEAD_LIMIT_REACHED)),
..
}
));
}
#[test]

View File

@ -17,7 +17,7 @@ use neqo_common::Datagram;
use std::cell::RefCell;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
use std::rc::Rc;
use std::time::Duration;
use std::time::{Duration, Instant};
use test_fixture::{self, addr, fixture_init, now};
/// This should be a valid-seeming transport parameter.
@ -74,6 +74,14 @@ fn assert_v6_path(dgram: &Datagram, padded: bool) {
}
}
/// As these tests use a new path, that path often has a non-zero RTT.
/// Pacing can be a problem when testing that path. This skips time forward.
fn skip_pacing(c: &mut Connection, now: Instant) -> Instant {
let pacing = c.process_output(now).callback();
assert_ne!(pacing, Duration::new(0, 0));
now + pacing
}
#[test]
fn rebinding_port() {
let mut client = default_client();
@ -101,18 +109,19 @@ fn path_forwarding_attack() {
let mut client = default_client();
let mut server = default_server();
connect_force_idle(&mut client, &mut server);
let mut now = now();
let dgram = send_something(&mut client, now());
let dgram = send_something(&mut client, now);
let dgram = change_path(&dgram, addr_v4());
server.process_input(dgram, now());
server.process_input(dgram, now);
// The server now probes the new (primary) path.
let new_probe = server.process_output(now()).dgram().unwrap();
let new_probe = server.process_output(now).dgram().unwrap();
assert_eq!(server.stats().frame_tx.path_challenge, 1);
assert_v4_path(&new_probe, false); // Can't be padded.
// The server also probes the old path.
let old_probe = server.process_output(now()).dgram().unwrap();
let old_probe = server.process_output(now).dgram().unwrap();
assert_eq!(server.stats().frame_tx.path_challenge, 2);
assert_v6_path(&old_probe, true);
@ -120,56 +129,57 @@ fn path_forwarding_attack() {
// now constrained by the amplification limit.
let stream_id = server.stream_create(StreamType::UniDi).unwrap();
server.stream_close_send(stream_id).unwrap();
assert!(server.process_output(now()).dgram().is_none());
assert!(server.process_output(now).dgram().is_none());
// The client should respond to the challenge on the new path.
// The server couldn't pad, so the client is also amplification limited.
let new_resp = client.process(Some(new_probe), now()).dgram().unwrap();
let new_resp = client.process(Some(new_probe), now).dgram().unwrap();
assert_eq!(client.stats().frame_rx.path_challenge, 1);
assert_eq!(client.stats().frame_tx.path_challenge, 1);
assert_eq!(client.stats().frame_tx.path_response, 1);
assert_v4_path(&new_resp, false);
// The client also responds to probes on the old path.
let old_resp = client.process(Some(old_probe), now()).dgram().unwrap();
let old_resp = client.process(Some(old_probe), now).dgram().unwrap();
assert_eq!(client.stats().frame_rx.path_challenge, 2);
assert_eq!(client.stats().frame_tx.path_challenge, 1);
assert_eq!(client.stats().frame_tx.path_response, 2);
assert_v6_path(&old_resp, true);
// But the client still sends data on the old path.
let client_data1 = send_something(&mut client, now());
let client_data1 = send_something(&mut client, now);
assert_v6_path(&client_data1, false); // Just data.
// Receiving the PATH_RESPONSE from the client opens the amplification
// limit enough for the server to respond.
// This is padded because it includes PATH_CHALLENGE.
let server_data1 = server.process(Some(new_resp), now()).dgram().unwrap();
let server_data1 = server.process(Some(new_resp), now).dgram().unwrap();
assert_v4_path(&server_data1, true);
assert_eq!(server.stats().frame_tx.path_challenge, 3);
// The client responds to this probe on the new path.
client.process_input(server_data1, now());
client.process_input(server_data1, now);
let stream_before = client.stats().frame_tx.stream;
let padded_resp = send_something(&mut client, now());
let padded_resp = send_something(&mut client, now);
assert_eq!(stream_before, client.stats().frame_tx.stream);
assert_v4_path(&padded_resp, true); // This is padded!
// But new data from the client stays on the old path.
let client_data2 = client.process_output(now()).dgram().unwrap();
let client_data2 = client.process_output(now).dgram().unwrap();
assert_v6_path(&client_data2, false);
// The server keeps sending on the new path.
let server_data2 = send_something(&mut server, now());
now = skip_pacing(&mut server, now);
let server_data2 = send_something(&mut server, now);
assert_v4_path(&server_data2, false);
// Until new data is received from the client on the old path.
server.process_input(client_data2, now());
server.process_input(client_data2, now);
// The server sends a probe on the "old" path.
let server_data3 = send_something(&mut server, now());
let server_data3 = send_something(&mut server, now);
assert_v4_path(&server_data3, true);
// But switches data transmission to the "new" path.
let server_data4 = server.process_output(now()).dgram().unwrap();
let server_data4 = server.process_output(now).dgram().unwrap();
assert_v6_path(&server_data4, false);
}
@ -178,35 +188,37 @@ fn migrate_immediate() {
let mut client = default_client();
let mut server = default_server();
connect_force_idle(&mut client, &mut server);
let mut now = now();
client
.migrate(Some(addr_v4()), Some(addr_v4()), true, now())
.migrate(Some(addr_v4()), Some(addr_v4()), true, now)
.unwrap();
let client1 = send_something(&mut client, now());
let client1 = send_something(&mut client, now);
assert_v4_path(&client1, true); // Contains PATH_CHALLENGE.
let client2 = send_something(&mut client, now());
let client2 = send_something(&mut client, now);
assert_v4_path(&client2, false); // Doesn't.
let server_delayed = send_something(&mut server, now());
let server_delayed = send_something(&mut server, now);
// The server accepts the first packet and migrates (but probes).
let server1 = server.process(Some(client1), now()).dgram().unwrap();
let server1 = server.process(Some(client1), now).dgram().unwrap();
assert_v4_path(&server1, true);
let server2 = server.process_output(now()).dgram().unwrap();
let server2 = server.process_output(now).dgram().unwrap();
assert_v6_path(&server2, true);
// The second packet has no real effect, it just elicits an ACK.
let all_before = server.stats().frame_tx.all;
let ack_before = server.stats().frame_tx.ack;
let server3 = server.process(Some(client2), now()).dgram();
let server3 = server.process(Some(client2), now).dgram();
assert!(server3.is_some());
assert_eq!(server.stats().frame_tx.all, all_before + 1);
assert_eq!(server.stats().frame_tx.ack, ack_before + 1);
// Receiving a packet sent by the server before migration doesn't change path.
client.process_input(server_delayed, now());
let client3 = send_something(&mut client, now());
client.process_input(server_delayed, now);
now = skip_pacing(&mut client, now);
let client3 = send_something(&mut client, now);
assert_v4_path(&client3, false);
}
@ -391,7 +403,11 @@ fn migration(mut client: Connection) {
let client_confirmation = client.process_output(now).dgram().unwrap();
assert_v4_path(&client_confirmation, false);
let server_confirmation = send_something(&mut server, now);
// The server has now sent 2 packets, so it is blocked on the pacer. Wait.
let server_pacing = server.process_output(now).callback();
assert_ne!(server_pacing, Duration::new(0, 0));
// ... then confirm that the server sends on the new path still.
let server_confirmation = send_something(&mut server, now + server_pacing);
assert_v4_path(&server_confirmation, false);
}
@ -411,6 +427,7 @@ fn migration_client_empty_cid() {
addr(),
addr(),
ConnectionParameters::default(),
now(),
)
.unwrap();
migration(client);
@ -470,6 +487,7 @@ fn preferred_address(hs_client: SocketAddr, hs_server: SocketAddr, preferred: So
hs_client,
hs_server,
ConnectionParameters::default(),
now(),
)
.unwrap();
let spa = if preferred.ip().is_ipv6() {

View File

@ -33,6 +33,7 @@ mod handshake;
mod idle;
mod keys;
mod migration;
mod priority;
mod recovery;
mod resumption;
mod stream;
@ -96,6 +97,7 @@ pub fn new_client(params: ConnectionParameters) -> Connection {
addr(),
addr(),
params,
now(),
)
.expect("create a default client")
}
@ -144,7 +146,12 @@ fn handshake(
let mut now = now;
let mut input = None;
let is_done = |c: &mut Connection| matches!(c.state(), State::Confirmed | State::Closing { .. } | State::Closed(..));
let is_done = |c: &mut Connection| {
matches!(
c.state(),
State::Confirmed | State::Closing { .. } | State::Closed(..)
)
};
while !is_done(a) {
let _ = maybe_authenticate(a);
@ -181,8 +188,8 @@ fn connect_with_rtt(
assert_eq!(*client.state(), State::Confirmed);
assert_eq!(*server.state(), State::Confirmed);
assert_eq!(client.loss_recovery.rtt(), rtt);
assert_eq!(server.loss_recovery.rtt(), rtt);
assert_eq!(client.paths.rtt(), rtt);
assert_eq!(server.paths.rtt(), rtt);
now
}
@ -262,6 +269,7 @@ fn connect_rtt_idle(client: &mut Connection, server: &mut Connection, rtt: Durat
// Drain events from both as well.
let _ = client.events().count();
let _ = server.events().count();
qtrace!("----- connected and idle with RTT {:?}", rtt);
now
}
@ -275,36 +283,33 @@ fn connect_force_idle(client: &mut Connection, server: &mut Connection) {
/// from the return value whether a timeout is an ACK delay, PTO, or
/// pacing, this looks at the congestion window to tell when to stop.
/// Returns a list of datagrams and the new time.
fn fill_cwnd(src: &mut Connection, stream: u64, mut now: Instant) -> (Vec<Datagram>, Instant) {
fn fill_cwnd(c: &mut Connection, stream: u64, mut now: Instant) -> (Vec<Datagram>, Instant) {
const BLOCK_SIZE: usize = 4_096;
let mut total_dgrams = Vec::new();
// Train wreck function to get the remaining congestion window on the primary path.
fn cwnd(c: &Connection) -> usize {
c.paths.primary().borrow().sender().cwnd_avail()
}
qtrace!(
"fill_cwnd starting cwnd: {}",
src.loss_recovery.cwnd_avail()
);
qtrace!("fill_cwnd starting cwnd: {}", cwnd(c));
loop {
let bytes_sent = src.stream_send(stream, &[0x42; BLOCK_SIZE]).unwrap();
let bytes_sent = c.stream_send(stream, &[0x42; BLOCK_SIZE]).unwrap();
qtrace!("fill_cwnd wrote {} bytes", bytes_sent);
if bytes_sent < BLOCK_SIZE {
break;
}
}
let mut total_dgrams = Vec::new();
loop {
let pkt = src.process_output(now);
qtrace!(
"fill_cwnd cwnd remaining={}, output: {:?}",
src.loss_recovery.cwnd_avail(),
pkt
);
let pkt = c.process_output(now);
qtrace!("fill_cwnd cwnd remaining={}, output: {:?}", cwnd(c), pkt);
match pkt {
Output::Datagram(dgram) => {
total_dgrams.push(dgram);
}
Output::Callback(t) => {
if src.loss_recovery.cwnd_avail() < ACK_ONLY_SIZE_LIMIT {
if cwnd(c) < ACK_ONLY_SIZE_LIMIT {
break;
}
now += t;
@ -313,6 +318,10 @@ fn fill_cwnd(src: &mut Connection, stream: u64, mut now: Instant) -> (Vec<Datagr
}
}
qtrace!(
"fill_cwnd sent {} bytes",
total_dgrams.iter().map(|d| d.len()).sum::<usize>()
);
(total_dgrams, now)
}

View File

@ -0,0 +1,401 @@
// 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::super::{Connection, Error, Output};
use super::{connect, default_client, default_server, fill_cwnd, maybe_authenticate};
use crate::addr_valid::{AddressValidation, ValidateAddress};
use crate::send_stream::{RetransmissionPriority, TransmissionPriority};
use crate::{ConnectionEvent, StreamType};
use neqo_common::event::Provider;
use std::cell::RefCell;
use std::mem;
use std::rc::Rc;
use test_fixture::{self, now};
const BLOCK_SIZE: usize = 4_096;
fn fill_stream(c: &mut Connection, id: u64) {
loop {
if c.stream_send(id, &[0x42; BLOCK_SIZE]).unwrap() < BLOCK_SIZE {
return;
}
}
}
/// A receive stream cannot be prioritized (yet).
#[test]
fn receive_stream() {
const MESSAGE: &[u8] = b"hello";
let mut client = default_client();
let mut server = default_server();
connect(&mut client, &mut server);
let id = client.stream_create(StreamType::UniDi).unwrap();
assert_eq!(MESSAGE.len(), client.stream_send(id, MESSAGE).unwrap());
let dgram = client.process_output(now()).dgram();
server.process_input(dgram.unwrap(), now());
assert_eq!(
server
.stream_priority(
id,
TransmissionPriority::default(),
RetransmissionPriority::default()
)
.unwrap_err(),
Error::InvalidStreamId,
"Priority doesn't apply to inbound unidirectional streams"
);
// But the stream does exist and can be read.
let mut buf = [0; 10];
let (len, end) = server.stream_recv(id, &mut buf).unwrap();
assert_eq!(MESSAGE, &buf[..len]);
assert!(!end);
}
/// Higher priority streams get sent ahead of lower ones, even when
/// the higher priority stream is written to later.
#[test]
fn relative() {
let mut client = default_client();
let mut server = default_server();
connect(&mut client, &mut server);
// id_normal is created first, but it is lower priority.
let id_normal = client.stream_create(StreamType::UniDi).unwrap();
fill_stream(&mut client, id_normal);
let high = client.stream_create(StreamType::UniDi).unwrap();
fill_stream(&mut client, high);
client
.stream_priority(
high,
TransmissionPriority::High,
RetransmissionPriority::default(),
)
.unwrap();
let dgram = client.process_output(now()).dgram();
server.process_input(dgram.unwrap(), now());
// The "id_normal" stream will get a `NewStream` event, but no data.
for e in server.events() {
if let ConnectionEvent::RecvStreamReadable { stream_id } = e {
assert_ne!(stream_id, id_normal);
}
}
}
/// Check that changing priority has effect on the next packet that is sent.
#[test]
fn reprioritize() {
let mut client = default_client();
let mut server = default_server();
connect(&mut client, &mut server);
// id_normal is created first, but it is lower priority.
let id_normal = client.stream_create(StreamType::UniDi).unwrap();
fill_stream(&mut client, id_normal);
let id_high = client.stream_create(StreamType::UniDi).unwrap();
fill_stream(&mut client, id_high);
client
.stream_priority(
id_high,
TransmissionPriority::High,
RetransmissionPriority::default(),
)
.unwrap();
let dgram = client.process_output(now()).dgram();
server.process_input(dgram.unwrap(), now());
// The "id_normal" stream will get a `NewStream` event, but no data.
for e in server.events() {
if let ConnectionEvent::RecvStreamReadable { stream_id } = e {
assert_ne!(stream_id, id_normal);
}
}
// When the high priority stream drops in priority, the streams are equal
// priority and so their stream ID determines what is sent.
client
.stream_priority(
id_high,
TransmissionPriority::Normal,
RetransmissionPriority::default(),
)
.unwrap();
let dgram = client.process_output(now()).dgram();
server.process_input(dgram.unwrap(), now());
for e in server.events() {
if let ConnectionEvent::RecvStreamReadable { stream_id } = e {
assert_ne!(stream_id, id_high);
}
}
}
/// Retransmission can be prioritized differently (usually higher).
#[test]
fn repairing_loss() {
let mut client = default_client();
let mut server = default_server();
connect(&mut client, &mut server);
let mut now = now();
// Send a few packets at low priority, lose one.
let id_low = client.stream_create(StreamType::UniDi).unwrap();
fill_stream(&mut client, id_low);
client
.stream_priority(
id_low,
TransmissionPriority::Low,
RetransmissionPriority::Higher,
)
.unwrap();
let _lost = client.process_output(now).dgram();
for _ in 0..5 {
match client.process_output(now) {
Output::Datagram(d) => server.process_input(d, now),
Output::Callback(delay) => now += delay,
Output::None => unreachable!(),
}
}
// Generate an ACK. The first packet is now considered lost.
let ack = server.process_output(now).dgram();
let _ = server.events().count(); // Drain events.
let id_normal = client.stream_create(StreamType::UniDi).unwrap();
fill_stream(&mut client, id_normal);
let dgram = client.process(ack, now).dgram();
assert_eq!(client.stats().lost, 1); // Client should have noticed the loss.
server.process_input(dgram.unwrap(), now);
// Only the low priority stream has data as the retransmission of the data from
// the lost packet is now more important than new data from the high priority stream.
for e in server.events() {
println!("Event: {:?}", e);
if let ConnectionEvent::RecvStreamReadable { stream_id } = e {
assert_eq!(stream_id, id_low);
}
}
// However, only the retransmission is prioritized.
// Though this might contain some retransmitted data, as other frames might push
// the retransmitted data into a second packet, it will also contain data from the
// normal priority stream.
let dgram = client.process_output(now).dgram();
server.process_input(dgram.unwrap(), now);
assert!(server.events().any(
|e| matches!(e, ConnectionEvent::RecvStreamReadable { stream_id } if stream_id == id_normal),
));
}
#[test]
fn critical() {
let mut client = default_client();
let mut server = default_server();
let now = now();
// Rather than connect, send stream data in 0.5-RTT.
// That allows this to test that critical streams pre-empt most frame types.
let dgram = client.process_output(now).dgram();
let dgram = server.process(dgram, now).dgram();
client.process_input(dgram.unwrap(), now);
maybe_authenticate(&mut client);
let id = server.stream_create(StreamType::UniDi).unwrap();
server
.stream_priority(
id,
TransmissionPriority::Critical,
RetransmissionPriority::default(),
)
.unwrap();
// Can't use fill_cwnd here because the server is blocked on the amplification
// limit, so it can't fill the congestion window.
while server.stream_create(StreamType::UniDi).is_ok() {}
fill_stream(&mut server, id);
let stats_before = server.stats().frame_tx;
let dgram = server.process_output(now).dgram();
let stats_after = server.stats().frame_tx;
assert_eq!(stats_after.crypto, stats_before.crypto);
assert_eq!(stats_after.streams_blocked, 0);
assert_eq!(stats_after.new_connection_id, 0);
assert_eq!(stats_after.new_token, 0);
assert_eq!(stats_after.handshake_done, 0);
// Complete the handshake.
let dgram = client.process(dgram, now).dgram();
server.process_input(dgram.unwrap(), now);
// Critical beats everything but HANDSHAKE_DONE.
let stats_before = server.stats().frame_tx;
mem::drop(fill_cwnd(&mut server, id, now));
let stats_after = server.stats().frame_tx;
assert_eq!(stats_after.crypto, stats_before.crypto);
assert_eq!(stats_after.streams_blocked, 0);
assert_eq!(stats_after.new_connection_id, 0);
assert_eq!(stats_after.new_token, 0);
assert_eq!(stats_after.handshake_done, 1);
}
#[test]
fn important() {
let mut client = default_client();
let mut server = default_server();
let now = now();
// Rather than connect, send stream data in 0.5-RTT.
// That allows this to test that important streams pre-empt most frame types.
let dgram = client.process_output(now).dgram();
let dgram = server.process(dgram, now).dgram();
client.process_input(dgram.unwrap(), now);
maybe_authenticate(&mut client);
let id = server.stream_create(StreamType::UniDi).unwrap();
server
.stream_priority(
id,
TransmissionPriority::Important,
RetransmissionPriority::default(),
)
.unwrap();
fill_stream(&mut server, id);
// Important beats everything but flow control.
// Make enough streams to get a STREAMS_BLOCKED frame out.
while server.stream_create(StreamType::UniDi).is_ok() {}
let stats_before = server.stats().frame_tx;
let dgram = server.process_output(now).dgram();
let stats_after = server.stats().frame_tx;
assert_eq!(stats_after.crypto, stats_before.crypto);
assert_eq!(stats_after.streams_blocked, 1);
assert_eq!(stats_after.new_connection_id, 0);
assert_eq!(stats_after.new_token, 0);
assert_eq!(stats_after.handshake_done, 0);
assert_eq!(stats_after.stream, stats_before.stream + 1);
// Complete the handshake.
let dgram = client.process(dgram, now).dgram();
server.process_input(dgram.unwrap(), now);
// Important beats everything but flow control.
let stats_before = server.stats().frame_tx;
mem::drop(fill_cwnd(&mut server, id, now));
let stats_after = server.stats().frame_tx;
assert_eq!(stats_after.crypto, stats_before.crypto);
assert_eq!(stats_after.streams_blocked, 1);
assert_eq!(stats_after.new_connection_id, 0);
assert_eq!(stats_after.new_token, 0);
assert_eq!(stats_after.handshake_done, 1);
assert!(stats_after.stream > stats_before.stream);
}
#[test]
fn high_normal() {
let mut client = default_client();
let mut server = default_server();
let now = now();
// Rather than connect, send stream data in 0.5-RTT.
// That allows this to test that important streams pre-empt most frame types.
let dgram = client.process_output(now).dgram();
let dgram = server.process(dgram, now).dgram();
client.process_input(dgram.unwrap(), now);
maybe_authenticate(&mut client);
let id = server.stream_create(StreamType::UniDi).unwrap();
server
.stream_priority(
id,
TransmissionPriority::High,
RetransmissionPriority::default(),
)
.unwrap();
fill_stream(&mut server, id);
// Important beats everything but flow control.
// Make enough streams to get a STREAMS_BLOCKED frame out.
while server.stream_create(StreamType::UniDi).is_ok() {}
let stats_before = server.stats().frame_tx;
let dgram = server.process_output(now).dgram();
let stats_after = server.stats().frame_tx;
assert_eq!(stats_after.crypto, stats_before.crypto);
assert_eq!(stats_after.streams_blocked, 1);
assert_eq!(stats_after.new_connection_id, 0);
assert_eq!(stats_after.new_token, 0);
assert_eq!(stats_after.handshake_done, 0);
assert_eq!(stats_after.stream, stats_before.stream + 1);
// Complete the handshake.
let dgram = client.process(dgram, now).dgram();
server.process_input(dgram.unwrap(), now);
// High or Normal doesn't beat NEW_CONNECTION_ID,
// but they beat CRYPTO/NEW_TOKEN.
let stats_before = server.stats().frame_tx;
server.send_ticket(now, &[]).unwrap();
mem::drop(fill_cwnd(&mut server, id, now));
let stats_after = server.stats().frame_tx;
assert_eq!(stats_after.crypto, stats_before.crypto);
assert_eq!(stats_after.streams_blocked, 1);
assert_ne!(stats_after.new_connection_id, 0); // Note: > 0
assert_eq!(stats_after.new_token, 0);
assert_eq!(stats_after.handshake_done, 1);
assert!(stats_after.stream > stats_before.stream);
}
#[test]
fn low() {
let mut client = default_client();
let mut server = default_server();
let now = now();
// Use address validation; note that we need to hold a strong reference
// as the server will only hold a weak reference.
let validation = Rc::new(RefCell::new(
AddressValidation::new(now, ValidateAddress::Never).unwrap(),
));
server.set_validation(Rc::clone(&validation));
connect(&mut client, &mut server);
let id = server.stream_create(StreamType::UniDi).unwrap();
server
.stream_priority(
id,
TransmissionPriority::Low,
RetransmissionPriority::default(),
)
.unwrap();
fill_stream(&mut server, id);
// Send a session ticket and make it big enough to require a whole packet.
// The resulting CRYPTO frame beats out the stream data.
let stats_before = server.stats().frame_tx;
server.send_ticket(now, &[0; 2048]).unwrap();
let _ = server.process_output(now);
let stats_after = server.stats().frame_tx;
assert_eq!(stats_after.crypto, stats_before.crypto + 1);
assert_eq!(stats_after.stream, stats_before.stream);
// The above can't test if NEW_TOKEN wins because once that fits in a packet,
// it is very hard to ensure that the STREAM frame won't also fit.
// However, we can ensure that the next packet doesn't consist of just STREAM.
let stats_before = server.stats().frame_tx;
let _ = server.process_output(now);
let stats_after = server.stats().frame_tx;
assert_eq!(stats_after.crypto, stats_before.crypto + 1);
assert_eq!(stats_after.new_token, 1);
assert_eq!(stats_after.stream, stats_before.stream + 1);
}

View File

@ -4,14 +4,14 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use super::super::{Output, State, LOCAL_IDLE_TIMEOUT};
use super::super::{Connection, Output, State, LOCAL_IDLE_TIMEOUT};
use super::{
assert_full_cwnd, connect, connect_force_idle, connect_rtt_idle, connect_with_rtt,
default_client, default_server, fill_cwnd, maybe_authenticate, send_and_receive,
send_something, AT_LEAST_PTO, DEFAULT_RTT, POST_HANDSHAKE_CWND,
};
use crate::path::PATH_MTU_V6;
use crate::recovery::PTO_PACKET_COUNT;
use crate::recovery::{MAX_OUTSTANDING_UNACK, MIN_OUTSTANDING_UNACK, PTO_PACKET_COUNT};
use crate::stats::MAX_PTO_COUNTS;
use crate::tparams::TransportParameter;
use crate::tracking::ACK_DELAY;
@ -19,7 +19,7 @@ use crate::StreamType;
use neqo_common::qdebug;
use neqo_crypto::AuthenticationStatus;
use std::time::Duration;
use std::time::{Duration, Instant};
use test_fixture::{self, now, split_datagram};
#[test]
@ -77,11 +77,11 @@ fn pto_works_full_cwnd() {
assert_eq!(dgrams.len(), 2);
assert_eq!(dgrams[0].len(), PATH_MTU_V6);
// Both datagrams contain a STREAM frame.
// Both datagrams contain one or more STREAM frames.
for d in dgrams {
let stream_before = server.stats().frame_rx.stream;
server.process_input(d, now);
assert_eq!(server.stats().frame_rx.stream, stream_before + 1);
assert!(server.stats().frame_rx.stream > stream_before);
}
}
@ -105,7 +105,7 @@ fn pto_works_ping() {
// Nothing to do, should return callback
let cb = client.process(None, now).callback();
assert_eq!(cb, Duration::from_millis(45));
assert_eq!(cb, Duration::from_millis(26)); // MAX_ACK_DELAY + GRANULARITY
// Process these by server, skipping pkt0
let srv0 = server.process(Some(pkt1), now).dgram();
@ -614,3 +614,91 @@ fn loss_time_past_largest_acked() {
assert_ne!(delay, Duration::from_secs(0));
assert!(delay > lr_time);
}
/// `sender` sends a little, `receiver` acknowledges it.
/// Repeat until `count` acknowledgements are sent.
/// Returns the last packet containing acknowledgements, if any.
fn trickle(sender: &mut Connection, receiver: &mut Connection, mut count: usize, now: Instant) {
let id = sender.stream_create(StreamType::UniDi).unwrap();
let mut maybe_ack = None;
while count > 0 {
qdebug!("trickle: remaining={}", count);
assert_eq!(sender.stream_send(id, &[9]).unwrap(), 1);
let dgram = sender.process(maybe_ack, now).dgram();
maybe_ack = receiver.process(dgram, now).dgram();
count -= usize::from(maybe_ack.is_some());
}
sender.process_input(maybe_ack.unwrap(), now);
}
/// Ensure that a PING frame is sent with ACK sometimes.
/// `fast` allows testing of when `MAX_OUTSTANDING_UNACK` packets are
/// outstanding (`fast` is `true`) within 1 PTO and when only
/// `MIN_OUTSTANDING_UNACK` packets arrive after 2 PTOs (`fast` is `false`).
fn ping_with_ack(fast: bool) {
let mut sender = default_client();
let mut receiver = default_server();
let mut now = now();
connect_force_idle(&mut sender, &mut receiver);
let sender_acks_before = sender.stats().frame_tx.ack;
let receiver_acks_before = receiver.stats().frame_tx.ack;
let count = if fast {
MAX_OUTSTANDING_UNACK
} else {
MIN_OUTSTANDING_UNACK
};
trickle(&mut sender, &mut receiver, count, now);
assert_eq!(sender.stats().frame_tx.ack, sender_acks_before);
assert_eq!(receiver.stats().frame_tx.ack, receiver_acks_before + count);
assert_eq!(receiver.stats().frame_tx.ping, 0);
if !fast {
// Wait at least one PTO, from the reciever's perspective.
// A receiver that hasn't received MAX_OUTSTANDING_UNACK won't send PING.
now += receiver.pto() + Duration::from_micros(1);
trickle(&mut sender, &mut receiver, 1, now);
assert_eq!(receiver.stats().frame_tx.ping, 0);
}
// After a second PTO (or the first if fast), new acknowledgements come
// with a PING frame and cause an ACK to be sent by the sender.
now += receiver.pto() + Duration::from_micros(1);
trickle(&mut sender, &mut receiver, 1, now);
assert_eq!(receiver.stats().frame_tx.ping, 1);
if let Output::Callback(t) = sender.process_output(now) {
assert_eq!(t, ACK_DELAY);
assert!(sender.process_output(now + t).dgram().is_some());
}
assert_eq!(sender.stats().frame_tx.ack, sender_acks_before + 1);
}
#[test]
fn ping_with_ack_fast() {
ping_with_ack(true);
}
#[test]
fn ping_with_ack_slow() {
ping_with_ack(false);
}
#[test]
fn ping_with_ack_min() {
const COUNT: usize = MIN_OUTSTANDING_UNACK - 2;
let mut sender = default_client();
let mut receiver = default_server();
let mut now = now();
connect_force_idle(&mut sender, &mut receiver);
let sender_acks_before = sender.stats().frame_tx.ack;
let receiver_acks_before = receiver.stats().frame_tx.ack;
trickle(&mut sender, &mut receiver, COUNT, now);
assert_eq!(sender.stats().frame_tx.ack, sender_acks_before);
assert_eq!(receiver.stats().frame_tx.ack, receiver_acks_before + COUNT);
assert_eq!(receiver.stats().frame_tx.ping, 0);
// After 3 PTO, no PING because there are too few outstanding packets.
now += receiver.pto() * 3 + Duration::from_micros(1);
trickle(&mut sender, &mut receiver, 1, now);
assert_eq!(receiver.stats().frame_tx.ping, 0);
}

View File

@ -41,21 +41,21 @@ fn remember_smoothed_rtt() {
let mut server = default_server();
let now = connect_with_rtt(&mut client, &mut server, now(), RTT1);
assert_eq!(client.loss_recovery.rtt(), RTT1);
assert_eq!(client.paths.rtt(), RTT1);
let token = exchange_ticket(&mut client, &mut server, now);
let mut client = default_client();
let mut server = default_server();
client.enable_resumption(now, token).unwrap();
assert_eq!(
client.loss_recovery.rtt(),
client.paths.rtt(),
RTT1,
"client should remember previous RTT"
);
connect_with_rtt(&mut client, &mut server, now, RTT2);
assert_eq!(
client.loss_recovery.rtt(),
client.paths.rtt(),
RTT2,
"previous RTT should be completely erased"
);
@ -114,19 +114,19 @@ fn two_tickets_on_timer() {
// We need to wait for release_resumption_token_timer to expire. The timer will be
// set to 3 * PTO
let mut now = now() + 3 * client.get_pto();
let mut now = now() + 3 * client.pto();
let _ = client.process(None, now);
let mut recv_tokens = get_tokens(&mut client);
assert_eq!(recv_tokens.len(), 1);
let token1 = recv_tokens.pop().unwrap();
// Wai for anottheer 3 * PTO to get the nex okeen.
now += 3 * client.get_pto();
now += 3 * client.pto();
let _ = client.process(None, now);
let mut recv_tokens = get_tokens(&mut client);
assert_eq!(recv_tokens.len(), 1);
let token2 = recv_tokens.pop().unwrap();
// Wait for 3 * PTO, but now there are no more tokens.
now += 3 * client.get_pto();
now += 3 * client.pto();
let _ = client.process(None, now);
assert_eq!(get_tokens(&mut client).len(), 0);
assert_ne!(token1.as_ref(), token2.as_ref());

View File

@ -11,7 +11,7 @@ use super::{
};
use crate::events::ConnectionEvent;
use crate::recv_stream::RECV_BUFFER_SIZE;
use crate::send_stream::SEND_BUFFER_SIZE;
use crate::send_stream::{SendStreamState, SEND_BUFFER_SIZE};
use crate::tparams::{self, TransportParameter};
use crate::tracking::MAX_UNACKED_PKTS;
use crate::{Error, StreamId, StreamType};
@ -130,7 +130,7 @@ fn report_fin_when_stream_closed_wo_data() {
server.stream_close_send(stream_id).unwrap();
let out = server.process(None, now());
let _ = client.process(out.dgram(), now());
let stream_readable = |e| matches!(e, ConnectionEvent::RecvStreamReadable {..});
let stream_readable = |e| matches!(e, ConnectionEvent::RecvStreamReadable { .. });
assert!(client.events().any(stream_readable));
}
@ -203,7 +203,10 @@ fn max_data() {
let evts = client.events().collect::<Vec<_>>();
assert_eq!(evts.len(), 1);
assert!(matches!(evts[0], ConnectionEvent::SendStreamWritable{..}));
assert!(matches!(
evts[0],
ConnectionEvent::SendStreamWritable { .. }
));
}
#[test]
@ -221,7 +224,7 @@ fn do_not_accept_data_after_stop_sending() {
let out = client.process(None, now());
let _ = server.process(out.dgram(), now());
let stream_readable = |e| matches!(e, ConnectionEvent::RecvStreamReadable {..});
let stream_readable = |e| matches!(e, ConnectionEvent::RecvStreamReadable { .. });
assert!(server.events().any(stream_readable));
// Send one more packet from client. The packet should arrive after the server
@ -386,28 +389,36 @@ fn stream_data_blocked_generates_max_stream_data() {
let now = now();
// Send some data and include STREAM_DATA_BLOCKED with any value.
// Send some data and consume some flow control.
let stream_id = server.stream_create(StreamType::UniDi).unwrap();
let _ = server.stream_send(stream_id, DEFAULT_STREAM_DATA).unwrap();
server.flow_mgr.borrow_mut().stream_data_blocked(
StreamId::from(stream_id),
u64::try_from(DEFAULT_STREAM_DATA.len()).unwrap(),
);
let dgram = server.process(None, now).dgram();
assert!(dgram.is_some());
let sdb_before = client.stats().frame_rx.stream_data_blocked;
client.process_input(dgram.unwrap(), now);
assert_eq!(client.stats().frame_rx.stream_data_blocked, sdb_before + 1);
// Consume the data.
client.process_input(dgram.unwrap(), now);
let mut buf = [0; 10];
let (count, end) = client.stream_recv(stream_id, &mut buf[..]).unwrap();
assert_eq!(count, DEFAULT_STREAM_DATA.len());
assert!(!end);
let dgram = client.process_output(now).dgram();
// Now send `STREAM_DATA_BLOCKED`.
let internal_stream = server
.send_streams
.get_mut(StreamId::from(stream_id))
.unwrap();
if let SendStreamState::Send { fc, .. } = internal_stream.state() {
fc.blocked();
} else {
panic!("unexpected stream state");
}
let dgram = server.process_output(now).dgram();
assert!(dgram.is_some());
let sdb_before = client.stats().frame_rx.stream_data_blocked;
let dgram = client.process(dgram, now).dgram();
assert_eq!(client.stats().frame_rx.stream_data_blocked, sdb_before + 1);
assert!(dgram.is_some());
// Client should have sent a MAX_STREAM_DATA frame with just a small increase
// on the default window size.
@ -415,7 +426,7 @@ fn stream_data_blocked_generates_max_stream_data() {
server.process_input(dgram.unwrap(), now);
assert_eq!(server.stats().frame_rx.max_stream_data, msd_before + 1);
// Test that more space is available, but that it is small.
// Test that the entirety of the receive buffer is available now.
let mut written = 0;
loop {
const LARGE_BUFFER: &[u8] = &[0; 1024];
@ -425,7 +436,7 @@ fn stream_data_blocked_generates_max_stream_data() {
}
written += amount;
}
assert_eq!(written, RECV_BUFFER_SIZE - DEFAULT_STREAM_DATA.len());
assert_eq!(written, RECV_BUFFER_SIZE);
}
/// See <https://github.com/mozilla/neqo/issues/871>
@ -496,7 +507,7 @@ fn no_dupdata_readable_events() {
let _ = server.process(out.dgram(), now());
// We have a data_readable event.
let stream_readable = |e| matches!(e, ConnectionEvent::RecvStreamReadable {..});
let stream_readable = |e| matches!(e, ConnectionEvent::RecvStreamReadable { .. });
assert!(server.events().any(stream_readable));
// Send one more data frame from client. The previous stream data has not been read yet,
@ -528,7 +539,7 @@ fn no_dupdata_readable_events_empty_last_frame() {
let _ = server.process(out.dgram(), now());
// We have a data_readable event.
let stream_readable = |e| matches!(e, ConnectionEvent::RecvStreamReadable {..});
let stream_readable = |e| matches!(e, ConnectionEvent::RecvStreamReadable { .. });
assert!(server.events().any(stream_readable));
// An empty frame with a fin will not produce a new DataReadable event, because

View File

@ -6,7 +6,8 @@
use super::super::Connection;
use super::{
connect, default_client, default_server, exchange_ticket, CountingConnectionIdGenerator,
connect, default_client, default_server, exchange_ticket, new_server,
CountingConnectionIdGenerator,
};
use crate::events::ConnectionEvent;
use crate::{ConnectionParameters, Error, StreamType};
@ -196,3 +197,62 @@ fn zero_rtt_send_reject() {
server.process_input(client_after_reject.unwrap(), now());
assert!(server.events().any(recvd_stream_evt));
}
#[test]
fn zero_rtt_update_flow_control() {
const LOW: u64 = 3;
const HIGH: u64 = 10;
#[allow(clippy::cast_possible_truncation)]
const MESSAGE: &[u8] = &[0; HIGH as usize];
let mut client = default_client();
let mut server = new_server(
ConnectionParameters::default()
.max_stream_data(StreamType::UniDi, true, LOW)
.max_stream_data(StreamType::BiDi, true, LOW),
);
connect(&mut client, &mut server);
let token = exchange_ticket(&mut client, &mut server, now());
let mut client = default_client();
client
.enable_resumption(now(), token)
.expect("should set token");
let mut server = new_server(
ConnectionParameters::default()
.max_stream_data(StreamType::UniDi, true, HIGH)
.max_stream_data(StreamType::BiDi, true, HIGH),
);
// Stream limits should be low for 0-RTT.
let client_hs = client.process(None, now()).dgram();
let uni_stream = client.stream_create(StreamType::UniDi).unwrap();
assert!(!client.stream_send_atomic(uni_stream, MESSAGE).unwrap());
let bidi_stream = client.stream_create(StreamType::BiDi).unwrap();
assert!(!client.stream_send_atomic(bidi_stream, MESSAGE).unwrap());
// Now get the server transport parameters.
let server_hs = server.process(client_hs, now()).dgram();
client.process_input(server_hs.unwrap(), now());
// The streams should report a writeable event.
let mut uni_stream_event = false;
let mut bidi_stream_event = false;
for e in client.events() {
if let ConnectionEvent::SendStreamWritable { stream_id } = e {
if stream_id.is_uni() {
uni_stream_event = true;
} else {
bidi_stream_event = true;
}
}
}
assert!(uni_stream_event);
assert!(bidi_stream_event);
// But no MAX_STREAM_DATA frame was received.
assert_eq!(client.stats().frame_rx.max_stream_data, 0);
// And the new limit applies.
assert!(client.stream_send_atomic(uni_stream, MESSAGE).unwrap());
assert!(client.stream_send_atomic(bidi_stream, MESSAGE).unwrap());
}

View File

@ -25,6 +25,7 @@ use crate::packet::{PacketBuilder, PacketNumber, QuicVersion};
use crate::recovery::RecoveryToken;
use crate::recv_stream::RxStreamOrderer;
use crate::send_stream::TxBuffer;
use crate::stats::FrameStats;
use crate::tparams::{TpZeroRttChecker, TransportParameters, TransportParametersHandler};
use crate::tracking::PNSpace;
use crate::{Error, Res};
@ -54,7 +55,12 @@ pub struct Crypto {
type TpHandler = Rc<RefCell<TransportParametersHandler>>;
impl Crypto {
pub fn new(mut agent: Agent, protocols: &[impl AsRef<str>], tphandler: TpHandler) -> Res<Self> {
pub fn new(
version: QuicVersion,
mut agent: Agent,
protocols: &[impl AsRef<str>],
tphandler: TpHandler,
) -> Res<Self> {
agent.set_version_range(TLS_VERSION_1_3, TLS_VERSION_1_3)?;
agent.set_ciphers(&[
TLS_AES_128_GCM_SHA256,
@ -68,7 +74,16 @@ impl Crypto {
if let Agent::Client(c) = &mut agent {
c.enable_0rtt()?;
}
agent.extension_handler(0xffa5, tphandler)?;
let extension = match version {
QuicVersion::Version1 => 0x39,
QuicVersion::Draft27
| QuicVersion::Draft28
| QuicVersion::Draft29
| QuicVersion::Draft30
| QuicVersion::Draft31
| QuicVersion::Draft32 => 0xffa5,
};
agent.extension_handler(extension, tphandler)?;
Ok(Self {
tls: agent,
streams: Default::default(),
@ -225,6 +240,16 @@ impl Crypto {
Ok(())
}
pub fn write_frame(
&mut self,
space: PNSpace,
builder: &mut PacketBuilder,
tokens: &mut Vec<RecoveryToken>,
stats: &mut FrameStats,
) -> Res<()> {
self.streams.write_frame(space, builder, tokens, stats)
}
pub fn acked(&mut self, token: &CryptoRecoveryToken) {
qinfo!(
"Acked crypto frame space={} offset={} length={}",
@ -358,7 +383,10 @@ impl CryptoDxState {
label: &str,
dcid: &[u8],
) -> Self {
qtrace!("new_initial for {:?}", quic_version);
const INITIAL_SALT_V1: &[u8] = &[
0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17, 0x9a, 0xe6, 0xa4, 0xc8,
0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a,
];
const INITIAL_SALT_27: &[u8] = &[
0xc3, 0xee, 0xf7, 0x12, 0xc7, 0x2e, 0xbb, 0x5a, 0x11, 0xa7, 0xd2, 0x43, 0x2b, 0xb4,
0x63, 0x65, 0xbe, 0xf9, 0xf5, 0x02,
@ -367,7 +395,9 @@ impl CryptoDxState {
0xaf, 0xbf, 0xec, 0x28, 0x99, 0x93, 0xd2, 0x4c, 0x9e, 0x97, 0x86, 0xf1, 0x9c, 0x61,
0x11, 0xe0, 0x43, 0x90, 0xa8, 0x99,
];
qtrace!("new_initial for {:?}", quic_version);
let salt = match quic_version {
QuicVersion::Version1 => INITIAL_SALT_V1,
QuicVersion::Draft27 | QuicVersion::Draft28 => INITIAL_SALT_27,
QuicVersion::Draft29
| QuicVersion::Draft30
@ -549,7 +579,10 @@ impl CryptoDxState {
hex(body)
);
// The numbers in `Self::limit` assume a maximum packet size of 2^11.
assert!(body.len() <= 2048);
if body.len() > 2048 {
debug_assert!(false);
return Err(Error::InternalError(12));
}
self.invoked()?;
let size = body.len() + MAX_AUTH_TAG;
@ -1149,7 +1182,7 @@ impl CryptoStreams {
*self = Self::ApplicationData {
application: mem::take(application),
};
} else if matches!(self, Self::Initial {..}) {
} else if matches!(self, Self::Initial { .. }) {
panic!("Discarding handshake before initial discarded");
}
}
@ -1241,14 +1274,16 @@ impl CryptoStreams {
&mut self,
space: PNSpace,
builder: &mut PacketBuilder,
) -> Res<Option<RecoveryToken>> {
tokens: &mut Vec<RecoveryToken>,
stats: &mut FrameStats,
) -> Res<()> {
let cs = self.get_mut(space).unwrap();
if let Some((offset, data)) = cs.tx.next_bytes() {
let mut header_len = 1 + Encoder::varint_len(offset) + 1;
// Don't bother if there isn't room for the header and some data.
if builder.remaining() < header_len + 1 {
return Ok(None);
return Ok(());
}
// Calculate length of data based on the minimum of:
// - available data
@ -1268,14 +1303,14 @@ impl CryptoStreams {
cs.tx.mark_as_sent(offset, length);
qdebug!("CRYPTO for {} offset={}, len={}", space, offset, length);
Ok(Some(RecoveryToken::Crypto(CryptoRecoveryToken {
tokens.push(RecoveryToken::Crypto(CryptoRecoveryToken {
space,
offset,
length,
})))
} else {
Ok(None)
}));
stats.crypto += 1;
}
Ok(())
}
}

View File

@ -0,0 +1,441 @@
// 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.
// Tracks possibly-redundant flow control signals from other code and converts
// into flow control frames needing to be sent to the remote.
use crate::frame::{
write_varint_frame, FRAME_TYPE_DATA_BLOCKED, FRAME_TYPE_MAX_DATA, FRAME_TYPE_MAX_STREAM_DATA,
FRAME_TYPE_STREAM_DATA_BLOCKED,
};
use crate::packet::PacketBuilder;
use crate::recovery::RecoveryToken;
use crate::stats::FrameStats;
use crate::stream_id::StreamId;
use crate::Res;
use std::convert::TryFrom;
use std::fmt::Debug;
#[derive(Debug)]
pub struct SenderFlowControl<T>
where
T: Debug + Sized,
{
/// The thing that we're counting for.
subject: T,
/// The limit.
limit: u64,
/// How much of that limit we've used.
used: u64,
/// The point at which blocking occurred. This is updated each time
/// the sender decides that it is blocked. It only ever changes
/// when blocking occurs. This ensures that blocking at any given limit
/// is only reported once.
/// Note: All values are one greater than the corresponding `limit` to
/// allow distinguishing between blocking at a limit of 0 and no blocking.
blocked_at: u64,
/// Whether a blocked frame should be sent.
blocked_frame: bool,
}
impl<T> SenderFlowControl<T>
where
T: Debug + Sized,
{
/// Make a new instance with the initial value and subject.
pub fn new(subject: T, initial: u64) -> Self {
Self {
subject,
limit: initial,
used: 0,
blocked_at: 0,
blocked_frame: false,
}
}
/// Update the maximum. Returns `true` if the change was an increase.
pub fn update(&mut self, limit: u64) -> bool {
debug_assert!(limit < u64::MAX);
if limit > self.limit {
self.limit = limit;
self.blocked_frame = false;
true
} else {
false
}
}
/// Consume flow control.
pub fn consume(&mut self, count: usize) {
let amt = u64::try_from(count).unwrap();
debug_assert!(self.used + amt <= self.limit);
self.used += amt;
}
/// Get available flow control.
pub fn available(&self) -> usize {
usize::try_from(self.limit - self.used).unwrap_or(usize::MAX)
}
/// How much data has been written.
pub fn used(&self) -> u64 {
self.used
}
/// Mark flow control as blocked.
/// This only does something if the current limit exceeds the last reported blocking limit.
pub fn blocked(&mut self) {
if self.limit >= self.blocked_at {
self.blocked_at = self.limit + 1;
self.blocked_frame = true;
}
}
/// Return whether a blocking frame needs to be sent.
/// This is `Some` with the active limit if `blocked` has been called,
/// if a blocking frame has not been sent (or it has been lost), and
/// if the blocking condition remains.
fn blocked_needed(&self) -> Option<u64> {
if self.blocked_frame && self.limit < self.blocked_at {
Some(self.blocked_at - 1)
} else {
None
}
}
/// Clear the need to send a blocked frame.
fn blocked_sent(&mut self) {
self.blocked_frame = false;
}
/// Mark a blocked frame as having been lost.
/// Only send again if value of `self.blocked_at` hasn't increased since sending.
/// That would imply that the limit has since increased.
pub fn lost(&mut self, limit: u64) {
if self.blocked_at == limit + 1 {
self.blocked_frame = true;
}
}
}
impl SenderFlowControl<()> {
pub fn write_frames(
&mut self,
builder: &mut PacketBuilder,
tokens: &mut Vec<RecoveryToken>,
stats: &mut FrameStats,
) -> Res<()> {
if let Some(limit) = self.blocked_needed() {
if write_varint_frame(builder, &[FRAME_TYPE_DATA_BLOCKED, limit])? {
stats.data_blocked += 1;
tokens.push(RecoveryToken::DataBlocked(limit));
self.blocked_sent();
}
}
Ok(())
}
}
impl SenderFlowControl<StreamId> {
pub fn write_frames(
&mut self,
builder: &mut PacketBuilder,
tokens: &mut Vec<RecoveryToken>,
stats: &mut FrameStats,
) -> Res<()> {
if let Some(limit) = self.blocked_needed() {
if write_varint_frame(
builder,
&[FRAME_TYPE_STREAM_DATA_BLOCKED, self.subject.as_u64(), limit],
)? {
stats.stream_data_blocked += 1;
tokens.push(RecoveryToken::StreamDataBlocked {
stream_id: self.subject,
limit,
});
self.blocked_sent();
}
}
Ok(())
}
}
#[derive(Debug)]
pub struct ReceiverFlowControl<T>
where
T: Debug + Sized,
{
/// The thing that we're counting for.
subject: T,
/// The maximum amount of items that can be active (e.g., the size of the receive buffer).
max_active: u64,
// Last max data sent.
max_data: u64,
// Retired bytes.
retired: u64,
frame_pending: bool,
}
impl<T> ReceiverFlowControl<T>
where
T: Debug + Sized,
{
/// Make a new instance with the initial value and subject.
pub fn new(subject: T, max_bytes: u64) -> Self {
Self {
subject,
max_active: max_bytes,
max_data: max_bytes,
retired: 0,
frame_pending: false,
}
}
/// Check if received data exceeds the allowed flow control limit.
pub fn check_allowed(&self, new_end: u64) -> bool {
new_end <= self.max_data
}
/// Some data has been read, retired them and maybe send flow control
/// update.
pub fn retired(&mut self, retired: u64) {
if retired <= self.retired {
return;
}
self.retired = retired;
if self.retired + self.max_active / 2 > self.max_data {
self.frame_pending = true;
}
}
/// This function is called when STREAM_DATA_BLOCKED frame is received.
/// The flow control willl try to send an update if possible.
pub fn send_flowc_update(&mut self) {
if self.retired + self.max_active > self.max_data {
self.frame_pending = true;
}
}
pub fn max_data_needed(&self) -> Option<u64> {
if self.frame_pending {
Some(self.retired + self.max_active)
} else {
None
}
}
pub fn lost(&mut self, maximum_data: u64) {
if maximum_data == self.max_data {
self.frame_pending = true;
}
}
fn max_data_sent(&mut self, new_max: u64) {
self.max_data = new_max;
self.frame_pending = false;
}
}
impl ReceiverFlowControl<()> {
pub fn write_frames(
&mut self,
builder: &mut PacketBuilder,
tokens: &mut Vec<RecoveryToken>,
stats: &mut FrameStats,
) -> Res<()> {
if let Some(max_data) = self.max_data_needed() {
if write_varint_frame(builder, &[FRAME_TYPE_MAX_DATA, max_data])? {
stats.max_data += 1;
tokens.push(RecoveryToken::MaxData(max_data));
self.max_data_sent(max_data);
}
}
Ok(())
}
}
impl ReceiverFlowControl<StreamId> {
pub fn write_frames(
&mut self,
builder: &mut PacketBuilder,
tokens: &mut Vec<RecoveryToken>,
stats: &mut FrameStats,
) -> Res<()> {
if let Some(max_data) = self.max_data_needed() {
if write_varint_frame(
builder,
&[FRAME_TYPE_MAX_STREAM_DATA, self.subject.as_u64(), max_data],
)? {
stats.max_stream_data += 1;
tokens.push(RecoveryToken::MaxStreamData {
stream_id: self.subject,
max_data,
});
self.max_data_sent(max_data);
}
}
Ok(())
}
}
#[cfg(test)]
mod test {
use super::{ReceiverFlowControl, SenderFlowControl};
#[test]
fn blocked_at_zero() {
let mut fc = SenderFlowControl::new((), 0);
fc.blocked();
assert_eq!(fc.blocked_needed(), Some(0));
}
#[test]
fn blocked() {
let mut fc = SenderFlowControl::new((), 10);
fc.blocked();
assert_eq!(fc.blocked_needed(), Some(10));
}
#[test]
fn update_consume() {
let mut fc = SenderFlowControl::new((), 10);
fc.consume(10);
assert_eq!(fc.available(), 0);
fc.update(5); // An update lower than the current limit does nothing.
assert_eq!(fc.available(), 0);
fc.update(15);
assert_eq!(fc.available(), 5);
fc.consume(3);
assert_eq!(fc.available(), 2);
}
#[test]
fn update_clears_blocked() {
let mut fc = SenderFlowControl::new((), 10);
fc.blocked();
assert_eq!(fc.blocked_needed(), Some(10));
fc.update(5); // An update lower than the current limit does nothing.
assert_eq!(fc.blocked_needed(), Some(10));
fc.update(11);
assert_eq!(fc.blocked_needed(), None);
}
#[test]
fn lost_blocked_resent() {
let mut fc = SenderFlowControl::new((), 10);
fc.blocked();
fc.blocked_sent();
assert_eq!(fc.blocked_needed(), None);
fc.lost(10);
assert_eq!(fc.blocked_needed(), Some(10));
}
#[test]
fn lost_after_increase() {
let mut fc = SenderFlowControl::new((), 10);
fc.blocked();
fc.blocked_sent();
assert_eq!(fc.blocked_needed(), None);
fc.update(11);
fc.lost(10);
assert_eq!(fc.blocked_needed(), None);
}
#[test]
fn lost_after_higher_blocked() {
let mut fc = SenderFlowControl::new((), 10);
fc.blocked();
fc.blocked_sent();
fc.update(11);
fc.blocked();
assert_eq!(fc.blocked_needed(), Some(11));
fc.blocked_sent();
fc.lost(10);
assert_eq!(fc.blocked_needed(), None);
}
#[test]
fn do_no_need_max_data_frame_at_start() {
let fc = ReceiverFlowControl::new((), 0);
assert_eq!(fc.max_data_needed(), None);
}
#[test]
fn max_data_after_bytes_retired() {
let mut fc = ReceiverFlowControl::new((), 100);
fc.retired(49);
assert_eq!(fc.max_data_needed(), None);
fc.retired(51);
assert_eq!(fc.max_data_needed(), Some(151));
}
#[test]
fn need_max_data_frame_after_loss() {
let mut fc = ReceiverFlowControl::new((), 100);
fc.retired(100);
assert_eq!(fc.max_data_needed(), Some(200));
fc.max_data_sent(200);
assert_eq!(fc.max_data_needed(), None);
fc.lost(200);
assert_eq!(fc.max_data_needed(), Some(200));
}
#[test]
fn no_max_data_frame_after_old_loss() {
let mut fc = ReceiverFlowControl::new((), 100);
fc.retired(51);
assert_eq!(fc.max_data_needed(), Some(151));
fc.max_data_sent(151);
assert_eq!(fc.max_data_needed(), None);
fc.retired(102);
assert_eq!(fc.max_data_needed(), Some(202));
fc.max_data_sent(202);
assert_eq!(fc.max_data_needed(), None);
fc.lost(151);
assert_eq!(fc.max_data_needed(), None);
}
#[test]
fn force_send_max_data() {
let mut fc = ReceiverFlowControl::new((), 100);
fc.retired(10);
assert_eq!(fc.max_data_needed(), None);
}
#[test]
fn multiple_retries_after_frame_pending_is_set() {
let mut fc = ReceiverFlowControl::new((), 100);
fc.retired(51);
assert_eq!(fc.max_data_needed(), Some(151));
fc.retired(61);
assert_eq!(fc.max_data_needed(), Some(161));
fc.retired(88);
assert_eq!(fc.max_data_needed(), Some(188));
fc.retired(90);
assert_eq!(fc.max_data_needed(), Some(190));
fc.max_data_sent(190);
assert_eq!(fc.max_data_needed(), None);
fc.retired(141);
assert_eq!(fc.max_data_needed(), Some(241));
fc.max_data_sent(241);
assert_eq!(fc.max_data_needed(), None);
}
#[test]
fn new_retired_before_loss() {
let mut fc = ReceiverFlowControl::new((), 100);
fc.retired(51);
assert_eq!(fc.max_data_needed(), Some(151));
fc.max_data_sent(151);
assert_eq!(fc.max_data_needed(), None);
fc.retired(62);
assert_eq!(fc.max_data_needed(), None);
fc.lost(151);
assert_eq!(fc.max_data_needed(), Some(162));
}
}

View File

@ -10,136 +10,27 @@
use std::collections::HashMap;
use std::mem;
use neqo_common::{qinfo, qwarn, Encoder};
use neqo_common::qwarn;
use smallvec::{smallvec, SmallVec};
use crate::frame::Frame;
use crate::frame::{write_varint_frame, Frame};
use crate::packet::PacketBuilder;
use crate::recovery::RecoveryToken;
use crate::recv_stream::RecvStreams;
use crate::send_stream::SendStreams;
use crate::stats::FrameStats;
use crate::stream_id::{StreamId, StreamIndex, StreamIndexes, StreamType};
use crate::{AppError, Error, Res};
use crate::stream_id::{StreamIndex, StreamIndexes, StreamType};
use crate::Res;
type FlowFrame = Frame<'static>;
pub type FlowControlRecoveryToken = FlowFrame;
#[derive(Debug, Default)]
pub struct FlowMgr {
// Discriminant as key ensures only 1 of every frame type will be queued.
from_conn: HashMap<mem::Discriminant<FlowFrame>, FlowFrame>,
// (id, discriminant) as key ensures only 1 of every frame type per stream
// will be queued.
from_streams: HashMap<(StreamId, mem::Discriminant<FlowFrame>), FlowFrame>,
// (stream_type, discriminant) as key ensures only 1 of every frame type
// per stream type will be queued.
from_stream_types: HashMap<(StreamType, mem::Discriminant<FlowFrame>), FlowFrame>,
used_data: u64,
max_data: u64,
}
impl FlowMgr {
pub fn conn_credit_avail(&self) -> u64 {
self.max_data - self.used_data
}
pub fn conn_increase_credit_used(&mut self, amount: u64) {
self.used_data += amount;
assert!(self.used_data <= self.max_data)
}
// Dummy DataBlocked frame for discriminant use below
/// Returns whether max credit was actually increased.
pub fn conn_increase_max_credit(&mut self, new: u64) -> bool {
const DB_FRAME: FlowFrame = Frame::DataBlocked { data_limit: 0 };
if new > self.max_data {
self.max_data = new;
self.from_conn.remove(&mem::discriminant(&DB_FRAME));
true
} else {
false
}
}
// -- frames scoped on connection --
pub fn data_blocked(&mut self) {
let frame = Frame::DataBlocked {
data_limit: self.max_data,
};
self.from_conn.insert(mem::discriminant(&frame), frame);
}
pub fn max_data(&mut self, maximum_data: u64) {
let frame = Frame::MaxData { maximum_data };
self.from_conn.insert(mem::discriminant(&frame), frame);
}
// -- frames scoped on stream --
/// Indicate to receiving remote the stream is reset
pub fn stream_reset(
&mut self,
stream_id: StreamId,
application_error_code: AppError,
final_size: u64,
) {
let frame = Frame::ResetStream {
stream_id,
application_error_code,
final_size,
};
self.from_streams
.insert((stream_id, mem::discriminant(&frame)), frame);
}
/// Indicate to sending remote we are no longer interested in the stream
pub fn stop_sending(&mut self, stream_id: StreamId, application_error_code: AppError) {
let frame = Frame::StopSending {
stream_id,
application_error_code,
};
self.from_streams
.insert((stream_id, mem::discriminant(&frame)), frame);
}
/// Update sending remote with more credits
pub fn max_stream_data(&mut self, stream_id: StreamId, maximum_stream_data: u64) {
let frame = Frame::MaxStreamData {
stream_id,
maximum_stream_data,
};
self.from_streams
.insert((stream_id, mem::discriminant(&frame)), frame);
}
/// Don't send stream data updates if no more data is coming
pub fn clear_max_stream_data(&mut self, stream_id: StreamId) {
let frame = Frame::MaxStreamData {
stream_id,
maximum_stream_data: 0,
};
self.from_streams
.remove(&(stream_id, mem::discriminant(&frame)));
}
/// Indicate to receiving remote we need more credits
pub fn stream_data_blocked(&mut self, stream_id: StreamId, stream_data_limit: u64) {
let frame = Frame::StreamDataBlocked {
stream_id,
stream_data_limit,
};
self.from_streams
.insert((stream_id, mem::discriminant(&frame)), frame);
}
// -- frames scoped on stream type --
pub fn max_streams(&mut self, stream_limit: StreamIndex, stream_type: StreamType) {
@ -161,67 +52,14 @@ impl FlowMgr {
}
pub fn peek(&self) -> Option<&Frame> {
if let Some(key) = self.from_conn.keys().next() {
self.from_conn.get(key)
} else if let Some(key) = self.from_streams.keys().next() {
self.from_streams.get(key)
} else if let Some(key) = self.from_stream_types.keys().next() {
self.from_stream_types.get(key)
} else {
None
if let Some(key) = self.from_stream_types.keys().next() {
return self.from_stream_types.get(key);
}
None
}
pub(crate) fn acked(
&mut self,
token: &FlowControlRecoveryToken,
send_streams: &mut SendStreams,
) {
const RESET_STREAM: &Frame = &Frame::ResetStream {
stream_id: StreamId::new(0),
application_error_code: 0,
final_size: 0,
};
if let Frame::ResetStream { stream_id, .. } = token {
qinfo!("Reset received stream={}", stream_id.as_u64());
if self
.from_streams
.remove(&(*stream_id, mem::discriminant(RESET_STREAM)))
.is_some()
{
qinfo!("Removed RESET_STREAM frame for {}", stream_id.as_u64());
}
send_streams.reset_acked(*stream_id);
}
}
pub(crate) fn lost(
&mut self,
token: &FlowControlRecoveryToken,
send_streams: &mut SendStreams,
recv_streams: &mut RecvStreams,
indexes: &mut StreamIndexes,
) {
pub(crate) fn lost(&mut self, token: &FlowControlRecoveryToken, indexes: &mut StreamIndexes) {
match *token {
// Always resend ResetStream if lost
Frame::ResetStream {
stream_id,
application_error_code,
final_size,
} => {
qinfo!(
"Reset lost stream={} err={} final_size={}",
stream_id.as_u64(),
application_error_code,
final_size
);
if send_streams.get(stream_id).is_ok() {
self.stream_reset(stream_id, application_error_code, final_size);
}
}
// Resend MaxStreams if lost (with updated value)
Frame::MaxStreams { stream_type, .. } => {
let local_max = match stream_type {
@ -232,18 +70,6 @@ impl FlowMgr {
self.max_streams(*local_max, stream_type)
}
// Only resend "*Blocked" frames if still blocked
Frame::DataBlocked { .. } => {
if self.conn_credit_avail() == 0 {
self.data_blocked()
}
}
Frame::StreamDataBlocked { stream_id, .. } => {
if let Ok(ss) = send_streams.get(stream_id) {
if ss.credit_avail() == 0 {
self.stream_data_blocked(stream_id, ss.max_stream_data())
}
}
}
Frame::StreamsBlocked { stream_type, .. } => match stream_type {
StreamType::UniDi => {
if indexes.remote_next_stream_uni >= indexes.remote_max_stream_uni {
@ -256,18 +82,6 @@ impl FlowMgr {
}
}
},
// Resend StopSending
Frame::StopSending {
stream_id,
application_error_code,
} => self.stop_sending(stream_id, application_error_code),
Frame::MaxStreamData { stream_id, .. } => {
if let Some(rs) = recv_streams.get_mut(&stream_id) {
if let Some(msd) = rs.max_stream_data() {
self.max_stream_data(stream_id, msd)
}
}
}
_ => qwarn!("Unexpected Flow frame {:?} lost, not re-sent", token),
}
}
@ -281,77 +95,21 @@ impl FlowMgr {
while let Some(frame) = self.peek() {
// All these frames are bags of varints, so we can just extract the
// varints and use common code for writing.
let values: SmallVec<[_; 3]> = match frame {
Frame::ResetStream {
stream_id,
application_error_code,
final_size,
} => {
stats.reset_stream += 1;
smallvec![stream_id.as_u64(), *application_error_code, *final_size]
}
Frame::StopSending {
stream_id,
application_error_code,
} => {
stats.stop_sending += 1;
smallvec![stream_id.as_u64(), *application_error_code]
}
let (mut values, stat): (SmallVec<[_; 3]>, _) = match frame {
Frame::MaxStreams {
maximum_streams, ..
} => {
stats.max_streams += 1;
smallvec![maximum_streams.as_u64()]
}
} => (smallvec![maximum_streams.as_u64()], &mut stats.max_streams),
Frame::StreamsBlocked { stream_limit, .. } => {
stats.streams_blocked += 1;
smallvec![stream_limit.as_u64()]
(smallvec![stream_limit.as_u64()], &mut stats.streams_blocked)
}
Frame::MaxData { maximum_data } => {
stats.max_data += 1;
smallvec![*maximum_data]
}
Frame::DataBlocked { data_limit } => {
stats.data_blocked += 1;
smallvec![*data_limit]
}
Frame::MaxStreamData {
stream_id,
maximum_stream_data,
} => {
stats.max_stream_data += 1;
smallvec![stream_id.as_u64(), *maximum_stream_data]
}
Frame::StreamDataBlocked {
stream_id,
stream_data_limit,
} => {
stats.stream_data_blocked += 1;
smallvec![stream_id.as_u64(), *stream_data_limit]
}
_ => unreachable!("{:?}", frame),
};
values.insert(0, frame.get_type());
debug_assert!(!values.spilled());
if builder.remaining()
>= Encoder::varint_len(frame.get_type())
+ values
.iter()
.map(|&v| Encoder::varint_len(v))
.sum::<usize>()
{
builder.encode_varint(frame.get_type());
for v in values {
builder.encode_varint(v);
}
if builder.len() > builder.limit() {
return Err(Error::InternalError(16));
}
if write_varint_frame(builder, &values)? {
tokens.push(RecoveryToken::Flow(self.next().unwrap()));
*stat += 1;
} else {
return Ok(());
}
@ -365,21 +123,10 @@ impl Iterator for FlowMgr {
/// Used by generator to get a flow control frame.
fn next(&mut self) -> Option<Self::Item> {
let first_key = self.from_conn.keys().next();
if let Some(&first_key) = first_key {
return self.from_conn.remove(&first_key);
}
let first_key = self.from_streams.keys().next();
if let Some(&first_key) = first_key {
return self.from_streams.remove(&first_key);
}
let first_key = self.from_stream_types.keys().next();
if let Some(&first_key) = first_key {
return self.from_stream_types.remove(&first_key);
}
None
}
}

View File

@ -6,10 +6,10 @@
// Directly relating to QUIC frames.
use neqo_common::{qtrace, Decoder};
use neqo_common::{qtrace, Decoder, Encoder};
use crate::cid::MAX_CONNECTION_ID_LEN;
use crate::packet::PacketType;
use crate::packet::{PacketBuilder, PacketType};
use crate::stream_id::{StreamId, StreamIndex, StreamType};
use crate::{AppError, ConnectionError, Error, Res, TransportError};
@ -23,20 +23,20 @@ const FRAME_TYPE_PADDING: FrameType = 0x0;
pub const FRAME_TYPE_PING: FrameType = 0x1;
pub const FRAME_TYPE_ACK: FrameType = 0x2;
const FRAME_TYPE_ACK_ECN: FrameType = 0x3;
const FRAME_TYPE_RST_STREAM: FrameType = 0x4;
const FRAME_TYPE_STOP_SENDING: FrameType = 0x5;
pub const FRAME_TYPE_RESET_STREAM: FrameType = 0x4;
pub const FRAME_TYPE_STOP_SENDING: FrameType = 0x5;
pub const FRAME_TYPE_CRYPTO: FrameType = 0x6;
pub const FRAME_TYPE_NEW_TOKEN: FrameType = 0x7;
const FRAME_TYPE_STREAM: FrameType = 0x8;
const FRAME_TYPE_STREAM_MAX: FrameType = 0xf;
const FRAME_TYPE_MAX_DATA: FrameType = 0x10;
const FRAME_TYPE_MAX_STREAM_DATA: FrameType = 0x11;
const FRAME_TYPE_MAX_STREAMS_BIDI: FrameType = 0x12;
const FRAME_TYPE_MAX_STREAMS_UNIDI: FrameType = 0x13;
const FRAME_TYPE_DATA_BLOCKED: FrameType = 0x14;
const FRAME_TYPE_STREAM_DATA_BLOCKED: FrameType = 0x15;
const FRAME_TYPE_STREAMS_BLOCKED_BIDI: FrameType = 0x16;
const FRAME_TYPE_STREAMS_BLOCKED_UNIDI: FrameType = 0x17;
pub const FRAME_TYPE_MAX_DATA: FrameType = 0x10;
pub const FRAME_TYPE_MAX_STREAM_DATA: FrameType = 0x11;
pub const FRAME_TYPE_MAX_STREAMS_BIDI: FrameType = 0x12;
pub const FRAME_TYPE_MAX_STREAMS_UNIDI: FrameType = 0x13;
pub const FRAME_TYPE_DATA_BLOCKED: FrameType = 0x14;
pub const FRAME_TYPE_STREAM_DATA_BLOCKED: FrameType = 0x15;
pub const FRAME_TYPE_STREAMS_BLOCKED_BIDI: FrameType = 0x16;
pub const FRAME_TYPE_STREAMS_BLOCKED_UNIDI: FrameType = 0x17;
pub const FRAME_TYPE_NEW_CONNECTION_ID: FrameType = 0x18;
pub const FRAME_TYPE_RETIRE_CONNECTION_ID: FrameType = 0x19;
pub const FRAME_TYPE_PATH_CHALLENGE: FrameType = 0x1a;
@ -93,6 +93,26 @@ pub struct AckRange {
pub(crate) range: u64,
}
/// A lot of frames here are just a collection of varints.
/// This helper functions writes a frame like that safely, returning `true` if
/// a frame was written.
pub fn write_varint_frame(builder: &mut PacketBuilder, values: &[u64]) -> Res<bool> {
let write = builder.remaining()
>= values
.iter()
.map(|&v| Encoder::varint_len(v))
.sum::<usize>();
if write {
for v in values {
builder.encode_varint(*v);
}
if builder.len() > builder.limit() {
return Err(Error::InternalError(16));
}
};
Ok(write)
}
#[derive(PartialEq, Debug, Clone)]
pub enum Frame<'a> {
Padding,
@ -194,7 +214,7 @@ impl<'a> Frame<'a> {
Self::Padding => FRAME_TYPE_PADDING,
Self::Ping => FRAME_TYPE_PING,
Self::Ack { .. } => FRAME_TYPE_ACK, // We don't do ACK ECN.
Self::ResetStream { .. } => FRAME_TYPE_RST_STREAM,
Self::ResetStream { .. } => FRAME_TYPE_RESET_STREAM,
Self::StopSending { .. } => FRAME_TYPE_STOP_SENDING,
Self::Crypto { .. } => FRAME_TYPE_CRYPTO,
Self::NewToken { .. } => FRAME_TYPE_NEW_TOKEN,
@ -239,17 +259,22 @@ impl<'a> Frame<'a> {
/// If the frame causes a recipient to generate an ACK within its
/// advertised maximum acknowledgement delay.
pub fn ack_eliciting(&self) -> bool {
!matches!(self, Self::Ack { .. } | Self::Padding | Self::ConnectionClose { .. })
!matches!(
self,
Self::Ack { .. } | Self::Padding | Self::ConnectionClose { .. }
)
}
/// If the frame can be sent in a path probe
/// without initiating migration to that path.
pub fn path_probing(&self) -> bool {
matches!(self,
matches!(
self,
Self::Padding
| Self::NewConnectionId { .. }
| Self::PathChallenge { .. }
| Self::PathResponse { .. })
| Self::NewConnectionId { .. }
| Self::PathChallenge { .. }
| Self::PathResponse { .. }
)
}
/// Converts AckRanges as encoded in a ACK frame (see -transport
@ -348,7 +373,7 @@ impl<'a> Frame<'a> {
match t {
FRAME_TYPE_PADDING => Ok(Self::Padding),
FRAME_TYPE_PING => Ok(Self::Ping),
FRAME_TYPE_RST_STREAM => Ok(Self::ResetStream {
FRAME_TYPE_RESET_STREAM => Ok(Self::ResetStream {
stream_id: StreamId::from(dv(dec)?),
application_error_code: d(dec.decode_varint())?,
final_size: match dec.decode_varint() {

View File

@ -16,6 +16,7 @@ mod connection;
mod crypto;
mod dump;
mod events;
mod fc;
mod flow_mgr;
mod frame;
mod pace;
@ -24,6 +25,7 @@ mod path;
mod qlog;
mod recovery;
mod recv_stream;
mod rtt;
mod send_stream;
mod sender;
pub mod server;
@ -41,7 +43,6 @@ pub use self::connection::{params::ConnectionParameters, Connection, Output, Sta
pub use self::events::{ConnectionEvent, ConnectionEvents};
pub use self::frame::CloseError;
pub use self::packet::QuicVersion;
pub use self::sender::PacketSender;
pub use self::stats::Stats;
pub use self::stream_id::{StreamId, StreamType};

View File

@ -37,14 +37,14 @@ pub struct Pacer {
}
impl Pacer {
/// Create a new `Pacer`. This takes the current time and the
/// initial congestion window.
/// Create a new `Pacer`. This takes the current time, the maximum burst size,
/// and the packet size.
///
/// The value of `m` is the maximum capacity. `m` primes the pacer
/// The value of `m` is the maximum capacity in bytes. `m` primes the pacer
/// with credit and determines the burst size. `m` must not exceed
/// the initial congestion window, but it should probably be lower.
///
/// The value of `p` is the packet size, which determines the minimum
/// The value of `p` is the packet size in bytes, which determines the minimum
/// credit needed before a packet is sent. This should be a substantial
/// fraction of the maximum packet size, if not the packet size.
pub fn new(now: Instant, m: usize, p: usize) -> Self {

View File

@ -91,6 +91,7 @@ impl From<CryptoSpace> for PacketType {
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum QuicVersion {
Version1,
Draft27,
Draft28,
Draft29,
@ -102,6 +103,7 @@ pub enum QuicVersion {
impl QuicVersion {
pub fn as_u32(self) -> Version {
match self {
Self::Version1 => 1,
Self::Draft27 => 0xff00_0000 + 27,
Self::Draft28 => 0xff00_0000 + 28,
Self::Draft29 => 0xff00_0000 + 29,
@ -114,7 +116,7 @@ impl QuicVersion {
impl Default for QuicVersion {
fn default() -> Self {
Self::Draft29
Self::Version1
}
}
@ -122,7 +124,9 @@ impl TryFrom<Version> for QuicVersion {
type Error = Error;
fn try_from(ver: Version) -> Res<Self> {
if ver == 0xff00_0000 + 27 {
if ver == 1 {
Ok(Self::Version1)
} else if ver == 0xff00_0000 + 27 {
Ok(Self::Draft27)
} else if ver == 0xff00_0000 + 28 {
Ok(Self::Draft28)
@ -157,6 +161,8 @@ pub struct PacketBuilder {
header: Range<usize>,
offsets: PacketBuilderOffsets,
limit: usize,
/// Whether to pad the packet before construction.
padding: bool,
}
impl PacketBuilder {
@ -169,13 +175,28 @@ impl PacketBuilder {
}
/// Start building a short header packet.
///
/// This doesn't fail if there isn't enough space; instead it returns a builder that
/// has no available space left. This allows the caller to extract the encoder
/// and any packets that might have been added before as adding a packet header is
/// only likely to fail if there are other packets already written.
///
/// If, after calling this method, `remaining()` returns 0, then call `abort()` to get
/// the encoder back.
#[allow(clippy::unknown_clippy_lints)] // Until we require rust 1.45.
#[allow(clippy::reversed_empty_ranges)]
pub fn short(mut encoder: Encoder, key_phase: bool, dcid: impl AsRef<[u8]>) -> Self {
let mut limit = Self::infer_limit(&encoder);
let header_start = encoder.len();
encoder.encode_byte(PACKET_BIT_SHORT | PACKET_BIT_FIXED_QUIC | (u8::from(key_phase) << 2));
encoder.encode(dcid.as_ref());
let limit = Self::infer_limit(&encoder);
// Check that there is enough space for the header.
// 5 = 1 (first byte) + 4 (packet number)
if limit > encoder.len() && 5 + dcid.as_ref().len() < limit - encoder.len() {
encoder
.encode_byte(PACKET_BIT_SHORT | PACKET_BIT_FIXED_QUIC | (u8::from(key_phase) << 2));
encoder.encode(dcid.as_ref());
} else {
limit = 0;
}
Self {
encoder,
pn: u64::max_value(),
@ -186,12 +207,15 @@ impl PacketBuilder {
len: 0,
},
limit,
padding: false,
}
}
/// Start building a long header packet.
/// For an Initial packet you will need to call initial_token(),
/// even if the token is empty.
///
/// See `short()` for more on how to handle this in cases where there is no space.
#[allow(clippy::unknown_clippy_lints)] // Until we require rust 1.45.
#[allow(clippy::reversed_empty_ranges)] // For initializing an empty range.
pub fn long(
@ -201,12 +225,21 @@ impl PacketBuilder {
dcid: impl AsRef<[u8]>,
scid: impl AsRef<[u8]>,
) -> Self {
let mut limit = Self::infer_limit(&encoder);
let header_start = encoder.len();
encoder.encode_byte(PACKET_BIT_LONG | PACKET_BIT_FIXED_QUIC | pt.code() << 4);
encoder.encode_uint(4, quic_version.as_u32());
encoder.encode_vec(1, dcid.as_ref());
encoder.encode_vec(1, scid.as_ref());
let limit = Self::infer_limit(&encoder);
// Check that there is enough space for the header.
// 11 = 1 (first byte) + 4 (version) + 2 (dcid+scid length) + 4 (packet number)
if limit > encoder.len()
&& 11 + dcid.as_ref().len() + scid.as_ref().len() < limit - encoder.len()
{
encoder.encode_byte(PACKET_BIT_LONG | PACKET_BIT_FIXED_QUIC | pt.code() << 4);
encoder.encode_uint(4, quic_version.as_u32());
encoder.encode_vec(1, dcid.as_ref());
encoder.encode_vec(1, scid.as_ref());
} else {
limit = 0;
}
Self {
encoder,
pn: u64::max_value(),
@ -217,6 +250,7 @@ impl PacketBuilder {
len: 0,
},
limit,
padding: false,
}
}
@ -238,22 +272,29 @@ impl PacketBuilder {
/// How many bytes remain against the size limit for the builder.
#[must_use]
pub fn remaining(&self) -> usize {
self.limit - self.encoder.len()
self.limit.saturating_sub(self.encoder.len())
}
/// Pad with "PADDING" frames.
pub fn pad(&mut self) -> Res<()> {
self.encoder.pad_to(self.limit, 0);
if self.len() > self.limit {
qwarn!("Packet contents are more than the limit");
debug_assert!(false);
return Err(Error::InternalError(17));
/// Mark the packet as needing padding (or not).
pub fn enable_padding(&mut self, needs_padding: bool) {
self.padding = needs_padding;
}
/// Maybe pad with "PADDING" frames.
/// Only does so if padding was needed and this is a short packet.
/// Returns true if padding was added.
pub fn pad(&mut self) -> bool {
if self.padding && !self.is_long() {
self.encoder.pad_to(self.limit, 0);
true
} else {
false
}
Ok(())
}
/// Add unpredictable values for unprotected parts of the packet.
pub fn scramble(&mut self, quic_bit: bool) {
debug_assert!(self.len() > self.header.start);
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;
@ -262,25 +303,29 @@ impl PacketBuilder {
/// For an Initial packet, encode the token.
/// If you fail to do this, then you will not get a valid packet.
pub fn initial_token(&mut self, token: &[u8]) -> Res<()> {
pub fn initial_token(&mut self, token: &[u8]) {
debug_assert_eq!(
self.encoder[self.header.start] & 0xb0,
PACKET_BIT_LONG | PACKET_TYPE_INITIAL << 4
);
self.encoder.encode_vvec(token);
if self.len() > self.limit {
qwarn!("Packet contents are more than the limit");
debug_assert!(false);
return Err(Error::InternalError(18));
if Encoder::vvec_len(token.len()) < self.remaining() {
self.encoder.encode_vvec(token);
} else {
self.limit = 0;
}
Ok(())
}
/// Add a packet number of the given size.
/// For a long header packet, this also inserts a dummy length.
/// The length is filled in after calling `build`.
pub fn pn(&mut self, pn: PacketNumber, pn_len: usize) -> Res<()> {
/// Does nothing if there isn't 4 bytes available other than render this builder
/// unusable; if `remaining()` returns 0 at any point, call `abort()`.
pub fn pn(&mut self, pn: PacketNumber, pn_len: usize) {
if self.remaining() < 4 {
self.limit = 0;
return;
}
// Reserve space for a length in long headers.
if self.is_long() {
self.offsets.len = self.encoder.len();
@ -299,13 +344,6 @@ impl PacketBuilder {
self.encoder[self.header.start] |= u8::try_from(pn_len - 1).unwrap();
self.header.end = self.encoder.len();
self.pn = pn;
if self.len() > self.limit {
qwarn!("Packet contents are more than the limit");
debug_assert!(false);
return Err(Error::InternalError(19));
}
Ok(())
}
fn write_len(&mut self, expansion: usize) {
@ -424,6 +462,7 @@ impl PacketBuilder {
encoder.encode(&[0; 4]); // Zero version == VN.
encoder.encode_vec(1, dcid);
encoder.encode_vec(1, scid);
encoder.encode_uint(4, QuicVersion::Version1.as_u32());
encoder.encode_uint(4, QuicVersion::Draft27.as_u32());
encoder.encode_uint(4, QuicVersion::Draft28.as_u32());
encoder.encode_uint(4, QuicVersion::Draft29.as_u32());
@ -850,15 +889,15 @@ mod tests {
0xb9, 0xda, 0x1a, 0x00, 0x2b, 0x00, 0x02, 0x03, 0x04,
];
const SAMPLE_INITIAL: &[u8] = &[
0xc7, 0xff, 0x00, 0x00, 0x1d, 0x00, 0x08, 0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, 0xb5,
0x00, 0x40, 0x75, 0xfb, 0x12, 0xff, 0x07, 0x82, 0x3a, 0x5d, 0x24, 0x53, 0x4d, 0x90, 0x6c,
0xe4, 0xc7, 0x67, 0x82, 0xa2, 0x16, 0x7e, 0x34, 0x79, 0xc0, 0xf7, 0xf6, 0x39, 0x5d, 0xc2,
0xc9, 0x16, 0x76, 0x30, 0x2f, 0xe6, 0xd7, 0x0b, 0xb7, 0xcb, 0xeb, 0x11, 0x7b, 0x4d, 0xdb,
0x7d, 0x17, 0x34, 0x98, 0x44, 0xfd, 0x61, 0xda, 0xe2, 0x00, 0xb8, 0x33, 0x8e, 0x1b, 0x93,
0x29, 0x76, 0xb6, 0x1d, 0x91, 0xe6, 0x4a, 0x02, 0xe9, 0xe0, 0xee, 0x72, 0xe3, 0xa6, 0xf6,
0x3a, 0xba, 0x4c, 0xee, 0xee, 0xc5, 0xbe, 0x2f, 0x24, 0xf2, 0xd8, 0x60, 0x27, 0x57, 0x29,
0x43, 0x53, 0x38, 0x46, 0xca, 0xa1, 0x3e, 0x6f, 0x16, 0x3f, 0xb2, 0x57, 0x47, 0x3d, 0xcc,
0xa2, 0x53, 0x96, 0xe8, 0x87, 0x24, 0xf1, 0xe5, 0xd9, 0x64, 0xde, 0xde, 0xe9, 0xb6, 0x33,
0xcf, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, 0xb5,
0x00, 0x40, 0x75, 0xc0, 0xd9, 0x5a, 0x48, 0x2c, 0xd0, 0x99, 0x1c, 0xd2, 0x5b, 0x0a, 0xac,
0x40, 0x6a, 0x58, 0x16, 0xb6, 0x39, 0x41, 0x00, 0xf3, 0x7a, 0x1c, 0x69, 0x79, 0x75, 0x54,
0x78, 0x0b, 0xb3, 0x8c, 0xc5, 0xa9, 0x9f, 0x5e, 0xde, 0x4c, 0xf7, 0x3c, 0x3e, 0xc2, 0x49,
0x3a, 0x18, 0x39, 0xb3, 0xdb, 0xcb, 0xa3, 0xf6, 0xea, 0x46, 0xc5, 0xb7, 0x68, 0x4d, 0xf3,
0x54, 0x8e, 0x7d, 0xde, 0xb9, 0xc3, 0xbf, 0x9c, 0x73, 0xcc, 0x3f, 0x3b, 0xde, 0xd7, 0x4b,
0x56, 0x2b, 0xfb, 0x19, 0xfb, 0x84, 0x02, 0x2f, 0x8e, 0xf4, 0xcd, 0xd9, 0x37, 0x95, 0xd7,
0x7d, 0x06, 0xed, 0xbb, 0x7a, 0xaf, 0x2f, 0x58, 0x89, 0x18, 0x50, 0xab, 0xbd, 0xca, 0x3d,
0x20, 0x39, 0x8c, 0x27, 0x64, 0x56, 0xcb, 0xc4, 0x21, 0x58, 0x40, 0x7d, 0xd0, 0x74, 0xee,
];
#[test]
@ -878,8 +917,8 @@ mod tests {
&ConnectionId::from(&[][..]),
&ConnectionId::from(SERVER_CID),
);
builder.initial_token(&[]).unwrap();
builder.pn(1, 2).unwrap();
builder.initial_token(&[]);
builder.pn(1, 2);
builder.encode(&SAMPLE_INITIAL_PAYLOAD);
let packet = builder.build(&mut prot).expect("build");
assert_eq!(&packet[..], SAMPLE_INITIAL);
@ -930,8 +969,8 @@ mod tests {
}
const SAMPLE_SHORT: &[u8] = &[
0x55, 0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, 0xb5, 0x99, 0x9c, 0xbd, 0x77, 0xf5, 0xd7,
0x0a, 0x28, 0xe8, 0xfb, 0xc3, 0xed, 0xf5, 0x71, 0xb1, 0x04, 0x32, 0x2a, 0xae, 0xae,
0x40, 0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, 0xb5, 0xf4, 0xa8, 0x30, 0x39, 0xc4, 0x7d,
0x99, 0xe3, 0x94, 0x1c, 0x9b, 0xb9, 0x7a, 0x30, 0x1d, 0xd5, 0x8f, 0xf3, 0xdd, 0xa9,
];
const SAMPLE_SHORT_PAYLOAD: &[u8] = &[0; 3];
@ -940,7 +979,7 @@ mod tests {
fixture_init();
let mut builder =
PacketBuilder::short(Encoder::new(), true, &ConnectionId::from(SERVER_CID));
builder.pn(0, 1).unwrap();
builder.pn(0, 1);
builder.encode(SAMPLE_SHORT_PAYLOAD); // Enough payload for sampling.
let packet = builder
.build(&mut CryptoDxState::test_default())
@ -956,7 +995,7 @@ mod tests {
let mut builder =
PacketBuilder::short(Encoder::new(), true, &ConnectionId::from(SERVER_CID));
builder.scramble(true);
builder.pn(0, 1).unwrap();
builder.pn(0, 1);
firsts.push(builder[0]);
}
let is_set = |bit| move |v| v & bit == bit;
@ -1019,14 +1058,14 @@ mod tests {
&ConnectionId::from(SERVER_CID),
&ConnectionId::from(CLIENT_CID),
);
builder.pn(0, 1).unwrap();
builder.pn(0, 1);
builder.encode(&[0; 3]);
let encoder = builder.build(&mut prot).expect("build");
assert_eq!(encoder.len(), 45);
let first = encoder.clone();
let mut builder = PacketBuilder::short(encoder, false, &ConnectionId::from(SERVER_CID));
builder.pn(1, 3).unwrap();
builder.pn(1, 3);
builder.encode(&[0]); // Minimal size (packet number is big enough).
let encoder = builder.build(&mut prot).expect("build");
assert_eq!(
@ -1040,9 +1079,9 @@ mod tests {
#[test]
fn build_long() {
const EXPECTED: &[u8] = &[
0xe5, 0xff, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x40, 0x14, 0xa8, 0x9d, 0xbf, 0x74, 0x70,
0x32, 0xda, 0xba, 0xfb, 0x87, 0x61, 0xb8, 0x31, 0x90, 0xf3, 0x25, 0x52, 0x0b, 0xbe,
0xdb,
0xe4, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x40, 0x14, 0xfb, 0xa9, 0x32, 0x3a, 0xf8,
0xbb, 0x18, 0x63, 0xc6, 0xbd, 0x78, 0x0e, 0xba, 0x0c, 0x98, 0x65, 0x58, 0xc9, 0x62,
0x31,
];
fixture_init();
@ -1053,7 +1092,7 @@ mod tests {
&ConnectionId::from(&[][..]),
&ConnectionId::from(&[][..]),
);
builder.pn(0, 1).unwrap();
builder.pn(0, 1);
builder.encode(&[1, 2, 3]);
let packet = builder.build(&mut CryptoDxState::test_default()).unwrap();
assert_eq!(&packet[..], EXPECTED);
@ -1072,7 +1111,7 @@ mod tests {
&ConnectionId::from(&[][..]),
&ConnectionId::from(&[][..]),
);
builder.pn(0, 1).unwrap();
builder.pn(0, 1);
builder.scramble(true);
if (builder[0] & PACKET_BIT_FIXED_QUIC) == 0 {
found_unset = true;
@ -1093,12 +1132,49 @@ mod tests {
&ConnectionId::from(&[][..]),
&ConnectionId::from(SERVER_CID),
);
builder.initial_token(&[]).unwrap();
builder.pn(1, 2).unwrap();
assert_ne!(builder.remaining(), 0);
builder.initial_token(&[]);
assert_ne!(builder.remaining(), 0);
builder.pn(1, 2);
assert_ne!(builder.remaining(), 0);
let encoder = builder.abort();
assert!(encoder.is_empty());
}
#[test]
fn build_insufficient_space() {
fixture_init();
let mut builder = PacketBuilder::short(
Encoder::with_capacity(100),
true,
&ConnectionId::from(SERVER_CID),
);
builder.pn(0, 1);
// Pad, but not up to the full capacity. Leave enough space for the
// AEAD expansion and some extra, but not for an entire long header.
builder.set_limit(75);
builder.enable_padding(true);
assert!(builder.pad());
let encoder = builder.build(&mut CryptoDxState::test_default()).unwrap();
let encoder_copy = encoder.clone();
let builder = PacketBuilder::long(
encoder,
PacketType::Initial,
QuicVersion::default(),
&ConnectionId::from(SERVER_CID),
&ConnectionId::from(SERVER_CID),
);
assert_eq!(builder.remaining(), 0);
assert_eq!(builder.abort(), encoder_copy);
}
const SAMPLE_RETRY_V1: &[u8] = &[
0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, 0xb5,
0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x04, 0xa2, 0x65, 0xba, 0x2e, 0xff, 0x4d, 0x82, 0x90, 0x58,
0xfb, 0x3f, 0x0f, 0x24, 0x96, 0xba,
];
const SAMPLE_RETRY_27: &[u8] = &[
0xff, 0xff, 0x00, 0x00, 0x1b, 0x00, 0x08, 0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, 0xb5,
0x74, 0x6f, 0x6b, 0x65, 0x6e, 0xa5, 0x23, 0xcb, 0x5b, 0xa5, 0x24, 0x69, 0x5f, 0x65, 0x69,
@ -1158,6 +1234,11 @@ mod tests {
}
}
#[test]
fn build_retry_v1() {
build_retry_single(QuicVersion::Version1, SAMPLE_RETRY_V1);
}
#[test]
fn build_retry_27() {
build_retry_single(QuicVersion::Draft27, SAMPLE_RETRY_27);
@ -1194,10 +1275,13 @@ mod tests {
// Odds are approximately 1 in 8 that the full comparison doesn't happen
// for a given version.
for _ in 0..32 {
build_retry_v1();
build_retry_27();
build_retry_28();
build_retry_29();
build_retry_30();
build_retry_31();
build_retry_32();
}
}
@ -1278,9 +1362,9 @@ mod tests {
const SAMPLE_VN: &[u8] = &[
0x80, 0x00, 0x00, 0x00, 0x00, 0x08, 0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, 0xb5, 0x08,
0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08, 0xff, 0x00, 0x00, 0x1b, 0xff, 0x00, 0x00,
0x1c, 0xff, 0x00, 0x00, 0x1d, 0xff, 0x00, 0x00, 0x1e, 0xff, 0x00, 0x00, 0x1f, 0xff, 0x00,
0x00, 0x20, 0x0a, 0x0a, 0x0a, 0x0a,
0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08, 0x00, 0x00, 0x00, 0x01, 0xff, 0x00, 0x00,
0x1b, 0xff, 0x00, 0x00, 0x1c, 0xff, 0x00, 0x00, 0x1d, 0xff, 0x00, 0x00, 0x1e, 0xff, 0x00,
0x00, 0x1f, 0xff, 0x00, 0x00, 0x20, 0x0a, 0x0a, 0x0a, 0x0a,
];
#[test]

View File

@ -22,6 +22,10 @@ const RETRY_SECRET_29: &[u8] = &[
0x8b, 0x0d, 0x37, 0xeb, 0x85, 0x35, 0x02, 0x2e, 0xbc, 0x8d, 0x76, 0xa2, 0x07, 0xd8, 0x0d, 0xf2,
0x26, 0x46, 0xec, 0x06, 0xdc, 0x80, 0x96, 0x42, 0xc3, 0x0a, 0x8b, 0xaa, 0x2b, 0xaa, 0xff, 0x4c,
];
const RETRY_SECRET_V1: &[u8] = &[
0xd9, 0xc9, 0x94, 0x3e, 0x61, 0x01, 0xfd, 0x20, 0x00, 0x21, 0x50, 0x6b, 0xcc, 0x02, 0x81, 0x4c,
0x73, 0x03, 0x0f, 0x25, 0xc7, 0x9d, 0x71, 0xce, 0x87, 0x6e, 0xca, 0x87, 0x6e, 0x6f, 0xca, 0x8e,
];
/// The AEAD used for Retry is fixed, so use thread local storage.
fn make_aead(secret: &[u8]) -> Aead {
@ -33,6 +37,7 @@ fn make_aead(secret: &[u8]) -> Aead {
}
thread_local!(static RETRY_AEAD_27: RefCell<Aead> = RefCell::new(make_aead(RETRY_SECRET_27)));
thread_local!(static RETRY_AEAD_29: RefCell<Aead> = RefCell::new(make_aead(RETRY_SECRET_29)));
thread_local!(static RETRY_AEAD_V1: RefCell<Aead> = RefCell::new(make_aead(RETRY_SECRET_V1)));
/// Run a function with the appropriate Retry AEAD.
pub fn use_aead<F, T>(quic_version: QuicVersion, f: F) -> Res<T>
@ -40,6 +45,7 @@ where
F: FnOnce(&Aead) -> Res<T>,
{
match quic_version {
QuicVersion::Version1 => &RETRY_AEAD_V1,
QuicVersion::Draft27 | QuicVersion::Draft28 => &RETRY_AEAD_27,
QuicVersion::Draft29
| QuicVersion::Draft30

View File

@ -10,20 +10,25 @@
use std::cell::RefCell;
use std::convert::TryFrom;
use std::fmt::{self, Display};
use std::mem;
use std::net::{IpAddr, SocketAddr};
use std::rc::Rc;
use std::time::{Duration, Instant};
use crate::cc::CongestionControlAlgorithm;
use crate::cid::{ConnectionId, ConnectionIdRef, RemoteConnectionIdEntry};
use crate::frame::{
FRAME_TYPE_PATH_CHALLENGE, FRAME_TYPE_PATH_RESPONSE, FRAME_TYPE_RETIRE_CONNECTION_ID,
};
use crate::packet::PacketBuilder;
use crate::recovery::RecoveryToken;
use crate::rtt::RttEstimate;
use crate::sender::PacketSender;
use crate::stats::FrameStats;
use crate::tracking::{PNSpace, SentPacket};
use crate::{Error, Res};
use neqo_common::{hex, qdebug, qinfo, qtrace, Datagram, Encoder};
use neqo_common::{hex, qdebug, qinfo, qlog::NeqoQlog, qtrace, Datagram, Encoder};
use neqo_crypto::random;
/// This is the MTU that we assume when using IPv6.
@ -61,12 +66,21 @@ pub struct Paths {
/// Connection IDs that need to be retired.
to_retire: Vec<u64>,
/// QLog handler.
qlog: NeqoQlog,
}
impl Paths {
/// Find the path for the given addresses.
/// This might be a temporary path.
pub fn find_path(&self, local: SocketAddr, remote: SocketAddr) -> PathRef {
pub fn find_path(
&self,
local: SocketAddr,
remote: SocketAddr,
cc: CongestionControlAlgorithm,
now: Instant,
) -> PathRef {
self.paths
.iter()
.find_map(|p| {
@ -76,14 +90,26 @@ impl Paths {
None
}
})
.unwrap_or_else(|| Rc::new(RefCell::new(Path::temporary(local, remote))))
.unwrap_or_else(|| {
let mut p = Path::temporary(local, remote, cc, self.qlog.clone(), now);
if let Some(primary) = self.primary.as_ref() {
p.set_initial_rtt(primary.borrow().rtt().estimate());
}
Rc::new(RefCell::new(p))
})
}
/// Find the path, but allow for rebinding. That matches the pair of addresses
/// to paths that match the remote address only based on IP addres, not port.
/// We use this when the other side migrates to skip address validation and
/// creating a new path.
pub fn find_path_with_rebinding(&self, local: SocketAddr, remote: SocketAddr) -> PathRef {
pub fn find_path_with_rebinding(
&self,
local: SocketAddr,
remote: SocketAddr,
cc: CongestionControlAlgorithm,
now: Instant,
) -> PathRef {
self.paths
.iter()
.find_map(|p| {
@ -102,7 +128,15 @@ impl Paths {
}
})
})
.unwrap_or_else(|| Rc::new(RefCell::new(Path::temporary(local, remote))))
.unwrap_or_else(|| {
Rc::new(RefCell::new(Path::temporary(
local,
remote,
cc,
self.qlog.clone(),
now,
)))
})
}
/// Get a reference to the primary path. This will assert if there is no primary
@ -188,15 +222,18 @@ impl Paths {
/// is forcibly marked as valid and the path is used immediately.
/// Otherwise, migration will occur after probing succeeds.
/// The path is always probed and will be abandoned if probing fails.
pub fn migrate(&mut self, path: &PathRef, force: bool, now: Instant) {
/// Returns `true` if the path was migrated.
pub fn migrate(&mut self, path: &PathRef, force: bool, now: Instant) -> bool {
debug_assert!(!self.is_temporary(path));
if force || path.borrow().is_valid() {
path.borrow_mut().set_valid(now);
let _ = self.select_primary(path);
mem::drop(self.select_primary(path));
self.migration_target = None;
} else {
self.migration_target = Some(Rc::clone(path));
}
path.borrow_mut().probe();
self.migration_target.is_none()
}
/// Process elapsed time for active paths.
@ -232,7 +269,7 @@ impl Paths {
// Need a clone as `fallback` is borrowed from `self`.
let path = Rc::clone(fallback);
qinfo!([path.borrow()], "Failing over after primary path failed");
let _ = self.select_primary(&path);
mem::drop(self.select_primary(&path));
true
} else {
false
@ -289,7 +326,11 @@ impl Paths {
}
/// A `PATH_RESPONSE` was received.
pub fn path_response(&mut self, response: [u8; 8], now: Instant) {
/// Returns `true` if migration occurred.
#[must_use]
pub fn path_response(&mut self, response: [u8; 8], now: Instant) -> bool {
// TODO(mt) consider recording an RTT measurement here as we don't train
// RTT for non-primary paths.
for p in &self.paths {
if p.borrow_mut().path_response(response, now) {
// The response was accepted. If this path is one we intend
@ -300,11 +341,13 @@ impl Paths {
.map_or(false, |target| Rc::ptr_eq(target, p))
{
let primary = self.migration_target.take();
let _ = self.select_primary(&primary.unwrap());
mem::drop(self.select_primary(&primary.unwrap()));
return true;
}
break;
}
}
false
}
/// Write out any `RETIRE_CONNECTION_ID` frames that are outstanding.
@ -337,6 +380,26 @@ impl Paths {
pub fn acked_retire_cid(&mut self, acked: u64) {
self.to_retire.retain(|&seqno| seqno != acked);
}
/// Get an estimate of the RTT on the primary path.
#[cfg(test)]
pub fn rtt(&self) -> Duration {
// Rather than have this fail when there is no active path,
// make a new RTT esimate and interrogate that.
// That is more expensive, but it should be rare and breaking encapsulation
// is worse, especially as this is only used in tests.
self.primary_fallible()
.map_or(RttEstimate::default().estimate(), |p| {
p.borrow().rtt().estimate()
})
}
pub fn set_qlog(&mut self, qlog: NeqoQlog) {
for p in &mut self.paths {
p.borrow_mut().set_qlog(qlog.clone());
}
self.qlog = qlog;
}
}
/// The state of a path with respect to address validation.
@ -399,6 +462,11 @@ pub struct Path {
/// A path challenge was received and PATH_RESPONSE has not been sent.
challenge: Option<[u8; 8]>,
/// The round trip time estimate for this path.
rtt: RttEstimate,
/// A packet sender for the path, which includes congestion control and a pacer.
sender: PacketSender,
/// The number of bytes received on this path.
/// Note that this value might saturate on a long-lived connection,
/// but we only use it before the path is validated.
@ -410,7 +478,15 @@ pub struct Path {
impl Path {
/// Create a path from addresses and a remote connection ID.
/// This is used for migration and for new datagrams.
pub fn temporary(local: SocketAddr, remote: SocketAddr) -> Self {
pub fn temporary(
local: SocketAddr,
remote: SocketAddr,
cc: CongestionControlAlgorithm,
qlog: NeqoQlog,
now: Instant,
) -> Self {
let mut sender = PacketSender::new(cc, Self::mtu_by_addr(remote.ip()), now);
sender.set_qlog(qlog);
Self {
local,
remote,
@ -420,6 +496,8 @@ impl Path {
state: ProbeState::ProbeNeeded { probe_count: 0 },
validated: None,
challenge: None,
rtt: RttEstimate::default(),
sender,
received_bytes: 0,
sent_bytes: 0,
}
@ -438,7 +516,7 @@ impl Path {
/// By adding a remote connection ID, we make the path permanent
/// and one that we will later send packets on.
/// If `local_cid` is `None`, the existing value will be kept.
fn make_permanent(
pub(crate) fn make_permanent(
&mut self,
local_cid: Option<ConnectionId>,
remote_cid: RemoteConnectionIdEntry,
@ -465,9 +543,13 @@ impl Path {
}
/// Set whether this path is primary.
fn set_primary(&mut self, primary: bool) {
pub(crate) fn set_primary(&mut self, primary: bool) {
qtrace!([self], "Make primary {}", primary);
debug_assert!(self.remote_cid.is_some());
self.primary = primary;
if !primary {
self.sender.discard_in_flight();
}
}
/// Set the current path as valid. This updates the time that the path was
@ -486,14 +568,18 @@ impl Path {
}
}
/// Get the path MTU. This is currently a fixed value.
pub fn mtu(&self) -> usize {
match self.local.ip() {
fn mtu_by_addr(addr: IpAddr) -> usize {
match addr {
IpAddr::V4(_) => PATH_MTU_V4,
IpAddr::V6(_) => PATH_MTU_V6,
}
}
/// Get the path MTU. This is currently fixed based on IP version.
pub fn mtu(&self) -> usize {
Self::mtu_by_addr(self.remote.ip())
}
/// Get the first local connection ID.
/// Only do this for the primary path during the handshake.
pub fn local_cid(&self) -> &ConnectionId {
@ -695,6 +781,31 @@ impl Path {
}
}
/// Get the RTT estimator for this path.
pub fn rtt(&self) -> &RttEstimate {
&self.rtt
}
/// Mutably borrow the RTT estimator for this path.
pub fn rtt_mut(&mut self) -> &mut RttEstimate {
&mut self.rtt
}
/// Read-only access to the owned sender.
pub fn sender(&self) -> &PacketSender {
&self.sender
}
/// Pass on RTT configuration: the maximum acknowledgment delay of the peer.
pub fn set_max_ack_delay(&mut self, mad: Duration) {
self.rtt.set_max_ack_delay(mad);
}
/// Initialize the RTT for the path based on an existing estimate.
pub fn set_initial_rtt(&mut self, rtt: Duration) {
self.rtt.set_initial(rtt);
}
/// Record received bytes for the path.
pub fn add_received(&mut self, count: usize) {
self.received_bytes = self.received_bytes.saturating_add(count);
@ -705,6 +816,42 @@ impl Path {
self.sent_bytes = self.sent_bytes.saturating_add(count);
}
/// Record a packet as having been sent on this path.
pub fn packet_sent(&mut self, sent: &mut SentPacket) {
if !self.is_primary() {
sent.clear_primary_path();
}
self.sender.on_packet_sent(sent, self.rtt.estimate());
}
/// Discard a packet that previously might have been in-flight.
pub fn discard_packet(&mut self, sent: &SentPacket) {
self.sender.discard(sent);
}
/// Record packets as acknowledged with the sender.
pub fn on_packets_acked(&mut self, acked_pkts: &[SentPacket], now: Instant) {
debug_assert!(self.is_primary());
self.sender
.on_packets_acked(acked_pkts, self.rtt.minimum(), now);
}
/// Record packets as lost with the sender.
pub fn on_packets_lost(
&mut self,
prev_largest_acked_sent: Option<Instant>,
space: PNSpace,
lost_packets: &[SentPacket],
) {
debug_assert!(self.is_primary());
self.sender.on_packets_lost(
self.rtt.first_sample_time(),
prev_largest_acked_sent,
self.rtt.pto(space), // Important: the base PTO, not adjusted.
lost_packets,
)
}
/// Get the number of bytes that can be written to this path.
pub fn amplification_limit(&self) -> usize {
if matches!(self.state, ProbeState::Failed) {
@ -726,6 +873,11 @@ impl Path {
})
}
}
/// Update the `NeqoQLog` instance.
pub fn set_qlog(&mut self, qlog: NeqoQlog) {
self.sender.set_qlog(qlog);
}
}
impl Display for Path {

File diff suppressed because it is too large Load Diff

View File

@ -12,23 +12,89 @@ use std::cmp::max;
use std::collections::BTreeMap;
use std::convert::TryFrom;
use std::mem;
use std::ops::Bound::{Included, Unbounded};
use std::rc::Rc;
use smallvec::SmallVec;
use crate::events::ConnectionEvents;
use crate::fc::ReceiverFlowControl;
use crate::flow_mgr::FlowMgr;
use crate::frame::{write_varint_frame, FRAME_TYPE_STOP_SENDING};
use crate::packet::PacketBuilder;
use crate::recovery::RecoveryToken;
use crate::send_stream::SendStreams;
use crate::stats::FrameStats;
use crate::stream_id::StreamId;
use crate::{AppError, Error, Res};
use neqo_common::qtrace;
use neqo_common::{qtrace, Role};
const RX_STREAM_DATA_WINDOW: u64 = 0x10_0000; // 1MiB
// Export as usize for consistency with SEND_BUFFER_SIZE
pub const RECV_BUFFER_SIZE: usize = RX_STREAM_DATA_WINDOW as usize;
pub(crate) type RecvStreams = BTreeMap<StreamId, RecvStream>;
#[derive(Debug, Default)]
pub(crate) struct RecvStreams(BTreeMap<StreamId, RecvStream>);
impl RecvStreams {
pub fn write_frames(
&mut self,
builder: &mut PacketBuilder,
tokens: &mut Vec<RecoveryToken>,
stats: &mut FrameStats,
) -> Res<()> {
for stream in self.0.values_mut() {
stream.write_frame(builder, tokens, stats)?;
if builder.remaining() < 2 {
return Ok(());
}
}
Ok(())
}
pub fn insert(&mut self, id: StreamId, stream: RecvStream) {
self.0.insert(id, stream);
}
pub fn get_mut(&mut self, id: StreamId) -> Res<&mut RecvStream> {
self.0.get_mut(&id).ok_or(Error::InvalidStreamId)
}
pub fn clear(&mut self) {
self.0.clear();
}
pub fn clear_terminal(&mut self, send_streams: &SendStreams, role: Role) -> (u64, u64) {
let recv_to_remove = self
.0
.iter()
.filter_map(|(id, stream)| {
// Remove all streams for which the receiving is done (or aborted).
// But only if they are unidirectional, or we have finished sending.
if stream.is_terminal() && (id.is_uni() || !send_streams.exists(*id)) {
Some(*id)
} else {
None
}
})
.collect::<Vec<_>>();
let mut removed_bidi = 0;
let mut removed_uni = 0;
for id in &recv_to_remove {
self.0.remove(&id);
if id.is_remote_initiated(role) {
if id.is_bidi() {
removed_bidi += 1;
} else {
removed_uni += 1;
}
}
}
(removed_bidi, removed_uni)
}
}
/// Holds data not yet read by application. Orders and dedupes data ranges
/// from incoming STREAM frames.
@ -69,10 +135,8 @@ impl RxStreamOrderer {
return;
}
let extend = if let Some((&prev_start, prev_vec)) = self
.data_ranges
.range_mut((Unbounded, Included(new_start)))
.next_back()
let extend = if let Some((&prev_start, prev_vec)) =
self.data_ranges.range_mut(..=new_start).next_back()
{
let prev_end = prev_start + u64::try_from(prev_vec.len()).unwrap();
if new_end > prev_end {
@ -150,7 +214,7 @@ impl RxStreamOrderer {
if extend {
let (_, buf) = self
.data_ranges
.range_mut((Unbounded, Included(new_start)))
.range_mut(..=new_start)
.next_back()
.unwrap();
buf.extend_from_slice(to_add);
@ -270,9 +334,8 @@ impl RxStreamOrderer {
// Because a dead_code warning is easier than clippy::unused_self, see https://github.com/rust-lang/rust/issues/68408
enum RecvStreamState {
Recv {
fc: ReceiverFlowControl<StreamId>,
recv_buf: RxStreamOrderer,
max_bytes: u64, // Maximum size of recv_buf
max_stream_data: u64,
},
SizeKnown {
recv_buf: RxStreamOrderer,
@ -282,16 +345,19 @@ enum RecvStreamState {
recv_buf: RxStreamOrderer,
},
DataRead,
AbortReading {
frame_needed: bool,
err: AppError,
},
ResetRecvd,
// Defined by spec but we don't use it: ResetRead
}
impl RecvStreamState {
fn new(max_bytes: u64) -> Self {
fn new(max_bytes: u64, stream_id: StreamId) -> Self {
Self::Recv {
fc: ReceiverFlowControl::new(stream_id, max_bytes),
recv_buf: RxStreamOrderer::new(),
max_bytes,
max_stream_data: max_bytes,
}
}
@ -301,6 +367,7 @@ impl RecvStreamState {
Self::SizeKnown { .. } => "SizeKnown",
Self::DataRecvd { .. } => "DataRecvd",
Self::DataRead => "DataRead",
Self::AbortReading { .. } => "AbortReading",
Self::ResetRecvd => "ResetRecvd",
}
}
@ -310,7 +377,7 @@ impl RecvStreamState {
Self::Recv { recv_buf, .. }
| Self::SizeKnown { recv_buf, .. }
| Self::DataRecvd { recv_buf } => Some(recv_buf),
Self::DataRead | Self::ResetRecvd => None,
Self::DataRead | Self::AbortReading { .. } | Self::ResetRecvd => None,
}
}
@ -320,15 +387,6 @@ impl RecvStreamState {
_ => None,
}
}
fn max_stream_data(&self) -> Option<u64> {
match self {
Self::Recv {
max_stream_data, ..
} => Some(*max_stream_data),
_ => None,
}
}
}
/// Implement a QUIC receive stream.
@ -349,7 +407,7 @@ impl RecvStream {
) -> Self {
Self {
stream_id,
state: RecvStreamState::new(max_stream_data),
state: RecvStreamState::new(max_stream_data, stream_id),
flow_mgr,
conn_events,
}
@ -367,12 +425,6 @@ impl RecvStream {
new_state.name()
);
if let RecvStreamState::Recv { .. } = &self.state {
self.flow_mgr
.borrow_mut()
.clear_max_stream_data(self.stream_id)
}
if let RecvStreamState::DataRead = new_state {
self.conn_events.recv_stream_complete(self.stream_id);
}
@ -394,13 +446,9 @@ impl RecvStream {
}
match &mut self.state {
RecvStreamState::Recv {
recv_buf,
max_stream_data,
..
} => {
if new_end > *max_stream_data {
qtrace!("Stream RX window {} exceeded: {}", max_stream_data, new_end);
RecvStreamState::Recv { recv_buf, fc, .. } => {
if !fc.check_allowed(new_end) {
qtrace!("Stream RX window exceeded: {}", new_end);
return Err(Error::FlowControlError);
}
@ -436,6 +484,7 @@ impl RecvStream {
}
RecvStreamState::DataRecvd { .. }
| RecvStreamState::DataRead
| RecvStreamState::AbortReading { .. }
| RecvStreamState::ResetRecvd => {
qtrace!("data received when we are in state {}", self.state.name())
}
@ -450,7 +499,9 @@ impl RecvStream {
pub fn reset(&mut self, application_error_code: AppError) {
match self.state {
RecvStreamState::Recv { .. } | RecvStreamState::SizeKnown { .. } => {
RecvStreamState::Recv { .. }
| RecvStreamState::SizeKnown { .. }
| RecvStreamState::AbortReading { .. } => {
self.conn_events
.recv_stream_reset(self.stream_id, application_error_code);
self.set_state(RecvStreamState::ResetRecvd);
@ -464,29 +515,18 @@ impl RecvStream {
/// If we should tell the sender they have more credit, return an offset
pub fn maybe_send_flowc_update(&mut self) {
// Only ever needed if actively receiving and not in SizeKnown state
if let RecvStreamState::Recv {
max_bytes,
max_stream_data,
recv_buf,
} = &mut self.state
{
// Algo: send an update if app has consumed more than half
// the data in the current window
// TODO(agrover@mozilla.com): This algo is not great but
// should prevent Silly Window Syndrome. Spec refers to using
// highest seen offset somehow? RTT maybe?
let maybe_new_max = recv_buf.retired() + *max_bytes;
if maybe_new_max > (*max_bytes / 2) + *max_stream_data {
*max_stream_data = maybe_new_max;
self.flow_mgr
.borrow_mut()
.max_stream_data(self.stream_id, maybe_new_max)
}
if let RecvStreamState::Recv { fc, recv_buf } = &mut self.state {
fc.retired(recv_buf.retired());
}
}
pub fn max_stream_data(&self) -> Option<u64> {
self.state.max_stream_data()
/// Send a flow control update.
/// This is used when a peer declares that they are blocked.
/// This sends `MAX_STREAM_DATA` if there is any increase possible.
pub fn send_flowc_update(&mut self) {
if let RecvStreamState::Recv { fc, .. } = &mut self.state {
fc.send_flowc_update();
}
}
pub fn is_terminal(&self) -> bool {
@ -521,7 +561,9 @@ impl RecvStream {
}
Ok((bytes_read, fin_read))
}
RecvStreamState::DataRead | RecvStreamState::ResetRecvd => Err(Error::NoMoreData),
RecvStreamState::DataRead
| RecvStreamState::AbortReading { .. }
| RecvStreamState::ResetRecvd => Err(Error::NoMoreData),
};
self.maybe_send_flowc_update();
res
@ -531,21 +573,82 @@ impl RecvStream {
qtrace!("stop_sending called when in state {}", self.state.name());
match &self.state {
RecvStreamState::Recv { .. } | RecvStreamState::SizeKnown { .. } => {
self.set_state(RecvStreamState::ResetRecvd);
self.flow_mgr.borrow_mut().stop_sending(self.stream_id, err)
self.set_state(RecvStreamState::AbortReading {
frame_needed: true,
err,
})
}
RecvStreamState::DataRecvd { .. } => self.set_state(RecvStreamState::DataRead),
RecvStreamState::DataRead | RecvStreamState::ResetRecvd => {
RecvStreamState::DataRead
| RecvStreamState::AbortReading { .. }
| RecvStreamState::ResetRecvd => {
// Already in terminal state
}
}
}
/// Maybe write a `MAX_STREAM_DATA` frame.
pub fn write_frame(
&mut self,
builder: &mut PacketBuilder,
tokens: &mut Vec<RecoveryToken>,
stats: &mut FrameStats,
) -> Res<()> {
match &mut self.state {
// Maybe send MAX_STREAM_DATA
RecvStreamState::Recv { fc, .. } => fc.write_frames(builder, tokens, stats)?,
// Maybe send STOP_SENDING
RecvStreamState::AbortReading { frame_needed, err } => {
if *frame_needed
&& write_varint_frame(
builder,
&[FRAME_TYPE_STOP_SENDING, self.stream_id.as_u64(), *err],
)?
{
tokens.push(RecoveryToken::StopSending {
stream_id: self.stream_id,
});
stats.stop_sending += 1;
*frame_needed = false;
}
}
_ => {}
}
Ok(())
}
pub fn max_stream_data_lost(&mut self, maximum_data: u64) {
if let RecvStreamState::Recv { fc, .. } = &mut self.state {
fc.lost(maximum_data);
}
}
pub fn stop_sending_lost(&mut self) {
if let RecvStreamState::AbortReading { frame_needed, .. } = &mut self.state {
*frame_needed = true;
}
}
pub fn stop_sending_acked(&mut self) {
if let RecvStreamState::AbortReading { .. } = &mut self.state {
self.set_state(RecvStreamState::ResetRecvd);
}
}
#[cfg(test)]
pub fn has_frames_to_write(&self) -> bool {
if let RecvStreamState::Recv { fc, .. } = &self.state {
fc.max_data_needed().is_some()
} else {
false
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::frame::Frame;
use neqo_common::Encoder;
use std::ops::Range;
fn recv_ranges(ranges: &[Range<u64>], available: usize) {
@ -980,23 +1083,26 @@ mod tests {
let mut buf = vec![0u8; RECV_BUFFER_SIZE + 100]; // Make it overlarge
s.maybe_send_flowc_update();
assert_eq!(s.flow_mgr.borrow().peek(), None);
assert!(!s.has_frames_to_write());
s.inbound_stream_frame(false, 0, &frame1).unwrap();
s.maybe_send_flowc_update();
assert_eq!(s.flow_mgr.borrow().peek(), None);
assert!(!s.has_frames_to_write());
assert_eq!(s.read(&mut buf).unwrap(), (RECV_BUFFER_SIZE, false));
assert_eq!(s.data_ready(), false);
s.maybe_send_flowc_update();
// flow msg generated!
assert!(s.flow_mgr.borrow().peek().is_some());
assert!(s.has_frames_to_write());
// consume it
s.flow_mgr.borrow_mut().next().unwrap();
let mut builder = PacketBuilder::short(Encoder::new(), false, &[]);
let mut token = Vec::new();
s.write_frame(&mut builder, &mut token, &mut FrameStats::default())
.unwrap();
// it should be gone
s.maybe_send_flowc_update();
assert_eq!(s.flow_mgr.borrow().peek(), None);
assert!(!s.has_frames_to_write());
}
#[test]
@ -1013,7 +1119,7 @@ mod tests {
);
s.maybe_send_flowc_update();
assert_eq!(s.flow_mgr.borrow().peek(), None);
assert!(!s.has_frames_to_write());
s.inbound_stream_frame(false, 0, &frame1).unwrap();
s.inbound_stream_frame(false, RX_STREAM_DATA_WINDOW, &[1; 1])
.unwrap_err();
@ -1069,42 +1175,11 @@ mod tests {
);
s.inbound_stream_frame(false, 0, &frame1).unwrap();
flow_mgr.borrow_mut().max_stream_data(stream_id, 100);
assert!(matches!(s.flow_mgr.borrow().peek().unwrap(), Frame::MaxStreamData{..}));
let mut buf = [0; RECV_BUFFER_SIZE];
s.read(&mut buf).unwrap();
assert!(s.has_frames_to_write());
s.inbound_stream_frame(true, RX_STREAM_DATA_WINDOW, &[])
.unwrap();
assert!(matches!(s.flow_mgr.borrow().peek(), None));
}
#[test]
fn resend_flowc_if_lost() {
let flow_mgr = Rc::new(RefCell::new(FlowMgr::default()));
let conn_events = ConnectionEvents::default();
let frame1 = &[0; RECV_BUFFER_SIZE];
let stream_id = StreamId::from(67);
let mut s = RecvStream::new(
stream_id,
RX_STREAM_DATA_WINDOW,
Rc::clone(&flow_mgr),
conn_events,
);
// A flow control update is queued
s.inbound_stream_frame(false, 0, frame1).unwrap();
flow_mgr.borrow_mut().max_stream_data(stream_id, 100);
// Generates frame
assert!(matches!(
s.flow_mgr.borrow_mut().next().unwrap(),
Frame::MaxStreamData { .. }
));
// Nothing else queued
assert!(matches!(s.flow_mgr.borrow().peek(), None));
// Asking for another one won't get you one
s.maybe_send_flowc_update();
assert!(matches!(s.flow_mgr.borrow().peek(), None));
// But if lost, another frame is generated
flow_mgr.borrow_mut().max_stream_data(stream_id, 100);
assert!(matches!(s.flow_mgr.borrow_mut().next().unwrap(), Frame::MaxStreamData{..}));
assert!(!s.has_frames_to_write());
}
}

View File

@ -0,0 +1,169 @@
// 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.
// Tracking of sent packets and detecting their loss.
#![deny(clippy::pedantic)]
use std::cmp::{max, min};
use std::time::{Duration, Instant};
use neqo_common::{qlog::NeqoQlog, qtrace};
use crate::qlog::{self, QlogMetric};
use crate::tracking::PNSpace;
/// The smallest time that the system timer (via `sleep()`, `nanosleep()`,
/// `select()`, or similar) can reliably deliver; see `neqo_common::hrtime`.
const GRANULARITY: Duration = Duration::from_millis(1);
/// The default value for the maximum time a peer can delay acknowledgment
/// of an ack-eliciting packet.
const DEFAULT_MAX_ACK_DELAY: Duration = Duration::from_millis(25);
// Defined in -recovery 6.2 as 333ms but using lower value.
const INITIAL_RTT: Duration = Duration::from_millis(100);
#[derive(Debug)]
#[allow(clippy::module_name_repetitions)]
pub struct RttEstimate {
first_sample_time: Option<Instant>,
latest_rtt: Duration,
smoothed_rtt: Duration,
rttvar: Duration,
min_rtt: Duration,
max_ack_delay: Duration,
}
impl RttEstimate {
pub fn set_initial(&mut self, rtt: Duration) {
qtrace!("initial RTT={:?}", rtt);
if rtt >= GRANULARITY {
// Ignore if the value is too small.
self.init(rtt)
}
}
fn init(&mut self, rtt: Duration) {
// Only allow this when there are no samples.
debug_assert!(self.first_sample_time.is_none());
self.latest_rtt = rtt;
self.min_rtt = rtt;
self.smoothed_rtt = rtt;
self.rttvar = rtt / 2;
}
pub fn set_max_ack_delay(&mut self, mad: Duration) {
self.max_ack_delay = mad;
}
pub fn update(
&mut self,
mut qlog: &mut NeqoQlog,
mut rtt_sample: Duration,
ack_delay: Duration,
confirmed: bool,
now: Instant,
) {
// Limit ack delay by max_ack_delay if confirmed.
let ack_delay = if confirmed && ack_delay > self.max_ack_delay {
self.max_ack_delay
} else {
ack_delay
};
// min_rtt ignores ack delay.
self.min_rtt = min(self.min_rtt, rtt_sample);
// Adjust for ack delay unless it goes below `min_rtt`.
if rtt_sample - self.min_rtt >= ack_delay {
rtt_sample -= ack_delay;
}
if self.first_sample_time.is_none() {
self.init(rtt_sample);
self.first_sample_time = Some(now);
} else {
// Calculate EWMA RTT (based on {{?RFC6298}}).
let rttvar_sample = if self.smoothed_rtt > rtt_sample {
self.smoothed_rtt - rtt_sample
} else {
rtt_sample - self.smoothed_rtt
};
self.latest_rtt = rtt_sample;
self.rttvar = (self.rttvar * 3 + rttvar_sample) / 4;
self.smoothed_rtt = (self.smoothed_rtt * 7 + rtt_sample) / 8;
}
qtrace!(
"RTT latest={:?} -> estimate={:?}~{:?}",
self.latest_rtt,
self.smoothed_rtt,
self.rttvar
);
qlog::metrics_updated(
&mut qlog,
&[
QlogMetric::LatestRtt(self.latest_rtt),
QlogMetric::MinRtt(self.min_rtt),
QlogMetric::SmoothedRtt(self.smoothed_rtt),
],
);
}
/// Get the estimated value.
pub fn estimate(&self) -> Duration {
self.smoothed_rtt
}
pub fn pto(&self, pn_space: PNSpace) -> Duration {
self.estimate()
+ max(4 * self.rttvar, GRANULARITY)
+ if pn_space == PNSpace::ApplicationData {
self.max_ack_delay
} else {
Duration::from_millis(0)
}
}
/// Calculate the loss delay based on the current estimate and the last
/// RTT measurement received.
pub fn loss_delay(&self) -> Duration {
// kTimeThreshold = 9/8
// loss_delay = kTimeThreshold * max(latest_rtt, smoothed_rtt)
// loss_delay = max(loss_delay, kGranularity)
let rtt = max(self.latest_rtt, self.smoothed_rtt);
max(rtt * 9 / 8, GRANULARITY)
}
pub fn first_sample_time(&self) -> Option<Instant> {
self.first_sample_time
}
#[cfg(test)]
pub fn latest(&self) -> Duration {
self.latest_rtt
}
#[cfg(test)]
pub fn rttvar(&self) -> Duration {
self.rttvar
}
pub fn minimum(&self) -> Duration {
self.min_rtt
}
}
impl Default for RttEstimate {
fn default() -> Self {
Self {
first_sample_time: None,
latest_rtt: INITIAL_RTT,
smoothed_rtt: INITIAL_RTT,
rttvar: INITIAL_RTT / 2,
min_rtt: INITIAL_RTT,
max_ack_delay: DEFAULT_MAX_ACK_DELAY,
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -10,7 +10,6 @@
use crate::cc::{
ClassicCongestionControl, CongestionControl, CongestionControlAlgorithm, Cubic, NewReno,
MAX_DATAGRAM_SIZE,
};
use crate::pace::Pacer;
use crate::tracking::SentPacket;
@ -25,22 +24,18 @@ pub const PACING_BURST_SIZE: usize = 2;
#[derive(Debug)]
pub struct PacketSender {
cc: Box<dyn CongestionControl>,
pacer: Option<Pacer>,
pacer: Pacer,
}
impl Display for PacketSender {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.cc)?;
if let Some(p) = &self.pacer {
write!(f, " {}", p)?;
}
Ok(())
write!(f, "{} {}", self.cc, self.pacer)
}
}
impl PacketSender {
#[must_use]
pub fn new(alg: CongestionControlAlgorithm) -> Self {
pub fn new(alg: CongestionControlAlgorithm, mtu: usize, now: Instant) -> Self {
Self {
cc: match alg {
CongestionControlAlgorithm::NewReno => {
@ -50,7 +45,7 @@ impl PacketSender {
Box::new(ClassicCongestionControl::new(Cubic::default()))
}
},
pacer: None,
pacer: Pacer::new(now, mtu * PACING_BURST_SIZE, mtu),
}
}
@ -69,7 +64,6 @@ impl PacketSender {
self.cc.cwnd_avail()
}
// Multi-packet version of OnPacketAckedCC
pub fn on_packets_acked(&mut self, acked_pkts: &[SentPacket], min_rtt: Duration, now: Instant) {
self.cc.on_packets_acked(acked_pkts, min_rtt, now);
}
@ -93,28 +87,23 @@ impl PacketSender {
self.cc.discard(pkt);
}
pub fn on_packet_sent(&mut self, pkt: &SentPacket, rtt: Duration) {
self.pacer
.as_mut()
.unwrap()
.spend(pkt.time_sent, rtt, self.cc.cwnd(), pkt.size);
self.cc.on_packet_sent(pkt);
/// When we migrate, the congestion controller for the previously active path drops
/// all bytes in flight.
pub fn discard_in_flight(&mut self) {
self.cc.discard_in_flight();
}
pub fn start_pacer(&mut self, now: Instant) {
// Start the pacer with a small burst size.
self.pacer = Some(Pacer::new(
now,
MAX_DATAGRAM_SIZE * PACING_BURST_SIZE,
MAX_DATAGRAM_SIZE,
));
pub fn on_packet_sent(&mut self, pkt: &SentPacket, rtt: Duration) {
self.pacer
.spend(pkt.time_sent, rtt, self.cc.cwnd(), pkt.size);
self.cc.on_packet_sent(pkt);
}
#[must_use]
pub fn next_paced(&self, rtt: Duration) -> Option<Instant> {
// Only pace if there are bytes in flight.
if self.cc.bytes_in_flight() > 0 {
Some(self.pacer.as_ref().unwrap().next(rtt, self.cc.cwnd()))
Some(self.pacer.next(rtt, self.cc.cwnd()))
} else {
None
}

View File

@ -39,7 +39,12 @@ pub enum InitialResult {
/// MIN_INITIAL_PACKET_SIZE is the smallest packet that can be used to establish
/// a new connection across all QUIC versions this server supports.
const MIN_INITIAL_PACKET_SIZE: usize = 1200;
const TIMER_GRANULARITY: Duration = Duration::from_millis(10);
/// The size of timer buckets. This is higher than the actual timer granularity
/// as this depends on there being some distribution of events.
const TIMER_GRANULARITY: Duration = Duration::from_millis(4);
/// The number of buckets in the timer. As mentioned in the definition of `Timer`,
/// the granularity and capacity need to multiply to be larger than the largest
/// delay that might be used. That's the idle timeout (currently 30s).
const TIMER_CAPACITY: usize = 16384;
type StateRef = Rc<RefCell<ServerConnectionState>>;
@ -610,11 +615,11 @@ pub struct ActiveConnectionRef {
}
impl ActiveConnectionRef {
pub fn borrow<'a>(&'a self) -> impl Deref<Target = Connection> + 'a {
pub fn borrow(&self) -> impl Deref<Target = Connection> + '_ {
std::cell::Ref::map(self.c.borrow(), |c| &c.c)
}
pub fn borrow_mut<'a>(&'a mut self) -> impl DerefMut<Target = Connection> + 'a {
pub fn borrow_mut(&mut self) -> impl DerefMut<Target = Connection> + '_ {
std::cell::RefMut::map(self.c.borrow_mut(), |c| &mut c.c)
}

View File

@ -137,6 +137,7 @@ pub struct SentPacket {
pub pn: PacketNumber,
ack_eliciting: bool,
pub time_sent: Instant,
primary_path: bool,
pub tokens: Vec<RecoveryToken>,
time_declared_lost: Option<Instant>,
@ -160,6 +161,7 @@ impl SentPacket {
pn,
time_sent,
ack_eliciting,
primary_path: true,
tokens,
time_declared_lost: None,
pto: false,
@ -172,6 +174,17 @@ impl SentPacket {
self.ack_eliciting
}
/// Returns `true` if the packet was sent on the primary path.
pub fn on_primary_path(&self) -> bool {
self.primary_path
}
/// Clears the flag that had this packet on the primary path.
/// Used when migrating to clear out state.
pub fn clear_primary_path(&mut self) {
self.primary_path = false;
}
/// Whether the packet has been declared lost.
pub fn lost(&self) -> bool {
self.time_declared_lost.is_some()
@ -184,7 +197,12 @@ impl SentPacket {
/// Note that this should count packets that contain only ACK and PADDING,
/// but we don't send PADDING, so we don't track that.
pub fn cc_outstanding(&self) -> bool {
self.ack_eliciting() && !self.lost()
self.ack_eliciting() && self.on_primary_path() && !self.lost()
}
/// Whether the packet should be tracked as in-flight.
pub fn cc_in_flight(&self) -> bool {
self.ack_eliciting() && self.on_primary_path()
}
/// Declare the packet as lost. Returns `true` if this is the first time.
@ -502,15 +520,16 @@ impl RecvdPackets {
&mut self,
now: Instant,
builder: &mut PacketBuilder,
tokens: &mut Vec<RecoveryToken>,
stats: &mut FrameStats,
) -> Option<RecoveryToken> {
) {
// The worst possible ACK frame, assuming only one range.
// Note that this assumes one byte for the type and count of extra ranges.
const LONGEST_ACK_HEADER: usize = 1 + 8 + 8 + 1 + 8;
// Check that we aren't delaying ACKs.
if !self.ack_now(now) {
return None;
return;
}
// Drop extra ACK ranges to fit the available space. Do this based on
@ -523,7 +542,7 @@ impl RecvdPackets {
// Apply a hard maximum to keep plenty of space for other stuff.
min(1 + (avail / 16), MAX_ACKS_PER_FRAME)
} else {
return None;
return;
};
let ranges = self
@ -538,7 +557,7 @@ impl RecvdPackets {
let mut iter = ranges.iter();
let first = match iter.next() {
Some(v) => v,
None => return None, // Nothing to send.
None => return, // Nothing to send.
};
builder.encode_varint(first.largest);
stats.largest_acknowledged = first.largest;
@ -565,10 +584,10 @@ impl RecvdPackets {
self.ack_time = None;
self.pkts_since_last_ack = 0;
Some(RecoveryToken::Ack(AckToken {
tokens.push(RecoveryToken::Ack(AckToken {
space: self.space,
ranges,
}))
}));
}
}
@ -636,16 +655,16 @@ impl AckTracker {
pn_space: PNSpace,
now: Instant,
builder: &mut PacketBuilder,
tokens: &mut Vec<RecoveryToken>,
stats: &mut FrameStats,
) -> Res<Option<RecoveryToken>> {
let res = self
.get_mut(pn_space)
.and_then(|space| space.write_frame(now, builder, stats));
if builder.len() > builder.limit() {
return Err(Error::InternalError(24));
) -> Res<()> {
if let Some(space) = self.get_mut(pn_space) {
space.write_frame(now, builder, tokens, stats);
if builder.len() > builder.limit() {
return Err(Error::InternalError(24));
}
}
Ok(res)
Ok(())
}
}
@ -855,15 +874,19 @@ mod tests {
.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 - Duration::from_millis(1)).is_some());
let token = tracker
let mut tokens = Vec::new();
let mut stats = FrameStats::default();
tracker
.write_frame(
PNSpace::Initial,
*NOW,
&mut builder,
&mut FrameStats::default(),
&mut tokens,
&mut stats,
)
.unwrap();
assert!(token.is_some());
assert_eq!(stats.ack, 1);
// Mark another packet as received so we have cause to send another ACK in that space.
tracker
@ -877,17 +900,18 @@ mod tests {
assert!(tracker.get_mut(PNSpace::Initial).is_none());
assert!(tracker.ack_time(*NOW - Duration::from_millis(1)).is_none());
assert!(tracker
tracker
.write_frame(
PNSpace::Initial,
*NOW,
&mut builder,
&mut FrameStats::default()
&mut tokens,
&mut stats,
)
.unwrap()
.is_none());
if let RecoveryToken::Ack(tok) = token.unwrap() {
tracker.acked(&tok); // Should be a noop.
.unwrap();
assert_eq!(stats.ack, 1);
if let RecoveryToken::Ack(tok) = &tokens[0] {
tracker.acked(tok); // Should be a noop.
} else {
panic!("not an ACK token");
}
@ -905,15 +929,17 @@ mod tests {
let mut builder = PacketBuilder::short(Encoder::new(), false, &[]);
builder.set_limit(10);
let token = tracker
let mut stats = FrameStats::default();
tracker
.write_frame(
PNSpace::Initial,
*NOW,
&mut builder,
&mut FrameStats::default(),
&mut Vec::new(),
&mut stats,
)
.unwrap();
assert!(token.is_none());
assert_eq!(stats.ack, 0);
assert_eq!(builder.len(), 1); // Only the short packet header has been added.
}
@ -933,15 +959,17 @@ mod tests {
let mut builder = PacketBuilder::short(Encoder::new(), false, &[]);
builder.set_limit(32);
let token = tracker
let mut stats = FrameStats::default();
tracker
.write_frame(
PNSpace::Initial,
*NOW,
&mut builder,
&mut FrameStats::default(),
&mut Vec::new(),
&mut stats,
)
.unwrap();
assert!(token.is_some());
assert_eq!(stats.ack, 1);
let mut dec = builder.as_decoder();
let _ = dec.decode_byte().unwrap(); // Skip the short header.

View File

@ -7,7 +7,7 @@
// Tests with the test vectors from the spec.
#![deny(clippy::pedantic)]
use neqo_common::{Datagram, Encoder};
use neqo_common::Datagram;
use neqo_transport::{
Connection, ConnectionParameters, QuicVersion, RandomConnectionIdGenerator, State,
};
@ -16,157 +16,239 @@ use test_fixture::{self, addr, now};
use std::cell::RefCell;
use std::rc::Rc;
const INITIAL_PACKET_27: &str = "c0ff00001b088394c8f03e5157080000\
449e3b343aa8535064a4268a0d9d7b1c\
9d250ae355162276e9b1e3011ef6bbc0\
ab48ad5bcc2681e953857ca62becd752\
4daac473e68d7405fbba4e9ee616c870\
38bdbe908c06d9605d9ac49030359eec\
b1d05a14e117db8cede2bb09d0dbbfee\
271cb374d8f10abec82d0f59a1dee29f\
e95638ed8dd41da07487468791b719c5\
5c46968eb3b54680037102a28e53dc1d\
12903db0af5821794b41c4a93357fa59\
ce69cfe7f6bdfa629eef78616447e1d6\
11c4baf71bf33febcb03137c2c75d253\
17d3e13b684370f668411c0f00304b50\
1c8fd422bd9b9ad81d643b20da89ca05\
25d24d2b142041cae0af205092e43008\
0cd8559ea4c5c6e4fa3f66082b7d303e\
52ce0162baa958532b0bbc2bc785681f\
cf37485dff6595e01e739c8ac9efba31\
b985d5f656cc092432d781db95221724\
87641c4d3ab8ece01e39bc85b1543661\
4775a98ba8fa12d46f9b35e2a55eb72d\
7f85181a366663387ddc20551807e007\
673bd7e26bf9b29b5ab10a1ca87cbb7a\
d97e99eb66959c2a9bc3cbde4707ff77\
20b110fa95354674e395812e47a0ae53\
b464dcb2d1f345df360dc227270c7506\
76f6724eb479f0d2fbb6124429990457\
ac6c9167f40aab739998f38b9eccb24f\
d47c8410131bf65a52af841275d5b3d1\
880b197df2b5dea3e6de56ebce3ffb6e\
9277a82082f8d9677a6767089b671ebd\
244c214f0bde95c2beb02cd1172d58bd\
f39dce56ff68eb35ab39b49b4eac7c81\
5ea60451d6e6ab82119118df02a58684\
4a9ffe162ba006d0669ef57668cab38b\
62f71a2523a084852cd1d079b3658dc2\
f3e87949b550bab3e177cfc49ed190df\
f0630e43077c30de8f6ae081537f1e83\
da537da980afa668e7b7fb25301cf741\
524be3c49884b42821f17552fbd1931a\
813017b6b6590a41ea18b6ba49cd48a4\
40bd9a3346a7623fb4ba34a3ee571e3c\
731f35a7a3cf25b551a680fa68763507\
b7fde3aaf023c50b9d22da6876ba337e\
b5e9dd9ec3daf970242b6c5aab3aa4b2\
96ad8b9f6832f686ef70fa938b31b4e5\
ddd7364442d3ea72e73d668fb0937796\
f462923a81a47e1cee7426ff6d922126\
9b5a62ec03d6ec94d12606cb485560ba\
b574816009e96504249385bb61a819be\
04f62c2066214d8360a2022beb316240\
b6c7d78bbe56c13082e0ca272661210a\
bf020bf3b5783f1426436cf9ff418405\
93a5d0638d32fc51c5c65ff291a3a7a5\
2fd6775e623a4439cc08dd25582febc9\
44ef92d8dbd329c91de3e9c9582e41f1\
7f3d186f104ad3f90995116c682a2a14\
a3b4b1f547c335f0be710fc9fc03e0e5\
87b8cda31ce65b969878a4ad4283e6d5\
b0373f43da86e9e0ffe1ae0fddd35162\
55bd74566f36a38703d5f34249ded1f6\
6b3d9b45b9af2ccfefe984e13376b1b2\
c6404aa48c8026132343da3f3a33659e\
c1b3e95080540b28b7f3fcd35fa5d843\
b579a84c089121a60d8c1754915c344e\
eaf45a9bf27dc0c1e784161691220913\
13eb0e87555abd706626e557fc36a04f\
cd191a58829104d6075c5594f627ca50\
6bf181daec940f4a4f3af0074eee89da\
acde6758312622d4fa675b39f728e062\
d2bee680d8f41a597c262648bb18bcfc\
13c8b3d97b1a77b2ac3af745d61a34cc\
4709865bac824a94bb19058015e4e42d\
38d3b779d72edc00c5cd088eff802b05";
const INITIAL_PACKET_V1: &[u8] = &[
0xc0, 0x00, 0x00, 0x00, 0x01, 0x08, 0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08, 0x00, 0x00,
0x44, 0x9e, 0x7b, 0x9a, 0xec, 0x34, 0xd1, 0xb1, 0xc9, 0x8d, 0xd7, 0x68, 0x9f, 0xb8, 0xec, 0x11,
0xd2, 0x42, 0xb1, 0x23, 0xdc, 0x9b, 0xd8, 0xba, 0xb9, 0x36, 0xb4, 0x7d, 0x92, 0xec, 0x35, 0x6c,
0x0b, 0xab, 0x7d, 0xf5, 0x97, 0x6d, 0x27, 0xcd, 0x44, 0x9f, 0x63, 0x30, 0x00, 0x99, 0xf3, 0x99,
0x1c, 0x26, 0x0e, 0xc4, 0xc6, 0x0d, 0x17, 0xb3, 0x1f, 0x84, 0x29, 0x15, 0x7b, 0xb3, 0x5a, 0x12,
0x82, 0xa6, 0x43, 0xa8, 0xd2, 0x26, 0x2c, 0xad, 0x67, 0x50, 0x0c, 0xad, 0xb8, 0xe7, 0x37, 0x8c,
0x8e, 0xb7, 0x53, 0x9e, 0xc4, 0xd4, 0x90, 0x5f, 0xed, 0x1b, 0xee, 0x1f, 0xc8, 0xaa, 0xfb, 0xa1,
0x7c, 0x75, 0x0e, 0x2c, 0x7a, 0xce, 0x01, 0xe6, 0x00, 0x5f, 0x80, 0xfc, 0xb7, 0xdf, 0x62, 0x12,
0x30, 0xc8, 0x37, 0x11, 0xb3, 0x93, 0x43, 0xfa, 0x02, 0x8c, 0xea, 0x7f, 0x7f, 0xb5, 0xff, 0x89,
0xea, 0xc2, 0x30, 0x82, 0x49, 0xa0, 0x22, 0x52, 0x15, 0x5e, 0x23, 0x47, 0xb6, 0x3d, 0x58, 0xc5,
0x45, 0x7a, 0xfd, 0x84, 0xd0, 0x5d, 0xff, 0xfd, 0xb2, 0x03, 0x92, 0x84, 0x4a, 0xe8, 0x12, 0x15,
0x46, 0x82, 0xe9, 0xcf, 0x01, 0x2f, 0x90, 0x21, 0xa6, 0xf0, 0xbe, 0x17, 0xdd, 0xd0, 0xc2, 0x08,
0x4d, 0xce, 0x25, 0xff, 0x9b, 0x06, 0xcd, 0xe5, 0x35, 0xd0, 0xf9, 0x20, 0xa2, 0xdb, 0x1b, 0xf3,
0x62, 0xc2, 0x3e, 0x59, 0x6d, 0x11, 0xa4, 0xf5, 0xa6, 0xcf, 0x39, 0x48, 0x83, 0x8a, 0x3a, 0xec,
0x4e, 0x15, 0xda, 0xf8, 0x50, 0x0a, 0x6e, 0xf6, 0x9e, 0xc4, 0xe3, 0xfe, 0xb6, 0xb1, 0xd9, 0x8e,
0x61, 0x0a, 0xc8, 0xb7, 0xec, 0x3f, 0xaf, 0x6a, 0xd7, 0x60, 0xb7, 0xba, 0xd1, 0xdb, 0x4b, 0xa3,
0x48, 0x5e, 0x8a, 0x94, 0xdc, 0x25, 0x0a, 0xe3, 0xfd, 0xb4, 0x1e, 0xd1, 0x5f, 0xb6, 0xa8, 0xe5,
0xeb, 0xa0, 0xfc, 0x3d, 0xd6, 0x0b, 0xc8, 0xe3, 0x0c, 0x5c, 0x42, 0x87, 0xe5, 0x38, 0x05, 0xdb,
0x05, 0x9a, 0xe0, 0x64, 0x8d, 0xb2, 0xf6, 0x42, 0x64, 0xed, 0x5e, 0x39, 0xbe, 0x2e, 0x20, 0xd8,
0x2d, 0xf5, 0x66, 0xda, 0x8d, 0xd5, 0x99, 0x8c, 0xca, 0xbd, 0xae, 0x05, 0x30, 0x60, 0xae, 0x6c,
0x7b, 0x43, 0x78, 0xe8, 0x46, 0xd2, 0x9f, 0x37, 0xed, 0x7b, 0x4e, 0xa9, 0xec, 0x5d, 0x82, 0xe7,
0x96, 0x1b, 0x7f, 0x25, 0xa9, 0x32, 0x38, 0x51, 0xf6, 0x81, 0xd5, 0x82, 0x36, 0x3a, 0xa5, 0xf8,
0x99, 0x37, 0xf5, 0xa6, 0x72, 0x58, 0xbf, 0x63, 0xad, 0x6f, 0x1a, 0x0b, 0x1d, 0x96, 0xdb, 0xd4,
0xfa, 0xdd, 0xfc, 0xef, 0xc5, 0x26, 0x6b, 0xa6, 0x61, 0x17, 0x22, 0x39, 0x5c, 0x90, 0x65, 0x56,
0xbe, 0x52, 0xaf, 0xe3, 0xf5, 0x65, 0x63, 0x6a, 0xd1, 0xb1, 0x7d, 0x50, 0x8b, 0x73, 0xd8, 0x74,
0x3e, 0xeb, 0x52, 0x4b, 0xe2, 0x2b, 0x3d, 0xcb, 0xc2, 0xc7, 0x46, 0x8d, 0x54, 0x11, 0x9c, 0x74,
0x68, 0x44, 0x9a, 0x13, 0xd8, 0xe3, 0xb9, 0x58, 0x11, 0xa1, 0x98, 0xf3, 0x49, 0x1d, 0xe3, 0xe7,
0xfe, 0x94, 0x2b, 0x33, 0x04, 0x07, 0xab, 0xf8, 0x2a, 0x4e, 0xd7, 0xc1, 0xb3, 0x11, 0x66, 0x3a,
0xc6, 0x98, 0x90, 0xf4, 0x15, 0x70, 0x15, 0x85, 0x3d, 0x91, 0xe9, 0x23, 0x03, 0x7c, 0x22, 0x7a,
0x33, 0xcd, 0xd5, 0xec, 0x28, 0x1c, 0xa3, 0xf7, 0x9c, 0x44, 0x54, 0x6b, 0x9d, 0x90, 0xca, 0x00,
0xf0, 0x64, 0xc9, 0x9e, 0x3d, 0xd9, 0x79, 0x11, 0xd3, 0x9f, 0xe9, 0xc5, 0xd0, 0xb2, 0x3a, 0x22,
0x9a, 0x23, 0x4c, 0xb3, 0x61, 0x86, 0xc4, 0x81, 0x9e, 0x8b, 0x9c, 0x59, 0x27, 0x72, 0x66, 0x32,
0x29, 0x1d, 0x6a, 0x41, 0x82, 0x11, 0xcc, 0x29, 0x62, 0xe2, 0x0f, 0xe4, 0x7f, 0xeb, 0x3e, 0xdf,
0x33, 0x0f, 0x2c, 0x60, 0x3a, 0x9d, 0x48, 0xc0, 0xfc, 0xb5, 0x69, 0x9d, 0xbf, 0xe5, 0x89, 0x64,
0x25, 0xc5, 0xba, 0xc4, 0xae, 0xe8, 0x2e, 0x57, 0xa8, 0x5a, 0xaf, 0x4e, 0x25, 0x13, 0xe4, 0xf0,
0x57, 0x96, 0xb0, 0x7b, 0xa2, 0xee, 0x47, 0xd8, 0x05, 0x06, 0xf8, 0xd2, 0xc2, 0x5e, 0x50, 0xfd,
0x14, 0xde, 0x71, 0xe6, 0xc4, 0x18, 0x55, 0x93, 0x02, 0xf9, 0x39, 0xb0, 0xe1, 0xab, 0xd5, 0x76,
0xf2, 0x79, 0xc4, 0xb2, 0xe0, 0xfe, 0xb8, 0x5c, 0x1f, 0x28, 0xff, 0x18, 0xf5, 0x88, 0x91, 0xff,
0xef, 0x13, 0x2e, 0xef, 0x2f, 0xa0, 0x93, 0x46, 0xae, 0xe3, 0x3c, 0x28, 0xeb, 0x13, 0x0f, 0xf2,
0x8f, 0x5b, 0x76, 0x69, 0x53, 0x33, 0x41, 0x13, 0x21, 0x19, 0x96, 0xd2, 0x00, 0x11, 0xa1, 0x98,
0xe3, 0xfc, 0x43, 0x3f, 0x9f, 0x25, 0x41, 0x01, 0x0a, 0xe1, 0x7c, 0x1b, 0xf2, 0x02, 0x58, 0x0f,
0x60, 0x47, 0x47, 0x2f, 0xb3, 0x68, 0x57, 0xfe, 0x84, 0x3b, 0x19, 0xf5, 0x98, 0x40, 0x09, 0xdd,
0xc3, 0x24, 0x04, 0x4e, 0x84, 0x7a, 0x4f, 0x4a, 0x0a, 0xb3, 0x4f, 0x71, 0x95, 0x95, 0xde, 0x37,
0x25, 0x2d, 0x62, 0x35, 0x36, 0x5e, 0x9b, 0x84, 0x39, 0x2b, 0x06, 0x10, 0x85, 0x34, 0x9d, 0x73,
0x20, 0x3a, 0x4a, 0x13, 0xe9, 0x6f, 0x54, 0x32, 0xec, 0x0f, 0xd4, 0xa1, 0xee, 0x65, 0xac, 0xcd,
0xd5, 0xe3, 0x90, 0x4d, 0xf5, 0x4c, 0x1d, 0xa5, 0x10, 0xb0, 0xff, 0x20, 0xdc, 0xc0, 0xc7, 0x7f,
0xcb, 0x2c, 0x0e, 0x0e, 0xb6, 0x05, 0xcb, 0x05, 0x04, 0xdb, 0x87, 0x63, 0x2c, 0xf3, 0xd8, 0xb4,
0xda, 0xe6, 0xe7, 0x05, 0x76, 0x9d, 0x1d, 0xe3, 0x54, 0x27, 0x01, 0x23, 0xcb, 0x11, 0x45, 0x0e,
0xfc, 0x60, 0xac, 0x47, 0x68, 0x3d, 0x7b, 0x8d, 0x0f, 0x81, 0x13, 0x65, 0x56, 0x5f, 0xd9, 0x8c,
0x4c, 0x8e, 0xb9, 0x36, 0xbc, 0xab, 0x8d, 0x06, 0x9f, 0xc3, 0x3b, 0xd8, 0x01, 0xb0, 0x3a, 0xde,
0xa2, 0xe1, 0xfb, 0xc5, 0xaa, 0x46, 0x3d, 0x08, 0xca, 0x19, 0x89, 0x6d, 0x2b, 0xf5, 0x9a, 0x07,
0x1b, 0x85, 0x1e, 0x6c, 0x23, 0x90, 0x52, 0x17, 0x2f, 0x29, 0x6b, 0xfb, 0x5e, 0x72, 0x40, 0x47,
0x90, 0xa2, 0x18, 0x10, 0x14, 0xf3, 0xb9, 0x4a, 0x4e, 0x97, 0xd1, 0x17, 0xb4, 0x38, 0x13, 0x03,
0x68, 0xcc, 0x39, 0xdb, 0xb2, 0xd1, 0x98, 0x06, 0x5a, 0xe3, 0x98, 0x65, 0x47, 0x92, 0x6c, 0xd2,
0x16, 0x2f, 0x40, 0xa2, 0x9f, 0x0c, 0x3c, 0x87, 0x45, 0xc0, 0xf5, 0x0f, 0xba, 0x38, 0x52, 0xe5,
0x66, 0xd4, 0x45, 0x75, 0xc2, 0x9d, 0x39, 0xa0, 0x3f, 0x0c, 0xda, 0x72, 0x19, 0x84, 0xb6, 0xf4,
0x40, 0x59, 0x1f, 0x35, 0x5e, 0x12, 0xd4, 0x39, 0xff, 0x15, 0x0a, 0xab, 0x76, 0x13, 0x49, 0x9d,
0xbd, 0x49, 0xad, 0xab, 0xc8, 0x67, 0x6e, 0xef, 0x02, 0x3b, 0x15, 0xb6, 0x5b, 0xfc, 0x5c, 0xa0,
0x69, 0x48, 0x10, 0x9f, 0x23, 0xf3, 0x50, 0xdb, 0x82, 0x12, 0x35, 0x35, 0xeb, 0x8a, 0x74, 0x33,
0xbd, 0xab, 0xcb, 0x90, 0x92, 0x71, 0xa6, 0xec, 0xbc, 0xb5, 0x8b, 0x93, 0x6a, 0x88, 0xcd, 0x4e,
0x8f, 0x2e, 0x6f, 0xf5, 0x80, 0x01, 0x75, 0xf1, 0x13, 0x25, 0x3d, 0x8f, 0xa9, 0xca, 0x88, 0x85,
0xc2, 0xf5, 0x52, 0xe6, 0x57, 0xdc, 0x60, 0x3f, 0x25, 0x2e, 0x1a, 0x8e, 0x30, 0x8f, 0x76, 0xf0,
0xbe, 0x79, 0xe2, 0xfb, 0x8f, 0x5d, 0x5f, 0xbb, 0xe2, 0xe3, 0x0e, 0xca, 0xdd, 0x22, 0x07, 0x23,
0xc8, 0xc0, 0xae, 0xa8, 0x07, 0x8c, 0xdf, 0xcb, 0x38, 0x68, 0x26, 0x3f, 0xf8, 0xf0, 0x94, 0x00,
0x54, 0xda, 0x48, 0x78, 0x18, 0x93, 0xa7, 0xe4, 0x9a, 0xd5, 0xaf, 0xf4, 0xaf, 0x30, 0x0c, 0xd8,
0x04, 0xa6, 0xb6, 0x27, 0x9a, 0xb3, 0xff, 0x3a, 0xfb, 0x64, 0x49, 0x1c, 0x85, 0x19, 0x4a, 0xab,
0x76, 0x0d, 0x58, 0xa6, 0x06, 0x65, 0x4f, 0x9f, 0x44, 0x00, 0xe8, 0xb3, 0x85, 0x91, 0x35, 0x6f,
0xbf, 0x64, 0x25, 0xac, 0xa2, 0x6d, 0xc8, 0x52, 0x44, 0x25, 0x9f, 0xf2, 0xb1, 0x9c, 0x41, 0xb9,
0xf9, 0x6f, 0x3c, 0xa9, 0xec, 0x1d, 0xde, 0x43, 0x4d, 0xa7, 0xd2, 0xd3, 0x92, 0xb9, 0x05, 0xdd,
0xf3, 0xd1, 0xf9, 0xaf, 0x93, 0xd1, 0xaf, 0x59, 0x50, 0xbd, 0x49, 0x3f, 0x5a, 0xa7, 0x31, 0xb4,
0x05, 0x6d, 0xf3, 0x1b, 0xd2, 0x67, 0xb6, 0xb9, 0x0a, 0x07, 0x98, 0x31, 0xaa, 0xf5, 0x79, 0xbe,
0x0a, 0x39, 0x01, 0x31, 0x37, 0xaa, 0xc6, 0xd4, 0x04, 0xf5, 0x18, 0xcf, 0xd4, 0x68, 0x40, 0x64,
0x7e, 0x78, 0xbf, 0xe7, 0x06, 0xca, 0x4c, 0xf5, 0xe9, 0xc5, 0x45, 0x3e, 0x9f, 0x7c, 0xfd, 0x2b,
0x8b, 0x4c, 0x8d, 0x16, 0x9a, 0x44, 0xe5, 0x5c, 0x88, 0xd4, 0xa9, 0xa7, 0xf9, 0x47, 0x42, 0x41,
0xe2, 0x21, 0xaf, 0x44, 0x86, 0x00, 0x18, 0xab, 0x08, 0x56, 0x97, 0x2e, 0x19, 0x4c, 0xd9, 0x34,
];
const INITIAL_PACKET_29: &str = "cdff00001d088394c8f03e5157080000\
449e9cdb990bfb66bc6a93032b50dd89\
73972d149421874d3849e3708d71354e\
a33bcdc356f3ea6e2a1a1bd7c3d14003\
8d3e784d04c30a2cdb40c32523aba2da\
fe1c1bf3d27a6be38fe38ae033fbb071\
3c1c73661bb6639795b42b97f77068ea\
d51f11fbf9489af2501d09481e6c64d4\
b8551cd3cea70d830ce2aeeec789ef55\
1a7fbe36b3f7e1549a9f8d8e153b3fac\
3fb7b7812c9ed7c20b4be190ebd89956\
26e7f0fc887925ec6f0606c5d36aa81b\
ebb7aacdc4a31bb5f23d55faef5c5190\
5783384f375a43235b5c742c78ab1bae\
0a188b75efbde6b3774ed61282f9670a\
9dea19e1566103ce675ab4e21081fb58\
60340a1e88e4f10e39eae25cd685b109\
29636d4f02e7fad2a5a458249f5c0298\
a6d53acbe41a7fc83fa7cc01973f7a74\
d1237a51974e097636b6203997f921d0\
7bc1940a6f2d0de9f5a11432946159ed\
6cc21df65c4ddd1115f86427259a196c\
7148b25b6478b0dc7766e1c4d1b1f515\
9f90eabc61636226244642ee148b464c\
9e619ee50a5e3ddc836227cad938987c\
4ea3c1fa7c75bbf88d89e9ada642b2b8\
8fe8107b7ea375b1b64889a4e9e5c38a\
1c896ce275a5658d250e2d76e1ed3a34\
ce7e3a3f383d0c996d0bed106c2899ca\
6fc263ef0455e74bb6ac1640ea7bfedc\
59f03fee0e1725ea150ff4d69a7660c5\
542119c71de270ae7c3ecfd1af2c4ce5\
51986949cc34a66b3e216bfe18b347e6\
c05fd050f85912db303a8f054ec23e38\
f44d1c725ab641ae929fecc8e3cefa56\
19df4231f5b4c009fa0c0bbc60bc75f7\
6d06ef154fc8577077d9d6a1d2bd9bf0\
81dc783ece60111bea7da9e5a9748069\
d078b2bef48de04cabe3755b197d52b3\
2046949ecaa310274b4aac0d008b1948\
c1082cdfe2083e386d4fd84c0ed0666d\
3ee26c4515c4fee73433ac703b690a9f\
7bf278a77486ace44c489a0c7ac8dfe4\
d1a58fb3a730b993ff0f0d61b4d89557\
831eb4c752ffd39c10f6b9f46d8db278\
da624fd800e4af85548a294c1518893a\
8778c4f6d6d73c93df200960104e062b\
388ea97dcf4016bced7f62b4f062cb6c\
04c20693d9a0e3b74ba8fe74cc012378\
84f40d765ae56a51688d985cf0ceaef4\
3045ed8c3f0c33bced08537f6882613a\
cd3b08d665fce9dd8aa73171e2d3771a\
61dba2790e491d413d93d987e2745af2\
9418e428be34941485c93447520ffe23\
1da2304d6a0fd5d07d08372202369661\
59bef3cf904d722324dd852513df39ae\
030d8173908da6364786d3c1bfcb19ea\
77a63b25f1e7fc661def480c5d00d444\
56269ebd84efd8e3a8b2c257eec76060\
682848cbf5194bc99e49ee75e4d0d254\
bad4bfd74970c30e44b65511d4ad0e6e\
c7398e08e01307eeeea14e46ccd87cf3\
6b285221254d8fc6a6765c524ded0085\
dca5bd688ddf722e2c0faf9d0fb2ce7a\
0c3f2cee19ca0ffba461ca8dc5d2c817\
8b0762cf67135558494d2a96f1a139f0\
edb42d2af89a9c9122b07acbc29e5e72\
2df8615c343702491098478a389c9872\
a10b0c9875125e257c7bfdf27eef4060\
bd3d00f4c14fd3e3496c38d3c5d1a566\
8c39350effbc2d16ca17be4ce29f02ed\
969504dda2a8c6b9ff919e693ee79e09\
089316e7d1d89ec099db3b2b268725d8\
88536a4b8bf9aee8fb43e82a4d919d48\
1802771a449b30f3fa2289852607b660";
const INITIAL_PACKET_27: &[u8] = &[
0xc0, 0xff, 0x00, 0x00, 0x1b, 0x08, 0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08, 0x00, 0x00,
0x44, 0x9e, 0x3b, 0x34, 0x3a, 0xa8, 0x53, 0x50, 0x64, 0xa4, 0x26, 0x8a, 0x0d, 0x9d, 0x7b, 0x1c,
0x9d, 0x25, 0x0a, 0xe3, 0x55, 0x16, 0x22, 0x76, 0xe9, 0xb1, 0xe3, 0x01, 0x1e, 0xf6, 0xbb, 0xc0,
0xab, 0x48, 0xad, 0x5b, 0xcc, 0x26, 0x81, 0xe9, 0x53, 0x85, 0x7c, 0xa6, 0x2b, 0xec, 0xd7, 0x52,
0x4d, 0xaa, 0xc4, 0x73, 0xe6, 0x8d, 0x74, 0x05, 0xfb, 0xba, 0x4e, 0x9e, 0xe6, 0x16, 0xc8, 0x70,
0x38, 0xbd, 0xbe, 0x90, 0x8c, 0x06, 0xd9, 0x60, 0x5d, 0x9a, 0xc4, 0x90, 0x30, 0x35, 0x9e, 0xec,
0xb1, 0xd0, 0x5a, 0x14, 0xe1, 0x17, 0xdb, 0x8c, 0xed, 0xe2, 0xbb, 0x09, 0xd0, 0xdb, 0xbf, 0xee,
0x27, 0x1c, 0xb3, 0x74, 0xd8, 0xf1, 0x0a, 0xbe, 0xc8, 0x2d, 0x0f, 0x59, 0xa1, 0xde, 0xe2, 0x9f,
0xe9, 0x56, 0x38, 0xed, 0x8d, 0xd4, 0x1d, 0xa0, 0x74, 0x87, 0x46, 0x87, 0x91, 0xb7, 0x19, 0xc5,
0x5c, 0x46, 0x96, 0x8e, 0xb3, 0xb5, 0x46, 0x80, 0x03, 0x71, 0x02, 0xa2, 0x8e, 0x53, 0xdc, 0x1d,
0x12, 0x90, 0x3d, 0xb0, 0xaf, 0x58, 0x21, 0x79, 0x4b, 0x41, 0xc4, 0xa9, 0x33, 0x57, 0xfa, 0x59,
0xce, 0x69, 0xcf, 0xe7, 0xf6, 0xbd, 0xfa, 0x62, 0x9e, 0xef, 0x78, 0x61, 0x64, 0x47, 0xe1, 0xd6,
0x11, 0xc4, 0xba, 0xf7, 0x1b, 0xf3, 0x3f, 0xeb, 0xcb, 0x03, 0x13, 0x7c, 0x2c, 0x75, 0xd2, 0x53,
0x17, 0xd3, 0xe1, 0x3b, 0x68, 0x43, 0x70, 0xf6, 0x68, 0x41, 0x1c, 0x0f, 0x00, 0x30, 0x4b, 0x50,
0x1c, 0x8f, 0xd4, 0x22, 0xbd, 0x9b, 0x9a, 0xd8, 0x1d, 0x64, 0x3b, 0x20, 0xda, 0x89, 0xca, 0x05,
0x25, 0xd2, 0x4d, 0x2b, 0x14, 0x20, 0x41, 0xca, 0xe0, 0xaf, 0x20, 0x50, 0x92, 0xe4, 0x30, 0x08,
0x0c, 0xd8, 0x55, 0x9e, 0xa4, 0xc5, 0xc6, 0xe4, 0xfa, 0x3f, 0x66, 0x08, 0x2b, 0x7d, 0x30, 0x3e,
0x52, 0xce, 0x01, 0x62, 0xba, 0xa9, 0x58, 0x53, 0x2b, 0x0b, 0xbc, 0x2b, 0xc7, 0x85, 0x68, 0x1f,
0xcf, 0x37, 0x48, 0x5d, 0xff, 0x65, 0x95, 0xe0, 0x1e, 0x73, 0x9c, 0x8a, 0xc9, 0xef, 0xba, 0x31,
0xb9, 0x85, 0xd5, 0xf6, 0x56, 0xcc, 0x09, 0x24, 0x32, 0xd7, 0x81, 0xdb, 0x95, 0x22, 0x17, 0x24,
0x87, 0x64, 0x1c, 0x4d, 0x3a, 0xb8, 0xec, 0xe0, 0x1e, 0x39, 0xbc, 0x85, 0xb1, 0x54, 0x36, 0x61,
0x47, 0x75, 0xa9, 0x8b, 0xa8, 0xfa, 0x12, 0xd4, 0x6f, 0x9b, 0x35, 0xe2, 0xa5, 0x5e, 0xb7, 0x2d,
0x7f, 0x85, 0x18, 0x1a, 0x36, 0x66, 0x63, 0x38, 0x7d, 0xdc, 0x20, 0x55, 0x18, 0x07, 0xe0, 0x07,
0x67, 0x3b, 0xd7, 0xe2, 0x6b, 0xf9, 0xb2, 0x9b, 0x5a, 0xb1, 0x0a, 0x1c, 0xa8, 0x7c, 0xbb, 0x7a,
0xd9, 0x7e, 0x99, 0xeb, 0x66, 0x95, 0x9c, 0x2a, 0x9b, 0xc3, 0xcb, 0xde, 0x47, 0x07, 0xff, 0x77,
0x20, 0xb1, 0x10, 0xfa, 0x95, 0x35, 0x46, 0x74, 0xe3, 0x95, 0x81, 0x2e, 0x47, 0xa0, 0xae, 0x53,
0xb4, 0x64, 0xdc, 0xb2, 0xd1, 0xf3, 0x45, 0xdf, 0x36, 0x0d, 0xc2, 0x27, 0x27, 0x0c, 0x75, 0x06,
0x76, 0xf6, 0x72, 0x4e, 0xb4, 0x79, 0xf0, 0xd2, 0xfb, 0xb6, 0x12, 0x44, 0x29, 0x99, 0x04, 0x57,
0xac, 0x6c, 0x91, 0x67, 0xf4, 0x0a, 0xab, 0x73, 0x99, 0x98, 0xf3, 0x8b, 0x9e, 0xcc, 0xb2, 0x4f,
0xd4, 0x7c, 0x84, 0x10, 0x13, 0x1b, 0xf6, 0x5a, 0x52, 0xaf, 0x84, 0x12, 0x75, 0xd5, 0xb3, 0xd1,
0x88, 0x0b, 0x19, 0x7d, 0xf2, 0xb5, 0xde, 0xa3, 0xe6, 0xde, 0x56, 0xeb, 0xce, 0x3f, 0xfb, 0x6e,
0x92, 0x77, 0xa8, 0x20, 0x82, 0xf8, 0xd9, 0x67, 0x7a, 0x67, 0x67, 0x08, 0x9b, 0x67, 0x1e, 0xbd,
0x24, 0x4c, 0x21, 0x4f, 0x0b, 0xde, 0x95, 0xc2, 0xbe, 0xb0, 0x2c, 0xd1, 0x17, 0x2d, 0x58, 0xbd,
0xf3, 0x9d, 0xce, 0x56, 0xff, 0x68, 0xeb, 0x35, 0xab, 0x39, 0xb4, 0x9b, 0x4e, 0xac, 0x7c, 0x81,
0x5e, 0xa6, 0x04, 0x51, 0xd6, 0xe6, 0xab, 0x82, 0x11, 0x91, 0x18, 0xdf, 0x02, 0xa5, 0x86, 0x84,
0x4a, 0x9f, 0xfe, 0x16, 0x2b, 0xa0, 0x06, 0xd0, 0x66, 0x9e, 0xf5, 0x76, 0x68, 0xca, 0xb3, 0x8b,
0x62, 0xf7, 0x1a, 0x25, 0x23, 0xa0, 0x84, 0x85, 0x2c, 0xd1, 0xd0, 0x79, 0xb3, 0x65, 0x8d, 0xc2,
0xf3, 0xe8, 0x79, 0x49, 0xb5, 0x50, 0xba, 0xb3, 0xe1, 0x77, 0xcf, 0xc4, 0x9e, 0xd1, 0x90, 0xdf,
0xf0, 0x63, 0x0e, 0x43, 0x07, 0x7c, 0x30, 0xde, 0x8f, 0x6a, 0xe0, 0x81, 0x53, 0x7f, 0x1e, 0x83,
0xda, 0x53, 0x7d, 0xa9, 0x80, 0xaf, 0xa6, 0x68, 0xe7, 0xb7, 0xfb, 0x25, 0x30, 0x1c, 0xf7, 0x41,
0x52, 0x4b, 0xe3, 0xc4, 0x98, 0x84, 0xb4, 0x28, 0x21, 0xf1, 0x75, 0x52, 0xfb, 0xd1, 0x93, 0x1a,
0x81, 0x30, 0x17, 0xb6, 0xb6, 0x59, 0x0a, 0x41, 0xea, 0x18, 0xb6, 0xba, 0x49, 0xcd, 0x48, 0xa4,
0x40, 0xbd, 0x9a, 0x33, 0x46, 0xa7, 0x62, 0x3f, 0xb4, 0xba, 0x34, 0xa3, 0xee, 0x57, 0x1e, 0x3c,
0x73, 0x1f, 0x35, 0xa7, 0xa3, 0xcf, 0x25, 0xb5, 0x51, 0xa6, 0x80, 0xfa, 0x68, 0x76, 0x35, 0x07,
0xb7, 0xfd, 0xe3, 0xaa, 0xf0, 0x23, 0xc5, 0x0b, 0x9d, 0x22, 0xda, 0x68, 0x76, 0xba, 0x33, 0x7e,
0xb5, 0xe9, 0xdd, 0x9e, 0xc3, 0xda, 0xf9, 0x70, 0x24, 0x2b, 0x6c, 0x5a, 0xab, 0x3a, 0xa4, 0xb2,
0x96, 0xad, 0x8b, 0x9f, 0x68, 0x32, 0xf6, 0x86, 0xef, 0x70, 0xfa, 0x93, 0x8b, 0x31, 0xb4, 0xe5,
0xdd, 0xd7, 0x36, 0x44, 0x42, 0xd3, 0xea, 0x72, 0xe7, 0x3d, 0x66, 0x8f, 0xb0, 0x93, 0x77, 0x96,
0xf4, 0x62, 0x92, 0x3a, 0x81, 0xa4, 0x7e, 0x1c, 0xee, 0x74, 0x26, 0xff, 0x6d, 0x92, 0x21, 0x26,
0x9b, 0x5a, 0x62, 0xec, 0x03, 0xd6, 0xec, 0x94, 0xd1, 0x26, 0x06, 0xcb, 0x48, 0x55, 0x60, 0xba,
0xb5, 0x74, 0x81, 0x60, 0x09, 0xe9, 0x65, 0x04, 0x24, 0x93, 0x85, 0xbb, 0x61, 0xa8, 0x19, 0xbe,
0x04, 0xf6, 0x2c, 0x20, 0x66, 0x21, 0x4d, 0x83, 0x60, 0xa2, 0x02, 0x2b, 0xeb, 0x31, 0x62, 0x40,
0xb6, 0xc7, 0xd7, 0x8b, 0xbe, 0x56, 0xc1, 0x30, 0x82, 0xe0, 0xca, 0x27, 0x26, 0x61, 0x21, 0x0a,
0xbf, 0x02, 0x0b, 0xf3, 0xb5, 0x78, 0x3f, 0x14, 0x26, 0x43, 0x6c, 0xf9, 0xff, 0x41, 0x84, 0x05,
0x93, 0xa5, 0xd0, 0x63, 0x8d, 0x32, 0xfc, 0x51, 0xc5, 0xc6, 0x5f, 0xf2, 0x91, 0xa3, 0xa7, 0xa5,
0x2f, 0xd6, 0x77, 0x5e, 0x62, 0x3a, 0x44, 0x39, 0xcc, 0x08, 0xdd, 0x25, 0x58, 0x2f, 0xeb, 0xc9,
0x44, 0xef, 0x92, 0xd8, 0xdb, 0xd3, 0x29, 0xc9, 0x1d, 0xe3, 0xe9, 0xc9, 0x58, 0x2e, 0x41, 0xf1,
0x7f, 0x3d, 0x18, 0x6f, 0x10, 0x4a, 0xd3, 0xf9, 0x09, 0x95, 0x11, 0x6c, 0x68, 0x2a, 0x2a, 0x14,
0xa3, 0xb4, 0xb1, 0xf5, 0x47, 0xc3, 0x35, 0xf0, 0xbe, 0x71, 0x0f, 0xc9, 0xfc, 0x03, 0xe0, 0xe5,
0x87, 0xb8, 0xcd, 0xa3, 0x1c, 0xe6, 0x5b, 0x96, 0x98, 0x78, 0xa4, 0xad, 0x42, 0x83, 0xe6, 0xd5,
0xb0, 0x37, 0x3f, 0x43, 0xda, 0x86, 0xe9, 0xe0, 0xff, 0xe1, 0xae, 0x0f, 0xdd, 0xd3, 0x51, 0x62,
0x55, 0xbd, 0x74, 0x56, 0x6f, 0x36, 0xa3, 0x87, 0x03, 0xd5, 0xf3, 0x42, 0x49, 0xde, 0xd1, 0xf6,
0x6b, 0x3d, 0x9b, 0x45, 0xb9, 0xaf, 0x2c, 0xcf, 0xef, 0xe9, 0x84, 0xe1, 0x33, 0x76, 0xb1, 0xb2,
0xc6, 0x40, 0x4a, 0xa4, 0x8c, 0x80, 0x26, 0x13, 0x23, 0x43, 0xda, 0x3f, 0x3a, 0x33, 0x65, 0x9e,
0xc1, 0xb3, 0xe9, 0x50, 0x80, 0x54, 0x0b, 0x28, 0xb7, 0xf3, 0xfc, 0xd3, 0x5f, 0xa5, 0xd8, 0x43,
0xb5, 0x79, 0xa8, 0x4c, 0x08, 0x91, 0x21, 0xa6, 0x0d, 0x8c, 0x17, 0x54, 0x91, 0x5c, 0x34, 0x4e,
0xea, 0xf4, 0x5a, 0x9b, 0xf2, 0x7d, 0xc0, 0xc1, 0xe7, 0x84, 0x16, 0x16, 0x91, 0x22, 0x09, 0x13,
0x13, 0xeb, 0x0e, 0x87, 0x55, 0x5a, 0xbd, 0x70, 0x66, 0x26, 0xe5, 0x57, 0xfc, 0x36, 0xa0, 0x4f,
0xcd, 0x19, 0x1a, 0x58, 0x82, 0x91, 0x04, 0xd6, 0x07, 0x5c, 0x55, 0x94, 0xf6, 0x27, 0xca, 0x50,
0x6b, 0xf1, 0x81, 0xda, 0xec, 0x94, 0x0f, 0x4a, 0x4f, 0x3a, 0xf0, 0x07, 0x4e, 0xee, 0x89, 0xda,
0xac, 0xde, 0x67, 0x58, 0x31, 0x26, 0x22, 0xd4, 0xfa, 0x67, 0x5b, 0x39, 0xf7, 0x28, 0xe0, 0x62,
0xd2, 0xbe, 0xe6, 0x80, 0xd8, 0xf4, 0x1a, 0x59, 0x7c, 0x26, 0x26, 0x48, 0xbb, 0x18, 0xbc, 0xfc,
0x13, 0xc8, 0xb3, 0xd9, 0x7b, 0x1a, 0x77, 0xb2, 0xac, 0x3a, 0xf7, 0x45, 0xd6, 0x1a, 0x34, 0xcc,
0x47, 0x09, 0x86, 0x5b, 0xac, 0x82, 0x4a, 0x94, 0xbb, 0x19, 0x05, 0x80, 0x15, 0xe4, 0xe4, 0x2d,
0x38, 0xd3, 0xb7, 0x79, 0xd7, 0x2e, 0xdc, 0x00, 0xc5, 0xcd, 0x08, 0x8e, 0xff, 0x80, 0x2b, 0x05,
];
const INITIAL_PACKET_29: &[u8] = &[
0xcd, 0xff, 0x00, 0x00, 0x1d, 0x08, 0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08, 0x00, 0x00,
0x44, 0x9e, 0x9c, 0xdb, 0x99, 0x0b, 0xfb, 0x66, 0xbc, 0x6a, 0x93, 0x03, 0x2b, 0x50, 0xdd, 0x89,
0x73, 0x97, 0x2d, 0x14, 0x94, 0x21, 0x87, 0x4d, 0x38, 0x49, 0xe3, 0x70, 0x8d, 0x71, 0x35, 0x4e,
0xa3, 0x3b, 0xcd, 0xc3, 0x56, 0xf3, 0xea, 0x6e, 0x2a, 0x1a, 0x1b, 0xd7, 0xc3, 0xd1, 0x40, 0x03,
0x8d, 0x3e, 0x78, 0x4d, 0x04, 0xc3, 0x0a, 0x2c, 0xdb, 0x40, 0xc3, 0x25, 0x23, 0xab, 0xa2, 0xda,
0xfe, 0x1c, 0x1b, 0xf3, 0xd2, 0x7a, 0x6b, 0xe3, 0x8f, 0xe3, 0x8a, 0xe0, 0x33, 0xfb, 0xb0, 0x71,
0x3c, 0x1c, 0x73, 0x66, 0x1b, 0xb6, 0x63, 0x97, 0x95, 0xb4, 0x2b, 0x97, 0xf7, 0x70, 0x68, 0xea,
0xd5, 0x1f, 0x11, 0xfb, 0xf9, 0x48, 0x9a, 0xf2, 0x50, 0x1d, 0x09, 0x48, 0x1e, 0x6c, 0x64, 0xd4,
0xb8, 0x55, 0x1c, 0xd3, 0xce, 0xa7, 0x0d, 0x83, 0x0c, 0xe2, 0xae, 0xee, 0xc7, 0x89, 0xef, 0x55,
0x1a, 0x7f, 0xbe, 0x36, 0xb3, 0xf7, 0xe1, 0x54, 0x9a, 0x9f, 0x8d, 0x8e, 0x15, 0x3b, 0x3f, 0xac,
0x3f, 0xb7, 0xb7, 0x81, 0x2c, 0x9e, 0xd7, 0xc2, 0x0b, 0x4b, 0xe1, 0x90, 0xeb, 0xd8, 0x99, 0x56,
0x26, 0xe7, 0xf0, 0xfc, 0x88, 0x79, 0x25, 0xec, 0x6f, 0x06, 0x06, 0xc5, 0xd3, 0x6a, 0xa8, 0x1b,
0xeb, 0xb7, 0xaa, 0xcd, 0xc4, 0xa3, 0x1b, 0xb5, 0xf2, 0x3d, 0x55, 0xfa, 0xef, 0x5c, 0x51, 0x90,
0x57, 0x83, 0x38, 0x4f, 0x37, 0x5a, 0x43, 0x23, 0x5b, 0x5c, 0x74, 0x2c, 0x78, 0xab, 0x1b, 0xae,
0x0a, 0x18, 0x8b, 0x75, 0xef, 0xbd, 0xe6, 0xb3, 0x77, 0x4e, 0xd6, 0x12, 0x82, 0xf9, 0x67, 0x0a,
0x9d, 0xea, 0x19, 0xe1, 0x56, 0x61, 0x03, 0xce, 0x67, 0x5a, 0xb4, 0xe2, 0x10, 0x81, 0xfb, 0x58,
0x60, 0x34, 0x0a, 0x1e, 0x88, 0xe4, 0xf1, 0x0e, 0x39, 0xea, 0xe2, 0x5c, 0xd6, 0x85, 0xb1, 0x09,
0x29, 0x63, 0x6d, 0x4f, 0x02, 0xe7, 0xfa, 0xd2, 0xa5, 0xa4, 0x58, 0x24, 0x9f, 0x5c, 0x02, 0x98,
0xa6, 0xd5, 0x3a, 0xcb, 0xe4, 0x1a, 0x7f, 0xc8, 0x3f, 0xa7, 0xcc, 0x01, 0x97, 0x3f, 0x7a, 0x74,
0xd1, 0x23, 0x7a, 0x51, 0x97, 0x4e, 0x09, 0x76, 0x36, 0xb6, 0x20, 0x39, 0x97, 0xf9, 0x21, 0xd0,
0x7b, 0xc1, 0x94, 0x0a, 0x6f, 0x2d, 0x0d, 0xe9, 0xf5, 0xa1, 0x14, 0x32, 0x94, 0x61, 0x59, 0xed,
0x6c, 0xc2, 0x1d, 0xf6, 0x5c, 0x4d, 0xdd, 0x11, 0x15, 0xf8, 0x64, 0x27, 0x25, 0x9a, 0x19, 0x6c,
0x71, 0x48, 0xb2, 0x5b, 0x64, 0x78, 0xb0, 0xdc, 0x77, 0x66, 0xe1, 0xc4, 0xd1, 0xb1, 0xf5, 0x15,
0x9f, 0x90, 0xea, 0xbc, 0x61, 0x63, 0x62, 0x26, 0x24, 0x46, 0x42, 0xee, 0x14, 0x8b, 0x46, 0x4c,
0x9e, 0x61, 0x9e, 0xe5, 0x0a, 0x5e, 0x3d, 0xdc, 0x83, 0x62, 0x27, 0xca, 0xd9, 0x38, 0x98, 0x7c,
0x4e, 0xa3, 0xc1, 0xfa, 0x7c, 0x75, 0xbb, 0xf8, 0x8d, 0x89, 0xe9, 0xad, 0xa6, 0x42, 0xb2, 0xb8,
0x8f, 0xe8, 0x10, 0x7b, 0x7e, 0xa3, 0x75, 0xb1, 0xb6, 0x48, 0x89, 0xa4, 0xe9, 0xe5, 0xc3, 0x8a,
0x1c, 0x89, 0x6c, 0xe2, 0x75, 0xa5, 0x65, 0x8d, 0x25, 0x0e, 0x2d, 0x76, 0xe1, 0xed, 0x3a, 0x34,
0xce, 0x7e, 0x3a, 0x3f, 0x38, 0x3d, 0x0c, 0x99, 0x6d, 0x0b, 0xed, 0x10, 0x6c, 0x28, 0x99, 0xca,
0x6f, 0xc2, 0x63, 0xef, 0x04, 0x55, 0xe7, 0x4b, 0xb6, 0xac, 0x16, 0x40, 0xea, 0x7b, 0xfe, 0xdc,
0x59, 0xf0, 0x3f, 0xee, 0x0e, 0x17, 0x25, 0xea, 0x15, 0x0f, 0xf4, 0xd6, 0x9a, 0x76, 0x60, 0xc5,
0x54, 0x21, 0x19, 0xc7, 0x1d, 0xe2, 0x70, 0xae, 0x7c, 0x3e, 0xcf, 0xd1, 0xaf, 0x2c, 0x4c, 0xe5,
0x51, 0x98, 0x69, 0x49, 0xcc, 0x34, 0xa6, 0x6b, 0x3e, 0x21, 0x6b, 0xfe, 0x18, 0xb3, 0x47, 0xe6,
0xc0, 0x5f, 0xd0, 0x50, 0xf8, 0x59, 0x12, 0xdb, 0x30, 0x3a, 0x8f, 0x05, 0x4e, 0xc2, 0x3e, 0x38,
0xf4, 0x4d, 0x1c, 0x72, 0x5a, 0xb6, 0x41, 0xae, 0x92, 0x9f, 0xec, 0xc8, 0xe3, 0xce, 0xfa, 0x56,
0x19, 0xdf, 0x42, 0x31, 0xf5, 0xb4, 0xc0, 0x09, 0xfa, 0x0c, 0x0b, 0xbc, 0x60, 0xbc, 0x75, 0xf7,
0x6d, 0x06, 0xef, 0x15, 0x4f, 0xc8, 0x57, 0x70, 0x77, 0xd9, 0xd6, 0xa1, 0xd2, 0xbd, 0x9b, 0xf0,
0x81, 0xdc, 0x78, 0x3e, 0xce, 0x60, 0x11, 0x1b, 0xea, 0x7d, 0xa9, 0xe5, 0xa9, 0x74, 0x80, 0x69,
0xd0, 0x78, 0xb2, 0xbe, 0xf4, 0x8d, 0xe0, 0x4c, 0xab, 0xe3, 0x75, 0x5b, 0x19, 0x7d, 0x52, 0xb3,
0x20, 0x46, 0x94, 0x9e, 0xca, 0xa3, 0x10, 0x27, 0x4b, 0x4a, 0xac, 0x0d, 0x00, 0x8b, 0x19, 0x48,
0xc1, 0x08, 0x2c, 0xdf, 0xe2, 0x08, 0x3e, 0x38, 0x6d, 0x4f, 0xd8, 0x4c, 0x0e, 0xd0, 0x66, 0x6d,
0x3e, 0xe2, 0x6c, 0x45, 0x15, 0xc4, 0xfe, 0xe7, 0x34, 0x33, 0xac, 0x70, 0x3b, 0x69, 0x0a, 0x9f,
0x7b, 0xf2, 0x78, 0xa7, 0x74, 0x86, 0xac, 0xe4, 0x4c, 0x48, 0x9a, 0x0c, 0x7a, 0xc8, 0xdf, 0xe4,
0xd1, 0xa5, 0x8f, 0xb3, 0xa7, 0x30, 0xb9, 0x93, 0xff, 0x0f, 0x0d, 0x61, 0xb4, 0xd8, 0x95, 0x57,
0x83, 0x1e, 0xb4, 0xc7, 0x52, 0xff, 0xd3, 0x9c, 0x10, 0xf6, 0xb9, 0xf4, 0x6d, 0x8d, 0xb2, 0x78,
0xda, 0x62, 0x4f, 0xd8, 0x00, 0xe4, 0xaf, 0x85, 0x54, 0x8a, 0x29, 0x4c, 0x15, 0x18, 0x89, 0x3a,
0x87, 0x78, 0xc4, 0xf6, 0xd6, 0xd7, 0x3c, 0x93, 0xdf, 0x20, 0x09, 0x60, 0x10, 0x4e, 0x06, 0x2b,
0x38, 0x8e, 0xa9, 0x7d, 0xcf, 0x40, 0x16, 0xbc, 0xed, 0x7f, 0x62, 0xb4, 0xf0, 0x62, 0xcb, 0x6c,
0x04, 0xc2, 0x06, 0x93, 0xd9, 0xa0, 0xe3, 0xb7, 0x4b, 0xa8, 0xfe, 0x74, 0xcc, 0x01, 0x23, 0x78,
0x84, 0xf4, 0x0d, 0x76, 0x5a, 0xe5, 0x6a, 0x51, 0x68, 0x8d, 0x98, 0x5c, 0xf0, 0xce, 0xae, 0xf4,
0x30, 0x45, 0xed, 0x8c, 0x3f, 0x0c, 0x33, 0xbc, 0xed, 0x08, 0x53, 0x7f, 0x68, 0x82, 0x61, 0x3a,
0xcd, 0x3b, 0x08, 0xd6, 0x65, 0xfc, 0xe9, 0xdd, 0x8a, 0xa7, 0x31, 0x71, 0xe2, 0xd3, 0x77, 0x1a,
0x61, 0xdb, 0xa2, 0x79, 0x0e, 0x49, 0x1d, 0x41, 0x3d, 0x93, 0xd9, 0x87, 0xe2, 0x74, 0x5a, 0xf2,
0x94, 0x18, 0xe4, 0x28, 0xbe, 0x34, 0x94, 0x14, 0x85, 0xc9, 0x34, 0x47, 0x52, 0x0f, 0xfe, 0x23,
0x1d, 0xa2, 0x30, 0x4d, 0x6a, 0x0f, 0xd5, 0xd0, 0x7d, 0x08, 0x37, 0x22, 0x02, 0x36, 0x96, 0x61,
0x59, 0xbe, 0xf3, 0xcf, 0x90, 0x4d, 0x72, 0x23, 0x24, 0xdd, 0x85, 0x25, 0x13, 0xdf, 0x39, 0xae,
0x03, 0x0d, 0x81, 0x73, 0x90, 0x8d, 0xa6, 0x36, 0x47, 0x86, 0xd3, 0xc1, 0xbf, 0xcb, 0x19, 0xea,
0x77, 0xa6, 0x3b, 0x25, 0xf1, 0xe7, 0xfc, 0x66, 0x1d, 0xef, 0x48, 0x0c, 0x5d, 0x00, 0xd4, 0x44,
0x56, 0x26, 0x9e, 0xbd, 0x84, 0xef, 0xd8, 0xe3, 0xa8, 0xb2, 0xc2, 0x57, 0xee, 0xc7, 0x60, 0x60,
0x68, 0x28, 0x48, 0xcb, 0xf5, 0x19, 0x4b, 0xc9, 0x9e, 0x49, 0xee, 0x75, 0xe4, 0xd0, 0xd2, 0x54,
0xba, 0xd4, 0xbf, 0xd7, 0x49, 0x70, 0xc3, 0x0e, 0x44, 0xb6, 0x55, 0x11, 0xd4, 0xad, 0x0e, 0x6e,
0xc7, 0x39, 0x8e, 0x08, 0xe0, 0x13, 0x07, 0xee, 0xee, 0xa1, 0x4e, 0x46, 0xcc, 0xd8, 0x7c, 0xf3,
0x6b, 0x28, 0x52, 0x21, 0x25, 0x4d, 0x8f, 0xc6, 0xa6, 0x76, 0x5c, 0x52, 0x4d, 0xed, 0x00, 0x85,
0xdc, 0xa5, 0xbd, 0x68, 0x8d, 0xdf, 0x72, 0x2e, 0x2c, 0x0f, 0xaf, 0x9d, 0x0f, 0xb2, 0xce, 0x7a,
0x0c, 0x3f, 0x2c, 0xee, 0x19, 0xca, 0x0f, 0xfb, 0xa4, 0x61, 0xca, 0x8d, 0xc5, 0xd2, 0xc8, 0x17,
0x8b, 0x07, 0x62, 0xcf, 0x67, 0x13, 0x55, 0x58, 0x49, 0x4d, 0x2a, 0x96, 0xf1, 0xa1, 0x39, 0xf0,
0xed, 0xb4, 0x2d, 0x2a, 0xf8, 0x9a, 0x9c, 0x91, 0x22, 0xb0, 0x7a, 0xcb, 0xc2, 0x9e, 0x5e, 0x72,
0x2d, 0xf8, 0x61, 0x5c, 0x34, 0x37, 0x02, 0x49, 0x10, 0x98, 0x47, 0x8a, 0x38, 0x9c, 0x98, 0x72,
0xa1, 0x0b, 0x0c, 0x98, 0x75, 0x12, 0x5e, 0x25, 0x7c, 0x7b, 0xfd, 0xf2, 0x7e, 0xef, 0x40, 0x60,
0xbd, 0x3d, 0x00, 0xf4, 0xc1, 0x4f, 0xd3, 0xe3, 0x49, 0x6c, 0x38, 0xd3, 0xc5, 0xd1, 0xa5, 0x66,
0x8c, 0x39, 0x35, 0x0e, 0xff, 0xbc, 0x2d, 0x16, 0xca, 0x17, 0xbe, 0x4c, 0xe2, 0x9f, 0x02, 0xed,
0x96, 0x95, 0x04, 0xdd, 0xa2, 0xa8, 0xc6, 0xb9, 0xff, 0x91, 0x9e, 0x69, 0x3e, 0xe7, 0x9e, 0x09,
0x08, 0x93, 0x16, 0xe7, 0xd1, 0xd8, 0x9e, 0xc0, 0x99, 0xdb, 0x3b, 0x2b, 0x26, 0x87, 0x25, 0xd8,
0x88, 0x53, 0x6a, 0x4b, 0x8b, 0xf9, 0xae, 0xe8, 0xfb, 0x43, 0xe8, 0x2a, 0x4d, 0x91, 0x9d, 0x48,
0x18, 0x02, 0x77, 0x1a, 0x44, 0x9b, 0x30, 0xf3, 0xfa, 0x22, 0x89, 0x85, 0x26, 0x07, 0xb6, 0x60,
];
fn make_server(quic_version: QuicVersion) -> Connection {
test_fixture::fixture_init();
@ -179,23 +261,27 @@ fn make_server(quic_version: QuicVersion) -> Connection {
.expect("create a default server")
}
fn process_client_initial(quic_version: QuicVersion, packet: &str) {
fn process_client_initial(quic_version: QuicVersion, packet: &[u8]) {
let mut server = make_server(quic_version);
let pkt: Vec<u8> = Encoder::from_hex(packet).into();
let dgram = Datagram::new(addr(), addr(), pkt);
let dgram = Datagram::new(addr(), addr(), packet);
assert_eq!(*server.state(), State::Init);
let out = server.process(Some(dgram), now());
assert_eq!(*server.state(), State::Handshaking);
assert!(out.dgram().is_some());
}
#[test]
fn process_client_initial_v1() {
process_client_initial(QuicVersion::Version1, INITIAL_PACKET_V1);
}
#[test]
fn process_client_initial_27() {
process_client_initial(QuicVersion::Draft27, &INITIAL_PACKET_27);
process_client_initial(QuicVersion::Draft27, INITIAL_PACKET_27);
}
#[test]
fn process_client_initial_29() {
process_client_initial(QuicVersion::Draft29, &INITIAL_PACKET_29);
process_client_initial(QuicVersion::Draft29, INITIAL_PACKET_29);
}

View File

@ -98,7 +98,12 @@ fn complete_connection(
server: &mut Server,
mut datagram: Option<Datagram>,
) -> ActiveConnectionRef {
let is_done = |c: &Connection| matches!(c.state(), State::Confirmed | State::Closing { .. } | State::Closed(..));
let is_done = |c: &Connection| {
matches!(
c.state(),
State::Confirmed | State::Closing { .. } | State::Closed(..)
)
};
while !is_done(client) {
let _ = test_fixture::maybe_authenticate(client);
let out = client.process(datagram, now());
@ -695,8 +700,8 @@ fn vn_after_retry() {
// Generate an AEAD and header protection object for a client Initial.
fn client_initial_aead_and_hp(dcid: &[u8]) -> (Aead, HpKey) {
const INITIAL_SALT: &[u8] = &[
0xaf, 0xbf, 0xec, 0x28, 0x99, 0x93, 0xd2, 0x4c, 0x9e, 0x97, 0x86, 0xf1, 0x9c, 0x61, 0x11,
0xe0, 0x43, 0x90, 0xa8, 0x99,
0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17, 0x9a, 0xe6, 0xa4, 0xc8, 0x0c,
0xad, 0xcc, 0xbb, 0x7f, 0x0a,
];
let initial_secret = hkdf::extract(
TLS_VERSION_1_3,
@ -881,7 +886,7 @@ fn mitm_retry() {
assert!(dgram.is_some()); // Client sending CLOSE_CONNECTIONs
assert!(matches!(
*client.state(),
State::Closing{
State::Closing {
error: ConnectionError::Transport(Error::ProtocolViolation),
..
}