Backed out 2 changesets (bug 1687787) for causing xpcshell failures in netwerk/test/unit/test_http3_large_post. CLOSED TREE

Backed out changeset 5ca2c2f951ce (bug 1687787)
Backed out changeset 48f23619ddb8 (bug 1687787)
This commit is contained in:
smolnar 2021-01-21 15:33:28 +02:00
Родитель 87d8f32132
Коммит 24d0effbb0
65 изменённых файлов: 1433 добавлений и 5097 удалений

Просмотреть файл

@ -25,7 +25,7 @@ rev = "4ea371049a9cca212cc13c19b7952c1c014085c6"
[source."https://github.com/mozilla/neqo"]
git = "https://github.com/mozilla/neqo"
replace-with = "vendored-sources"
tag = "v0.4.20"
tag = "v0.4.19"
[source."https://github.com/mozilla/mp4parse-rust"]
git = "https://github.com/mozilla/mp4parse-rust"

20
Cargo.lock сгенерированный
Просмотреть файл

@ -3392,8 +3392,8 @@ dependencies = [
[[package]]
name = "neqo-common"
version = "0.4.20"
source = "git+https://github.com/mozilla/neqo?tag=v0.4.20#be938c6156e727428668379e7c038206183a6696"
version = "0.4.19"
source = "git+https://github.com/mozilla/neqo?tag=v0.4.19#7bed96666f977cfaef4288ebb792273e0ca0f41c"
dependencies = [
"chrono",
"env_logger 0.8.2",
@ -3404,8 +3404,8 @@ dependencies = [
[[package]]
name = "neqo-crypto"
version = "0.4.20"
source = "git+https://github.com/mozilla/neqo?tag=v0.4.20#be938c6156e727428668379e7c038206183a6696"
version = "0.4.19"
source = "git+https://github.com/mozilla/neqo?tag=v0.4.19#7bed96666f977cfaef4288ebb792273e0ca0f41c"
dependencies = [
"bindgen",
"log",
@ -3417,8 +3417,8 @@ dependencies = [
[[package]]
name = "neqo-http3"
version = "0.4.20"
source = "git+https://github.com/mozilla/neqo?tag=v0.4.20#be938c6156e727428668379e7c038206183a6696"
version = "0.4.19"
source = "git+https://github.com/mozilla/neqo?tag=v0.4.19#7bed96666f977cfaef4288ebb792273e0ca0f41c"
dependencies = [
"log",
"neqo-common",
@ -3431,8 +3431,8 @@ dependencies = [
[[package]]
name = "neqo-qpack"
version = "0.4.20"
source = "git+https://github.com/mozilla/neqo?tag=v0.4.20#be938c6156e727428668379e7c038206183a6696"
version = "0.4.19"
source = "git+https://github.com/mozilla/neqo?tag=v0.4.19#7bed96666f977cfaef4288ebb792273e0ca0f41c"
dependencies = [
"lazy_static",
"log",
@ -3445,8 +3445,8 @@ dependencies = [
[[package]]
name = "neqo-transport"
version = "0.4.20"
source = "git+https://github.com/mozilla/neqo?tag=v0.4.20#be938c6156e727428668379e7c038206183a6696"
version = "0.4.19"
source = "git+https://github.com/mozilla/neqo?tag=v0.4.19#7bed96666f977cfaef4288ebb792273e0ca0f41c"
dependencies = [
"indexmap",
"lazy_static",

Просмотреть файл

@ -8,10 +8,10 @@ edition = "2018"
name = "neqo_glue"
[dependencies]
neqo-http3 = { tag = "v0.4.20", git = "https://github.com/mozilla/neqo" }
neqo-transport = { tag = "v0.4.20", git = "https://github.com/mozilla/neqo" }
neqo-common = { tag = "v0.4.20", git = "https://github.com/mozilla/neqo" }
neqo-qpack = { tag = "v0.4.20", git = "https://github.com/mozilla/neqo" }
neqo-http3 = { tag = "v0.4.19", git = "https://github.com/mozilla/neqo" }
neqo-transport = { tag = "v0.4.19", git = "https://github.com/mozilla/neqo" }
neqo-common = { tag = "v0.4.19", git = "https://github.com/mozilla/neqo" }
neqo-qpack = { tag = "v0.4.19", 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.20"
tag = "v0.4.19"
git = "https://github.com/mozilla/neqo"
default-features = false
features = ["gecko"]

Просмотреть файл

@ -9,7 +9,7 @@ use neqo_http3::Error as Http3Error;
use neqo_http3::{Http3Client, Http3ClientEvent, Http3Parameters, Http3State};
use neqo_qpack::QpackSettings;
use neqo_transport::{
ConnectionParameters, Error as TransportError, RandomConnectionIdGenerator, Output, QuicVersion,
ConnectionParameters, Error as TransportError, FixedConnectionIdManager, Output, QuicVersion,
};
use nserror::*;
use nsstring::*;
@ -88,12 +88,10 @@ impl NeqoHttp3Conn {
let mut conn = match Http3Client::new(
origin_conv,
Rc::new(RefCell::new(RandomConnectionIdGenerator::new(3))),
Rc::new(RefCell::new(FixedConnectionIdManager::new(3))),
local,
remote,
ConnectionParameters::default()
.quic_version(quic_version)
.disable_preferred_address(),
&ConnectionParameters::default().quic_version(quic_version),
&http3_settings,
) {
Ok(c) => c,
@ -400,11 +398,11 @@ pub enum CloseError {
Http3AppError(u64),
}
impl From<neqo_transport::ConnectionError> for CloseError {
fn from(error: neqo_transport::ConnectionError) -> CloseError {
impl From<neqo_transport::CloseError> for CloseError {
fn from(error: neqo_transport::CloseError) -> CloseError {
match error {
neqo_transport::ConnectionError::Transport(c) => CloseError::QuicTransportError(c.code()),
neqo_transport::ConnectionError::Application(c) => CloseError::Http3AppError(c),
neqo_transport::CloseError::Transport(c) => CloseError::QuicTransportError(c),
neqo_transport::CloseError::Application(c) => CloseError::Http3AppError(c),
}
}
}

Просмотреть файл

@ -5,16 +5,16 @@ authors = ["Dragana Damjanovic <dragana.damjano@gmail.com>"]
edition = "2018"
[dependencies]
neqo-transport = { tag = "v0.4.20", git = "https://github.com/mozilla/neqo" }
neqo-common = { tag = "v0.4.20", git = "https://github.com/mozilla/neqo" }
neqo-http3 = { tag = "v0.4.20", git = "https://github.com/mozilla/neqo" }
neqo-qpack = { tag = "v0.4.20", git = "https://github.com/mozilla/neqo" }
neqo-transport = { tag = "v0.4.19", git = "https://github.com/mozilla/neqo" }
neqo-common = { tag = "v0.4.19", git = "https://github.com/mozilla/neqo" }
neqo-http3 = { tag = "v0.4.19", git = "https://github.com/mozilla/neqo" }
neqo-qpack = { tag = "v0.4.19", 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.20"
tag = "v0.4.19"
git = "https://github.com/mozilla/neqo"
default-features = false
features = ["gecko"]

Просмотреть файл

@ -11,7 +11,7 @@ use neqo_crypto::{init_db, AllowZeroRtt, AntiReplay};
use neqo_http3::{Error, Http3Server, Http3ServerEvent};
use neqo_qpack::QpackSettings;
use neqo_transport::server::Server;
use neqo_transport::{ConnectionEvent, ConnectionParameters, RandomConnectionIdGenerator, Output};
use neqo_transport::{ConnectionEvent, ConnectionParameters, FixedConnectionIdManager, Output};
use std::env;
use std::cell::RefCell;
@ -461,7 +461,7 @@ impl ServersRunner {
fn create_server(&self, server_type: ServerType) -> Box<dyn HttpServer> {
let anti_replay = AntiReplay::new(Instant::now(), Duration::from_secs(10), 7, 14)
.expect("unable to setup anti-replay");
let cid_mgr = Rc::new(RefCell::new(RandomConnectionIdGenerator::new(10)));
let cid_mgr = Rc::new(RefCell::new(FixedConnectionIdManager::new(10)));
match server_type {
ServerType::Http3 => Box::new(Http3TestServer::new(

Просмотреть файл

@ -1 +1 @@
{"files":{"Cargo.toml":"0a7608c74ae9237dd857dea7b263262264ba692659eb1307a2c9330e28c0f96a","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":"7bcd27df6a2b73bb3b892c6961f19e29ecfd493a767a5566149799820baa7508","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}

2
third_party/rust/neqo-common/Cargo.toml поставляемый
Просмотреть файл

@ -1,6 +1,6 @@
[package]
name = "neqo-common"
version = "0.4.20"
version = "0.4.19"
authors = ["Bobby Holley <bobbyholley@gmail.com>"]
edition = "2018"
license = "MIT/Apache-2.0"

Просмотреть файл

@ -1 +1 @@
{"files":{"Cargo.toml":"6ec9fee4ca5c1069cd0dee5d2c2a9b6722cb48f36610610f423a3a9ee3276611","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":"e6d3afebdbe6246f297bad3091b1ed1712b3e3756f5b219671c89b74409649e3","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}

2
third_party/rust/neqo-crypto/Cargo.toml поставляемый
Просмотреть файл

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

Просмотреть файл

@ -1 +1 @@
{"files":{"Cargo.toml":"068d834a82d0e82df5ced9e1c0e7ef6e720d344ef10470be93e5052751b3fd9e","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":"5440e4e28b6c878a7f9701ceacc002d0c967269fca726957574e688c9907b9c2","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":"ed985fda127fd81f2d8adc9c4ba94ccf0f369d238cc532be59384dfaed709fa9","src/server_connection_events.rs":"762ddb87f700abe91ae1ae78ebbb87a88da0c0a341748b0751b01099870e9985","src/server_events.rs":"e780daa0d19d9a5594eee73f7ff27d56151a5a2ea969f2b4dcc64253e9570dab","src/settings.rs":"127a51fa7857b870718baa14340b0461d86a67e59bf1a8cb42d7bae0240c0ef1","src/stream_type_reader.rs":"aacb2e865f79b3ac55a887fd670f2286d8ffef94f7d8b3ecfa7e0cccbfa9ec04","tests/httpconn.rs":"1a97a80f7abe11c6ba0bd9b41003be6b293049164daa21e907365d93b00a782f"},"package":null}
{"files":{"Cargo.toml":"68ef7bb6147bae527c3a803278291e93cfa5263692f1c3b7ecc90e978390aa35","src/client_events.rs":"87e7c323ec2ceb759da7d6f2bf24e774f6f2e3833f63454d09cc09f47fccfa8e","src/connection.rs":"1230a12e446ef029bd5987270cc54247124094c5420becb04ace84c7644b68cf","src/connection_client.rs":"e6bceaddc41ccf245c384dd055b27cfe5adb9a47fe155a5fb16bb43e6a35d524","src/connection_server.rs":"b3f4d42a984f7093fca737c64bb49c569f8f95d1586a6c02d4dba58f290ce92e","src/control_stream_local.rs":"2e9483d79dc00a3e5ef51ea2b0f28fda2b67348996c47729947c70a8be3007ed","src/control_stream_remote.rs":"1dfac4956a7d6971e2cef2c83963d838e73aa3bf3286b7bde97099978c41d527","src/hframe.rs":"3620c6d114e6d5b98447a2dd2a7cb6872562ebda2cc6de828177b745c8dcbf4d","src/lib.rs":"f0866bb1750c36a6413c48f1a7789599208d2a57842398b8355c22996a1455a4","src/push_controller.rs":"70811f87bf0562630a3a68dc0783fce3c4694ab12de9cac835a803e1115431a5","src/push_stream.rs":"5f3a5c6d72c0a8e4d1c1c5041125f7aff9a65950fa60c77f42cd4b35c768f585","src/qlog.rs":"29c0e3c4c9571eb7fe905967edeb1c4bc236b1e35a0e0f11a4a847f1d246681d","src/recv_message.rs":"975fe666de73493501987bdd285efbdef6efce8e721e3a3411af9baf9599c4aa","src/send_message.rs":"e83080b06748ce65ac737fab91f675c8292f84cdf43af64543ef1acf06ad502a","src/server.rs":"9be3d7df02ed14ec2e83b279f9a537ecf585cbe786dca26fd6b6ce4ca9e1ee81","src/server_connection_events.rs":"762ddb87f700abe91ae1ae78ebbb87a88da0c0a341748b0751b01099870e9985","src/server_events.rs":"e780daa0d19d9a5594eee73f7ff27d56151a5a2ea969f2b4dcc64253e9570dab","src/settings.rs":"127a51fa7857b870718baa14340b0461d86a67e59bf1a8cb42d7bae0240c0ef1","src/stream_type_reader.rs":"aacb2e865f79b3ac55a887fd670f2286d8ffef94f7d8b3ecfa7e0cccbfa9ec04","tests/httpconn.rs":"1a97a80f7abe11c6ba0bd9b41003be6b293049164daa21e907365d93b00a782f"},"package":null}

2
third_party/rust/neqo-http3/Cargo.toml поставляемый
Просмотреть файл

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

14
third_party/rust/neqo-http3/src/connection.rs поставляемый
Просмотреть файл

@ -17,7 +17,7 @@ use neqo_common::{qdebug, qerror, qinfo, qtrace, qwarn};
use neqo_qpack::decoder::{QPackDecoder, QPACK_UNI_STREAM_TYPE_DECODER};
use neqo_qpack::encoder::{QPackEncoder, QPACK_UNI_STREAM_TYPE_ENCODER};
use neqo_qpack::QpackSettings;
use neqo_transport::{AppError, Connection, ConnectionError, State, StreamType};
use neqo_transport::{AppError, CloseError, Connection, State, StreamType};
use std::collections::{BTreeSet, HashMap};
use std::fmt::Debug;
use std::mem;
@ -47,8 +47,8 @@ pub enum Http3State {
ZeroRtt,
Connected,
GoingAway(u64),
Closing(ConnectionError),
Closed(ConnectionError),
Closing(CloseError),
Closed(CloseError),
}
impl Http3State {
@ -401,7 +401,7 @@ impl Http3Connection {
if matches!(self.state, Http3State::Closing(_)| Http3State::Closed(_)) {
Ok(false)
} else {
self.state = Http3State::Closing(error.clone());
self.state = Http3State::Closing(error.clone().into());
Ok(true)
}
}
@ -409,7 +409,7 @@ impl Http3Connection {
if matches!(self.state, Http3State::Closed(_)) {
Ok(false)
} else {
self.state = Http3State::Closed(error.clone());
self.state = Http3State::Closed(error.clone().into());
Ok(true)
}
}
@ -434,7 +434,7 @@ impl Http3Connection {
Ok(())
} else {
debug_assert!(false, "Zero rtt rejected in the wrong state.");
Err(Error::HttpInternal(3))
Err(Error::HttpInternal)
}
}
@ -512,7 +512,7 @@ impl Http3Connection {
/// This is called when an application closes the connection.
pub fn close(&mut self, error: AppError) {
qinfo!([self], "Close connection error {:?}.", error);
self.state = Http3State::Closing(ConnectionError::Application(error));
self.state = Http3State::Closing(CloseError::Application(error));
if (!self.send_streams.is_empty() || !self.recv_streams.is_empty()) && (error == 0) {
qwarn!("close(0) called when streams still active");
}

Просмотреть файл

@ -20,9 +20,8 @@ use neqo_common::{
use neqo_crypto::{agent::CertificateInfo, AuthenticationStatus, ResumptionToken, SecretAgentInfo};
use neqo_qpack::{QpackSettings, Stats as QpackStats};
use neqo_transport::{
AppError, Connection, ConnectionEvent, ConnectionId, ConnectionIdGenerator,
ConnectionParameters, Output, QuicVersion, Stats as TransportStats, StreamId, StreamType,
ZeroRttState,
AppError, Connection, ConnectionEvent, ConnectionId, ConnectionIdManager, ConnectionParameters,
Output, QuicVersion, Stats as TransportStats, StreamId, StreamType, ZeroRttState,
};
use std::cell::RefCell;
use std::fmt::Display;
@ -92,10 +91,10 @@ impl Http3Client {
/// the socket can't be created or configured.
pub fn new(
server_name: &str,
cid_manager: Rc<RefCell<dyn ConnectionIdGenerator>>,
cid_manager: Rc<RefCell<dyn ConnectionIdManager>>,
local_addr: SocketAddr,
remote_addr: SocketAddr,
conn_params: ConnectionParameters,
conn_params: &ConnectionParameters,
http3_parameters: &Http3Parameters,
) -> Res<Self> {
Ok(Self::new_with_conn(
@ -743,20 +742,20 @@ mod tests {
use neqo_qpack::encoder::QPackEncoder;
use neqo_transport::tparams::{self, TransportParameter};
use neqo_transport::{
ConnectionError, ConnectionEvent, ConnectionParameters, Output, State, RECV_BUFFER_SIZE,
SEND_BUFFER_SIZE,
CloseError, ConnectionEvent, ConnectionParameters, FixedConnectionIdManager, Output, State,
RECV_BUFFER_SIZE, SEND_BUFFER_SIZE,
};
use std::convert::TryFrom;
use std::time::Duration;
use test_fixture::{
addr, anti_replay, default_server_h3, fixture_init, now, CountingConnectionIdGenerator,
DEFAULT_ALPN_H3, DEFAULT_KEYS, DEFAULT_SERVER_NAME,
anti_replay, default_server_h3, fixture_init, loopback, now, DEFAULT_ALPN_H3, DEFAULT_KEYS,
DEFAULT_SERVER_NAME,
};
fn assert_closed(client: &Http3Client, expected: &Error) {
match client.state() {
Http3State::Closing(err) | Http3State::Closed(err) => {
assert_eq!(err, ConnectionError::Application(expected.code()))
assert_eq!(err, CloseError::Application(expected.code()))
}
_ => panic!("Wrong state {:?}", client.state()),
};
@ -771,10 +770,10 @@ mod tests {
fixture_init();
Http3Client::new(
DEFAULT_SERVER_NAME,
Rc::new(RefCell::new(CountingConnectionIdGenerator::default())),
addr(),
addr(),
ConnectionParameters::default(),
Rc::new(RefCell::new(FixedConnectionIdManager::new(3))),
loopback(),
loopback(),
&ConnectionParameters::default(),
&Http3Parameters {
qpack_settings: QpackSettings {
max_table_size_encoder: max_table_size,
@ -3554,8 +3553,8 @@ mod tests {
let mut server = Connection::new_server(
test_fixture::DEFAULT_KEYS,
test_fixture::DEFAULT_ALPN_H3,
Rc::new(RefCell::new(CountingConnectionIdGenerator::default())),
ConnectionParameters::default(),
Rc::new(RefCell::new(FixedConnectionIdManager::new(10))),
&ConnectionParameters::default(),
)
.unwrap();
// Using a freshly initialized anti-replay context
@ -3705,7 +3704,7 @@ mod tests {
HSetting::new(HSettingType::BlockedStreams, 100),
HSetting::new(HSettingType::MaxHeaderListSize, 10000),
],
&Http3State::Closing(ConnectionError::Application(265)),
&Http3State::Closing(CloseError::Application(265)),
ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION,
);
}
@ -3723,7 +3722,7 @@ mod tests {
HSetting::new(HSettingType::MaxTableCapacity, 100),
HSetting::new(HSettingType::MaxHeaderListSize, 10000),
],
&Http3State::Closing(ConnectionError::Application(265)),
&Http3State::Closing(CloseError::Application(265)),
ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION,
);
}
@ -3760,7 +3759,7 @@ mod tests {
HSetting::new(HSettingType::BlockedStreams, 100),
HSetting::new(HSettingType::MaxHeaderListSize, 10000),
],
&Http3State::Closing(ConnectionError::Application(514)),
&Http3State::Closing(CloseError::Application(514)),
ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION,
);
}
@ -3779,7 +3778,7 @@ mod tests {
HSetting::new(HSettingType::BlockedStreams, 100),
HSetting::new(HSettingType::MaxHeaderListSize, 10000),
],
&Http3State::Closing(ConnectionError::Application(265)),
&Http3State::Closing(CloseError::Application(265)),
ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION,
);
}
@ -3817,7 +3816,7 @@ mod tests {
HSetting::new(HSettingType::BlockedStreams, 50),
HSetting::new(HSettingType::MaxHeaderListSize, 10000),
],
&Http3State::Closing(ConnectionError::Application(265)),
&Http3State::Closing(CloseError::Application(265)),
ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION,
);
}
@ -3855,7 +3854,7 @@ mod tests {
HSetting::new(HSettingType::BlockedStreams, 100),
HSetting::new(HSettingType::MaxHeaderListSize, 5000),
],
&Http3State::Closing(ConnectionError::Application(265)),
&Http3State::Closing(CloseError::Application(265)),
ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION,
);
}
@ -3912,7 +3911,7 @@ mod tests {
HSetting::new(HSettingType::BlockedStreams, 100),
HSetting::new(HSettingType::MaxHeaderListSize, 10000),
],
&Http3State::Closing(ConnectionError::Application(265)),
&Http3State::Closing(CloseError::Application(265)),
ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION,
);
}
@ -6116,7 +6115,7 @@ mod tests {
DEFAULT_KEYS,
DEFAULT_ALPN_H3,
anti_replay(),
Rc::new(RefCell::new(CountingConnectionIdGenerator::default())),
Rc::new(RefCell::new(FixedConnectionIdManager::new(5))),
QpackSettings {
max_table_size_encoder: MAX_TABLE_SIZE,
max_table_size_decoder: MAX_TABLE_SIZE,

Просмотреть файл

@ -169,7 +169,7 @@ impl Http3ServerHandler {
}
ConnectionEvent::AuthenticationNeeded
| ConnectionEvent::ZeroRttRejected
| ConnectionEvent::ResumptionToken(..) => return Err(Error::HttpInternal(4)),
| ConnectionEvent::ResumptionToken(..) => return Err(Error::HttpInternal),
ConnectionEvent::SendStreamWritable { .. }
| ConnectionEvent::SendStreamComplete { .. }
| ConnectionEvent::SendStreamCreatable { .. } => {}

15
third_party/rust/neqo-http3/src/lib.rs поставляемый
Просмотреть файл

@ -48,9 +48,7 @@ pub enum Error {
HttpNoError,
HttpGeneralProtocol,
HttpGeneralProtocolStream, //this is the same as the above but it should only close a stream not a connection.
// When using this error, you need to provide a value that is unique, which
// will allow the specific error to be identified. This will be validated in CI.
HttpInternal(u16),
HttpInternal,
HttpStreamCreation,
HttpClosedCriticalStream,
HttpFrameUnexpected,
@ -95,7 +93,7 @@ impl Error {
Self::HttpGeneralProtocol | Self::HttpGeneralProtocolStream | Self::InvalidHeader => {
0x101
}
Self::HttpInternal(..) => 0x102,
Self::HttpInternal => 0x102,
Self::HttpStreamCreation => 0x103,
Self::HttpClosedCriticalStream => 0x104,
Self::HttpFrameUnexpected => 0x105,
@ -120,7 +118,7 @@ impl Error {
matches!(
self,
Self::HttpGeneralProtocol
| Self::HttpInternal(..)
| Self::HttpInternal
| Self::HttpStreamCreation
| Self::HttpClosedCriticalStream
| Self::HttpFrameUnexpected
@ -194,8 +192,8 @@ impl Error {
/// Any error is mapped to the indicated type.
fn map_error<R>(r: Result<R, impl Into<Self>>, err: Self) -> Result<R, Self> {
Ok(r.map_err(|e| {
debug_assert!(!matches!(e.into(), Self::HttpInternal(..)));
debug_assert!(!matches!(err, Self::HttpInternal(..)));
debug_assert!(!matches!(e.into(), Self::HttpInternal));
debug_assert!(!matches!(err, Self::HttpInternal));
err
})?)
}
@ -211,6 +209,7 @@ impl From<QpackError> for Error {
fn from(err: QpackError) -> Self {
match err {
QpackError::ClosedCriticalStream => Error::HttpClosedCriticalStream,
QpackError::InternalError => Error::HttpInternal,
e => Self::QpackError(e),
}
}
@ -237,7 +236,7 @@ impl From<AppError> for Error {
0x200 => Self::QpackError(QpackError::DecompressionFailed),
0x201 => Self::QpackError(QpackError::EncoderStream),
0x202 => Self::QpackError(QpackError::DecoderStream),
_ => Self::HttpInternal(0),
_ => Self::HttpInternal,
}
}
}

Просмотреть файл

@ -238,20 +238,15 @@ impl SendMessage {
};
if let SendMessageState::SendingInitialMessage { ref mut buf, fin } = self.state {
let sent = Error::map_error(
conn.stream_send(self.stream_id, &buf),
Error::HttpInternal(5),
)?;
let sent =
Error::map_error(conn.stream_send(self.stream_id, &buf), Error::HttpInternal)?;
qlog::h3_data_moved_down(&mut conn.qlog_mut(), self.stream_id, sent);
qtrace!([label], "{} bytes sent", sent);
if sent == buf.len() {
if fin {
Error::map_error(
conn.stream_close_send(self.stream_id),
Error::HttpInternal(6),
)?;
Error::map_error(conn.stream_close_send(self.stream_id), Error::HttpInternal)?;
self.state = SendMessageState::Closed;
qtrace!([label], "done sending request");
} else {

20
third_party/rust/neqo-http3/src/server.rs поставляемый
Просмотреть файл

@ -16,9 +16,7 @@ use neqo_common::{qtrace, Datagram};
use neqo_crypto::{AntiReplay, Cipher};
use neqo_qpack::QpackSettings;
use neqo_transport::server::{ActiveConnectionRef, Server, ValidateAddress};
use neqo_transport::{
tparams::PreferredAddress, ConnectionIdGenerator, ConnectionParameters, Output,
};
use neqo_transport::{ConnectionIdManager, ConnectionParameters, Output};
use std::cell::RefCell;
use std::cell::RefMut;
use std::collections::HashMap;
@ -52,7 +50,7 @@ impl Http3Server {
certs: &[impl AsRef<str>],
protocols: &[impl AsRef<str>],
anti_replay: AntiReplay,
cid_manager: Rc<RefCell<dyn ConnectionIdGenerator>>,
cid_manager: Rc<RefCell<dyn ConnectionIdManager>>,
qpack_settings: QpackSettings,
) -> Res<Self> {
Ok(Self {
@ -83,10 +81,6 @@ impl Http3Server {
self.server.set_ciphers(ciphers);
}
pub fn set_preferred_address(&mut self, spa: PreferredAddress) {
self.server.set_preferred_address(spa);
}
pub fn process(&mut self, dgram: Option<Datagram>, now: Instant) -> Output {
qtrace!([self], "Process.");
let out = self.server.process(dgram, now);
@ -238,12 +232,12 @@ mod tests {
use neqo_qpack::encoder::QPackEncoder;
use neqo_qpack::QpackSettings;
use neqo_transport::{
Connection, ConnectionError, ConnectionEvent, State, StreamType, ZeroRttState,
CloseError, Connection, ConnectionEvent, FixedConnectionIdManager, State, StreamType,
ZeroRttState,
};
use std::ops::{Deref, DerefMut};
use test_fixture::{
anti_replay, default_client, fixture_init, now, CountingConnectionIdGenerator,
DEFAULT_ALPN, DEFAULT_KEYS,
anti_replay, default_client, fixture_init, now, DEFAULT_ALPN, DEFAULT_KEYS,
};
const DEFAULT_SETTINGS: QpackSettings = QpackSettings {
@ -259,7 +253,7 @@ mod tests {
DEFAULT_KEYS,
DEFAULT_ALPN,
anti_replay(),
Rc::new(RefCell::new(CountingConnectionIdGenerator::default())),
Rc::new(RefCell::new(FixedConnectionIdManager::new(5))),
settings,
)
.expect("create a server")
@ -271,7 +265,7 @@ mod tests {
}
fn assert_closed(hconn: &mut Http3Server, expected: &Error) {
let err = ConnectionError::Application(expected.code());
let err = CloseError::Application(expected.code());
let closed = |e| {
matches!(e,
Http3ServerEvent::StateChange{ state: Http3State::Closing(e), .. }

Просмотреть файл

@ -1 +1 @@
{"files":{"Cargo.toml":"1030e6dd7049afec3692c72fa8d711223d4eb4a1da904413b1d6de856db17e15","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":"a40b9b74114030968cfc0c80af11373136237f91d7680059856a430628e606d7","src/decoder.rs":"1dd87823a8aeb8673d2521d2f2735cea5aaf45713a4fdc3a01b3803c7a7c30f7","src/decoder_instructions.rs":"a8e04dff5fc4c658322a10daadab947dc2e41932c00c3f8d387671a86d0516af","src/encoder.rs":"ced950bb015d13ab90906824fba28ed7e0443d215ff505a758f3d51249c6f6a2","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":"1501db00b7575eb015138ba875d6f427919138b06b0825a23794242b9d9b5f47","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}

2
third_party/rust/neqo-qpack/Cargo.toml поставляемый
Просмотреть файл

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

4
third_party/rust/neqo-qpack/src/encoder.rs поставляемый
Просмотреть файл

@ -305,7 +305,7 @@ impl QPackEncoder {
false,
"can_evict_to should have checked and make sure this operation is possible"
);
return Err(Error::InternalError(1));
return Err(Error::InternalError);
}
self.max_entries = cap / 32;
self.next_capacity = None;
@ -520,7 +520,7 @@ fn map_stream_send_atomic_error(err: &TransportError) -> Error {
}
_ => {
debug_assert!(false, "Unexpected error");
Error::InternalError(2)
Error::InternalError
}
}
}

2
third_party/rust/neqo-qpack/src/lib.rs поставляемый
Просмотреть файл

@ -47,7 +47,7 @@ pub enum Error {
EncoderStream,
DecoderStream,
ClosedCriticalStream,
InternalError(u16),
InternalError,
// These are internal errors, they will be transformed into one of the above.
NeedMoreData, // Return when an input stream does not have more data that a decoder needs.(It does not mean that a stream is closed.)

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

2
third_party/rust/neqo-transport/Cargo.toml поставляемый
Просмотреть файл

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

Просмотреть файл

@ -16,7 +16,7 @@ use crate::cid::ConnectionId;
use crate::packet::PacketBuilder;
use crate::recovery::RecoveryToken;
use crate::stats::FrameStats;
use crate::{Error, Res};
use crate::Res;
use smallvec::SmallVec;
use std::convert::TryFrom;
@ -355,11 +355,10 @@ impl NewTokenState {
builder: &mut PacketBuilder,
tokens: &mut Vec<RecoveryToken>,
stats: &mut FrameStats,
) -> Res<()> {
) {
if let Self::Server(ref mut sender) = self {
sender.write_frames(builder, tokens, stats)?;
sender.write_frames(builder, tokens, stats);
}
Ok(())
}
/// If this a server, buffer a NEW_TOKEN for sending.
@ -430,22 +429,18 @@ impl NewTokenSender {
builder: &mut PacketBuilder,
tokens: &mut Vec<RecoveryToken>,
stats: &mut FrameStats,
) -> Res<()> {
) {
for t in self.tokens.iter_mut() {
if t.needs_sending && t.len() <= builder.remaining() {
t.needs_sending = false;
builder.encode_varint(crate::frame::FRAME_TYPE_NEW_TOKEN);
builder.encode_vvec(&t.token);
if builder.len() > builder.limit() {
return Err(Error::InternalError(7));
}
tokens.push(RecoveryToken::NewToken(t.seqno));
stats.new_token += 1;
}
}
Ok(())
}
pub fn lost(&mut self, seqno: usize) {

Просмотреть файл

@ -20,7 +20,7 @@ use crate::tracking::SentPacket;
use neqo_common::{const_max, const_min, qdebug, qinfo, qlog::NeqoQlog, qtrace};
pub const CWND_INITIAL_PKTS: usize = 10;
pub const CWND_INITIAL: usize = const_min(
const CWND_INITIAL: usize = const_min(
CWND_INITIAL_PKTS * MAX_DATAGRAM_SIZE,
const_max(2 * MAX_DATAGRAM_SIZE, 14720),
);
@ -76,27 +76,8 @@ impl State {
}
pub trait WindowAdjustment: Display + Debug {
/// This is called when an ack is received.
/// The function calculates the amount of acked bytes congestion controller needs
/// to collect before increasing its cwnd by `MAX_DATAGRAM_SIZE`.
fn bytes_for_cwnd_increase(
&mut self,
curr_cwnd: usize,
new_acked_bytes: usize,
min_rtt: Duration,
now: Instant,
) -> usize;
/// This function is called when a congestion event has beed detected and it
/// returns new (decreased) values of `curr_cwnd` and `acked_bytes`.
/// This value can be very small; the calling code is responsible for ensuring that the
/// congestion window doesn't drop below the minimum of `CWND_MIN`.
fn reduce_cwnd(&mut self, curr_cwnd: usize, acked_bytes: usize) -> (usize, usize);
/// Cubic needs this signal to reset its epoch.
fn on_app_limited(&mut self);
#[cfg(test)]
fn last_max_cwnd(&self) -> f64;
#[cfg(test)]
fn set_last_max_cwnd(&mut self, last_max_cwnd: f64);
fn on_packets_acked(&mut self, curr_cwnd: usize, acked_bytes: usize) -> (usize, usize);
fn on_congestion_event(&mut self, curr_cwnd: usize, acked_bytes: usize) -> (usize, usize);
}
#[derive(Debug)]
@ -146,7 +127,7 @@ impl<T: WindowAdjustment> CongestionControl for ClassicCongestionControl<T> {
}
// Multi-packet version of OnPacketAckedCC
fn on_packets_acked(&mut self, acked_pkts: &[SentPacket], min_rtt: Duration, now: Instant) {
fn on_packets_acked(&mut self, acked_pkts: &[SentPacket]) {
// Check whether we are app limited before acked packets are removed
// from bytes_in_flight.
let is_app_limited = self.app_limited();
@ -160,7 +141,7 @@ impl<T: WindowAdjustment> CongestionControl for ClassicCongestionControl<T> {
MAX_DATAGRAM_SIZE * PACING_BURST_SIZE,
);
let mut new_acked = 0;
let mut acked_bytes = 0;
for pkt in acked_pkts.iter().filter(|pkt| pkt.cc_outstanding()) {
assert!(self.bytes_in_flight >= pkt.size);
self.bytes_in_flight -= pkt.size;
@ -176,19 +157,17 @@ impl<T: WindowAdjustment> CongestionControl for ClassicCongestionControl<T> {
qlog::metrics_updated(&mut self.qlog, &[QlogMetric::InRecovery(false)]);
}
new_acked += pkt.size;
acked_bytes += pkt.size;
}
if is_app_limited {
self.cc_algorithm.on_app_limited();
return;
if !is_app_limited {
self.acked_bytes += acked_bytes;
}
qtrace!([self], "ACK received, acked_bytes = {}", self.acked_bytes);
// Slow start, up to the slow start threshold.
if self.congestion_window < self.ssthresh {
self.acked_bytes += new_acked;
let increase = min(self.ssthresh - self.congestion_window, self.acked_bytes);
self.congestion_window += increase;
self.acked_bytes -= increase;
@ -201,29 +180,11 @@ impl<T: WindowAdjustment> CongestionControl for ClassicCongestionControl<T> {
}
// Congestion avoidance, above the slow start threshold.
if self.congestion_window >= self.ssthresh {
// The following function return the amount acked bytes a controller needs
// to collect to be allowed to increase its cwnd by MAX_DATAGRAM_SIZE.
let bytes_for_increase = self.cc_algorithm.bytes_for_cwnd_increase(
self.congestion_window,
new_acked,
min_rtt,
now,
);
// If enough credit has been accumulated already, apply them gradually.
// If we have sudden increase in allowed rate we actually increase cwnd gently.
if self.acked_bytes >= bytes_for_increase {
self.acked_bytes = 0;
self.congestion_window += MAX_DATAGRAM_SIZE;
}
self.acked_bytes += new_acked;
if self.acked_bytes >= bytes_for_increase {
self.acked_bytes -= bytes_for_increase;
self.congestion_window += MAX_DATAGRAM_SIZE; // or is this the current MTU?
}
// The number of bytes we require can go down over time with Cubic.
// That might result in an excessive rate of increase, so limit the number of unused
// acknowledged bytes after increasing the congestion window twice.
self.acked_bytes = min(bytes_for_increase, self.acked_bytes);
let (cwnd, acked_bytes) = self
.cc_algorithm
.on_packets_acked(self.congestion_window, self.acked_bytes);
self.congestion_window = cwnd;
self.acked_bytes = acked_bytes;
}
qlog::metrics_updated(
&mut self.qlog,
@ -330,26 +291,6 @@ impl<T: WindowAdjustment> ClassicCongestionControl<T> {
self.ssthresh
}
#[cfg(test)]
pub fn set_ssthresh(&mut self, v: usize) {
self.ssthresh = v;
}
#[cfg(test)]
pub fn last_max_cwnd(&self) -> f64 {
self.cc_algorithm.last_max_cwnd()
}
#[cfg(test)]
pub fn set_last_max_cwnd(&mut self, last_max_cwnd: f64) {
self.cc_algorithm.set_last_max_cwnd(last_max_cwnd);
}
#[cfg(test)]
pub fn acked_bytes(&self) -> usize {
self.acked_bytes
}
fn set_state(&mut self, state: State) {
if self.state != state {
qdebug!([self], "state -> {:?}", state);
@ -436,7 +377,7 @@ impl<T: WindowAdjustment> ClassicCongestionControl<T> {
if self.after_recovery_start(last_packet) {
let (cwnd, acked_bytes) = self
.cc_algorithm
.reduce_cwnd(self.congestion_window, self.acked_bytes);
.on_congestion_event(self.congestion_window, self.acked_bytes);
self.congestion_window = max(cwnd, CWND_MIN);
self.acked_bytes = acked_bytes;
self.ssthresh = self.congestion_window;
@ -477,15 +418,11 @@ impl<T: WindowAdjustment> ClassicCongestionControl<T> {
#[cfg(test)]
mod tests {
use super::{
ClassicCongestionControl, WindowAdjustment, CWND_INITIAL, CWND_MIN, PERSISTENT_CONG_THRESH,
};
use crate::cc::cubic::{Cubic, CUBIC_BETA_USIZE_DIVISOR, CUBIC_BETA_USIZE_QUOTIENT};
use super::{ClassicCongestionControl, CWND_INITIAL, CWND_MIN, PERSISTENT_CONG_THRESH};
use crate::cc::new_reno::NewReno;
use crate::cc::{CongestionControl, 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;
@ -511,6 +448,109 @@ mod tests {
assert_eq!(cc.ssthresh(), CWND_INITIAL / 2);
}
#[test]
fn issue_876() {
let mut cc = ClassicCongestionControl::new(NewReno::default());
let time_now = now();
let time_before = time_now - Duration::from_millis(100);
let time_after = time_now + Duration::from_millis(150);
let sent_packets = &[
SentPacket::new(
PacketType::Short,
1, // pn
time_before, // time sent
true, // ack eliciting
Vec::new(), // tokens
MAX_DATAGRAM_SIZE - 1, // size
),
SentPacket::new(
PacketType::Short,
2, // pn
time_before, // time sent
true, // ack eliciting
Vec::new(), // tokens
MAX_DATAGRAM_SIZE - 2, // size
),
SentPacket::new(
PacketType::Short,
3, // pn
time_before, // time sent
true, // ack eliciting
Vec::new(), // tokens
MAX_DATAGRAM_SIZE, // size
),
SentPacket::new(
PacketType::Short,
4, // pn
time_before, // time sent
true, // ack eliciting
Vec::new(), // tokens
MAX_DATAGRAM_SIZE, // size
),
SentPacket::new(
PacketType::Short,
5, // pn
time_before, // time sent
true, // ack eliciting
Vec::new(), // tokens
MAX_DATAGRAM_SIZE, // size
),
SentPacket::new(
PacketType::Short,
6, // pn
time_before, // time sent
true, // ack eliciting
Vec::new(), // tokens
MAX_DATAGRAM_SIZE, // size
),
SentPacket::new(
PacketType::Short,
7, // pn
time_after, // time sent
true, // ack eliciting
Vec::new(), // tokens
MAX_DATAGRAM_SIZE - 3, // size
),
];
// Send some more packets so that the cc is not app-limited.
for p in &sent_packets[..6] {
cc.on_packet_sent(p);
}
assert_eq!(cc.acked_bytes, 0);
cwnd_is_default(&cc);
assert_eq!(cc.bytes_in_flight(), 6 * MAX_DATAGRAM_SIZE - 3);
cc.on_packets_lost(Some(time_now), None, PTO, &sent_packets[0..1]);
// We are now in recovery
assert!(cc.recovery_packet());
assert_eq!(cc.acked_bytes, 0);
cwnd_is_halved(&cc);
assert_eq!(cc.bytes_in_flight(), 5 * MAX_DATAGRAM_SIZE - 2);
// Send a packet after recovery starts
cc.on_packet_sent(&sent_packets[6]);
assert!(!cc.recovery_packet());
cwnd_is_halved(&cc);
assert_eq!(cc.acked_bytes, 0);
assert_eq!(cc.bytes_in_flight(), 6 * MAX_DATAGRAM_SIZE - 5);
// and ack it. cwnd increases slightly
cc.on_packets_acked(&sent_packets[6..]);
assert_eq!(cc.acked_bytes, sent_packets[6].size);
cwnd_is_halved(&cc);
assert_eq!(cc.bytes_in_flight(), 5 * MAX_DATAGRAM_SIZE - 2);
// Packet from before is lost. Should not hurt cwnd.
cc.on_packets_lost(Some(time_now), None, PTO, &sent_packets[1..2]);
assert!(!cc.recovery_packet());
assert_eq!(cc.acked_bytes, sent_packets[6].size);
cwnd_is_halved(&cc);
assert_eq!(cc.bytes_in_flight(), 4 * MAX_DATAGRAM_SIZE);
}
fn lost(pn: PacketNumber, ack_eliciting: bool, t: Duration) -> SentPacket {
SentPacket::new(
PacketType::Short,
@ -522,195 +562,153 @@ mod tests {
)
}
fn congestion_control(cc: CongestionControlAlgorithm) -> Box<dyn CongestionControl> {
match cc {
CongestionControlAlgorithm::NewReno => {
Box::new(ClassicCongestionControl::new(NewReno::default()))
}
CongestionControlAlgorithm::Cubic => {
Box::new(ClassicCongestionControl::new(Cubic::default()))
}
}
}
fn persistent_congestion_by_algorithm(
cc_alg: CongestionControlAlgorithm,
reduced_cwnd: usize,
lost_packets: &[SentPacket],
persistent_expected: bool,
) {
let mut cc = congestion_control(cc_alg);
fn persistent_congestion(lost_packets: &[SentPacket]) -> bool {
let mut cc = ClassicCongestionControl::new(NewReno::default());
for p in lost_packets {
cc.on_packet_sent(p);
}
cc.on_packets_lost(Some(now()), None, PTO, lost_packets);
let persistent = if cc.cwnd() == reduced_cwnd {
if cc.cwnd() == CWND_INITIAL / 2 {
false
} else if cc.cwnd() == CWND_MIN {
true
} else {
panic!("unexpected cwnd");
};
assert_eq!(persistent, persistent_expected);
}
fn persistent_congestion(lost_packets: &[SentPacket], persistent_expected: bool) {
persistent_congestion_by_algorithm(
CongestionControlAlgorithm::NewReno,
CWND_INITIAL / 2,
lost_packets,
persistent_expected,
);
persistent_congestion_by_algorithm(
CongestionControlAlgorithm::Cubic,
CWND_INITIAL * CUBIC_BETA_USIZE_QUOTIENT / CUBIC_BETA_USIZE_DIVISOR,
lost_packets,
persistent_expected,
);
}
}
/// A span of exactly the PC threshold only reduces the window on loss.
#[test]
fn persistent_congestion_none() {
persistent_congestion(&[lost(1, true, ZERO), lost(2, true, SUB_PC)], false);
assert!(!persistent_congestion(&[
lost(1, true, ZERO),
lost(2, true, SUB_PC),
]));
}
/// A span of just more than the PC threshold causes persistent congestion.
#[test]
fn persistent_congestion_simple() {
persistent_congestion(&[lost(1, true, ZERO), lost(2, true, PC)], true);
assert!(persistent_congestion(&[
lost(1, true, ZERO),
lost(2, true, PC),
]));
}
/// Both packets need to be ack-eliciting.
#[test]
fn persistent_congestion_non_ack_eliciting() {
persistent_congestion(&[lost(1, false, ZERO), lost(2, true, PC)], false);
persistent_congestion(&[lost(1, true, ZERO), lost(2, false, PC)], false);
assert!(!persistent_congestion(&[
lost(1, false, ZERO),
lost(2, true, PC),
]));
assert!(!persistent_congestion(&[
lost(1, true, ZERO),
lost(2, false, PC),
]));
}
/// Packets in the middle, of any type, are OK.
#[test]
fn persistent_congestion_middle() {
persistent_congestion(
&[lost(1, true, ZERO), lost(2, false, RTT), lost(3, true, PC)],
true,
);
persistent_congestion(
&[lost(1, true, ZERO), lost(2, true, RTT), lost(3, true, PC)],
true,
);
assert!(persistent_congestion(&[
lost(1, true, ZERO),
lost(2, false, RTT),
lost(3, true, PC),
]));
assert!(persistent_congestion(&[
lost(1, true, ZERO),
lost(2, true, RTT),
lost(3, true, PC),
]));
}
/// Leading non-ack-eliciting packets are skipped.
#[test]
fn persistent_congestion_leading_non_ack_eliciting() {
persistent_congestion(
&[lost(1, false, ZERO), lost(2, true, RTT), lost(3, true, PC)],
false,
);
persistent_congestion(
&[
lost(1, false, ZERO),
lost(2, true, RTT),
lost(3, true, RTT + PC),
],
true,
);
assert!(!persistent_congestion(&[
lost(1, false, ZERO),
lost(2, true, RTT),
lost(3, true, PC),
]));
assert!(persistent_congestion(&[
lost(1, false, ZERO),
lost(2, true, RTT),
lost(3, true, RTT + PC),
]));
}
/// Trailing non-ack-eliciting packets aren't relevant.
#[test]
fn persistent_congestion_trailing_non_ack_eliciting() {
persistent_congestion(
&[
lost(1, true, ZERO),
lost(2, true, PC),
lost(3, false, PC + EPSILON),
],
true,
);
persistent_congestion(
&[
lost(1, true, ZERO),
lost(2, true, SUB_PC),
lost(3, false, PC),
],
false,
);
assert!(persistent_congestion(&[
lost(1, true, ZERO),
lost(2, true, PC),
lost(3, false, PC + EPSILON),
]));
assert!(!persistent_congestion(&[
lost(1, true, ZERO),
lost(2, true, SUB_PC),
lost(3, false, PC),
]));
}
/// Gaps in the middle, of any type, restart the count.
#[test]
fn persistent_congestion_gap_reset() {
persistent_congestion(&[lost(1, true, ZERO), lost(3, true, PC)], false);
persistent_congestion(
&[
lost(1, true, ZERO),
lost(2, true, RTT),
lost(4, true, GAP),
lost(5, true, GAP + PTO * PERSISTENT_CONG_THRESH),
],
false,
);
assert!(!persistent_congestion(&[
lost(1, true, ZERO),
lost(3, true, PC),
]));
assert!(!persistent_congestion(&[
lost(1, true, ZERO),
lost(2, true, RTT),
lost(4, true, GAP),
lost(5, true, GAP + PTO * PERSISTENT_CONG_THRESH),
]));
}
/// A span either side of a gap will cause persistent congestion.
#[test]
fn persistent_congestion_gap_or() {
persistent_congestion(
&[
lost(1, true, ZERO),
lost(2, true, PC),
lost(4, true, GAP),
lost(5, true, GAP + PTO),
],
true,
);
persistent_congestion(
&[
lost(1, true, ZERO),
lost(2, true, PTO),
lost(4, true, GAP),
lost(5, true, GAP + PC),
],
true,
);
assert!(persistent_congestion(&[
lost(1, true, ZERO),
lost(2, true, PC),
lost(4, true, GAP),
lost(5, true, GAP + PTO),
]));
assert!(persistent_congestion(&[
lost(1, true, ZERO),
lost(2, true, PTO),
lost(4, true, GAP),
lost(5, true, GAP + PC),
]));
}
/// A gap only restarts after an ack-eliciting packet.
#[test]
fn persistent_congestion_gap_non_ack_eliciting() {
persistent_congestion(
&[
lost(1, true, ZERO),
lost(2, true, PTO),
lost(4, false, GAP),
lost(5, true, GAP + PC),
],
false,
);
persistent_congestion(
&[
lost(1, true, ZERO),
lost(2, true, PTO),
lost(4, false, GAP),
lost(5, true, GAP + RTT),
lost(6, true, GAP + RTT + SUB_PC),
],
false,
);
persistent_congestion(
&[
lost(1, true, ZERO),
lost(2, true, PTO),
lost(4, false, GAP),
lost(5, true, GAP + RTT),
lost(6, true, GAP + RTT + PC),
],
true,
);
assert!(!persistent_congestion(&[
lost(1, true, ZERO),
lost(2, true, PTO),
lost(4, false, GAP),
lost(5, true, GAP + PC),
]));
assert!(!persistent_congestion(&[
lost(1, true, ZERO),
lost(2, true, PTO),
lost(4, false, GAP),
lost(5, true, GAP + RTT),
lost(6, true, GAP + RTT + SUB_PC),
]));
assert!(persistent_congestion(&[
lost(1, true, ZERO),
lost(2, true, PTO),
lost(4, false, GAP),
lost(5, true, GAP + RTT),
lost(6, true, GAP + RTT + PC),
]));
}
/// Get a time, in multiples of `PTO`, relative to `now()`.
@ -740,12 +738,8 @@ mod tests {
/// Call `detect_persistent_congestion` using times relative to now and the fixed PTO time.
/// `last_ack` and `rtt_time` are times in multiples of `PTO`, relative to `now()`,
/// for the time of the largest acknowledged and the first RTT sample, respectively.
fn persistent_congestion_by_pto<T: WindowAdjustment>(
mut cc: ClassicCongestionControl<T>,
last_ack: u32,
rtt_time: u32,
lost: &[SentPacket],
) -> bool {
fn persistent_congestion_by_pto(last_ack: u32, rtt_time: u32, lost: &[SentPacket]) -> bool {
let mut cc = ClassicCongestionControl::new(NewReno::default());
assert_eq!(cc.cwnd(), CWND_INITIAL);
let last_ack = Some(by_pto(last_ack));
@ -765,36 +759,14 @@ mod tests {
#[test]
fn persistent_congestion_no_lost() {
let lost = make_lost(&[]);
assert!(!persistent_congestion_by_pto(
ClassicCongestionControl::new(NewReno::default()),
0,
0,
&lost
));
assert!(!persistent_congestion_by_pto(
ClassicCongestionControl::new(Cubic::default()),
0,
0,
&lost
));
assert!(!persistent_congestion_by_pto(0, 0, &lost));
}
/// No persistent congestion can be had if there is only one lost packet.
#[test]
fn persistent_congestion_one_lost() {
let lost = make_lost(&[1]);
assert!(!persistent_congestion_by_pto(
ClassicCongestionControl::new(NewReno::default()),
0,
0,
&lost
));
assert!(!persistent_congestion_by_pto(
ClassicCongestionControl::new(Cubic::default()),
0,
0,
&lost
));
assert!(!persistent_congestion_by_pto(0, 0, &lost));
}
/// Persistent congestion can't happen based on old packets.
@ -803,42 +775,9 @@ mod tests {
// Packets sent prior to either the last acknowledged or the first RTT
// sample are not considered. So 0 is ignored.
let lost = make_lost(&[0, PERSISTENT_CONG_THRESH + 1, PERSISTENT_CONG_THRESH + 2]);
assert!(!persistent_congestion_by_pto(
ClassicCongestionControl::new(NewReno::default()),
1,
1,
&lost
));
assert!(!persistent_congestion_by_pto(
ClassicCongestionControl::new(NewReno::default()),
0,
1,
&lost
));
assert!(!persistent_congestion_by_pto(
ClassicCongestionControl::new(NewReno::default()),
1,
0,
&lost
));
assert!(!persistent_congestion_by_pto(
ClassicCongestionControl::new(Cubic::default()),
1,
1,
&lost
));
assert!(!persistent_congestion_by_pto(
ClassicCongestionControl::new(Cubic::default()),
0,
1,
&lost
));
assert!(!persistent_congestion_by_pto(
ClassicCongestionControl::new(Cubic::default()),
1,
0,
&lost
));
assert!(!persistent_congestion_by_pto(1, 1, &lost));
assert!(!persistent_congestion_by_pto(0, 1, &lost));
assert!(!persistent_congestion_by_pto(1, 0, &lost));
}
/// Persistent congestion doesn't start unless the packet is ack-eliciting.
@ -853,18 +792,7 @@ mod tests {
Vec::new(),
lost[0].size,
);
assert!(!persistent_congestion_by_pto(
ClassicCongestionControl::new(NewReno::default()),
0,
0,
&lost
));
assert!(!persistent_congestion_by_pto(
ClassicCongestionControl::new(Cubic::default()),
0,
0,
&lost
));
assert!(!persistent_congestion_by_pto(0, 0, &lost));
}
/// Detect persistent congestion. Note that the first lost packet needs to have a time
@ -873,63 +801,26 @@ mod tests {
#[test]
fn persistent_congestion_min() {
let lost = make_lost(&[1, PERSISTENT_CONG_THRESH + 2]);
assert!(persistent_congestion_by_pto(
ClassicCongestionControl::new(NewReno::default()),
0,
0,
&lost
));
assert!(persistent_congestion_by_pto(
ClassicCongestionControl::new(Cubic::default()),
0,
0,
&lost
));
assert!(persistent_congestion_by_pto(0, 0, &lost));
}
/// Make sure that not having a previous largest acknowledged also results
/// in detecting persistent congestion. (This is not expected to happen, but
/// the code permits it).
#[test]
fn persistent_congestion_no_prev_ack_newreno() {
fn persistent_congestion_no_prev_ack() {
let lost = make_lost(&[1, PERSISTENT_CONG_THRESH + 2]);
let mut cc = ClassicCongestionControl::new(NewReno::default());
cc.detect_persistent_congestion(Some(by_pto(0)), None, PTO, &lost);
assert_eq!(cc.cwnd(), CWND_MIN);
}
#[test]
fn persistent_congestion_no_prev_ack_cubic() {
let lost = make_lost(&[1, PERSISTENT_CONG_THRESH + 2]);
let mut cc = ClassicCongestionControl::new(Cubic::default());
cc.detect_persistent_congestion(Some(by_pto(0)), None, PTO, &lost);
assert_eq!(cc.cwnd(), CWND_MIN);
}
/// The code asserts on ordering errors.
#[test]
#[should_panic]
fn persistent_congestion_unsorted_newreno() {
fn persistent_congestion_unsorted() {
let lost = make_lost(&[PERSISTENT_CONG_THRESH + 2, 1]);
assert!(!persistent_congestion_by_pto(
ClassicCongestionControl::new(NewReno::default()),
0,
0,
&lost
));
}
/// The code asserts on ordering errors.
#[test]
#[should_panic]
fn persistent_congestion_unsorted_cubic() {
let lost = make_lost(&[PERSISTENT_CONG_THRESH + 2, 1]);
assert!(!persistent_congestion_by_pto(
ClassicCongestionControl::new(Cubic::default()),
0,
0,
&lost
));
assert!(!persistent_congestion_by_pto(0, 0, &lost));
}
#[test]
@ -959,7 +850,7 @@ mod tests {
Vec::new(), // tokens
MAX_DATAGRAM_SIZE, // size
);
cc.on_packets_acked(&[acked], RTT, now());
cc.on_packets_acked(&[acked]);
assert_eq!(
cc.bytes_in_flight(),
@ -978,7 +869,7 @@ mod tests {
Vec::new(), // tokens
MAX_DATAGRAM_SIZE, // size
)];
cc.on_packets_acked(&p, RTT, now());
cc.on_packets_acked(&p);
assert_eq!(
cc.bytes_in_flight(),
@ -1017,7 +908,7 @@ mod tests {
MAX_DATAGRAM_SIZE, // size
);
cc.on_packet_sent(&p_not_lost);
cc.on_packets_acked(&[p_not_lost], RTT, now());
cc.on_packets_acked(&[p_not_lost]);
cwnd_is_halved(&cc);
// cc is app limited therefore cwnd in not increased.
assert_eq!(cc.acked_bytes, 0);
@ -1039,7 +930,7 @@ mod tests {
assert_eq!(cc.bytes_in_flight(), CWND_INITIAL / 2);
for i in 0..CWND_PKTS_CA - 2 {
cc.on_packets_acked(&pkts[i..=i], RTT, now());
cc.on_packets_acked(&pkts[i..=i]);
assert_eq!(
cc.bytes_in_flight(),
@ -1051,7 +942,7 @@ mod tests {
// Now we are app limited
for i in CWND_PKTS_CA - 2..CWND_PKTS_CA {
cc.on_packets_acked(&pkts[i..=i], RTT, now());
cc.on_packets_acked(&pkts[i..=i]);
assert_eq!(
cc.bytes_in_flight(),

Просмотреть файл

@ -1,203 +0,0 @@
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![deny(clippy::pedantic)]
use std::fmt::{self, Display};
use std::time::{Duration, Instant};
use crate::cc::{classic_cc::WindowAdjustment, MAX_DATAGRAM_SIZE_F64};
use neqo_common::qtrace;
use std::convert::TryFrom;
// CUBIC congestion control
// C is a constant fixed to determine the aggressiveness of window
// increase in high BDP networks.
pub const CUBIC_C: f64 = 0.4;
pub const CUBIC_ALPHA: f64 = 3.0 * (1.0 - 0.7) / (1.0 + 0.7);
// CUBIC_BETA = 0.7;
pub const CUBIC_BETA_USIZE_QUOTIENT: usize = 7;
pub const CUBIC_BETA_USIZE_DIVISOR: usize = 10;
/// The fast convergence ratio further reduces the congestion window when a congestion event
/// occurs before reaching the previous `W_max`.
pub const CUBIC_FAST_CONVERGENCE: f64 = 0.85; // (1.0 + CUBIC_BETA) / 2.0;
/// The minimum number of multiples of the datagram size that need
/// to be received to cause an increase in the congestion window.
/// When there is no loss, Cubic can return to exponential increase, but
/// this value reduces the magnitude of the resulting growth by a constant factor.
/// A value of 1.0 would mean a return to the rate used in slow start.
const EXPONENTIAL_GROWTH_REDUCTION: f64 = 2.0;
fn convert_to_f64(v: usize) -> f64 {
assert!(v < (1 << 53));
let mut f_64 = f64::try_from(u32::try_from(v >> 21).unwrap()).unwrap();
f_64 *= 2_097_152.0; // f_64 <<= 21
f_64 += f64::try_from(u32::try_from(v & 0x1f_ffff).unwrap()).unwrap();
f_64
}
#[derive(Debug)]
pub struct Cubic {
last_max_cwnd: f64,
estimated_tcp_cwnd: f64,
k: f64,
w_max: f64,
ca_epoch_start: Option<Instant>,
last_phase_was_tcp: bool,
tcp_acked_bytes: f64,
}
impl Default for Cubic {
fn default() -> Self {
Self {
last_max_cwnd: 0.0,
estimated_tcp_cwnd: 0.0,
k: 0.0,
w_max: 0.0,
ca_epoch_start: None,
last_phase_was_tcp: false,
tcp_acked_bytes: 0.0,
}
}
}
impl Display for Cubic {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"Cubic [last_max_cwnd: {}, k: {}, w_max: {}, ca_epoch_start: {:?}]",
self.last_max_cwnd, self.k, self.w_max, self.ca_epoch_start
)?;
Ok(())
}
}
#[allow(clippy::doc_markdown)]
impl Cubic {
/// Original equations is:
/// K = cubic_root(W_max*(1-beta_cubic)/C) (Eq. 2 RFC8312)
/// W_max is number of segments of the maximum segment size (MSS).
///
/// K is actually the time that W_cubic(t) = C*(t-K)^3 + W_max (Eq. 1) would
/// take to increase to W_max. We use bytes not MSS units, therefore this
/// equation will be: W_cubic(t) = C*MSS*(t-K)^3 + W_max.
///
/// From that equation we can calculate K as:
/// K = cubic_root((W_max - W_cubic) / C / MSS);
fn calc_k(&self, curr_cwnd: f64) -> f64 {
((self.w_max - curr_cwnd) / CUBIC_C / MAX_DATAGRAM_SIZE_F64).cbrt()
}
/// W_cubic(t) = C*(t-K)^3 + W_max (Eq. 1)
/// t is relative to the start of the congestion avoidance phase and it is in seconds.
fn w_cubic(&self, t: f64) -> f64 {
CUBIC_C * (t - self.k).powi(3) * MAX_DATAGRAM_SIZE_F64 + self.w_max
}
fn start_epoch(&mut self, curr_cwnd_f64: f64, new_acked_f64: f64, now: Instant) {
self.ca_epoch_start = Some(now);
// reset tcp_acked_bytes and estimated_tcp_cwnd;
self.tcp_acked_bytes = new_acked_f64;
self.estimated_tcp_cwnd = curr_cwnd_f64;
if self.last_max_cwnd <= curr_cwnd_f64 {
self.w_max = curr_cwnd_f64;
self.k = 0.0;
} else {
self.w_max = self.last_max_cwnd;
self.k = self.calc_k(curr_cwnd_f64);
}
qtrace!([self], "New epoch");
}
}
impl WindowAdjustment for Cubic {
// This is because of the cast in the last line from f64 to usize.
#[allow(clippy::cast_possible_truncation)]
#[allow(clippy::cast_sign_loss)]
fn bytes_for_cwnd_increase(
&mut self,
curr_cwnd: usize,
new_acked_bytes: usize,
min_rtt: Duration,
now: Instant,
) -> usize {
let curr_cwnd_f64 = convert_to_f64(curr_cwnd);
let new_acked_f64 = convert_to_f64(new_acked_bytes);
if self.ca_epoch_start.is_none() {
// This is a start of a new congestion avoidance phase.
self.start_epoch(curr_cwnd_f64, new_acked_f64, now);
} else {
self.tcp_acked_bytes += new_acked_f64;
}
let time_ca = self
.ca_epoch_start
.map_or(min_rtt, |t| now + min_rtt - t)
.as_secs_f64();
let target_cubic = self.w_cubic(time_ca);
let tcp_cnt = self.estimated_tcp_cwnd / CUBIC_ALPHA;
while self.tcp_acked_bytes > tcp_cnt {
self.tcp_acked_bytes -= tcp_cnt;
self.estimated_tcp_cwnd += MAX_DATAGRAM_SIZE_F64;
}
let target_cwnd = target_cubic.max(self.estimated_tcp_cwnd);
// Calculate the number of bytes that would need to be acknowledged for an increase
// of `MAX_DATAGRAM_SIZE` to match the increase of `target - cwnd / cwnd` as defined
// in the specification (Sections 4.4 and 4.5).
// The amount of data required therefore reduces asymptotically as the target increases.
// If the target is not significantly higher than the congestion window, require a very large
// amount of acknowledged data (effectively block increases).
let mut acked_to_increase =
MAX_DATAGRAM_SIZE_F64 * curr_cwnd_f64 / (target_cwnd - curr_cwnd_f64).max(1.0);
// Limit increase to max 1 MSS per EXPONENTIAL_GROWTH_REDUCTION ack packets.
// This effectively limits target_cwnd to (1 + 1 / EXPONENTIAL_GROWTH_REDUCTION) cwnd.
acked_to_increase =
acked_to_increase.max(EXPONENTIAL_GROWTH_REDUCTION * MAX_DATAGRAM_SIZE_F64);
acked_to_increase as usize
}
fn reduce_cwnd(&mut self, curr_cwnd: usize, acked_bytes: usize) -> (usize, usize) {
let curr_cwnd_f64 = convert_to_f64(curr_cwnd);
// Fast Convergence
// If congestion event occurs before the maximum congestion window before the last congestion event,
// we reduce the the maximum congestion window and thereby W_max.
// check cwnd + MAX_DATAGRAM_SIZE instead of cwnd because with cwnd in bytes, cwnd may be slightly off.
self.last_max_cwnd = if curr_cwnd_f64 + MAX_DATAGRAM_SIZE_F64 < self.last_max_cwnd {
curr_cwnd_f64 * CUBIC_FAST_CONVERGENCE
} else {
curr_cwnd_f64
};
self.ca_epoch_start = None;
(
curr_cwnd * CUBIC_BETA_USIZE_QUOTIENT / CUBIC_BETA_USIZE_DIVISOR,
acked_bytes * CUBIC_BETA_USIZE_QUOTIENT / CUBIC_BETA_USIZE_DIVISOR,
)
}
fn on_app_limited(&mut self) {
// Reset ca_epoch_start. Let it start again when the congestion controller
// exits the app-limited period.
self.ca_epoch_start = None;
}
#[cfg(test)]
fn last_max_cwnd(&self) -> f64 {
self.last_max_cwnd
}
#[cfg(test)]
fn set_last_max_cwnd(&mut self, last_max_cwnd: f64) {
self.last_max_cwnd = last_max_cwnd;
}
}

13
third_party/rust/neqo-transport/src/cc/mod.rs поставляемый
Просмотреть файл

@ -15,16 +15,13 @@ use std::fmt::{Debug, Display};
use std::time::{Duration, Instant};
mod classic_cc;
mod cubic;
mod new_reno;
pub use classic_cc::ClassicCongestionControl;
pub use classic_cc::{CWND_INITIAL, CWND_INITIAL_PKTS, CWND_MIN};
pub use cubic::Cubic;
pub use classic_cc::{CWND_INITIAL_PKTS, CWND_MIN};
pub use new_reno::NewReno;
pub const MAX_DATAGRAM_SIZE: usize = PATH_MTU_V6;
pub const MAX_DATAGRAM_SIZE_F64: f64 = 1337.0;
pub trait CongestionControl: Display + Debug {
fn set_qlog(&mut self, qlog: NeqoQlog);
@ -35,7 +32,7 @@ pub trait CongestionControl: Display + Debug {
fn cwnd_avail(&self) -> usize;
fn on_packets_acked(&mut self, acked_pkts: &[SentPacket], min_rtt: Duration, now: Instant);
fn on_packets_acked(&mut self, acked_pkts: &[SentPacket]);
fn on_packets_lost(
&mut self,
@ -52,11 +49,7 @@ pub trait CongestionControl: Display + Debug {
fn on_packet_sent(&mut self, pkt: &SentPacket);
}
#[derive(Debug, Copy, Clone)]
#[derive(Copy, Clone)]
pub enum CongestionControlAlgorithm {
NewReno,
Cubic,
}
#[cfg(test)]
mod tests;

Просмотреть файл

@ -9,8 +9,8 @@
use std::fmt::{self, Display};
use crate::cc::classic_cc::WindowAdjustment;
use std::time::{Duration, Instant};
use crate::cc::{classic_cc::WindowAdjustment, MAX_DATAGRAM_SIZE};
use neqo_common::qinfo;
#[derive(Debug)]
pub struct NewReno {}
@ -29,27 +29,16 @@ impl Display for NewReno {
}
impl WindowAdjustment for NewReno {
fn bytes_for_cwnd_increase(
&mut self,
curr_cwnd: usize,
_new_acked_bytes: usize,
_min_rtt: Duration,
_now: Instant,
) -> usize {
curr_cwnd
fn on_packets_acked(&mut self, mut curr_cwnd: usize, mut acked_bytes: usize) -> (usize, usize) {
if acked_bytes >= curr_cwnd {
acked_bytes -= curr_cwnd;
curr_cwnd += MAX_DATAGRAM_SIZE;
qinfo!([self], "congestion avoidance += {}", MAX_DATAGRAM_SIZE);
}
(curr_cwnd, acked_bytes)
}
fn reduce_cwnd(&mut self, curr_cwnd: usize, acked_bytes: usize) -> (usize, usize) {
fn on_congestion_event(&mut self, curr_cwnd: usize, acked_bytes: usize) -> (usize, usize) {
(curr_cwnd / 2, acked_bytes / 2)
}
fn on_app_limited(&mut self) {}
#[cfg(test)]
fn last_max_cwnd(&self) -> f64 {
0.0
}
#[cfg(test)]
fn set_last_max_cwnd(&mut self, _last_max_cwnd: f64) {}
}

Просмотреть файл

@ -1,303 +0,0 @@
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![allow(clippy::cast_possible_truncation)]
#![allow(clippy::cast_sign_loss)]
use crate::cc::{
classic_cc::{ClassicCongestionControl, CWND_INITIAL},
cubic::{
Cubic, CUBIC_ALPHA, CUBIC_BETA_USIZE_DIVISOR, CUBIC_BETA_USIZE_QUOTIENT, CUBIC_C,
CUBIC_FAST_CONVERGENCE,
},
CongestionControl, MAX_DATAGRAM_SIZE, MAX_DATAGRAM_SIZE_F64,
};
use crate::packet::PacketType;
use crate::tracking::SentPacket;
use std::convert::TryFrom;
use std::ops::Sub;
use std::time::{Duration, Instant};
use test_fixture::now;
const RTT: Duration = Duration::from_millis(100);
const CWND_INITIAL_F64: f64 = 10.0 * MAX_DATAGRAM_SIZE_F64;
const CWND_INITIAL_10_F64: f64 = 10.0 * CWND_INITIAL_F64;
const CWND_INITIAL_10: usize = 10 * CWND_INITIAL;
const CWND_AFTER_LOSS: usize = CWND_INITIAL * CUBIC_BETA_USIZE_QUOTIENT / CUBIC_BETA_USIZE_DIVISOR;
const CWND_AFTER_LOSS_SLOW_START: usize =
(CWND_INITIAL + MAX_DATAGRAM_SIZE) * CUBIC_BETA_USIZE_QUOTIENT / CUBIC_BETA_USIZE_DIVISOR;
fn fill_cwnd(cc: &mut ClassicCongestionControl<Cubic>, mut next_pn: u64, now: Instant) -> u64 {
while cc.bytes_in_flight() < cc.cwnd() {
let sent = SentPacket::new(
PacketType::Short,
next_pn, // pn
now, // time sent
true, // ack eliciting
Vec::new(), // tokens
MAX_DATAGRAM_SIZE, // size
);
cc.on_packet_sent(&sent);
next_pn += 1;
}
next_pn
}
fn ack_packet(cc: &mut ClassicCongestionControl<Cubic>, pn: u64, now: Instant) {
let acked = SentPacket::new(
PacketType::Short,
pn, // pn
now, // time sent
true, // ack eliciting
Vec::new(), // tokens
MAX_DATAGRAM_SIZE, // size
);
cc.on_packets_acked(&[acked], RTT, now);
}
fn packet_lost(cc: &mut ClassicCongestionControl<Cubic>, pn: u64) {
const PTO: Duration = Duration::from_millis(120);
let p_lost = SentPacket::new(
PacketType::Short,
pn, // pn
now(), // time sent
true, // ack eliciting
Vec::new(), // tokens
MAX_DATAGRAM_SIZE, // size
);
cc.on_packets_lost(None, None, PTO, &[p_lost]);
}
fn expected_tcp_acks(cwnd_rtt_start: usize) -> u64 {
(f64::try_from(i32::try_from(cwnd_rtt_start).unwrap()).unwrap()
/ MAX_DATAGRAM_SIZE_F64
/ CUBIC_ALPHA)
.round() as u64
}
#[test]
fn tcp_phase() {
let mut cubic = ClassicCongestionControl::new(Cubic::default());
// change to congestion avoidance state.
cubic.set_ssthresh(1);
let mut now = now();
let start_time = now;
// helper variables to remember the next packet number to be sent/acked.
let mut next_pn_send = 0;
let mut next_pn_ack = 0;
next_pn_send = fill_cwnd(&mut cubic, next_pn_send, now);
// This will start with TCP phase.
// in this phase cwnd is increase by CUBIC_ALPHA every RTT. We can look at it as
// increase of MAX_DATAGRAM_SIZE every 1 / CUBIC_ALPHA RTTs.
// The phase will end when cwnd calculated with cubic equation is equal to TCP estimate:
// CUBIC_C * (n * RTT / CUBIC_ALPHA)^3 * MAX_DATAGRAM_SIZE = n * MAX_DATAGRAM_SIZE
// from this n = sqrt(CUBIC_ALPHA^3/ (CUBIC_C * RTT^3)).
let num_tcp_increases = (CUBIC_ALPHA.powi(3) / (CUBIC_C * RTT.as_secs_f64().powi(3)))
.sqrt()
.floor() as u64;
for _ in 0..num_tcp_increases {
let cwnd_rtt_start = cubic.cwnd();
//Expected acks during a period of RTT / CUBIC_ALPHA.
let acks = expected_tcp_acks(cwnd_rtt_start);
// The time between acks if they are ideally paced over a RTT.
let time_increase = RTT / u32::try_from(cwnd_rtt_start / MAX_DATAGRAM_SIZE).unwrap();
for _ in 0..acks {
now += time_increase;
ack_packet(&mut cubic, next_pn_ack, now);
next_pn_ack += 1;
next_pn_send = fill_cwnd(&mut cubic, next_pn_send, now);
}
assert_eq!(cubic.cwnd() - cwnd_rtt_start, MAX_DATAGRAM_SIZE);
}
// The next increase will be according to the cubic equation.
let cwnd_rtt_start = cubic.cwnd();
// cwnd_rtt_start has change, therefore calculate new time_increase (the time
// between acks if they are ideally paced over a RTT).
let time_increase = RTT / u32::try_from(cwnd_rtt_start / MAX_DATAGRAM_SIZE).unwrap();
let mut num_acks = 0; // count the number of acks. until cwnd is increased by MAX_DATAGRAM_SIZE.
while cwnd_rtt_start == cubic.cwnd() {
num_acks += 1;
now += time_increase;
ack_packet(&mut cubic, next_pn_ack, now);
next_pn_ack += 1;
next_pn_send = fill_cwnd(&mut cubic, next_pn_send, now);
}
// Make sure that the increase is not according to TCP equation, i.e., that it took
// less than RTT / CUBIC_ALPHA.
let expected_ack_tcp_increase = expected_tcp_acks(cwnd_rtt_start);
assert!(num_acks < expected_ack_tcp_increase);
// This first increase after a TCP phase may be shorter than what it would take by a regular cubic phase,
// because of the proper byte counting and the credit it already had before entering this phase. Therefore
// We will perform another round and compare it to expected increase using the cubic equation.
let cwnd_rtt_start_after_tcp = cubic.cwnd();
let elapsed_time = now - start_time;
// calculate new time_increase.
let time_increase = RTT / u32::try_from(cwnd_rtt_start_after_tcp / MAX_DATAGRAM_SIZE).unwrap();
let mut num_acks2 = 0; // count the number of acks. until cwnd is increased by MAX_DATAGRAM_SIZE.
while cwnd_rtt_start_after_tcp == cubic.cwnd() {
num_acks2 += 1;
now += time_increase;
ack_packet(&mut cubic, next_pn_ack, now);
next_pn_ack += 1;
next_pn_send = fill_cwnd(&mut cubic, next_pn_send, now);
}
let expected_ack_tcp_increase2 = expected_tcp_acks(cwnd_rtt_start_after_tcp);
assert!(num_acks2 < expected_ack_tcp_increase2);
// The time needed to increase cwnd by MAX_DATAGRAM_SIZE using the cubic equation will be calculates from:
// W_cubic(elapsed_time + t_to_increase) - W_cubis(elapsed_time) = MAX_DATAGRAM_SIZE =>
// CUBIC_C * (elapsed_time + t_to_increase)^3 * MAX_DATAGRAM_SIZE + CWND_INITIAL -
// CUBIC_C * elapsed_time^3 * MAX_DATAGRAM_SIZE + CWND_INITIAL = MAX_DATAGRAM_SIZE =>
// t_to_increase = cbrt((1 + CUBIC_C * elapsed_time^3) / CUBIC_C) - elapsed_time
// (t_to_increase is in seconds)
// number of ack needed is t_to_increase / time_increase.
let expected_ack_cubic_increase =
((((1.0 + CUBIC_C * (elapsed_time).as_secs_f64().powi(3)) / CUBIC_C).cbrt()
- elapsed_time.as_secs_f64())
/ time_increase.as_secs_f64())
.ceil() as u64;
// num_acks is very close to the calculated value. The exact value is hard to calculate
// because the proportional increase(i.e. curr_cwnd_f64 / (target - curr_cwnd_f64) * MAX_DATAGRAM_SIZE_F64)
// and the byte counting.
assert_eq!(num_acks2, expected_ack_cubic_increase + 2);
}
#[test]
fn cubic_phase() {
let mut cubic = ClassicCongestionControl::new(Cubic::default());
// Set last_max_cwnd to a higher number make sure that cc is the cubic phase (cwnd is calculated by the cubic equation).
cubic.set_last_max_cwnd(CWND_INITIAL_10_F64);
// Set ssthresh to something small to make sure that cc is in the congection avoidance phase.
cubic.set_ssthresh(1);
let mut now = now();
let mut next_pn_send = 0;
let mut next_pn_ack = 0;
next_pn_send = fill_cwnd(&mut cubic, next_pn_send, now);
let k = ((CWND_INITIAL_10_F64 - CWND_INITIAL_F64) / CUBIC_C / MAX_DATAGRAM_SIZE_F64).cbrt();
let epoch_start = now;
// The number of RTT until W_max is reached.
let num_rtts_w_max = (k / RTT.as_secs_f64()).round() as u64;
for _ in 0..num_rtts_w_max {
let cwnd_rtt_start = cubic.cwnd();
//Expected acks
let acks = cwnd_rtt_start / MAX_DATAGRAM_SIZE;
let time_increase = RTT / u32::try_from(acks).unwrap();
for _ in 0..acks {
now += time_increase;
ack_packet(&mut cubic, next_pn_ack, now);
next_pn_ack += 1;
next_pn_send = fill_cwnd(&mut cubic, next_pn_send, now);
}
let expected =
(CUBIC_C * ((now - epoch_start).as_secs_f64() - k).powi(3) * MAX_DATAGRAM_SIZE_F64
+ CWND_INITIAL_10_F64)
.round() as usize;
assert_within(cubic.cwnd(), expected, MAX_DATAGRAM_SIZE);
}
assert_eq!(cubic.cwnd(), CWND_INITIAL_10);
}
fn assert_within<T: Sub<Output = T> + PartialOrd + Copy>(value: T, expected: T, margin: T) {
if value >= expected {
assert!(value - expected < margin);
} else {
assert!(expected - value < margin);
}
}
#[test]
fn congestion_event_slow_start() {
let mut cubic = ClassicCongestionControl::new(Cubic::default());
let _ = fill_cwnd(&mut cubic, 0, now());
ack_packet(&mut cubic, 0, now());
assert_within(cubic.last_max_cwnd(), 0.0, f64::EPSILON);
// cwnd is increased by 1 in slow start phase, after an ack.
assert_eq!(cubic.cwnd(), CWND_INITIAL + MAX_DATAGRAM_SIZE);
// Trigger a congestion_event in slow start phase
packet_lost(&mut cubic, 1);
// last_max_cwnd is equal to cwnd before decrease.
assert_within(
cubic.last_max_cwnd(),
CWND_INITIAL_F64 + MAX_DATAGRAM_SIZE_F64,
f64::EPSILON,
);
assert_eq!(cubic.cwnd(), CWND_AFTER_LOSS_SLOW_START);
}
#[test]
fn congestion_event_congestion_avoidance() {
let mut cubic = ClassicCongestionControl::new(Cubic::default());
// Set ssthresh to something small to make sure that cc is in the congection avoidance phase.
cubic.set_ssthresh(1);
// Set last_max_cwnd to something smaller than cwnd so that the fast convergence is not triggered.
cubic.set_last_max_cwnd(3.0 * MAX_DATAGRAM_SIZE_F64);
let _ = fill_cwnd(&mut cubic, 0, now());
ack_packet(&mut cubic, 0, now());
assert_eq!(cubic.cwnd(), CWND_INITIAL);
// Trigger a congestion_event in slow start phase
packet_lost(&mut cubic, 1);
assert_within(cubic.last_max_cwnd(), CWND_INITIAL_F64, f64::EPSILON);
assert_eq!(cubic.cwnd(), CWND_AFTER_LOSS);
}
#[test]
fn congestion_event_congestion_avoidance_2() {
let mut cubic = ClassicCongestionControl::new(Cubic::default());
// Set ssthresh to something small to make sure that cc is in the congection avoidance phase.
cubic.set_ssthresh(1);
// Set last_max_cwnd to something higher than cwnd so that the fast convergence is triggered.
cubic.set_last_max_cwnd(CWND_INITIAL_10_F64);
let _ = fill_cwnd(&mut cubic, 0, now());
ack_packet(&mut cubic, 0, now());
assert_within(cubic.last_max_cwnd(), CWND_INITIAL_10_F64, f64::EPSILON);
assert_eq!(cubic.cwnd(), CWND_INITIAL);
// Trigger a congestion_event.
packet_lost(&mut cubic, 1);
assert_within(
cubic.last_max_cwnd(),
CWND_INITIAL_F64 * CUBIC_FAST_CONVERGENCE,
f64::EPSILON,
);
assert_eq!(cubic.cwnd(), CWND_AFTER_LOSS);
}

Просмотреть файл

@ -1,7 +0,0 @@
// 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.
mod cubic;
mod new_reno;

Просмотреть файл

@ -1,131 +0,0 @@
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Congestion control
#![deny(clippy::pedantic)]
use crate::cc::new_reno::NewReno;
use crate::cc::{ClassicCongestionControl, CongestionControl, CWND_INITIAL, MAX_DATAGRAM_SIZE};
use crate::packet::PacketType;
use crate::tracking::SentPacket;
use std::time::Duration;
use test_fixture::now;
const PTO: Duration = Duration::from_millis(100);
const RTT: Duration = Duration::from_millis(98);
fn cwnd_is_default(cc: &ClassicCongestionControl<NewReno>) {
assert_eq!(cc.cwnd(), CWND_INITIAL);
assert_eq!(cc.ssthresh(), usize::MAX);
}
fn cwnd_is_halved(cc: &ClassicCongestionControl<NewReno>) {
assert_eq!(cc.cwnd(), CWND_INITIAL / 2);
assert_eq!(cc.ssthresh(), CWND_INITIAL / 2);
}
#[test]
fn issue_876() {
let mut cc = ClassicCongestionControl::new(NewReno::default());
let time_now = now();
let time_before = time_now - Duration::from_millis(100);
let time_after = time_now + Duration::from_millis(150);
let sent_packets = &[
SentPacket::new(
PacketType::Short,
1, // pn
time_before, // time sent
true, // ack eliciting
Vec::new(), // tokens
MAX_DATAGRAM_SIZE - 1, // size
),
SentPacket::new(
PacketType::Short,
2, // pn
time_before, // time sent
true, // ack eliciting
Vec::new(), // tokens
MAX_DATAGRAM_SIZE - 2, // size
),
SentPacket::new(
PacketType::Short,
3, // pn
time_before, // time sent
true, // ack eliciting
Vec::new(), // tokens
MAX_DATAGRAM_SIZE, // size
),
SentPacket::new(
PacketType::Short,
4, // pn
time_before, // time sent
true, // ack eliciting
Vec::new(), // tokens
MAX_DATAGRAM_SIZE, // size
),
SentPacket::new(
PacketType::Short,
5, // pn
time_before, // time sent
true, // ack eliciting
Vec::new(), // tokens
MAX_DATAGRAM_SIZE, // size
),
SentPacket::new(
PacketType::Short,
6, // pn
time_before, // time sent
true, // ack eliciting
Vec::new(), // tokens
MAX_DATAGRAM_SIZE, // size
),
SentPacket::new(
PacketType::Short,
7, // pn
time_after, // time sent
true, // ack eliciting
Vec::new(), // tokens
MAX_DATAGRAM_SIZE - 3, // size
),
];
// Send some more packets so that the cc is not app-limited.
for p in &sent_packets[..6] {
cc.on_packet_sent(p);
}
assert_eq!(cc.acked_bytes(), 0);
cwnd_is_default(&cc);
assert_eq!(cc.bytes_in_flight(), 6 * MAX_DATAGRAM_SIZE - 3);
cc.on_packets_lost(Some(time_now), None, PTO, &sent_packets[0..1]);
// We are now in recovery
assert!(cc.recovery_packet());
assert_eq!(cc.acked_bytes(), 0);
cwnd_is_halved(&cc);
assert_eq!(cc.bytes_in_flight(), 5 * MAX_DATAGRAM_SIZE - 2);
// Send a packet after recovery starts
cc.on_packet_sent(&sent_packets[6]);
assert!(!cc.recovery_packet());
cwnd_is_halved(&cc);
assert_eq!(cc.acked_bytes(), 0);
assert_eq!(cc.bytes_in_flight(), 6 * MAX_DATAGRAM_SIZE - 5);
// and ack it. cwnd increases slightly
cc.on_packets_acked(&sent_packets[6..], RTT, time_now);
assert_eq!(cc.acked_bytes(), sent_packets[6].size);
cwnd_is_halved(&cc);
assert_eq!(cc.bytes_in_flight(), 5 * MAX_DATAGRAM_SIZE - 2);
// Packet from before is lost. Should not hurt cwnd.
cc.on_packets_lost(Some(time_now), None, PTO, &sent_packets[1..2]);
assert!(!cc.recovery_packet());
assert_eq!(cc.acked_bytes(), sent_packets[6].size);
cwnd_is_halved(&cc);
assert_eq!(cc.bytes_in_flight(), 4 * MAX_DATAGRAM_SIZE);
}

465
third_party/rust/neqo-transport/src/cid.rs поставляемый
Просмотреть файл

@ -4,45 +4,26 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Representation and management of connection IDs.
// Encoding and decoding packets off the wire.
use crate::frame::FRAME_TYPE_NEW_CONNECTION_ID;
use crate::packet::PacketBuilder;
use crate::recovery::RecoveryToken;
use crate::stats::FrameStats;
use crate::{Error, Res};
use neqo_common::{hex, hex_with_len, qinfo, Decoder, Encoder};
use neqo_common::{hex, hex_with_len, Decoder};
use neqo_crypto::random;
use smallvec::SmallVec;
use std::borrow::Borrow;
use std::cell::{Ref, RefCell};
use std::cmp::max;
use std::cmp::min;
use std::convert::AsRef;
use std::convert::TryFrom;
use std::ops::Deref;
use std::rc::Rc;
pub const MAX_CONNECTION_ID_LEN: usize = 20;
pub const LOCAL_ACTIVE_CID_LIMIT: usize = 8;
pub const CONNECTION_ID_SEQNO_INITIAL: u64 = 0;
pub const CONNECTION_ID_SEQNO_PREFERRED: u64 = 1;
/// A special value. See `ConnectionIdManager::add_odcid`.
const CONNECTION_ID_SEQNO_ODCID: u64 = u64::MAX;
/// A special value. See `ConnectionIdEntry::empty_remote`.
const CONNECTION_ID_SEQNO_EMPTY: u64 = u64::MAX - 1;
#[derive(Clone, Default, Eq, Hash, PartialEq)]
pub struct ConnectionId {
pub(crate) cid: SmallVec<[u8; MAX_CONNECTION_ID_LEN]>,
pub(crate) cid: Vec<u8>,
}
impl ConnectionId {
pub fn generate(len: usize) -> Self {
assert!(matches!(len, 0..=MAX_CONNECTION_ID_LEN));
Self::from(random(len))
Self { cid: random(len) }
}
// Apply a wee bit of greasing here in picking a length between 8 and 20 bytes long.
@ -70,27 +51,19 @@ impl Borrow<[u8]> for ConnectionId {
}
}
impl From<SmallVec<[u8; MAX_CONNECTION_ID_LEN]>> for ConnectionId {
fn from(cid: SmallVec<[u8; MAX_CONNECTION_ID_LEN]>) -> Self {
Self { cid }
}
}
impl From<Vec<u8>> for ConnectionId {
fn from(cid: Vec<u8>) -> Self {
Self::from(SmallVec::from(cid))
}
}
impl<T: AsRef<[u8]> + ?Sized> From<&T> for ConnectionId {
fn from(buf: &T) -> Self {
Self::from(SmallVec::from(buf.as_ref()))
impl From<&[u8]> for ConnectionId {
fn from(buf: &[u8]) -> Self {
Self {
cid: Vec::from(buf),
}
}
}
impl<'a> From<&ConnectionIdRef<'a>> for ConnectionId {
fn from(cidref: &ConnectionIdRef<'a>) -> Self {
Self::from(SmallVec::from(cidref.cid))
Self {
cid: Vec::from(cidref.cid),
}
}
}
@ -137,9 +110,9 @@ impl<'a> ::std::fmt::Display for ConnectionIdRef<'a> {
}
}
impl<'a, T: AsRef<[u8]> + ?Sized> From<&'a T> for ConnectionIdRef<'a> {
fn from(cid: &'a T) -> Self {
Self { cid: cid.as_ref() }
impl<'a> From<&'a [u8]> for ConnectionIdRef<'a> {
fn from(cid: &'a [u8]) -> Self {
Self { cid }
}
}
@ -158,418 +131,14 @@ impl<'a> PartialEq<ConnectionId> for ConnectionIdRef<'a> {
}
pub trait ConnectionIdDecoder {
/// Decodes a connection ID from the provided decoder.
fn decode_cid<'a>(&self, dec: &mut Decoder<'a>) -> Option<ConnectionIdRef<'a>>;
}
pub trait ConnectionIdGenerator: ConnectionIdDecoder {
/// Generates a connection ID. This can return `None` if the generator
/// is exhausted.
fn generate_cid(&mut self) -> Option<ConnectionId>;
/// Indicates whether the connection IDs are zero-length.
/// If this returns true, `generate_cid` must always produce an empty value
/// and never `None`.
/// If this returns false, `generate_cid` must never produce an empty value,
/// though it can return `None`.
///
/// You should not need to implement this: if you want zero-length connection IDs,
/// use `EmptyConnectionIdGenerator` instead.
fn generates_empty_cids(&self) -> bool {
false
}
pub trait ConnectionIdManager: ConnectionIdDecoder {
fn generate_cid(&mut self) -> ConnectionId;
fn as_decoder(&self) -> &dyn ConnectionIdDecoder;
}
/// An `EmptyConnectionIdGenerator` generates empty connection IDs.
#[derive(Default)]
pub struct EmptyConnectionIdGenerator {}
impl ConnectionIdDecoder for EmptyConnectionIdGenerator {
fn decode_cid<'a>(&self, _: &mut Decoder<'a>) -> Option<ConnectionIdRef<'a>> {
Some(ConnectionIdRef::from(&[]))
}
}
impl ConnectionIdGenerator for EmptyConnectionIdGenerator {
fn generate_cid(&mut self) -> Option<ConnectionId> {
Some(ConnectionId::from(&[]))
}
fn as_decoder(&self) -> &dyn ConnectionIdDecoder {
self
}
fn generates_empty_cids(&self) -> bool {
true
}
}
/// An RandomConnectionIdGenerator produces connection IDs of
/// a fixed length and random content. No effort is made to
/// prevent collisions.
pub struct RandomConnectionIdGenerator {
len: usize,
}
impl RandomConnectionIdGenerator {
pub fn new(len: usize) -> Self {
Self { len }
}
}
impl ConnectionIdDecoder for RandomConnectionIdGenerator {
fn decode_cid<'a>(&self, dec: &mut Decoder<'a>) -> Option<ConnectionIdRef<'a>> {
dec.decode(self.len).map(ConnectionIdRef::from)
}
}
impl ConnectionIdGenerator for RandomConnectionIdGenerator {
fn generate_cid(&mut self) -> Option<ConnectionId> {
Some(ConnectionId::from(&random(self.len)))
}
fn as_decoder(&self) -> &dyn ConnectionIdDecoder {
self
}
fn generates_empty_cids(&self) -> bool {
self.len == 0
}
}
/// A single connection ID, as saved from NEW_CONNECTION_ID.
/// This is templated so that the connection ID entries from a peer can be
/// saved with a stateless reset token. Local entries don't need that.
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct ConnectionIdEntry<SRT: Clone + PartialEq> {
/// The sequence number.
seqno: u64,
/// The connection ID.
cid: ConnectionId,
/// The corresponding stateless reset token.
srt: SRT,
}
impl ConnectionIdEntry<[u8; 16]> {
/// Create a random stateless reset token so that it is hard to guess the correct
/// value and reset the connection.
fn random_srt() -> [u8; 16] {
<[u8; 16]>::try_from(&random(16)[..]).unwrap()
}
/// Create the first entry, which won't have a stateless reset token.
pub fn initial_remote(cid: ConnectionId) -> Self {
Self::new(CONNECTION_ID_SEQNO_INITIAL, cid, Self::random_srt())
}
/// Create an empty for when the peer chooses empty connection IDs.
/// This uses a special sequence number just because it can.
pub fn empty_remote() -> Self {
Self::new(
CONNECTION_ID_SEQNO_EMPTY,
ConnectionId::from(&[]),
Self::random_srt(),
)
}
fn token_equal(a: &[u8; 16], b: &[u8; 16]) -> bool {
// rustc might decide to optimize this and make this non-constant-time
// with respect to `t`, but it doesn't appear to currently.
let mut c = 0;
for (&a, &b) in a.iter().zip(b) {
c |= a ^ b;
}
c == 0
}
/// Determine whether this is a valid stateless reset.
pub fn is_stateless_reset(&self, token: &[u8; 16]) -> bool {
// A sequence number of 2^62 or more has no corresponding stateless reset token.
(self.seqno < (1 << 62)) && Self::token_equal(&self.srt, token)
}
/// Return true if the two contain any equal parts.
fn any_part_equal(&self, other: &Self) -> bool {
self.seqno == other.seqno || self.cid == other.cid || self.srt == other.srt
}
/// The sequence number of this entry.
pub fn sequence_number(&self) -> u64 {
self.seqno
}
}
impl ConnectionIdEntry<()> {
/// Create an initial entry.
pub fn initial_local(cid: ConnectionId) -> Self {
Self::new(0, cid, ())
}
}
impl<SRT: Clone + PartialEq> ConnectionIdEntry<SRT> {
pub fn new(seqno: u64, cid: ConnectionId, srt: SRT) -> Self {
Self { seqno, cid, srt }
}
/// Update the stateless reset token. This panics if the sequence number is non-zero.
pub fn set_stateless_reset_token(&mut self, srt: SRT) {
assert_eq!(self.seqno, CONNECTION_ID_SEQNO_INITIAL);
self.srt = srt;
}
/// Replace the connection ID. This panics if the sequence number is non-zero.
pub fn update_cid(&mut self, cid: ConnectionId) {
assert_eq!(self.seqno, CONNECTION_ID_SEQNO_INITIAL);
self.cid = cid;
}
pub fn connection_id(&self) -> &ConnectionId {
&self.cid
}
}
pub type RemoteConnectionIdEntry = ConnectionIdEntry<[u8; 16]>;
/// A collection of connection IDs that are indexed by a sequence number.
/// Used to store connection IDs that are provided by a peer.
#[derive(Debug, Default)]
pub struct ConnectionIdStore<SRT: Clone + PartialEq> {
cids: SmallVec<[ConnectionIdEntry<SRT>; 8]>,
}
impl<SRT: Clone + PartialEq> ConnectionIdStore<SRT> {
pub fn retire(&mut self, seqno: u64) {
self.cids.retain(|c| c.seqno != seqno);
}
pub fn contains(&self, cid: &ConnectionIdRef) -> bool {
self.cids.iter().any(|c| &c.cid == cid)
}
pub fn next(&mut self) -> Option<ConnectionIdEntry<SRT>> {
if self.cids.is_empty() {
None
} else {
Some(self.cids.remove(0))
}
}
pub fn len(&self) -> usize {
self.cids.len()
}
}
impl ConnectionIdStore<[u8; 16]> {
pub fn add_remote(&mut self, entry: ConnectionIdEntry<[u8; 16]>) -> Res<()> {
// It's OK if this perfectly matches an existing entry.
if self.cids.iter().any(|c| c == &entry) {
return Ok(());
}
// It's not OK if any individual piece matches though.
if self.cids.iter().any(|c| c.any_part_equal(&entry)) {
qinfo!("ConnectionIdStore found reused part in NEW_CONNECTION_ID");
return Err(Error::ProtocolViolation);
}
if self.cids.len() >= LOCAL_ACTIVE_CID_LIMIT {
qinfo!("ConnectionIdStore received too many connection IDs");
return Err(Error::ConnectionIdLimitExceeded);
}
self.cids.push(entry);
Ok(())
}
}
impl ConnectionIdStore<()> {
fn add_local(&mut self, entry: ConnectionIdEntry<()>) {
self.cids.push(entry);
}
}
pub struct ConnectionIdDecoderRef<'a> {
generator: Ref<'a, dyn ConnectionIdGenerator>,
}
// Ideally this would be an implementation of `Deref`, but it doesn't
// seem to be possible to convince the compiler to build anything useful.
impl<'a: 'b, 'b> ConnectionIdDecoderRef<'a> {
pub fn as_ref(&'a self) -> &'b dyn ConnectionIdDecoder {
self.generator.as_decoder()
}
}
/// A connection ID manager looks after the generation of connection IDs,
/// the set of connection IDs that are valid for the connection, and the
/// generation of `NEW_CONNECTION_ID` frames.
pub struct ConnectionIdManager {
/// The `ConnectionIdGenerator` instance that is used to create connection IDs.
generator: Rc<RefCell<dyn ConnectionIdGenerator>>,
/// The connection IDs that we will accept.
/// This includes any we advertise in `NEW_CONNECTION_ID` that haven't been bound to a path yet.
/// During the handshake at the server, it also includes the randomized DCID pick by the client.
connection_ids: ConnectionIdStore<()>,
/// The maximum number of connection IDs this will accept. This is at least 2 and won't
/// be more than `LOCAL_ACTIVE_CID_LIMIT`.
limit: usize,
/// The next sequence number that will be used for sending `NEW_CONNECTION_ID` frames.
next_seqno: u64,
/// Outstanding, but lost NEW_CONNECTION_ID frames will be stored here.
lost_new_connection_id: Vec<ConnectionIdEntry<[u8; 16]>>,
}
impl ConnectionIdManager {
pub fn new(generator: Rc<RefCell<dyn ConnectionIdGenerator>>, initial: ConnectionId) -> Self {
let mut connection_ids = ConnectionIdStore::default();
connection_ids.add_local(ConnectionIdEntry::initial_local(initial));
Self {
generator,
connection_ids,
// A note about initializing the limit to 2.
// For a server, the number of connection IDs that are tracked at the point that
// it is first possible to send `NEW_CONNECTION_ID` is 2. One is the client-generated
// destination connection (stored with a sequence number of `HANDSHAKE_SEQNO`); the
// other being the handshake value (seqno 0). As a result, `NEW_CONNECTION_ID`
// won't be sent until until after the handshake completes, because this initial
// value remains until the connection completes and transport parameters are handled.
limit: 2,
next_seqno: 2, // A different value.
lost_new_connection_id: Vec::new(),
}
}
pub fn decoder(&self) -> ConnectionIdDecoderRef {
ConnectionIdDecoderRef {
generator: self.generator.deref().borrow(),
}
}
/// Generate a connection ID and stateless reset token for a preferred address.
pub fn preferred_address_cid(&mut self) -> Res<(ConnectionId, [u8; 16])> {
if self.generator.deref().borrow().generates_empty_cids() {
return Err(Error::ConnectionIdsExhausted);
}
if let Some(cid) = self.generator.borrow_mut().generate_cid() {
assert_ne!(cid.len(), 0);
self.connection_ids.add_local(ConnectionIdEntry::new(
CONNECTION_ID_SEQNO_PREFERRED,
cid.clone(),
(),
));
let srt = <[u8; 16]>::try_from(&random(16)[..]).unwrap();
Ok((cid, srt))
} else {
Err(Error::ConnectionIdsExhausted)
}
}
pub fn is_valid(&self, cid: &ConnectionIdRef) -> bool {
self.connection_ids.contains(cid)
}
pub fn retire(&mut self, seqno: u64) {
// TODO(mt) - consider keeping connection IDs around for a short while.
self.connection_ids.retire(seqno);
self.lost_new_connection_id.retain(|cid| cid.seqno != seqno);
}
/// During the handshake, a server needs to regard the client's choice of destination
/// connection ID as valid. This function saves it in the store in a special place.
pub fn add_odcid(&mut self, cid: ConnectionId) {
let entry = ConnectionIdEntry::new(CONNECTION_ID_SEQNO_ODCID, cid, ());
self.connection_ids.add_local(entry);
}
/// Stop treating the original destination connection ID as valid.
pub fn remove_odcid(&mut self) {
self.connection_ids.retire(CONNECTION_ID_SEQNO_ODCID);
}
pub fn set_limit(&mut self, limit: u64) {
debug_assert!(limit >= 2);
self.limit = min(
LOCAL_ACTIVE_CID_LIMIT,
usize::try_from(limit).unwrap_or(LOCAL_ACTIVE_CID_LIMIT),
);
}
fn write_entry(
&mut self,
entry: &ConnectionIdEntry<[u8; 16]>,
builder: &mut PacketBuilder,
stats: &mut FrameStats,
) -> Res<bool> {
let len = 1 + Encoder::varint_len(entry.seqno) + 1 + 1 + entry.cid.len() + 16;
if builder.remaining() < len {
return Ok(false);
}
builder.encode_varint(FRAME_TYPE_NEW_CONNECTION_ID);
builder.encode_varint(entry.seqno);
builder.encode_varint(0u64);
builder.encode_vec(1, &entry.cid);
builder.encode(&entry.srt);
if builder.len() > builder.limit() {
return Err(Error::InternalError(8));
}
stats.new_connection_id += 1;
Ok(true)
}
pub fn write_frames(
&mut self,
builder: &mut PacketBuilder,
tokens: &mut Vec<RecoveryToken>,
stats: &mut FrameStats,
) -> Res<()> {
if self.generator.deref().borrow().generates_empty_cids() {
debug_assert_eq!(self.generator.borrow_mut().generate_cid().unwrap().len(), 0);
return Ok(());
}
while let Some(entry) = self.lost_new_connection_id.pop() {
if self.write_entry(&entry, builder, stats)? {
tokens.push(RecoveryToken::NewConnectionId(entry));
} else {
// This shouldn't happen often.
self.lost_new_connection_id.push(entry);
break;
}
}
// Keep writing while we have fewer than the limit of active connection IDs
// and while there is room for more. This uses the longest connection ID
// length to simplify (assuming Retire Prior To is just 1 byte).
while self.connection_ids.len() < self.limit && builder.remaining() >= 47 {
let maybe_cid = self.generator.borrow_mut().generate_cid();
if let Some(cid) = maybe_cid {
assert_ne!(cid.len(), 0);
// TODO: generate the stateless reset tokens from the connection ID and a key.
let srt = <[u8; 16]>::try_from(&random(16)[..]).unwrap();
let seqno = self.next_seqno;
self.next_seqno += 1;
self.connection_ids
.add_local(ConnectionIdEntry::new(seqno, cid.clone(), ()));
let entry = ConnectionIdEntry::new(seqno, cid, srt);
debug_assert!(self.write_entry(&entry, builder, stats)?);
tokens.push(RecoveryToken::NewConnectionId(entry));
}
}
Ok(())
}
pub fn lost(&mut self, entry: &ConnectionIdEntry<[u8; 16]>) {
self.lost_new_connection_id.push(entry.clone());
}
pub fn acked(&mut self, entry: &ConnectionIdEntry<[u8; 16]>) {
self.lost_new_connection_id
.retain(|e| e.seqno != entry.seqno);
}
}
#[cfg(test)]
mod tests {
use super::*;

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -4,33 +4,19 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use crate::stream_id::{StreamIndex, StreamType};
use crate::tparams::PreferredAddress;
use crate::{CongestionControlAlgorithm, QuicVersion};
const LOCAL_STREAM_LIMIT_BIDI: StreamIndex = StreamIndex::new(16);
const LOCAL_STREAM_LIMIT_UNI: StreamIndex = StreamIndex::new(16);
/// What to do with preferred addresses.
#[derive(Debug, Clone)]
pub enum PreferredAddressConfig {
/// Disabled, whether for client or server.
Disabled,
/// Enabled at a client, disabled at a server.
Default,
/// Enabled at both client and server.
Address(PreferredAddress),
}
use crate::frame::StreamType;
use crate::{
CongestionControlAlgorithm, QuicVersion, LOCAL_STREAM_LIMIT_BIDI, LOCAL_STREAM_LIMIT_UNI,
};
/// ConnectionParameters use for setting intitial value for QUIC parameters.
/// This collect like initial limits, protocol version and congestion control.
#[derive(Debug, Clone)]
#[derive(Clone)]
pub struct ConnectionParameters {
quic_version: QuicVersion,
cc_algorithm: CongestionControlAlgorithm,
max_streams_bidi: StreamIndex,
max_streams_uni: StreamIndex,
preferred_address: PreferredAddressConfig,
max_streams_bidi: u64,
max_streams_uni: u64,
}
impl Default for ConnectionParameters {
@ -40,7 +26,6 @@ impl Default for ConnectionParameters {
cc_algorithm: CongestionControlAlgorithm::NewReno,
max_streams_bidi: LOCAL_STREAM_LIMIT_BIDI,
max_streams_uni: LOCAL_STREAM_LIMIT_UNI,
preferred_address: PreferredAddressConfig::Default,
}
}
}
@ -64,15 +49,15 @@ impl ConnectionParameters {
self
}
pub fn get_max_streams(&self, stream_type: StreamType) -> StreamIndex {
pub fn get_max_streams(&self, stream_type: StreamType) -> u64 {
match stream_type {
StreamType::BiDi => self.max_streams_bidi,
StreamType::UniDi => self.max_streams_uni,
}
}
pub fn max_streams(mut self, stream_type: StreamType, v: StreamIndex) -> Self {
assert!(v.as_u64() <= (1 << 60), "max_streams is too large");
pub fn max_streams(mut self, stream_type: StreamType, v: u64) -> Self {
assert!(v <= (1 << 60), "max_streams's parameter too big");
match stream_type {
StreamType::BiDi => {
self.max_streams_bidi = v;
@ -83,20 +68,4 @@ impl ConnectionParameters {
}
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);
self
}
/// Disable the use of preferred addresses.
pub fn disable_preferred_address(mut self) -> Self {
self.preferred_address = PreferredAddressConfig::Disabled;
self
}
pub fn get_preferred_address(&self) -> &PreferredAddressConfig {
&self.preferred_address
}
}

Просмотреть файл

@ -4,20 +4,14 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use neqo_common::Encoder;
use std::cmp::{min, Ordering};
use std::cmp::Ordering;
use std::mem;
use std::rc::Rc;
use std::time::Instant;
use crate::frame::{
FrameType, FRAME_TYPE_CONNECTION_CLOSE_APPLICATION, FRAME_TYPE_CONNECTION_CLOSE_TRANSPORT,
FRAME_TYPE_HANDSHAKE_DONE,
};
use crate::frame::{Frame, FrameType};
use crate::packet::PacketBuilder;
use crate::path::PathRef;
use crate::recovery::RecoveryToken;
use crate::{ConnectionError, Error, Res};
use crate::{CloseError, ConnectionError};
#[derive(Clone, Debug, PartialEq, Eq)]
/// The state of the Connection.
@ -52,8 +46,28 @@ impl State {
// Implement `PartialOrd` so that we can enforce monotonic state progression.
impl PartialOrd for State {
#[allow(clippy::match_same_arms)] // Lint bug: rust-lang/rust-clippy#860
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
if mem::discriminant(self) == mem::discriminant(other) {
return Some(Ordering::Equal);
}
Some(match (self, other) {
(Self::Init, _) => Ordering::Less,
(_, Self::Init) => Ordering::Greater,
(Self::WaitInitial, _) => Ordering::Less,
(_, Self::WaitInitial) => Ordering::Greater,
(Self::Handshaking, _) => Ordering::Less,
(_, Self::Handshaking) => Ordering::Greater,
(Self::Connected, _) => Ordering::Less,
(_, Self::Connected) => Ordering::Greater,
(Self::Confirmed, _) => Ordering::Less,
(_, Self::Confirmed) => Ordering::Greater,
(Self::Closing { .. }, _) => Ordering::Less,
(_, Self::Closing { .. }) => Ordering::Greater,
(Self::Draining { .. }, _) => Ordering::Less,
(_, Self::Draining { .. }) => Ordering::Greater,
(Self::Closed(_), _) => unreachable!(),
})
}
}
@ -62,7 +76,6 @@ impl Ord for State {
if mem::discriminant(self) == mem::discriminant(other) {
return Ordering::Equal;
}
#[allow(clippy::match_same_arms)] // Lint bug: rust-lang/rust-clippy#860
match (self, other) {
(Self::Init, _) => Ordering::Less,
(_, Self::Init) => Ordering::Greater,
@ -83,77 +96,7 @@ impl Ord for State {
}
}
#[derive(Debug, Clone)]
pub struct ClosingFrame {
path: PathRef,
error: ConnectionError,
frame_type: FrameType,
reason_phrase: Vec<u8>,
}
impl ClosingFrame {
fn new(
path: PathRef,
error: ConnectionError,
frame_type: FrameType,
message: impl AsRef<str>,
) -> Self {
let reason_phrase = message.as_ref().as_bytes().to_vec();
Self {
path,
error,
frame_type,
reason_phrase,
}
}
pub fn path(&self) -> &PathRef {
&self.path
}
pub fn sanitize(&self) -> Option<Self> {
if let ConnectionError::Application(_) = self.error {
// The default CONNECTION_CLOSE frame that is sent when an application
// error code needs to be sent in an Initial or Handshake packet.
Some(Self {
path: Rc::clone(&self.path),
error: ConnectionError::Transport(Error::ApplicationError),
frame_type: 0,
reason_phrase: Vec::new(),
})
} else {
None
}
}
pub fn write_frame(&self, builder: &mut PacketBuilder) {
// Allow 8 bytes for the reason phrase to ensure that if it needs to be
// truncated there is still at least a few bytes of the value.
if builder.remaining() < 1 + 8 + 8 + 2 + 8 {
return;
}
match &self.error {
ConnectionError::Transport(e) => {
builder.encode_varint(FRAME_TYPE_CONNECTION_CLOSE_TRANSPORT);
builder.encode_varint(e.code());
builder.encode_varint(self.frame_type);
}
ConnectionError::Application(code) => {
builder.encode_varint(FRAME_TYPE_CONNECTION_CLOSE_APPLICATION);
builder.encode_varint(*code);
}
}
// Truncate the reason phrase if it doesn't fit. As we send this frame in
// multiple packet number spaces, limit the overall size to 256.
let available = min(256, builder.remaining());
let reason = if available < Encoder::vvec_len(self.reason_phrase.len()) {
&self.reason_phrase[..available - 2]
} else {
&self.reason_phrase
};
builder.encode_vvec(reason);
}
}
type ClosingFrame = Frame<'static>;
/// `StateSignaling` manages whether we need to send HANDSHAKE_DONE and CONNECTION_CLOSE.
/// Valid state transitions are:
@ -163,7 +106,7 @@ impl ClosingFrame {
/// * Closing/Draining -> CloseSent: after sending CONNECTION_CLOSE
/// * CloseSent -> Closing: any time a new CONNECTION_CLOSE is needed
/// * -> Reset: from any state in case of a stateless reset
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq)]
pub enum StateSignaling {
Idle,
HandshakeDone,
@ -178,47 +121,55 @@ pub enum StateSignaling {
impl StateSignaling {
pub fn handshake_done(&mut self) {
if !matches!(self, Self::Idle) {
if *self != Self::Idle {
debug_assert!(false, "StateSignaling must be in Idle state.");
return;
}
*self = Self::HandshakeDone
}
pub fn write_done(&mut self, builder: &mut PacketBuilder) -> Res<Option<RecoveryToken>> {
if matches!(self, Self::HandshakeDone) && builder.remaining() >= 1 {
pub fn write_done(&mut self, builder: &mut PacketBuilder) -> Option<RecoveryToken> {
if *self == Self::HandshakeDone && builder.remaining() >= 1 {
*self = Self::Idle;
builder.encode_varint(FRAME_TYPE_HANDSHAKE_DONE);
if builder.len() > builder.limit() {
return Err(Error::InternalError(14));
}
Ok(Some(RecoveryToken::HandshakeDone))
builder.encode_varint(Frame::HandshakeDone.get_type());
Some(RecoveryToken::HandshakeDone)
} else {
Ok(None)
None
}
}
fn make_close_frame(
error: ConnectionError,
frame_type: FrameType,
message: impl AsRef<str>,
) -> ClosingFrame {
let reason_phrase = message.as_ref().as_bytes().to_owned();
Frame::ConnectionClose {
error_code: CloseError::from(error),
frame_type,
reason_phrase,
}
}
pub fn close(
&mut self,
path: PathRef,
error: ConnectionError,
frame_type: FrameType,
message: impl AsRef<str>,
) {
if !matches!(self, Self::Reset) {
*self = Self::Closing(ClosingFrame::new(path, error, frame_type, message));
if *self != Self::Reset {
*self = Self::Closing(Self::make_close_frame(error, frame_type, message));
}
}
pub fn drain(
&mut self,
path: PathRef,
error: ConnectionError,
frame_type: FrameType,
message: impl AsRef<str>,
) {
if !matches!(self, Self::Reset) {
*self = Self::Draining(ClosingFrame::new(path, error, frame_type, message));
if *self != Self::Reset {
*self = Self::Draining(Self::make_close_frame(error, frame_type, message));
}
}
@ -227,15 +178,15 @@ impl StateSignaling {
match self {
Self::Closing(frame) => {
// When we are closing, we might need to send the close frame again.
let res = Some(frame.clone());
let frame = mem::replace(frame, Frame::Padding);
*self = Self::CloseSent(Some(frame.clone()));
res
Some(frame)
}
Self::Draining(frame) => {
// When we are draining, just send once.
let res = Some(frame.clone());
let frame = mem::replace(frame, Frame::Padding);
*self = Self::CloseSent(None);
res
Some(frame)
}
_ => None,
}
@ -244,7 +195,8 @@ impl StateSignaling {
/// If a close can be sent again, prepare to send it again.
pub fn send_close(&mut self) {
if let Self::CloseSent(Some(frame)) = self {
*self = Self::Closing(frame.clone());
let frame = mem::replace(frame, Frame::Padding);
*self = Self::Closing(frame);
}
}

Просмотреть файл

@ -6,20 +6,22 @@
use super::super::{Connection, Output};
use super::{
assert_full_cwnd, connect_rtt_idle, cwnd_packets, default_client, default_server, fill_cwnd,
send_something, AT_LEAST_PTO, DEFAULT_RTT, FORCE_IDLE_CLIENT_1RTT_PACKETS, POST_HANDSHAKE_CWND,
assert_full_cwnd, connect_force_idle, connect_rtt_idle, cwnd_packets, default_client,
default_server, fill_cwnd, send_something, AT_LEAST_PTO, DEFAULT_RTT, POST_HANDSHAKE_CWND,
};
use crate::cc::{CWND_MIN, MAX_DATAGRAM_SIZE};
use crate::frame::StreamType;
use crate::packet::PacketNumber;
use crate::recovery::{ACK_ONLY_SIZE_LIMIT, PACKET_THRESHOLD};
use crate::sender::PACING_BURST_SIZE;
use crate::stats::MAX_PTO_COUNTS;
use crate::stream_id::StreamType;
use crate::tparams::{self, TransportParameter};
use crate::tracking::MAX_UNACKED_PKTS;
use neqo_common::{qdebug, qinfo, qtrace, Datagram};
use std::convert::TryFrom;
use std::time::{Duration, Instant};
use test_fixture::{self, now};
fn induce_persistent_congestion(
client: &mut Connection,
@ -109,11 +111,18 @@ where
fn cc_slow_start() {
let mut client = default_client();
let mut server = default_server();
server
.set_local_tparam(
tparams::INITIAL_MAX_DATA,
TransportParameter::Integer(65536),
)
.unwrap();
let now = connect_rtt_idle(&mut client, &mut server, DEFAULT_RTT);
// Try to send a lot of data
let stream_id = client.stream_create(StreamType::UniDi).unwrap();
let (c_tx_dgrams, _) = fill_cwnd(&mut client, stream_id, now);
assert_eq!(client.stream_create(StreamType::UniDi).unwrap(), 2);
let (c_tx_dgrams, _) = fill_cwnd(&mut client, 2, now);
assert_full_cwnd(&c_tx_dgrams, POST_HANDSHAKE_CWND);
assert!(client.loss_recovery.cwnd_avail() < ACK_ONLY_SIZE_LIMIT);
}
@ -132,10 +141,9 @@ fn cc_slow_start_to_cong_avoidance_recovery_period() {
let (c_tx_dgrams, mut now) = fill_cwnd(&mut client, 0, now);
assert_full_cwnd(&c_tx_dgrams, POST_HANDSHAKE_CWND);
// Predict the packet number of the last packet sent.
// We have already sent packets in `connect_rtt_idle`,
// so include a fudge factor.
let flight1_largest =
PacketNumber::try_from(c_tx_dgrams.len() + FORCE_IDLE_CLIENT_1RTT_PACKETS).unwrap();
// We have already sent one packet in `connect_force_idle` (an ACK),
// so this will be equal to the number of packets in this flight.
let flight1_largest = PacketNumber::try_from(c_tx_dgrams.len()).unwrap();
// Server: Receive and generate ack
now += DEFAULT_RTT / 2;
@ -186,13 +194,13 @@ fn cc_slow_start_to_cong_avoidance_recovery_period() {
fn cc_cong_avoidance_recovery_period_unchanged() {
let mut client = default_client();
let mut server = default_server();
let now = connect_rtt_idle(&mut client, &mut server, DEFAULT_RTT);
connect_force_idle(&mut client, &mut server);
// Create stream 0
assert_eq!(client.stream_create(StreamType::BiDi).unwrap(), 0);
// Buffer up lot of data and generate packets
let (mut c_tx_dgrams, now) = fill_cwnd(&mut client, 0, now);
let (mut c_tx_dgrams, now) = fill_cwnd(&mut client, 0, now());
assert_full_cwnd(&c_tx_dgrams, POST_HANDSHAKE_CWND);
// Drop 0th packet. When acked, this should put client into CARP.
@ -228,31 +236,31 @@ fn cc_cong_avoidance_recovery_period_unchanged() {
fn single_packet_on_recovery() {
let mut client = default_client();
let mut server = default_server();
let now = connect_rtt_idle(&mut client, &mut server, DEFAULT_RTT);
connect_force_idle(&mut client, &mut server);
// Drop a few packets, up to the reordering threshold.
for _ in 0..PACKET_THRESHOLD {
let _dropped = send_something(&mut client, now);
let _dropped = send_something(&mut client, now());
}
let delivered = send_something(&mut client, now);
let delivered = send_something(&mut client, now());
// Now fill the congestion window.
assert_eq!(client.stream_create(StreamType::BiDi).unwrap(), 0);
let (_, now) = fill_cwnd(&mut client, 0, now);
let _ = fill_cwnd(&mut client, 0, now());
assert!(client.loss_recovery.cwnd_avail() < 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.
let ack = server.process(Some(delivered), now).dgram();
let ack = server.process(Some(delivered), now()).dgram();
assert!(ack.is_some());
// 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);
client.process_input(ack.unwrap(), now());
assert_eq!(client.loss_recovery.cwnd_avail(), 0);
// The client should send one packet, ignoring the cwnd.
let dgram = client.process_output(now).dgram();
let dgram = client.process_output(now()).dgram();
assert!(dgram.is_some());
}
@ -367,13 +375,13 @@ fn cc_slow_start_to_persistent_congestion_no_acks() {
fn cc_slow_start_to_persistent_congestion_some_acks() {
let mut client = default_client();
let mut server = default_server();
let now = connect_rtt_idle(&mut client, &mut server, DEFAULT_RTT);
connect_force_idle(&mut client, &mut server);
// Create stream 0
assert_eq!(client.stream_create(StreamType::BiDi).unwrap(), 0);
// Buffer up lot of data and generate packets
let (c_tx_dgrams, mut now) = fill_cwnd(&mut client, 0, now);
let (c_tx_dgrams, mut now) = fill_cwnd(&mut client, 0, now());
assert_full_cwnd(&c_tx_dgrams, POST_HANDSHAKE_CWND);
// Server: Receive and generate ack
@ -398,13 +406,13 @@ fn cc_slow_start_to_persistent_congestion_some_acks() {
fn cc_persistent_congestion_to_slow_start() {
let mut client = default_client();
let mut server = default_server();
let now = connect_rtt_idle(&mut client, &mut server, DEFAULT_RTT);
connect_force_idle(&mut client, &mut server);
// Create stream 0
assert_eq!(client.stream_create(StreamType::BiDi).unwrap(), 0);
// Buffer up lot of data and generate packets
let (c_tx_dgrams, mut now) = fill_cwnd(&mut client, 0, now);
let (c_tx_dgrams, mut now) = fill_cwnd(&mut client, 0, now());
assert_full_cwnd(&c_tx_dgrams, POST_HANDSHAKE_CWND);
// Server: Receive and generate ack
@ -442,13 +450,13 @@ fn cc_persistent_congestion_to_slow_start() {
fn ack_are_not_cc() {
let mut client = default_client();
let mut server = default_server();
let now = connect_rtt_idle(&mut client, &mut server, DEFAULT_RTT);
connect_force_idle(&mut client, &mut server);
// Create a stream
assert_eq!(client.stream_create(StreamType::BiDi).unwrap(), 0);
// Buffer up lot of data and generate packets, so that cc window is filled.
let (c_tx_dgrams, now) = fill_cwnd(&mut client, 0, now);
let (c_tx_dgrams, now) = fill_cwnd(&mut client, 0, now());
assert_full_cwnd(&c_tx_dgrams, POST_HANDSHAKE_CWND);
// The server hasn't received any of these packets yet, the server
@ -477,10 +485,11 @@ fn ack_are_not_cc() {
#[test]
fn pace() {
const RTT: Duration = Duration::from_millis(1000);
const DATA: &[u8] = &[0xcc; 4_096];
let mut client = default_client();
let mut server = default_server();
let mut now = connect_rtt_idle(&mut client, &mut server, DEFAULT_RTT);
let mut now = connect_rtt_idle(&mut client, &mut server, RTT);
// Now fill up the pipe and watch it trickle out.
let stream = client.stream_create(StreamType::BiDi).unwrap();
@ -492,32 +501,24 @@ fn pace() {
}
let mut count = 0;
// We should get a burst at first.
// The first packet is not subject to pacing as there are no bytes in flight.
// After that we allow the burst to continue up to a number of packets (2).
for _ in 0..=PACING_BURST_SIZE {
for _ in 0..PACING_BURST_SIZE {
let dgram = client.process_output(now).dgram();
assert!(dgram.is_some());
count += 1;
}
let gap = client.process_output(now).callback();
assert_ne!(gap, Duration::new(0, 0));
for _ in (1 + PACING_BURST_SIZE)..cwnd_packets(POST_HANDSHAKE_CWND) {
match client.process_output(now) {
Output::Callback(t) => assert_eq!(t, gap),
Output::Datagram(_) => {
// The last packet might not be paced.
count += 1;
break;
}
Output::None => panic!(),
}
// The last one will not be paced.
for _ in PACING_BURST_SIZE..cwnd_packets(POST_HANDSHAKE_CWND) - 1 {
assert_eq!(client.process_output(now).callback(), gap);
now += gap;
let dgram = client.process_output(now).dgram();
assert!(dgram.is_some());
count += 1;
}
let dgram = client.process_output(now).dgram();
assert!(dgram.is_none());
assert!(dgram.is_some());
count += 1;
assert_eq!(count, cwnd_packets(POST_HANDSHAKE_CWND));
let fin = client.process_output(now).callback();
assert_ne!(fin, Duration::new(0, 0));

Просмотреть файл

@ -11,7 +11,7 @@ use crate::{AppError, ConnectionError, Error, ERROR_APPLICATION_CLOSE};
use neqo_common::Datagram;
use std::time::Duration;
use test_fixture::{self, addr, now};
use test_fixture::{self, loopback, now};
fn assert_draining(c: &Connection, expected: &Error) {
assert!(c.state().closed());
@ -201,6 +201,6 @@ fn stateless_reset_client() {
.unwrap();
connect_force_idle(&mut client, &mut server);
client.process_input(Datagram::new(addr(), addr(), vec![77; 21]), now());
client.process_input(Datagram::new(loopback(), loopback(), vec![77; 21]), now());
assert_draining(&client, &Error::StatelessReset);
}

Просмотреть файл

@ -4,25 +4,24 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use super::super::{Connection, Output, State, LOCAL_IDLE_TIMEOUT};
use super::super::{Connection, FixedConnectionIdManager, Output, State, LOCAL_IDLE_TIMEOUT};
use super::{
assert_error, connect_force_idle, connect_with_rtt, default_client, default_server, get_tokens,
handshake, maybe_authenticate, send_something, CountingConnectionIdGenerator, AT_LEAST_PTO,
DEFAULT_RTT, DEFAULT_STREAM_DATA,
handshake, maybe_authenticate, send_something, AT_LEAST_PTO, DEFAULT_RTT, DEFAULT_STREAM_DATA,
};
use crate::connection::AddressValidation;
use crate::events::ConnectionEvent;
use crate::frame::StreamType;
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, Error};
use neqo_common::{event::Provider, qdebug, Datagram};
use neqo_crypto::{constants::TLS_CHACHA20_POLY1305_SHA256, AuthenticationStatus};
use std::cell::RefCell;
use std::rc::Rc;
use std::time::Duration;
use test_fixture::{self, addr, assertions, fixture_init, now, split_datagram};
use test_fixture::{self, assertions, fixture_init, loopback, now, split_datagram};
#[test]
fn full_handshake() {
@ -104,10 +103,10 @@ fn no_alpn() {
let mut client = Connection::new_client(
"example.com",
&["bad-alpn"],
Rc::new(RefCell::new(CountingConnectionIdGenerator::default())),
addr(),
addr(),
ConnectionParameters::default(),
Rc::new(RefCell::new(FixedConnectionIdManager::new(9))),
loopback(),
loopback(),
&ConnectionParameters::default(),
)
.unwrap();
let mut server = default_server();
@ -177,8 +176,8 @@ fn crypto_frame_split() {
let mut server = Connection::new_server(
test_fixture::LONG_CERT_KEYS,
test_fixture::DEFAULT_ALPN,
Rc::new(RefCell::new(CountingConnectionIdGenerator::default())),
ConnectionParameters::default(),
Rc::new(RefCell::new(FixedConnectionIdManager::new(6))),
&ConnectionParameters::default(),
)
.expect("create a server");
@ -232,10 +231,10 @@ fn chacha20poly1305() {
let mut client = Connection::new_client(
test_fixture::DEFAULT_SERVER_NAME,
test_fixture::DEFAULT_ALPN,
Rc::new(RefCell::new(EmptyConnectionIdGenerator::default())),
addr(),
addr(),
ConnectionParameters::default(),
Rc::new(RefCell::new(FixedConnectionIdManager::new(0))),
loopback(),
loopback(),
&ConnectionParameters::default(),
)
.expect("create a default client");
client.set_ciphers(&[TLS_CHACHA20_POLY1305_SHA256]).unwrap();
@ -552,9 +551,8 @@ fn reorder_1rtt() {
now += RTT / 2;
let s2 = server.process(c2, now).dgram();
// The server has now received those packets, and saved them.
// The three additional are: an Initial ACK, a Handshake,
// and a 1-RTT (containing NEW_CONNECTION_ID).
assert_eq!(server.stats().packets_rx, PACKETS * 2 + 5);
// The two additional are an Initial ACK and Handshake.
assert_eq!(server.stats().packets_rx, PACKETS * 2 + 4);
assert_eq!(server.stats().saved_datagrams, PACKETS);
assert_eq!(server.stats().dropped_rx, 1);
assert_eq!(*server.state(), State::Confirmed);
@ -620,8 +618,8 @@ fn verify_pkt_honors_mtu() {
assert_eq!(res, Output::Callback(LOCAL_IDLE_TIMEOUT));
// Try to send a large stream and verify first packet is correctly sized
let stream_id = client.stream_create(StreamType::UniDi).unwrap();
assert_eq!(client.stream_send(stream_id, &[0xbb; 2000]).unwrap(), 2000);
assert_eq!(client.stream_create(StreamType::UniDi).unwrap(), 2);
assert_eq!(client.stream_send(2, &[0xbb; 2000]).unwrap(), 2000);
let pkt0 = client.process(None, now);
assert!(matches!(pkt0, Output::Datagram(_)));
assert_eq!(pkt0.as_dgram_ref().unwrap().len(), PATH_MTU_V6);
@ -697,52 +695,3 @@ fn extra_initial_invalid_cid() {
let nothing = client.process(Some(dgram_copy), now).dgram();
assert!(nothing.is_none());
}
#[test]
fn anti_amplification() {
let mut client = default_client();
let mut server = default_server();
let mut now = now();
// With a gigantic transport parameter, the server is unable to complete
// the handshake within the amplification limit.
let very_big = TransportParameter::Bytes(vec![0; PATH_MTU_V6 * 3]);
server.set_local_tparam(0xce16, very_big).unwrap();
let c_init = client.process_output(now).dgram();
now += DEFAULT_RTT / 2;
let s_init1 = server.process(c_init, now).dgram().unwrap();
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);
let s_init3 = server.process_output(now).dgram().unwrap();
assert_eq!(s_init3.len(), PATH_MTU_V6);
let cb = server.process_output(now).callback();
assert_ne!(cb, Duration::new(0, 0));
now += DEFAULT_RTT / 2;
client.process_input(s_init1, now);
client.process_input(s_init2, now);
let ack_count = client.stats().frame_tx.ack;
let frame_count = client.stats().frame_tx.all;
let ack = client.process(Some(s_init3), now).dgram().unwrap();
assert!(!maybe_authenticate(&mut client)); // No need yet.
// The client sends a padded datagram, with just ACK for Initial + Handshake.
assert_eq!(client.stats().frame_tx.ack, ack_count + 2);
assert_eq!(client.stats().frame_tx.all, frame_count + 2);
assert_ne!(ack.len(), PATH_MTU_V6); // Not padded (it includes Handshake).
now += DEFAULT_RTT / 2;
let remainder = server.process(Some(ack), now).dgram();
now += DEFAULT_RTT / 2;
client.process_input(remainder.unwrap(), now);
assert!(maybe_authenticate(&mut client)); // OK, we have all of it.
let fin = client.process_output(now).dgram();
assert_eq!(*client.state(), State::Connected);
now += DEFAULT_RTT / 2;
server.process_input(fin.unwrap(), now);
assert_eq!(*server.state(), State::Confirmed);
}

Просмотреть файл

@ -9,10 +9,10 @@ use super::{
connect, connect_force_idle, connect_with_rtt, default_client, default_server,
maybe_authenticate, send_something, AT_LEAST_PTO,
};
use crate::frame::StreamType;
use crate::packet::PacketBuilder;
use crate::tparams::{self, TransportParameter};
use crate::tracking::PNSpace;
use crate::StreamType;
use neqo_common::Encoder;
use std::time::Duration;
@ -58,19 +58,13 @@ fn asymmetric_idle_timeout() {
server.idle_timeout = IdleTimeout::new(LOWER_TIMEOUT);
// Now connect and force idleness manually.
// We do that by following what `force_idle` does and have each endpoint
// send two packets, which are delivered out of order. See `force_idle`.
connect(&mut client, &mut server);
let c1 = send_something(&mut client, now());
let c2 = send_something(&mut client, now());
server.process_input(c2, now());
server.process_input(c1, now());
let s1 = send_something(&mut server, now());
let s2 = send_something(&mut server, now());
client.process_input(s2, now());
let ack = client.process(Some(s1), now()).dgram();
let p1 = send_something(&mut server, now());
let p2 = send_something(&mut server, now());
client.process_input(p2, now());
let ack = client.process(Some(p1), now()).dgram();
assert!(ack.is_some());
// Now both should have received ACK frames so should be idle.
// Now the server has its ACK and both should be idle.
assert_eq!(server.process(ack, now()), Output::Callback(LOWER_TIMEOUT));
assert_eq!(client.process(None, now()), Output::Callback(LOWER_TIMEOUT));
}
@ -97,16 +91,11 @@ fn tiny_idle_timeout() {
// Now connect with an RTT and force idleness manually.
let mut now = connect_with_rtt(&mut client, &mut server, now(), RTT);
let c1 = send_something(&mut client, now);
let c2 = send_something(&mut client, now);
let p1 = send_something(&mut server, now);
let p2 = send_something(&mut server, now);
now += RTT / 2;
server.process_input(c2, now);
server.process_input(c1, now);
let s1 = send_something(&mut server, now);
let s2 = send_something(&mut server, now);
now += RTT / 2;
client.process_input(s2, now);
let ack = client.process(Some(s1), now).dgram();
client.process_input(p2, now);
let ack = client.process(Some(p1), now).dgram();
assert!(ack.is_some());
// The client should be idle now, but with a different timer.
@ -136,8 +125,8 @@ fn idle_send_packet1() {
let res = client.process(None, now);
assert_eq!(res, Output::Callback(LOCAL_IDLE_TIMEOUT));
let stream_id = client.stream_create(StreamType::UniDi).unwrap();
assert_eq!(client.stream_send(stream_id, b"hello").unwrap(), 5);
assert_eq!(client.stream_create(StreamType::UniDi).unwrap(), 2);
assert_eq!(client.stream_send(2, b"hello").unwrap(), 5);
let out = client.process(None, now + Duration::from_secs(10));
let out = server.process(out.dgram(), now + Duration::from_secs(10));
@ -167,12 +156,12 @@ fn idle_send_packet2() {
let res = client.process(None, now);
assert_eq!(res, Output::Callback(LOCAL_IDLE_TIMEOUT));
let stream_id = client.stream_create(StreamType::UniDi).unwrap();
assert_eq!(client.stream_send(stream_id, b"hello").unwrap(), 5);
assert_eq!(client.stream_create(StreamType::UniDi).unwrap(), 2);
assert_eq!(client.stream_send(2, b"hello").unwrap(), 5);
let _out = client.process(None, now + Duration::from_secs(10));
assert_eq!(client.stream_send(stream_id, b"there").unwrap(), 5);
assert_eq!(client.stream_send(2, b"there").unwrap(), 5);
let _out = client.process(None, now + Duration::from_secs(20));
// Still connected after 39 seconds.
@ -253,14 +242,12 @@ fn idle_caching() {
let crypto = server
.crypto
.streams
.write_frame(PNSpace::Initial, &mut builder)
.unwrap();
.write_frame(PNSpace::Initial, &mut builder);
assert!(crypto.is_some());
let crypto = server
.crypto
.streams
.write_frame(PNSpace::Initial, &mut builder)
.unwrap();
.write_frame(PNSpace::Initial, &mut builder);
assert!(crypto.is_none());
let dgram = server.process_output(middle).dgram();
@ -277,7 +264,6 @@ fn idle_caching() {
// Now let the server Initial through, with the CRYPTO frame.
let dgram = server.process_output(end).dgram();
let (initial, _) = split_datagram(&dgram.unwrap());
neqo_common::qwarn!("client ingests initial, finally");
let _ = client.process(Some(initial), end);
maybe_authenticate(&mut client);
let dgram = client.process_output(end).dgram();

Просмотреть файл

@ -1,741 +0,0 @@
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use super::super::{Connection, Output, State, StreamType};
use super::{
connect_fail, connect_force_idle, default_client, default_server, maybe_authenticate,
new_client, new_server, send_something,
};
use crate::path::{PATH_MTU_V4, PATH_MTU_V6};
use crate::tparams::{self, PreferredAddress, TransportParameter};
use crate::{ConnectionError, ConnectionParameters, EmptyConnectionIdGenerator, Error};
use neqo_common::Datagram;
use std::cell::RefCell;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
use std::rc::Rc;
use std::time::Duration;
use test_fixture::{self, addr, fixture_init, now};
/// This should be a valid-seeming transport parameter.
/// And it should have different values to `addr` and `addr_v4`.
const SAMPLE_PREFERRED_ADDRESS: &[u8] = &[
0xc0, 0x00, 0x02, 0x02, 0x01, 0xbb, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0xbb, 0x05, 0x01, 0x02, 0x03, 0x04, 0x05, 0x03, 0x03,
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
];
// These tests generally use two paths:
// The connection is established on a path with the same IPv6 address on both ends.
// Migrations move to a path with the same IPv4 address on both ends.
// This simplifies validation as the same assertions can be used for client and server.
// The risk is that there is a place where source/destination local/remote is inverted.
fn addr_v4() -> SocketAddr {
let localhost_v4 = IpAddr::V4(Ipv4Addr::from(0xc000_0201));
SocketAddr::new(localhost_v4, addr().port())
}
fn loopback() -> SocketAddr {
SocketAddr::new(IpAddr::V6(Ipv6Addr::from(1)), 443)
}
fn change_path(d: &Datagram, a: SocketAddr) -> Datagram {
Datagram::new(a, a, &d[..])
}
fn new_port(a: SocketAddr) -> SocketAddr {
let (port, _) = a.port().overflowing_add(410);
SocketAddr::new(a.ip(), port)
}
fn change_source_port(d: &Datagram) -> Datagram {
Datagram::new(new_port(d.source()), d.destination(), &d[..])
}
fn assert_path(dgram: &Datagram, path_addr: SocketAddr) {
assert_eq!(dgram.source(), path_addr);
assert_eq!(dgram.destination(), path_addr);
}
fn assert_v4_path(dgram: &Datagram, padded: bool) {
assert_path(dgram, addr_v4());
if padded {
assert_eq!(dgram.len(), PATH_MTU_V4);
}
}
fn assert_v6_path(dgram: &Datagram, padded: bool) {
assert_path(dgram, addr());
if padded {
assert_eq!(dgram.len(), PATH_MTU_V6);
}
}
#[test]
fn rebinding_port() {
let mut client = default_client();
let mut server = default_server();
connect_force_idle(&mut client, &mut server);
let dgram = send_something(&mut client, now());
let dgram = change_source_port(&dgram);
server.process_input(dgram, now());
// Have the server send something so that it generates a packet.
let stream_id = server.stream_create(StreamType::UniDi).unwrap();
server.stream_close_send(stream_id).unwrap();
let dgram = server.process_output(now()).dgram();
let dgram = dgram.unwrap();
assert_eq!(dgram.source(), addr());
assert_eq!(dgram.destination(), new_port(addr()));
}
/// This simulates an attack where a valid packet is forwarded on
/// a different path. This shows how both paths are probed and the
/// server eventually returns to the original path.
#[test]
fn path_forwarding_attack() {
let mut client = default_client();
let mut server = default_server();
connect_force_idle(&mut client, &mut server);
let dgram = send_something(&mut client, now());
let dgram = change_path(&dgram, addr_v4());
server.process_input(dgram, now());
// The server now probes the new (primary) path.
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();
assert_eq!(server.stats().frame_tx.path_challenge, 2);
assert_v6_path(&old_probe, true);
// New data from the server is sent on the new path, but that is
// 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());
// 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();
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();
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());
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();
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());
let stream_before = client.stats().frame_tx.stream;
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();
assert_v6_path(&client_data2, false);
// The server keeps sending on the new path.
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());
// The server sends a probe on the "old" path.
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();
assert_v6_path(&server_data4, false);
}
#[test]
fn migrate_immediate() {
let mut client = default_client();
let mut server = default_server();
connect_force_idle(&mut client, &mut server);
client
.migrate(Some(addr_v4()), Some(addr_v4()), true, now())
.unwrap();
let client1 = send_something(&mut client, now());
assert_v4_path(&client1, true); // Contains PATH_CHALLENGE.
let client2 = send_something(&mut client, now());
assert_v4_path(&client2, false); // Doesn't.
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();
assert_v4_path(&server1, true);
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();
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());
assert_v4_path(&client3, false);
}
#[test]
fn migrate_immediate_fail() {
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)
.unwrap();
let probe = client.process_output(now).dgram().unwrap();
assert_v4_path(&probe, true); // Contains PATH_CHALLENGE.
for _ in 0..2 {
let cb = client.process_output(now).callback();
assert_ne!(cb, Duration::new(0, 0));
now += cb;
let before = client.stats().frame_tx;
let probe = client.process_output(now).dgram().unwrap();
assert_v4_path(&probe, true); // Contains PATH_CHALLENGE.
let after = client.stats().frame_tx;
assert_eq!(after.path_challenge, before.path_challenge + 1);
assert_eq!(after.padding, before.padding + 1);
assert_eq!(after.all, before.all + 2);
// This might be a PTO, which will result in sending a probe.
if let Some(probe) = client.process_output(now).dgram() {
assert_v4_path(&probe, false); // Contains PATH_CHALLENGE.
let after = client.stats().frame_tx;
assert_eq!(after.ping, before.ping + 1);
assert_eq!(after.all, before.all + 3);
}
}
let pto = client.process_output(now).callback();
assert_ne!(pto, Duration::new(0, 0));
now += pto;
// The client should fall back to the original path and retire the connection ID.
let fallback = client.process_output(now).dgram();
assert_v6_path(&fallback.unwrap(), false);
assert_eq!(client.stats().frame_tx.retire_connection_id, 1);
}
/// Migrating to the same path shouldn't do anything special,
/// except that the path is probed.
#[test]
fn migrate_same() {
let mut client = default_client();
let mut server = default_server();
connect_force_idle(&mut client, &mut server);
let now = now();
client
.migrate(Some(addr()), Some(addr()), true, now)
.unwrap();
let probe = client.process_output(now).dgram().unwrap();
assert_v6_path(&probe, true); // Contains PATH_CHALLENGE.
assert_eq!(client.stats().frame_tx.path_challenge, 1);
let resp = server.process(Some(probe), now).dgram().unwrap();
assert_v6_path(&resp, true);
assert_eq!(server.stats().frame_tx.path_response, 1);
assert_eq!(server.stats().frame_tx.path_challenge, 0);
// Everything continues happily.
client.process_input(resp, now);
let contd = send_something(&mut client, now);
assert_v6_path(&contd, false);
}
/// Migrating to the same path, if it fails, causes the connection to fail.
#[test]
fn migrate_same_fail() {
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()), Some(addr()), true, now)
.unwrap();
let probe = client.process_output(now).dgram().unwrap();
assert_v6_path(&probe, true); // Contains PATH_CHALLENGE.
for _ in 0..2 {
let cb = client.process_output(now).callback();
assert_ne!(cb, Duration::new(0, 0));
now += cb;
let before = client.stats().frame_tx;
let probe = client.process_output(now).dgram().unwrap();
assert_v6_path(&probe, true); // Contains PATH_CHALLENGE.
let after = client.stats().frame_tx;
assert_eq!(after.path_challenge, before.path_challenge + 1);
assert_eq!(after.padding, before.padding + 1);
assert_eq!(after.all, before.all + 2);
// This might be a PTO, which will result in sending a probe.
if let Some(probe) = client.process_output(now).dgram() {
assert_v6_path(&probe, false); // Contains PATH_CHALLENGE.
let after = client.stats().frame_tx;
assert_eq!(after.ping, before.ping + 1);
assert_eq!(after.all, before.all + 3);
}
}
let pto = client.process_output(now).callback();
assert_ne!(pto, Duration::new(0, 0));
now += pto;
// The client should mark this path as failed and close immediately.
let res = client.process_output(now);
assert!(matches!(res, Output::None));
assert!(matches!(
client.state(),
State::Closed(ConnectionError::Transport(Error::NoAvailablePath))
));
}
fn migration(mut client: Connection) {
let mut server = default_server();
connect_force_idle(&mut client, &mut server);
let now = now();
client
.migrate(Some(addr_v4()), Some(addr_v4()), false, now)
.unwrap();
let probe = client.process_output(now).dgram().unwrap();
assert_v4_path(&probe, true); // Contains PATH_CHALLENGE.
assert_eq!(client.stats().frame_tx.path_challenge, 1);
let resp = server.process(Some(probe), now).dgram().unwrap();
assert_v4_path(&resp, true);
assert_eq!(server.stats().frame_tx.path_response, 1);
assert_eq!(server.stats().frame_tx.path_challenge, 1);
// Data continues to be exchanged on the new path.
let client_data = send_something(&mut client, now);
assert_v6_path(&client_data, false);
server.process_input(client_data, now);
let server_data = send_something(&mut server, now);
assert_v6_path(&server_data, false);
// Once the client receives the probe response, it migrates to the new path.
client.process_input(resp, now);
assert_eq!(client.stats().frame_rx.path_challenge, 1);
let migrate_client = send_something(&mut client, now);
assert_v4_path(&migrate_client, true); // Responds to server probe.
// The server now sees the migration and will switch over.
// However, it will probe the old path again, even though it has just
// received a response to its last probe, because it needs to verify
// that the migration is genuine.
server.process_input(migrate_client, now);
let stream_before = server.stats().frame_tx.stream;
let probe_old_server = send_something(&mut server, now);
// This is just the double-check probe; no STREAM frames.
assert_v6_path(&probe_old_server, true);
assert_eq!(server.stats().frame_tx.path_challenge, 2);
assert_eq!(server.stats().frame_tx.stream, stream_before);
// The server then sends data on the new path.
let migrate_server = server.process_output(now).dgram().unwrap();
assert_v4_path(&migrate_server, false);
assert_eq!(server.stats().frame_tx.path_challenge, 2);
assert_eq!(server.stats().frame_tx.stream, stream_before + 1);
// The client receives these checks and responds to the probe, but uses the new path.
client.process_input(migrate_server, now);
client.process_input(probe_old_server, now);
let old_probe_resp = send_something(&mut client, now);
assert_v6_path(&old_probe_resp, true);
let client_confirmation = client.process_output(now).dgram().unwrap();
assert_v4_path(&client_confirmation, false);
let server_confirmation = send_something(&mut server, now);
assert_v4_path(&server_confirmation, false);
}
#[test]
fn migration_graceful() {
migration(default_client());
}
/// A client should be able to migrate when it has a zero-length connection ID.
#[test]
fn migration_client_empty_cid() {
fixture_init();
let client = Connection::new_client(
test_fixture::DEFAULT_SERVER_NAME,
test_fixture::DEFAULT_ALPN,
Rc::new(RefCell::new(EmptyConnectionIdGenerator::default())),
addr(),
addr(),
ConnectionParameters::default(),
)
.unwrap();
migration(client);
}
/// Drive the handshake in the most expeditious fashion.
/// Returns the packet containing `HANDSHAKE_DONE` from the server.
fn fast_handshake(client: &mut Connection, server: &mut Connection) -> Option<Datagram> {
let dgram = client.process_output(now()).dgram();
let dgram = server.process(dgram, now()).dgram();
client.process_input(dgram.unwrap(), now());
assert!(maybe_authenticate(client));
let dgram = client.process_output(now()).dgram();
server.process(dgram, now()).dgram()
}
fn preferred_address(hs_client: SocketAddr, hs_server: SocketAddr, preferred: SocketAddr) {
let mtu = match hs_client.ip() {
IpAddr::V4(_) => PATH_MTU_V4,
IpAddr::V6(_) => PATH_MTU_V6,
};
let assert_orig_path = |d: &Datagram, full_mtu: bool| {
assert_eq!(
d.destination(),
if d.source() == hs_client {
hs_server
} else if d.source() == hs_server {
hs_client
} else {
panic!();
}
);
if full_mtu {
assert_eq!(d.len(), mtu);
}
};
let assert_toward_spa = |d: &Datagram, full_mtu: bool| {
assert_eq!(d.destination(), preferred);
assert_eq!(d.source(), hs_client);
if full_mtu {
assert_eq!(d.len(), mtu);
}
};
let assert_from_spa = |d: &Datagram, full_mtu: bool| {
assert_eq!(d.destination(), hs_client);
assert_eq!(d.source(), preferred);
if full_mtu {
assert_eq!(d.len(), mtu);
}
};
fixture_init();
let mut client = Connection::new_client(
test_fixture::DEFAULT_SERVER_NAME,
test_fixture::DEFAULT_ALPN,
Rc::new(RefCell::new(EmptyConnectionIdGenerator::default())),
hs_client,
hs_server,
ConnectionParameters::default(),
)
.unwrap();
let spa = if preferred.ip().is_ipv6() {
PreferredAddress::new(None, Some(preferred))
} else {
PreferredAddress::new(Some(preferred), None)
};
let mut server = new_server(ConnectionParameters::default().preferred_address(spa));
let dgram = fast_handshake(&mut client, &mut server);
// The client is about to process HANDSHAKE_DONE.
// It should start probing toward the server's preferred address.
let probe = client.process(dgram, now()).dgram().unwrap();
assert_toward_spa(&probe, true);
assert_eq!(client.stats().frame_tx.path_challenge, 1);
assert_ne!(client.process_output(now()).callback(), Duration::new(0, 0));
// Data continues on the main path for the client.
let data = send_something(&mut client, now());
assert_orig_path(&data, false);
// The server responds to the probe.
let resp = server.process(Some(probe), now()).dgram().unwrap();
assert_from_spa(&resp, true);
assert_eq!(server.stats().frame_tx.path_challenge, 1);
assert_eq!(server.stats().frame_tx.path_response, 1);
// Data continues on the main path for the server.
server.process_input(data, now());
let data = send_something(&mut server, now());
assert_orig_path(&data, false);
// Client gets the probe response back and it migrates.
client.process_input(resp, now());
client.process_input(data, now());
let data = send_something(&mut client, now());
assert_toward_spa(&data, true);
assert_eq!(client.stats().frame_tx.stream, 2);
assert_eq!(client.stats().frame_tx.path_response, 1);
// The server sees the migration and probes the old path.
let probe = server.process(Some(data), now()).dgram().unwrap();
assert_orig_path(&probe, true);
assert_eq!(server.stats().frame_tx.path_challenge, 2);
// But data now goes on the new path.
let data = send_something(&mut server, now());
assert_from_spa(&data, false);
}
/// Migration works for a new port number.
#[test]
fn preferred_address_new_port() {
let a = addr();
preferred_address(a, a, new_port(a));
}
/// Migration works for a new address too.
#[test]
fn preferred_address_new_address() {
let mut preferred = addr();
preferred.set_ip(IpAddr::V6(Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 2)));
preferred_address(addr(), addr(), preferred);
}
/// Migration works for IPv4 addresses.
#[test]
fn preferred_address_new_port_v4() {
let a = addr_v4();
preferred_address(a, a, new_port(a));
}
/// Migrating to a loopback address is OK if we started there.
#[test]
fn preferred_address_loopback() {
let a = loopback();
preferred_address(a, a, new_port(a));
}
fn expect_no_migration(client: &mut Connection, server: &mut Connection) {
let dgram = fast_handshake(client, server);
// The client won't probe now, though it could; it remains idle.
let out = client.process(dgram, now());
assert_ne!(out.callback(), Duration::new(0, 0));
// Data continues on the main path for the client.
let data = send_something(client, now());
assert_v6_path(&data, false);
assert_eq!(client.stats().frame_tx.path_challenge, 0);
}
fn preferred_address_ignored(spa: PreferredAddress) {
let mut client = default_client();
let mut server = new_server(ConnectionParameters::default().preferred_address(spa));
expect_no_migration(&mut client, &mut server);
}
/// Using a loopback address in the preferred address is ignored.
#[test]
fn preferred_address_ignore_loopback() {
preferred_address_ignored(PreferredAddress::new(None, Some(loopback())));
}
/// A preferred address in the wrong address family is ignored.
#[test]
fn preferred_address_ignore_different_family() {
preferred_address_ignored(PreferredAddress::new(Some(addr_v4()), None));
}
/// Disabling preferred addresses at the client means that it ignores a perfectly
/// good preferred address.
#[test]
fn preferred_address_disabled_client() {
let mut client = new_client(ConnectionParameters::default().disable_preferred_address());
let mut preferred = addr();
preferred.set_ip(IpAddr::V6(Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 2)));
let spa = PreferredAddress::new(None, Some(preferred));
let mut server = new_server(ConnectionParameters::default().preferred_address(spa));
expect_no_migration(&mut client, &mut server);
}
#[test]
fn preferred_address_empty_cid() {
fixture_init();
let spa = PreferredAddress::new(None, Some(new_port(addr())));
let res = Connection::new_server(
test_fixture::DEFAULT_KEYS,
test_fixture::DEFAULT_ALPN,
Rc::new(RefCell::new(EmptyConnectionIdGenerator::default())),
ConnectionParameters::default().preferred_address(spa),
);
assert_eq!(res.unwrap_err(), Error::ConnectionIdsExhausted);
}
/// A server cannot include a preferred address if it chooses an empty connection ID.
#[test]
fn preferred_address_server_empty_cid() {
let mut client = default_client();
let mut server = Connection::new_server(
test_fixture::DEFAULT_KEYS,
test_fixture::DEFAULT_ALPN,
Rc::new(RefCell::new(EmptyConnectionIdGenerator::default())),
ConnectionParameters::default(),
)
.unwrap();
server
.set_local_tparam(
tparams::PREFERRED_ADDRESS,
TransportParameter::Bytes(SAMPLE_PREFERRED_ADDRESS.to_vec()),
)
.unwrap();
connect_fail(
&mut client,
&mut server,
Error::TransportParameterError,
Error::PeerError(Error::TransportParameterError.code()),
);
}
/// A client shouldn't send a preferred address transport parameter.
#[test]
fn preferred_address_client() {
let mut client = default_client();
let mut server = default_server();
client
.set_local_tparam(
tparams::PREFERRED_ADDRESS,
TransportParameter::Bytes(SAMPLE_PREFERRED_ADDRESS.to_vec()),
)
.unwrap();
connect_fail(
&mut client,
&mut server,
Error::PeerError(Error::TransportParameterError.code()),
Error::TransportParameterError,
);
}
/// Test that migration isn't permitted if the connection isn't in the right state.
#[test]
fn migration_invalid_state() {
let mut client = default_client();
assert!(client
.migrate(Some(addr()), Some(addr()), false, now())
.is_err());
let mut server = default_server();
assert!(server
.migrate(Some(addr()), Some(addr()), false, now())
.is_err());
connect_force_idle(&mut client, &mut server);
assert!(server
.migrate(Some(addr()), Some(addr()), false, now())
.is_err());
client.close(now(), 0, "closing");
assert!(client
.migrate(Some(addr()), Some(addr()), false, now())
.is_err());
let close = client.process(None, now()).dgram();
let dgram = server.process(close, now()).dgram();
assert!(server
.migrate(Some(addr()), Some(addr()), false, now())
.is_err());
client.process_input(dgram.unwrap(), now());
assert!(client
.migrate(Some(addr()), Some(addr()), false, now())
.is_err());
}
#[test]
fn migration_invalid_address() {
let mut client = default_client();
let mut server = default_server();
connect_force_idle(&mut client, &mut server);
let mut cant_migrate = |local, remote| {
assert_eq!(
client.migrate(local, remote, true, now()).unwrap_err(),
Error::InvalidMigration
)
};
// Providing neither address is pointless and therefore an error.
cant_migrate(None, None);
// Providing a zero port number isn't valid.
let mut zero_port = addr();
zero_port.set_port(0);
cant_migrate(None, Some(zero_port));
cant_migrate(Some(zero_port), None);
// An unspecified remote address is bad.
let mut remote_unspecified = addr();
remote_unspecified.set_ip(IpAddr::V6(Ipv6Addr::from(0)));
cant_migrate(None, Some(remote_unspecified));
// Mixed address families is bad.
cant_migrate(Some(addr()), Some(addr_v4()));
cant_migrate(Some(addr_v4()), Some(addr()));
// Loopback to non-loopback is bad.
cant_migrate(Some(addr()), Some(loopback()));
cant_migrate(Some(loopback()), Some(addr()));
assert_eq!(
client
.migrate(Some(addr()), Some(loopback()), true, now())
.unwrap_err(),
Error::InvalidMigration
);
assert_eq!(
client
.migrate(Some(loopback()), Some(addr()), true, now())
.unwrap_err(),
Error::InvalidMigration
);
}

Просмотреть файл

@ -7,24 +7,24 @@
#![deny(clippy::pedantic)]
use super::{
Connection, ConnectionError, ConnectionId, ConnectionIdRef, Output, State, LOCAL_IDLE_TIMEOUT,
Connection, ConnectionError, FixedConnectionIdManager, Output, State, LOCAL_IDLE_TIMEOUT,
};
use crate::addr_valid::{AddressValidation, ValidateAddress};
use crate::cc::CWND_INITIAL_PKTS;
use crate::events::ConnectionEvent;
use crate::frame::StreamType;
use crate::path::PATH_MTU_V6;
use crate::recovery::ACK_ONLY_SIZE_LIMIT;
use crate::{ConnectionIdDecoder, ConnectionIdGenerator, ConnectionParameters, Error, StreamType};
use crate::ConnectionParameters;
use std::cell::RefCell;
use std::convert::TryFrom;
use std::mem;
use std::rc::Rc;
use std::time::{Duration, Instant};
use neqo_common::{event::Provider, qdebug, qtrace, Datagram, Decoder};
use neqo_crypto::{random, AllowZeroRtt, AuthenticationStatus, ResumptionToken};
use test_fixture::{self, addr, fixture_init, now};
use neqo_common::{event::Provider, qdebug, qtrace, Datagram};
use neqo_crypto::{AllowZeroRtt, AuthenticationStatus, ResumptionToken};
use test_fixture::{self, fixture_init, loopback, now};
// All the tests.
mod cc;
@ -32,7 +32,6 @@ mod close;
mod handshake;
mod idle;
mod keys;
mod migration;
mod recovery;
mod resumption;
mod stream;
@ -42,84 +41,39 @@ mod zerortt;
const DEFAULT_RTT: Duration = Duration::from_millis(100);
const AT_LEAST_PTO: Duration = Duration::from_secs(1);
const DEFAULT_STREAM_DATA: &[u8] = b"message";
/// The number of 1-RTT packets sent in `force_idle` by a client.
const FORCE_IDLE_CLIENT_1RTT_PACKETS: usize = 3;
/// WARNING! In this module, this version of the generator needs to be used.
/// This copies the implementation from
/// `test_fixture::CountingConnectionIdGenerator`, but it uses the different
/// types that are exposed to this module. See also `default_client`.
///
/// This version doesn't randomize the length; as the congestion control tests
/// count the amount of data sent precisely.
#[derive(Debug, Default)]
pub struct CountingConnectionIdGenerator {
counter: u32,
}
impl ConnectionIdDecoder for CountingConnectionIdGenerator {
fn decode_cid<'a>(&self, dec: &mut Decoder<'a>) -> Option<ConnectionIdRef<'a>> {
let len = usize::from(dec.peek_byte().unwrap());
dec.decode(len).map(ConnectionIdRef::from)
}
}
impl ConnectionIdGenerator for CountingConnectionIdGenerator {
fn generate_cid(&mut self) -> Option<ConnectionId> {
let mut r = random(20);
r[0] = 8;
r[1] = u8::try_from(self.counter >> 24).unwrap();
r[2] = u8::try_from((self.counter >> 16) & 0xff).unwrap();
r[3] = u8::try_from((self.counter >> 8) & 0xff).unwrap();
r[4] = u8::try_from(self.counter & 0xff).unwrap();
self.counter += 1;
Some(ConnectionId::from(&r[..8]))
}
fn as_decoder(&self) -> &dyn ConnectionIdDecoder {
self
}
}
// This is fabulous: because test_fixture uses the public API for Connection,
// it gets a different type to the ones that are referenced via super::super::*.
// Thus, this code can't use default_client() and default_server() from
// test_fixture because they produce different - and incompatible - types.
// test_fixture because they produce different types.
//
// These are a direct copy of those functions.
pub fn new_client(params: ConnectionParameters) -> Connection {
pub fn default_client() -> Connection {
fixture_init();
Connection::new_client(
test_fixture::DEFAULT_SERVER_NAME,
test_fixture::DEFAULT_ALPN,
Rc::new(RefCell::new(CountingConnectionIdGenerator::default())),
addr(),
addr(),
params,
Rc::new(RefCell::new(FixedConnectionIdManager::new(3))),
loopback(),
loopback(),
&ConnectionParameters::default(),
)
.expect("create a default client")
}
pub fn default_client() -> Connection {
new_client(ConnectionParameters::default())
}
pub fn new_server(params: ConnectionParameters) -> Connection {
pub fn default_server() -> Connection {
fixture_init();
let mut c = Connection::new_server(
test_fixture::DEFAULT_KEYS,
test_fixture::DEFAULT_ALPN,
Rc::new(RefCell::new(CountingConnectionIdGenerator::default())),
params,
Rc::new(RefCell::new(FixedConnectionIdManager::new(5))),
&ConnectionParameters::default(),
)
.expect("create a default server");
c.server_enable_0rtt(&test_fixture::anti_replay(), AllowZeroRtt {})
.expect("enable 0-RTT");
c
}
pub fn default_server() -> Connection {
new_server(ConnectionParameters::default())
}
/// If state is `AuthenticationNeeded` call `authenticated()`. This function will
/// consume all outstanding events on the connection.
@ -152,7 +106,7 @@ fn handshake(
let output = a.process(input, now).dgram();
assert!(had_input || output.is_some());
input = output;
qtrace!("handshake: t += {:?}", rtt / 2);
qtrace!("t += {:?}", rtt / 2);
now += rtt / 2;
mem::swap(&mut a, &mut b);
}
@ -160,17 +114,6 @@ fn handshake(
now
}
fn connect_fail(
client: &mut Connection,
server: &mut Connection,
client_error: Error,
server_error: Error,
) {
handshake(client, server, now(), Duration::new(0, 0));
assert_error(client, &ConnectionError::Transport(client_error));
assert_error(server, &ConnectionError::Transport(server_error));
}
fn connect_with_rtt(
client: &mut Connection,
server: &mut Connection,
@ -179,7 +122,7 @@ fn connect_with_rtt(
) -> Instant {
let now = handshake(client, server, now, rtt);
assert_eq!(*client.state(), State::Confirmed);
assert_eq!(*server.state(), State::Confirmed);
assert_eq!(*client.state(), State::Confirmed);
assert_eq!(client.loss_recovery.rtt(), rtt);
assert_eq!(server.loss_recovery.rtt(), rtt);
@ -215,53 +158,28 @@ fn exchange_ticket(
get_tokens(client).pop().expect("should have token")
}
/// Connect with an RTT and then force both peers to be idle.
/// Getting the client and server to reach an idle state is surprisingly hard.
/// The server sends `HANDSHAKE_DONE` at the end of the handshake, and the client
/// doesn't immediately acknowledge it. Reordering packets does the trick.
fn force_idle(
client: &mut Connection,
server: &mut Connection,
rtt: Duration,
mut now: Instant,
) -> Instant {
// The client has sent NEW_CONNECTION_ID, so ensure that the server generates
// an acknowledgment by sending some reordered packets.
qtrace!("force_idle: send reordered client packets");
let c1 = send_something(client, now);
let c2 = send_something(client, now);
fn connect_rtt_idle(client: &mut Connection, server: &mut Connection, rtt: Duration) -> Instant {
let mut now = connect_with_rtt(client, server, now(), rtt);
let p1 = send_something(server, now);
let p2 = send_something(server, now);
now += rtt / 2;
server.process_input(c2, now);
server.process_input(c1, now);
// Now do the same for the server. (The ACK is in the first one.)
qtrace!("force_idle: send reordered server packets");
let s1 = send_something(server, now);
let s2 = send_something(server, now);
now += rtt / 2;
// Delivering s2 first at the client causes it to want to ACK.
client.process_input(s2, now);
// Delivering s1 should not have the client change its mind about the ACK.
let ack = client.process(Some(s1), now).dgram();
// Delivering p2 first at the client causes it to want to ACK.
client.process_input(p2, now);
// Delivering p1 should not have the client change its mind about the ACK.
let ack = client.process(Some(p1), now).dgram();
assert!(ack.is_some());
assert_eq!(
client.process_output(now),
Output::Callback(LOCAL_IDLE_TIMEOUT)
);
now += rtt / 2;
assert_eq!(
server.process(ack, now),
Output::Callback(LOCAL_IDLE_TIMEOUT)
);
now
}
/// Connect with an RTT and then force both peers to be idle.
fn connect_rtt_idle(client: &mut Connection, server: &mut Connection, rtt: Duration) -> Instant {
let now = connect_with_rtt(client, server, now(), rtt);
let now = force_idle(client, server, rtt, now);
// Drain events from both as well.
let _ = client.events().count();
let _ = server.events().count();
assert_eq!(
client.process_output(now),
Output::Callback(LOCAL_IDLE_TIMEOUT)
);
now
}
@ -329,8 +247,7 @@ const POST_HANDSHAKE_CWND: usize = PATH_MTU_V6 * CWND_INITIAL_PKTS;
/// Determine the number of packets required to fill the CWND.
const fn cwnd_packets(data: usize) -> usize {
// Add one if the last chunk is >= ACK_ONLY_SIZE_LIMIT.
(data + PATH_MTU_V6 - ACK_ONLY_SIZE_LIMIT) / PATH_MTU_V6
(data + ACK_ONLY_SIZE_LIMIT - 1) / PATH_MTU_V6
}
/// Determine the size of the last packet.

Просмотреть файл

@ -6,16 +6,16 @@
use super::super::{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,
assert_full_cwnd, connect, connect_force_idle, connect_with_rtt, default_client,
default_server, fill_cwnd, maybe_authenticate, send_and_receive, send_something, AT_LEAST_PTO,
POST_HANDSHAKE_CWND,
};
use crate::frame::StreamType;
use crate::path::PATH_MTU_V6;
use crate::recovery::PTO_PACKET_COUNT;
use crate::stats::MAX_PTO_COUNTS;
use crate::tparams::TransportParameter;
use crate::tracking::ACK_DELAY;
use crate::StreamType;
use neqo_common::qdebug;
use neqo_crypto::AuthenticationStatus;
@ -34,12 +34,12 @@ fn pto_works_basic() {
assert_eq!(res, Output::Callback(LOCAL_IDLE_TIMEOUT));
// Send data on two streams
let stream1 = client.stream_create(StreamType::UniDi).unwrap();
assert_eq!(client.stream_send(stream1, b"hello").unwrap(), 5);
assert_eq!(client.stream_send(stream1, b" world!").unwrap(), 7);
assert_eq!(client.stream_create(StreamType::UniDi).unwrap(), 2);
assert_eq!(client.stream_send(2, b"hello").unwrap(), 5);
assert_eq!(client.stream_send(2, b" world").unwrap(), 6);
let stream2 = client.stream_create(StreamType::UniDi).unwrap();
assert_eq!(client.stream_send(stream2, b"there!").unwrap(), 6);
assert_eq!(client.stream_create(StreamType::UniDi).unwrap(), 6);
assert_eq!(client.stream_send(6, b"there!").unwrap(), 6);
// Send a packet after some time.
now += Duration::from_secs(10);
@ -63,15 +63,19 @@ fn pto_works_basic() {
fn pto_works_full_cwnd() {
let mut client = default_client();
let mut server = default_server();
let now = connect_rtt_idle(&mut client, &mut server, DEFAULT_RTT);
connect_force_idle(&mut client, &mut server);
let res = client.process(None, now());
assert_eq!(res, Output::Callback(LOCAL_IDLE_TIMEOUT));
// Send lots of data.
let stream_id = client.stream_create(StreamType::UniDi).unwrap();
let (dgrams, now) = fill_cwnd(&mut client, stream_id, now);
assert_eq!(client.stream_create(StreamType::UniDi).unwrap(), 2);
let (dgrams, now) = fill_cwnd(&mut client, 2, now());
assert_full_cwnd(&dgrams, POST_HANDSHAKE_CWND);
neqo_common::qwarn!("waiting over");
// Fill the CWND after waiting for a PTO.
let (dgrams, now) = fill_cwnd(&mut client, stream_id, now + AT_LEAST_PTO);
let (dgrams, now) = fill_cwnd(&mut client, 2, now + AT_LEAST_PTO);
// Two packets in the PTO.
// The first should be full sized; the second might be small.
assert_eq!(dgrams.len(), 2);
@ -86,65 +90,94 @@ fn pto_works_full_cwnd() {
}
#[test]
#[allow(clippy::cognitive_complexity)]
fn pto_works_ping() {
let mut client = default_client();
let mut server = default_server();
connect_force_idle(&mut client, &mut server);
let mut now = now();
let now = now();
let res = client.process(None, now);
assert_eq!(res, Output::Callback(LOCAL_IDLE_TIMEOUT));
now += Duration::from_secs(10);
// Send "zero" pkt
assert_eq!(client.stream_create(StreamType::UniDi).unwrap(), 2);
assert_eq!(client.stream_send(2, b"zero").unwrap(), 4);
let pkt0 = client.process(None, now + Duration::from_secs(10));
assert!(matches!(pkt0, Output::Datagram(_)));
// Send a few packets from the client.
let pkt0 = send_something(&mut client, now);
let pkt1 = send_something(&mut client, now);
let pkt2 = send_something(&mut client, now);
let pkt3 = send_something(&mut client, now);
// Send "one" pkt
assert_eq!(client.stream_send(2, b"one").unwrap(), 3);
let pkt1 = client.process(None, now + Duration::from_secs(10));
// Send "two" pkt
assert_eq!(client.stream_send(2, b"two").unwrap(), 3);
let pkt2 = client.process(None, now + Duration::from_secs(10));
// Send "three" pkt
assert_eq!(client.stream_send(2, b"three").unwrap(), 5);
let pkt3 = client.process(None, now + Duration::from_secs(10));
// Nothing to do, should return callback
let cb = client.process(None, now).callback();
assert_eq!(cb, Duration::from_millis(45));
let out = client.process(None, now + Duration::from_secs(10));
// Check callback delay is what we expect
assert!(matches!(out, Output::Callback(x) if x == Duration::from_millis(45)));
// Process these by server, skipping pkt0
let srv0 = server.process(Some(pkt1), now).dgram();
assert!(srv0.is_some()); // ooo, ack client pkt1
now += Duration::from_millis(20);
let srv0_pkt1 = server.process(pkt1.dgram(), now + Duration::from_secs(10));
// ooo, ack client pkt 1
assert!(matches!(srv0_pkt1, Output::Datagram(_)));
// process pkt2 (no ack yet)
let srv1 = server.process(Some(pkt2), now).dgram();
assert!(srv1.is_none());
let srv2 = server.process(
pkt2.dgram(),
now + Duration::from_secs(10) + Duration::from_millis(20),
);
assert!(matches!(srv2, Output::Callback(_)));
// process pkt3 (acked)
let srv2 = server.process(Some(pkt3), now).dgram();
let srv2 = server.process(
pkt3.dgram(),
now + Duration::from_secs(10) + Duration::from_millis(20),
);
// ack client pkt 2 & 3
assert!(srv2.is_some());
assert!(matches!(srv2, Output::Datagram(_)));
now += Duration::from_millis(20);
// client processes ack
let pkt4 = client.process(srv2, now).dgram();
let pkt4 = client.process(
srv2.dgram(),
now + Duration::from_secs(10) + Duration::from_millis(40),
);
// client resends data from pkt0
assert!(pkt4.is_some());
assert!(matches!(pkt4, Output::Datagram(_)));
// server sees ooo pkt0 and generates ack
let srv3 = server.process(Some(pkt0), now).dgram();
assert!(srv3.is_some());
let srv_pkt2 = server.process(
pkt0.dgram(),
now + Duration::from_secs(10) + Duration::from_millis(40),
);
assert!(matches!(srv_pkt2, Output::Datagram(_)));
// Accept the acknowledgment.
let pkt5 = client.process(srv3, now).dgram();
assert!(pkt5.is_none());
// Orig data is acked
let pkt5 = client.process(
srv_pkt2.dgram(),
now + Duration::from_secs(10) + Duration::from_millis(40),
);
assert!(matches!(pkt5, Output::Callback(_)));
now += Duration::from_millis(70);
// PTO expires. No unacked data. Only send PING.
let client_pings = client.stats().frame_tx.ping;
let pkt6 = client.process(None, now).dgram();
assert_eq!(client.stats().frame_tx.ping, client_pings + 1);
let pkt6 = client.process(
None,
now + Duration::from_secs(10) + Duration::from_millis(110),
);
let server_pings = server.stats().frame_rx.ping;
server.process_input(pkt6.unwrap(), now);
assert_eq!(server.stats().frame_rx.ping, server_pings + 1);
let ping_before = server.stats().frame_rx.ping;
server.process_input(
pkt6.dgram().unwrap(),
now + Duration::from_secs(10) + Duration::from_millis(110),
);
assert_eq!(server.stats().frame_rx.ping, ping_before + 1);
}
#[test]
@ -228,7 +261,6 @@ fn pto_handshake_complete() {
qdebug!("---- client: SH..FIN -> FIN");
let pkt1 = client.process(None, now).dgram();
assert!(pkt1.is_some());
assert_eq!(*client.state(), State::Connected);
let cb = client.process(None, now).callback();
assert_eq!(cb, Duration::from_millis(60));
@ -236,22 +268,17 @@ fn pto_handshake_complete() {
let mut pto_counts = [0; MAX_PTO_COUNTS];
assert_eq!(client.stats.borrow().pto_counts, pto_counts);
// Wait for PTO to expire and resend a handshake packet.
// Wait long enough that the 1-RTT PTO also fires.
qdebug!("---- client: PTO");
// Wait for PTO to expire and resend a handshake packet
now += Duration::from_millis(60);
let pkt2 = client.process(None, now).dgram();
assert!(pkt2.is_some());
pto_counts[0] = 1;
assert_eq!(client.stats.borrow().pto_counts, pto_counts);
// Get a second PTO packet.
// Add some application data to this datagram, then split the 1-RTT off.
// We'll use that packet to force the server to acknowledge 1-RTT.
let stream_id = client.stream_create(StreamType::UniDi).unwrap();
client.stream_close_send(stream_id).unwrap();
let pkt3 = client.process(None, now).dgram();
let (pkt3_hs, pkt3_1rtt) = split_datagram(&pkt3.unwrap());
assert!(pkt3.is_some());
// PTO has been doubled.
let cb = client.process(None, now).callback();
@ -260,42 +287,33 @@ fn pto_handshake_complete() {
// We still have only a single PTO
assert_eq!(client.stats.borrow().pto_counts, pto_counts);
qdebug!("---- server: receive FIN and send ACK");
now += Duration::from_millis(10);
// Now let the server have pkt1 and expect an immediate Handshake ACK.
// The output will be a Handshake packet with ACK and 1-RTT packet with
// HANDSHAKE_DONE and (because of pkt3_1rtt) an ACK.
// This should remove the 1-RTT PTO from messing this test up.
let server_acks = server.stats().frame_tx.ack;
let server_done = server.stats().frame_tx.handshake_done;
server.process_input(pkt3_1rtt.unwrap(), now);
let ack = server.process(pkt1, now).dgram();
assert!(ack.is_some());
assert_eq!(server.stats().frame_tx.ack, server_acks + 2);
assert_eq!(server.stats().frame_tx.handshake_done, server_done + 1);
// Server receives the first packet.
// The output will be a Handshake packet with an ack and a app pn space packet with
// HANDSHAKE_DONE.
let pkt = server.process(pkt1, now).dgram();
assert!(pkt.is_some());
// Check that the other packets (pkt2, pkt3) are Handshake packets.
// Check that the PTO packets (pkt2, pkt3) are Handshake packets.
// The server discarded the Handshake keys already, therefore they are dropped.
// Note that these don't include 1-RTT packets, because 1-RTT isn't send on PTO.
let dropped_before1 = server.stats().dropped_rx;
let server_frames = server.stats().frame_rx.all;
let frames_before = server.stats().frame_rx.all;
server.process_input(pkt2.unwrap(), now);
assert_eq!(1, server.stats().dropped_rx - dropped_before1);
assert_eq!(server.stats().frame_rx.all, server_frames);
assert_eq!(server.stats().frame_rx.all, frames_before);
let dropped_before2 = server.stats().dropped_rx;
server.process_input(pkt3_hs, now);
server.process_input(pkt3.unwrap(), now);
assert_eq!(1, server.stats().dropped_rx - dropped_before2);
assert_eq!(server.stats().frame_rx.all, server_frames);
assert_eq!(server.stats().frame_rx.all, frames_before);
now += Duration::from_millis(10);
// Let the client receive the ACK.
// It should now be wait to acknowledge the HANDSHAKE_DONE.
let cb = client.process(ack, now).callback();
// Client receive ack for the first packet
let cb = client.process(pkt, now).callback();
// Ack delay timer for the packet carrying HANDSHAKE_DONE.
assert_eq!(cb, ACK_DELAY);
// Let the ACK delay timer expire.
// Let the ack timer expire.
now += cb;
let out = client.process(None, now).dgram();
assert!(out.is_some());
@ -304,6 +322,8 @@ fn pto_handshake_complete() {
// We don't send another PING because the handshake space is done and there
// is nothing to probe for.
pto_counts[0] = 1;
assert_eq!(client.stats.borrow().pto_counts, pto_counts);
assert_eq!(cb, LOCAL_IDLE_TIMEOUT - ACK_DELAY);
}

Просмотреть файл

@ -6,15 +6,16 @@
use super::super::State;
use super::{
connect, connect_force_idle, default_client, default_server, maybe_authenticate,
send_something, DEFAULT_STREAM_DATA,
connect, default_client, default_server, maybe_authenticate, send_something,
DEFAULT_STREAM_DATA,
};
use crate::events::ConnectionEvent;
use crate::frame::StreamType;
use crate::recv_stream::RECV_BUFFER_SIZE;
use crate::send_stream::SEND_BUFFER_SIZE;
use crate::tparams::{self, TransportParameter};
use crate::tracking::MAX_UNACKED_PKTS;
use crate::{Error, StreamId, StreamType};
use crate::{Error, StreamId};
use neqo_common::{event::Provider, qdebug};
use std::convert::TryFrom;
@ -48,13 +49,52 @@ fn stream_create() {
}
#[test]
#[allow(clippy::cognitive_complexity)]
// tests stream send/recv after connection is established.
fn transfer() {
let mut client = default_client();
let mut server = default_server();
connect_force_idle(&mut client, &mut server);
qdebug!("---- client sends");
qdebug!("---- client");
let out = client.process(None, now());
assert!(out.as_dgram_ref().is_some());
qdebug!("Output={:0x?}", out.as_dgram_ref());
// -->> Initial[0]: CRYPTO[CH]
qdebug!("---- server");
let out = server.process(out.dgram(), now());
assert!(out.as_dgram_ref().is_some());
qdebug!("Output={:0x?}", out.as_dgram_ref());
// <<-- Initial[0]: CRYPTO[SH] ACK[0]
// <<-- Handshake[0]: CRYPTO[EE, CERT, CV, FIN]
qdebug!("---- client");
let out = client.process(out.dgram(), now());
assert!(out.as_dgram_ref().is_some());
qdebug!("Output={:0x?}", out.as_dgram_ref());
// -->> Initial[1]: ACK[0]
let out = server.process(out.dgram(), now());
assert!(out.as_dgram_ref().is_none());
assert!(maybe_authenticate(&mut client));
qdebug!("---- client");
let out = client.process(out.dgram(), now());
assert!(out.as_dgram_ref().is_some());
assert_eq!(*client.state(), State::Connected);
qdebug!("Output={:0x?}", out.as_dgram_ref());
// -->> Handshake[0]: CRYPTO[FIN], ACK[0]
qdebug!("---- server");
let out = server.process(out.dgram(), now());
assert!(out.as_dgram_ref().is_some());
assert_eq!(*server.state(), State::Confirmed);
qdebug!("Output={:0x?}", out.as_dgram_ref());
// ACK and HANDSHAKE_DONE
// -->> nothing
qdebug!("---- client");
// Send
let client_stream_id = client.stream_create(StreamType::UniDi).unwrap();
client.stream_send(client_stream_id, &[6; 100]).unwrap();
@ -68,15 +108,15 @@ fn transfer() {
client.stream_send(client_stream_id2, &[7; 50]).unwrap_err();
// Sending this much takes a few datagrams.
let mut datagrams = vec![];
let mut out = client.process_output(now());
let mut out = client.process(out.dgram(), now());
while let Some(d) = out.dgram() {
datagrams.push(d);
out = client.process_output(now());
out = client.process(None, now());
}
assert_eq!(datagrams.len(), 4);
assert_eq!(*client.state(), State::Confirmed);
qdebug!("---- server receives");
qdebug!("---- server");
for (d_num, d) in datagrams.into_iter().enumerate() {
let out = server.process(Some(d), now());
assert_eq!(
@ -127,7 +167,7 @@ fn report_fin_when_stream_closed_wo_data() {
let out = client.process(None, now());
let _ = server.process(out.dgram(), now());
server.stream_close_send(stream_id).unwrap();
assert_eq!(Ok(()), server.stream_close_send(stream_id));
let out = server.process(None, now());
let _ = client.process(out.dgram(), now());
let stream_readable = |e| matches!(e, ConnectionEvent::RecvStreamReadable {..});
@ -249,6 +289,8 @@ fn do_not_accept_data_after_stop_sending() {
#[test]
// Server sends stop_sending, the client simultaneous sends reset.
fn simultaneous_stop_sending_and_reset() {
// Note that the two servers in this test will get different anti-replay filters.
// That's OK because we aren't testing anti-replay.
let mut client = default_client();
let mut server = default_server();
connect(&mut client, &mut server);
@ -257,32 +299,30 @@ fn simultaneous_stop_sending_and_reset() {
let stream_id = client.stream_create(StreamType::BiDi).unwrap();
client.stream_send(stream_id, &[0x00]).unwrap();
let out = client.process(None, now());
let ack = server.process(out.dgram(), now()).dgram();
let _ = server.process(out.dgram(), now());
let stream_readable =
|e| matches!(e, ConnectionEvent::RecvStreamReadable { stream_id: id } if id == stream_id);
let stream_readable = |e| matches!(e, ConnectionEvent::RecvStreamReadable {..});
assert!(server.events().any(stream_readable));
// The client resets the stream. The packet with reset should arrive after the server
// has already requested stop_sending.
client.stream_reset_send(stream_id, 0).unwrap();
let out_reset_frame = client.process(ack, now()).dgram();
// Send something out of order to force the server to generate an
// acknowledgment at the next opportunity.
let force_ack = send_something(&mut client, now());
server.process_input(force_ack, now());
client
.stream_reset_send(stream_id, Error::NoError.code())
.unwrap();
let out_reset_frame = client.process(None, now());
// Call stop sending.
server.stream_stop_sending(stream_id, 0).unwrap();
assert_eq!(
Ok(()),
server.stream_stop_sending(stream_id, Error::NoError.code())
);
// Receive the second data frame. The frame should be ignored and
// DataReadable events shouldn't be posted.
let ack = server.process(out_reset_frame, now()).dgram();
assert!(ack.is_some());
let out = server.process(out_reset_frame.dgram(), now());
assert!(!server.events().any(stream_readable));
// The client gets the STOP_SENDING frame.
client.process_input(ack.unwrap(), now());
let _ = client.process(out.dgram(), now());
assert_eq!(
Err(Error::InvalidStreamId),
client.stream_send(stream_id, &[0x00])

Просмотреть файл

@ -11,7 +11,7 @@ use crate::{Error, QuicVersion};
use neqo_common::{Datagram, Decoder, Encoder};
use std::time::Duration;
use test_fixture::{self, addr, now};
use test_fixture::{self, loopback, now};
// The expected PTO duration after the first Initial is sent.
const INITIAL_PTO: Duration = Duration::from_millis(300);
@ -25,7 +25,11 @@ fn unknown_version() {
let mut unknown_version_packet = vec![0x80, 0x1a, 0x1a, 0x1a, 0x1a];
unknown_version_packet.resize(1200, 0x0);
let _ = client.process(
Some(Datagram::new(addr(), addr(), unknown_version_packet)),
Some(Datagram::new(
loopback(),
loopback(),
unknown_version_packet,
)),
now(),
);
assert_eq!(1, client.stats().dropped_rx);
@ -40,7 +44,11 @@ fn server_receive_unknown_first_packet() {
assert_eq!(
server.process(
Some(Datagram::new(addr(), addr(), unknown_version_packet,)),
Some(Datagram::new(
loopback(),
loopback(),
unknown_version_packet,
)),
now(),
),
Output::None
@ -81,7 +89,7 @@ fn version_negotiation_current_version() {
&[0x1a1a_1a1a, QuicVersion::default().as_u32()],
);
let dgram = Datagram::new(addr(), addr(), vn);
let dgram = Datagram::new(loopback(), loopback(), vn);
let delay = client.process(Some(dgram), now()).callback();
assert_eq!(delay, INITIAL_PTO);
assert_eq!(*client.state(), State::WaitInitial);
@ -100,7 +108,7 @@ fn version_negotiation_only_reserved() {
let vn = create_vn(&initial_pkt, &[0x1a1a_1a1a, 0x2a2a_2a2a]);
let dgram = Datagram::new(addr(), addr(), vn);
let dgram = Datagram::new(loopback(), loopback(), vn);
assert_eq!(client.process(Some(dgram), now()), Output::None);
match client.state() {
State::Closed(err) => {
@ -122,7 +130,7 @@ fn version_negotiation_corrupted() {
let vn = create_vn(&initial_pkt, &[0x1a1a_1a1a, 0x2a2a_2a2a]);
let dgram = Datagram::new(addr(), addr(), &vn[..vn.len() - 1]);
let dgram = Datagram::new(loopback(), loopback(), &vn[..vn.len() - 1]);
let delay = client.process(Some(dgram), now()).callback();
assert_eq!(delay, INITIAL_PTO);
assert_eq!(*client.state(), State::WaitInitial);
@ -141,7 +149,7 @@ fn version_negotiation_empty() {
let vn = create_vn(&initial_pkt, &[]);
let dgram = Datagram::new(addr(), addr(), vn);
let dgram = Datagram::new(loopback(), loopback(), vn);
let delay = client.process(Some(dgram), now()).callback();
assert_eq!(delay, INITIAL_PTO);
assert_eq!(*client.state(), State::WaitInitial);
@ -161,7 +169,7 @@ fn version_negotiation_not_supported() {
let vn = create_vn(&initial_pkt, &[0x1a1a_1a1a, 0x2a2a_2a2a, 0xff00_0001]);
assert_eq!(
client.process(Some(Datagram::new(addr(), addr(), vn)), now(),),
client.process(Some(Datagram::new(loopback(), loopback(), vn)), now(),),
Output::None
);
match client.state() {
@ -185,7 +193,7 @@ fn version_negotiation_bad_cid() {
let mut vn = create_vn(&initial_pkt, &[0x1a1a_1a1a, 0x2a2a_2a2a, 0xff00_0001]);
vn[6] ^= 0xc4;
let dgram = Datagram::new(addr(), addr(), vn);
let dgram = Datagram::new(loopback(), loopback(), vn);
let delay = client.process(Some(dgram), now()).callback();
assert_eq!(delay, INITIAL_PTO);
assert_eq!(*client.state(), State::WaitInitial);

Просмотреть файл

@ -4,12 +4,11 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use super::super::Connection;
use super::{
connect, default_client, default_server, exchange_ticket, CountingConnectionIdGenerator,
};
use super::super::{Connection, FixedConnectionIdManager};
use super::{connect, default_client, default_server, exchange_ticket};
use crate::events::ConnectionEvent;
use crate::{ConnectionParameters, Error, StreamType};
use crate::frame::StreamType;
use crate::{ConnectionParameters, Error};
use neqo_common::event::Provider;
use neqo_crypto::{AllowZeroRtt, AntiReplay};
@ -63,13 +62,8 @@ fn zero_rtt_send_recv() {
let server_hs = server.process(client_hs.dgram(), now());
assert!(server_hs.as_dgram_ref().is_some()); // ServerHello, etc...
let all_frames = server.stats().frame_tx.all;
let ack_frames = server.stats().frame_tx.ack;
let server_process_0rtt = server.process(client_0rtt.dgram(), now());
assert!(server_process_0rtt.as_dgram_ref().is_some());
assert_eq!(server.stats().frame_tx.all, all_frames + 1);
assert_eq!(server.stats().frame_tx.ack, ack_frames + 1);
assert!(server_process_0rtt.as_dgram_ref().is_none());
let server_stream_id = server
.events()
@ -138,8 +132,8 @@ fn zero_rtt_send_reject() {
let mut server = Connection::new_server(
test_fixture::DEFAULT_KEYS,
test_fixture::DEFAULT_ALPN,
Rc::new(RefCell::new(CountingConnectionIdGenerator::default())),
ConnectionParameters::default(),
Rc::new(RefCell::new(FixedConnectionIdManager::new(10))),
&ConnectionParameters::default(),
)
.unwrap();
// Using a freshly initialized anti-replay context
@ -189,10 +183,11 @@ fn zero_rtt_send_reject() {
let stream_id_after_reject = client.stream_create(StreamType::UniDi).unwrap();
assert_eq!(stream_id, stream_id_after_reject);
client.stream_send(stream_id_after_reject, MESSAGE).unwrap();
let client_after_reject = client.process(None, now()).dgram();
assert!(client_after_reject.is_some());
let client_after_reject = client.process(None, now());
assert!(client_after_reject.as_dgram_ref().is_some());
// The server should receive new stream
server.process_input(client_after_reject.unwrap(), now());
let server_out = server.process(client_after_reject.dgram(), now());
assert!(server_out.as_dgram_ref().is_none()); // suppress the ack
assert!(server.events().any(recvd_stream_evt));
}

22
third_party/rust/neqo-transport/src/crypto.rs поставляемый
Просмотреть файл

@ -148,7 +148,7 @@ impl Crypto {
self.tls.read_secret(TLS_EPOCH_ZERO_RTT),
),
};
let secret = secret.ok_or(Error::InternalError(1))?;
let secret = secret.ok_or(Error::InternalError)?;
self.states.set_0rtt_keys(dir, &secret, cipher.unwrap());
Ok(true)
}
@ -177,12 +177,12 @@ impl Crypto {
let read_secret = self
.tls
.read_secret(TLS_EPOCH_HANDSHAKE)
.ok_or(Error::InternalError(2))?;
.ok_or(Error::InternalError)?;
let cipher = match self.tls.info() {
None => self.tls.preinfo()?.cipher_suite(),
Some(info) => Some(info.cipher_suite()),
}
.ok_or(Error::InternalError(3))?;
.ok_or(Error::InternalError)?;
self.states
.set_handshake_keys(&write_secret, &read_secret, cipher);
qdebug!([self], "Handshake keys installed");
@ -206,7 +206,7 @@ impl Crypto {
let read_secret = self
.tls
.read_secret(TLS_EPOCH_APPLICATION_DATA)
.ok_or(Error::InternalError(4))?;
.ok_or(Error::InternalError)?;
self.states
.set_application_read_key(read_secret, expire_0rtt)?;
qdebug!([self], "application read keys installed");
@ -1241,14 +1241,14 @@ impl CryptoStreams {
&mut self,
space: PNSpace,
builder: &mut PacketBuilder,
) -> Res<Option<RecoveryToken>> {
) -> Option<RecoveryToken> {
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 None;
}
// Calculate length of data based on the minimum of:
// - available data
@ -1261,20 +1261,16 @@ impl CryptoStreams {
builder.encode_varint(crate::frame::FRAME_TYPE_CRYPTO);
builder.encode_varint(offset);
builder.encode_vvec(&data[..length]);
if builder.len() > builder.limit() {
return Err(Error::InternalError(15));
}
cs.tx.mark_as_sent(offset, length);
qdebug!("CRYPTO for {} offset={}, len={}", space, offset, length);
Ok(Some(RecoveryToken::Crypto(CryptoRecoveryToken {
Some(RecoveryToken::Crypto(CryptoRecoveryToken {
space,
offset,
length,
})))
}))
} else {
Ok(None)
None
}
}
}

12
third_party/rust/neqo-transport/src/dump.rs поставляемый
Просмотреть файл

@ -10,18 +10,10 @@
use crate::connection::Connection;
use crate::frame::Frame;
use crate::packet::{PacketNumber, PacketType};
use crate::path::PathRef;
use neqo_common::{qdebug, Decoder};
#[allow(clippy::module_name_repetitions)]
pub fn dump_packet(
conn: &Connection,
path: &PathRef,
dir: &str,
pt: PacketType,
pn: PacketNumber,
payload: &[u8],
) {
pub fn dump_packet(conn: &Connection, dir: &str, pt: PacketType, pn: PacketNumber, payload: &[u8]) {
let mut s = String::from("");
let mut d = Decoder::from(payload);
while d.remaining() > 0 {
@ -36,5 +28,5 @@ pub fn dump_packet(
s.push_str(&format!("\n {} {}", dir, &x));
}
}
qdebug!([conn], "pn={} type={:?} {}{}", pn, pt, path.borrow(), s);
qdebug!([conn], "pn={} type={:?}{}", pn, pt, s);
}

Просмотреть файл

@ -11,7 +11,8 @@ use std::collections::VecDeque;
use std::rc::Rc;
use crate::connection::State;
use crate::stream_id::{StreamId, StreamType};
use crate::frame::StreamType;
use crate::stream_id::StreamId;
use crate::AppError;
use neqo_common::event::Provider as EventProvider;
use neqo_crypto::ResumptionToken;

Просмотреть файл

@ -13,14 +13,14 @@ use std::mem;
use neqo_common::{qinfo, qwarn, Encoder};
use smallvec::{smallvec, SmallVec};
use crate::frame::Frame;
use crate::frame::{Frame, StreamType};
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::{StreamId, StreamIndex, StreamIndexes};
use crate::AppError;
type FlowFrame = Frame<'static>;
pub type FlowControlRecoveryToken = FlowFrame;
@ -77,6 +77,11 @@ impl FlowMgr {
self.from_conn.insert(mem::discriminant(&frame), frame);
}
pub fn path_response(&mut self, data: [u8; 8]) {
let frame = Frame::PathResponse { 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);
@ -268,6 +273,7 @@ impl FlowMgr {
}
}
}
Frame::PathResponse { .. } => qinfo!("Path Response lost, not re-sent"),
_ => qwarn!("Unexpected Flow frame {:?} lost, not re-sent", token),
}
}
@ -277,7 +283,7 @@ impl FlowMgr {
builder: &mut PacketBuilder,
tokens: &mut Vec<RecoveryToken>,
stats: &mut FrameStats,
) -> Res<()> {
) {
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.
@ -333,6 +339,19 @@ impl FlowMgr {
smallvec![stream_id.as_u64(), *stream_data_limit]
}
// A special case, just write it out and move on..
Frame::PathResponse { data } => {
stats.path_response += 1;
if builder.remaining() >= Encoder::varint_len(frame.get_type()) + data.len() {
builder.encode_varint(frame.get_type());
builder.encode(data);
tokens.push(RecoveryToken::Flow(self.next().unwrap()));
continue;
} else {
return;
}
}
_ => unreachable!("{:?}", frame),
};
debug_assert!(!values.spilled());
@ -348,15 +367,11 @@ impl FlowMgr {
for v in values {
builder.encode_varint(v);
}
if builder.len() > builder.limit() {
return Err(Error::InternalError(16));
}
tokens.push(RecoveryToken::Flow(self.next().unwrap()));
} else {
return Ok(());
return;
}
}
Ok(())
}
}

94
third_party/rust/neqo-transport/src/frame.rs поставляемый
Просмотреть файл

@ -10,8 +10,8 @@ use neqo_common::{qtrace, Decoder};
use crate::cid::MAX_CONNECTION_ID_LEN;
use crate::packet::PacketType;
use crate::stream_id::{StreamId, StreamIndex, StreamType};
use crate::{AppError, ConnectionError, Error, Res, TransportError};
use crate::stream_id::{StreamId, StreamIndex};
use crate::{AppError, ConnectionError, Error, Res, TransportError, ERROR_APPLICATION_CLOSE};
use std::convert::TryFrom;
use std::ops::RangeInclusive;
@ -37,18 +37,50 @@ 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_NEW_CONNECTION_ID: FrameType = 0x18;
pub const FRAME_TYPE_RETIRE_CONNECTION_ID: FrameType = 0x19;
pub const FRAME_TYPE_PATH_CHALLENGE: FrameType = 0x1a;
pub const FRAME_TYPE_PATH_RESPONSE: FrameType = 0x1b;
const FRAME_TYPE_NEW_CONNECTION_ID: FrameType = 0x18;
const FRAME_TYPE_RETIRE_CONNECTION_ID: FrameType = 0x19;
const FRAME_TYPE_PATH_CHALLENGE: FrameType = 0x1a;
const FRAME_TYPE_PATH_RESPONSE: FrameType = 0x1b;
pub const FRAME_TYPE_CONNECTION_CLOSE_TRANSPORT: FrameType = 0x1c;
pub const FRAME_TYPE_CONNECTION_CLOSE_APPLICATION: FrameType = 0x1d;
pub const FRAME_TYPE_HANDSHAKE_DONE: FrameType = 0x1e;
const FRAME_TYPE_HANDSHAKE_DONE: FrameType = 0x1e;
const STREAM_FRAME_BIT_FIN: u64 = 0x01;
const STREAM_FRAME_BIT_LEN: u64 = 0x02;
const STREAM_FRAME_BIT_OFF: u64 = 0x04;
/// `FRAME_APPLICATION_CLOSE` is the default CONNECTION_CLOSE frame that
/// is sent when an application error code needs to be sent in an
/// Initial or Handshake packet.
const FRAME_APPLICATION_CLOSE: &Frame = &Frame::ConnectionClose {
error_code: CloseError::Transport(ERROR_APPLICATION_CLOSE),
frame_type: 0,
reason_phrase: Vec::new(),
};
#[derive(PartialEq, Debug, Copy, Clone, PartialOrd, Eq, Ord, Hash)]
/// Bi-Directional or Uni-Directional.
pub enum StreamType {
BiDi,
UniDi,
}
impl StreamType {
fn frame_type_bit(self) -> u64 {
match self {
Self::BiDi => 0,
Self::UniDi => 1,
}
}
fn from_type_bit(bit: u64) -> Self {
if (bit & 0x01) == 0 {
Self::BiDi
} else {
Self::UniDi
}
}
}
#[derive(PartialEq, Eq, Debug, PartialOrd, Ord, Clone, Copy)]
pub enum CloseError {
Transport(TransportError),
@ -174,21 +206,6 @@ pub enum Frame<'a> {
}
impl<'a> Frame<'a> {
fn get_stream_type_bit(stream_type: StreamType) -> u64 {
match stream_type {
StreamType::BiDi => 0,
StreamType::UniDi => 1,
}
}
fn stream_type_from_bit(bit: u64) -> StreamType {
if (bit & 0x01) == 0 {
StreamType::BiDi
} else {
StreamType::UniDi
}
}
pub fn get_type(&self) -> FrameType {
match self {
Self::Padding => FRAME_TYPE_PADDING,
@ -204,12 +221,12 @@ impl<'a> Frame<'a> {
Self::MaxData { .. } => FRAME_TYPE_MAX_DATA,
Self::MaxStreamData { .. } => FRAME_TYPE_MAX_STREAM_DATA,
Self::MaxStreams { stream_type, .. } => {
FRAME_TYPE_MAX_STREAMS_BIDI + Self::get_stream_type_bit(*stream_type)
FRAME_TYPE_MAX_STREAMS_BIDI + stream_type.frame_type_bit()
}
Self::DataBlocked { .. } => FRAME_TYPE_DATA_BLOCKED,
Self::StreamDataBlocked { .. } => FRAME_TYPE_STREAM_DATA_BLOCKED,
Self::StreamsBlocked { stream_type, .. } => {
FRAME_TYPE_STREAMS_BLOCKED_BIDI + Self::get_stream_type_bit(*stream_type)
FRAME_TYPE_STREAMS_BLOCKED_BIDI + stream_type.frame_type_bit()
}
Self::NewConnectionId { .. } => FRAME_TYPE_NEW_CONNECTION_ID,
Self::RetireConnectionId { .. } => FRAME_TYPE_RETIRE_CONNECTION_ID,
@ -236,20 +253,21 @@ impl<'a> Frame<'a> {
t
}
/// 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 { .. })
/// Convert a CONNECTION_CLOSE into a nicer CONNECTION_CLOSE.
pub fn sanitize_close(&self) -> &Self {
if let Self::ConnectionClose { error_code, .. } = &self {
if let CloseError::Application(_) = error_code {
FRAME_APPLICATION_CLOSE
} else {
self
}
} else {
panic!("Attempted to sanitize a non-close frame");
}
}
/// If the frame can be sent in a path probe
/// without initiating migration to that path.
pub fn path_probing(&self) -> bool {
matches!(self,
Self::Padding
| Self::NewConnectionId { .. }
| Self::PathChallenge { .. }
| Self::PathResponse { .. })
pub fn ack_eliciting(&self) -> bool {
!matches!(self, Self::Ack { .. } | Self::Padding | Self::ConnectionClose { .. })
}
/// Converts AckRanges as encoded in a ACK frame (see -transport
@ -442,7 +460,7 @@ impl<'a> Frame<'a> {
return Err(Error::StreamLimitError);
}
Ok(Self::MaxStreams {
stream_type: Self::stream_type_from_bit(t),
stream_type: StreamType::from_type_bit(t),
maximum_streams: StreamIndex::new(m),
})
}
@ -455,7 +473,7 @@ impl<'a> Frame<'a> {
}),
FRAME_TYPE_STREAMS_BLOCKED_BIDI | FRAME_TYPE_STREAMS_BLOCKED_UNIDI => {
Ok(Self::StreamsBlocked {
stream_type: Self::stream_type_from_bit(t),
stream_type: StreamType::from_type_bit(t),
stream_limit: StreamIndex::new(dv(dec)?),
})
}

25
third_party/rust/neqo-transport/src/lib.rs поставляемый
Просмотреть файл

@ -28,22 +28,22 @@ mod send_stream;
mod sender;
pub mod server;
mod stats;
pub mod stream_id;
mod stream_id;
pub mod tparams;
mod tracking;
pub use self::cc::CongestionControlAlgorithm;
pub use self::cid::{
ConnectionId, ConnectionIdDecoder, ConnectionIdGenerator, ConnectionIdRef,
EmptyConnectionIdGenerator, RandomConnectionIdGenerator,
pub use self::cid::{ConnectionId, ConnectionIdManager};
pub use self::connection::{
params::ConnectionParameters, Connection, FixedConnectionIdManager, Output, State,
ZeroRttState, LOCAL_STREAM_LIMIT_BIDI, LOCAL_STREAM_LIMIT_UNI,
};
pub use self::connection::{params::ConnectionParameters, Connection, Output, State, ZeroRttState};
pub use self::events::{ConnectionEvent, ConnectionEvents};
pub use self::frame::CloseError;
pub use self::frame::{CloseError, StreamType};
pub use self::packet::QuicVersion;
pub use self::sender::PacketSender;
pub use self::stats::Stats;
pub use self::stream_id::{StreamId, StreamType};
pub use self::stream_id::StreamId;
pub use self::recv_stream::RECV_BUFFER_SIZE;
pub use self::send_stream::SEND_BUFFER_SIZE;
@ -56,9 +56,7 @@ const ERROR_AEAD_LIMIT_REACHED: TransportError = 15;
#[allow(clippy::pub_enum_variant_names)]
pub enum Error {
NoError,
// Each time tihe error is return a different parameter is supply.
// This will be use to distinguish each occurance of this error.
InternalError(u16),
InternalError,
ConnectionRefused,
FlowControlError,
StreamLimitError,
@ -73,10 +71,8 @@ pub enum Error {
QlogError,
CryptoAlert(u8),
// All internal errors from here. Please keep these sorted.
// All internal errors from here.
AckedUnsentPacket,
ConnectionIdLimitExceeded,
ConnectionIdsExhausted,
ConnectionState,
DecodingFrame,
DecryptError,
@ -98,7 +94,6 @@ pub enum Error {
/// An attempt to update keys can be blocked if
/// a packet sent with the current keys hasn't been acknowledged.
KeyUpdateBlocked,
NoAvailablePath,
NoMoreData,
NotConnected,
PacketNumberOverlap,
@ -107,7 +102,6 @@ pub enum Error {
StatelessReset,
TooMuchData,
UnexpectedMessage,
UnknownConnectionId,
UnknownFrameType,
VersionNegotiation,
WrongRole,
@ -131,7 +125,6 @@ impl Error {
Self::InvalidToken => 11,
Self::KeysExhausted => ERROR_AEAD_LIMIT_REACHED,
Self::ApplicationError => ERROR_APPLICATION_CLOSE,
Self::NoAvailablePath => 16,
Self::CryptoAlert(a) => 0x100 + u64::from(*a),
// All the rest are internal errors.
_ => 1,

Просмотреть файл

@ -231,10 +231,6 @@ impl PacketBuilder {
self.limit = limit;
}
pub fn limit(&mut self) -> usize {
self.limit
}
/// How many bytes remain against the size limit for the builder.
#[must_use]
pub fn remaining(&self) -> usize {
@ -242,14 +238,8 @@ impl PacketBuilder {
}
/// Pad with "PADDING" frames.
pub fn pad(&mut self) -> Res<()> {
pub fn pad(&mut self) {
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));
}
Ok(())
}
/// Add unpredictable values for unprotected parts of the packet.
@ -262,25 +252,18 @@ 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));
}
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<()> {
pub fn pn(&mut self, pn: PacketNumber, pn_len: usize) {
// Reserve space for a length in long headers.
if self.is_long() {
self.offsets.len = self.encoder.len();
@ -299,13 +282,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) {
@ -331,7 +307,7 @@ impl PacketBuilder {
if self.len() > self.limit {
qwarn!("Packet contents are more than the limit");
debug_assert!(false);
return Err(Error::InternalError(5));
return Err(Error::InternalError);
}
self.pad_for_crypto(crypto);
@ -524,11 +500,6 @@ impl<'a> PublicPacket<'a> {
let first = Self::opt(decoder.decode_byte())?;
if first & 0x80 == PACKET_BIT_SHORT {
// Conveniently, this also guarantees that there is enough space
// for a connection ID of any size.
if decoder.remaining() < SAMPLE_OFFSET + SAMPLE_SIZE {
return Err(Error::InvalidPacket);
}
let dcid = Self::opt(dcid_decoder.decode_cid(&mut decoder))?;
if decoder.remaining() < SAMPLE_OFFSET + SAMPLE_SIZE {
return Err(Error::InvalidPacket);
@ -828,7 +799,7 @@ impl Deref for DecryptedPacket {
mod tests {
use super::*;
use crate::crypto::{CryptoDxState, CryptoStates};
use crate::{EmptyConnectionIdGenerator, QuicVersion, RandomConnectionIdGenerator};
use crate::{FixedConnectionIdManager, QuicVersion};
use neqo_common::Encoder;
use test_fixture::{fixture_init, now};
@ -836,8 +807,8 @@ mod tests {
const SERVER_CID: &[u8] = &[0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, 0xb5];
/// This is a connection ID manager, which is only used for decoding short header packets.
fn cid_mgr() -> RandomConnectionIdGenerator {
RandomConnectionIdGenerator::new(SERVER_CID.len())
fn cid_mgr() -> FixedConnectionIdManager {
FixedConnectionIdManager::new(SERVER_CID.len())
}
const SAMPLE_INITIAL_PAYLOAD: &[u8] = &[
@ -878,8 +849,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);
@ -940,7 +911,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 +927,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;
@ -988,7 +959,7 @@ mod tests {
fixture_init();
let (packet, remainder) = PublicPacket::decode(
SAMPLE_SHORT,
&RandomConnectionIdGenerator::new(SERVER_CID.len() - 1),
&FixedConnectionIdManager::new(SERVER_CID.len() - 1),
)
.unwrap();
assert_eq!(packet.packet_type(), PacketType::Short);
@ -1003,7 +974,7 @@ mod tests {
fn decode_short_long_cid() {
assert!(PublicPacket::decode(
SAMPLE_SHORT,
&RandomConnectionIdGenerator::new(SERVER_CID.len() + 1)
&FixedConnectionIdManager::new(SERVER_CID.len() + 1)
)
.is_err());
}
@ -1019,14 +990,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!(
@ -1053,7 +1024,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 +1043,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,8 +1064,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);
let encoder = builder.abort();
assert!(encoder.is_empty());
}
@ -1204,7 +1175,7 @@ mod tests {
fn decode_retry(quic_version: QuicVersion, sample_retry: &[u8]) {
fixture_init();
let (packet, remainder) =
PublicPacket::decode(sample_retry, &RandomConnectionIdGenerator::new(5)).unwrap();
PublicPacket::decode(sample_retry, &FixedConnectionIdManager::new(5)).unwrap();
assert!(packet.is_valid_retry(&ConnectionId::from(CLIENT_CID)));
assert_eq!(Some(quic_version), packet.quic_version);
assert!(packet.dcid().is_empty());
@ -1247,7 +1218,7 @@ mod tests {
#[test]
fn invalid_retry() {
fixture_init();
let cid_mgr = RandomConnectionIdGenerator::new(5);
let cid_mgr = FixedConnectionIdManager::new(5);
let odcid = ConnectionId::from(CLIENT_CID);
assert!(PublicPacket::decode(&[], &cid_mgr).is_err());
@ -1299,7 +1270,7 @@ mod tests {
#[test]
fn parse_vn() {
let (packet, remainder) =
PublicPacket::decode(SAMPLE_VN, &EmptyConnectionIdGenerator::default()).unwrap();
PublicPacket::decode(SAMPLE_VN, &FixedConnectionIdManager::new(5)).unwrap();
assert!(remainder.is_empty());
assert_eq!(&packet.dcid[..], SERVER_CID);
assert!(packet.scid.is_some());
@ -1320,7 +1291,7 @@ mod tests {
enc.encode_uint(4, 0x5a6a_7a8a_u64);
let (packet, remainder) =
PublicPacket::decode(&enc, &EmptyConnectionIdGenerator::default()).unwrap();
PublicPacket::decode(&enc, &FixedConnectionIdManager::new(5)).unwrap();
assert!(remainder.is_empty());
assert_eq!(&packet.dcid[..], BIG_DCID);
assert!(packet.scid.is_some());
@ -1356,7 +1327,7 @@ mod tests {
];
fixture_init();
let (packet, slice) =
PublicPacket::decode(PACKET, &EmptyConnectionIdGenerator::default()).unwrap();
PublicPacket::decode(PACKET, &FixedConnectionIdManager::new(0)).unwrap();
assert!(slice.is_empty());
let decrypted = packet
.decrypt(&mut CryptoStates::test_chacha(), now())

Просмотреть файл

@ -49,7 +49,7 @@ where
.try_with(|aead| f(&aead.borrow()))
.map_err(|e| {
qerror!("Unable to access Retry AEAD: {:?}", e);
Error::InternalError(6)
Error::InternalError
})?
}

717
third_party/rust/neqo-transport/src/path.rs поставляемый
Просмотреть файл

@ -4,27 +4,11 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![deny(clippy::pedantic)]
#![allow(clippy::module_name_repetitions)]
use std::net::SocketAddr;
use std::cell::RefCell;
use std::convert::TryFrom;
use std::fmt::{self, Display};
use std::net::{IpAddr, SocketAddr};
use std::rc::Rc;
use std::time::{Duration, Instant};
use crate::cid::{ConnectionId, ConnectionIdRef};
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::stats::FrameStats;
use crate::{Error, Res};
use neqo_common::{hex, qdebug, qinfo, qtrace, Datagram, Encoder};
use neqo_crypto::random;
use neqo_common::Datagram;
/// This is the MTU that we assume when using IPv6.
/// We use this size for Initial packets, so we don't need to worry about probing for support.
@ -34,501 +18,78 @@ use neqo_crypto::random;
pub const PATH_MTU_V6: usize = 1337;
/// The path MTU for IPv4 can be 20 bytes larger than for v6.
pub const PATH_MTU_V4: usize = PATH_MTU_V6 + 20;
/// The number of times that a path will be probed before it is considered failed.
const MAX_PATH_PROBES: usize = 3;
/// The maximum number of paths that `Paths` will track.
const MAX_PATHS: usize = 15;
pub type PathRef = Rc<RefCell<Path>>;
/// A collection for network paths.
/// This holds a collection of paths that have been used for sending or
/// receiving, plus an additional "temporary" path that is held only while
/// processing a packet.
/// This structure limits its storage and will forget about paths if it
/// is exposed to too many paths.
#[derive(Debug, Default)]
pub struct Paths {
/// All of the paths.
paths: Vec<PathRef>,
/// This is the primary path. This will only be `None` initially, so
/// care needs to be taken regarding that only during the handshake.
/// This path will also be in `paths`.
primary: Option<PathRef>,
/// The path that we would prefer to migrate to.
migration_target: Option<PathRef>,
/// Connection IDs that need to be retired.
to_retire: Vec<u64>,
}
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 {
self.paths
.iter()
.find_map(|p| {
if p.borrow().received_on(local, remote, false) {
Some(Rc::clone(p))
} else {
None
}
})
.unwrap_or_else(|| Rc::new(RefCell::new(Path::temporary(local, remote))))
}
/// 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 {
self.paths
.iter()
.find_map(|p| {
if p.borrow().received_on(local, remote, false) {
Some(Rc::clone(p))
} else {
None
}
})
.or_else(|| {
self.paths.iter().find_map(|p| {
if p.borrow().received_on(local, remote, true) {
Some(Rc::clone(p))
} else {
None
}
})
})
.unwrap_or_else(|| Rc::new(RefCell::new(Path::temporary(local, remote))))
}
/// Get a reference to the primary path. This will assert if there is no primary
/// path, which happens at a server prior to receiving a valid Initial packet
/// from a client. So be careful using this method.
pub fn primary(&self) -> PathRef {
self.primary_fallible().unwrap()
}
/// Get a reference to the primary path. Use this prior to handshake completion.
pub fn primary_fallible(&self) -> Option<PathRef> {
self.primary.as_ref().map(Rc::clone)
}
/// Returns true if the path is not permanent.
pub fn is_temporary(&self, path: &PathRef) -> bool {
// Ask the path first, which is simpler.
path.borrow().is_temporary() || !self.paths.iter().any(|p| Rc::ptr_eq(p, path))
}
fn retire(to_retire: &mut Vec<u64>, retired: &PathRef) {
let seqno = retired
.borrow()
.remote_cid
.as_ref()
.unwrap()
.sequence_number();
to_retire.push(seqno);
}
/// Adopt a temporary path as permanent.
/// The first path that is made permanent is made primary.
pub fn make_permanent(
&mut self,
path: &PathRef,
local_cid: Option<ConnectionId>,
remote_cid: RemoteConnectionIdEntry,
) {
debug_assert!(self.is_temporary(&path));
// Make sure not to track too many paths.
// This protects index 0, which contains the primary path.
if self.paths.len() >= MAX_PATHS {
debug_assert_eq!(self.paths.len(), MAX_PATHS);
let removed = self.paths.remove(1);
Self::retire(&mut self.to_retire, &removed);
debug_assert_eq!(Rc::strong_count(&removed), 1);
}
qdebug!([path.borrow()], "Make permanent");
path.borrow_mut().make_permanent(local_cid, remote_cid);
self.paths.push(Rc::clone(&path));
if self.primary.is_none() {
assert!(self.select_primary(&path).is_none());
}
}
/// Select a path as the primary. Returns the old primary path.
/// The old path is only necessary if this change in path is a reaction to a
/// migration from a peer, in which case the old path needs to be probed.
#[must_use]
fn select_primary(&mut self, path: &PathRef) -> Option<PathRef> {
qinfo!([path.borrow()], "set as primary path");
let old_path = self.primary.replace(Rc::clone(path)).map(|old| {
old.borrow_mut().set_primary(false);
old
});
// Swap the primary path into slot 0, so that it is protected from eviction.
let idx = self
.paths
.iter()
.enumerate()
.find_map(|(i, p)| if Rc::ptr_eq(p, path) { Some(i) } else { None })
.expect("migration target should be permanent");
self.paths.swap(0, idx);
path.borrow_mut().set_primary(true);
old_path
}
/// Migrate to the identified path. If `force` is true, the path
/// 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) {
debug_assert!(!self.is_temporary(path));
if force || path.borrow().is_valid() {
path.borrow_mut().set_valid(now);
let _ = self.select_primary(path);
} else {
self.migration_target = Some(Rc::clone(path));
}
path.borrow_mut().probe();
}
/// Process elapsed time for active paths.
/// Returns an true if there are viable paths remaining after tidying up.
///
/// TODO(mt) - the paths should own the RTT estimator, so they can find the PTO
/// for themselves.
pub fn process_timeout(&mut self, now: Instant, pto: Duration) -> bool {
let to_retire = &mut self.to_retire;
let mut primary_failed = false;
self.paths.retain(|p| {
if p.borrow_mut().process_timeout(now, pto) {
true
} else {
qdebug!([p.borrow()], "Retiring path");
if p.borrow().is_primary() {
primary_failed = true;
}
Self::retire(to_retire, p);
false
}
});
if primary_failed {
self.primary = None;
// Find a valid path to fall back to.
if let Some(fallback) = self
.paths
.iter()
.rev() // More recent paths are toward the end.
.find(|p| p.borrow().is_valid())
{
// 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);
true
} else {
false
}
} else {
true
}
}
/// Get when the next call to `process_timeout()` should be scheduled.
pub fn next_timeout(&self, pto: Duration) -> Option<Instant> {
self.paths
.iter()
.filter_map(|p| p.borrow().next_timeout(pto))
.min()
}
/// Set the identified path to be primary.
/// This panics if `make_permanent` hasn't been called.
pub fn handle_migration(&mut self, path: &PathRef, remote: SocketAddr, now: Instant) {
qtrace!([self.primary().borrow()], "handle_migration");
// The update here needs to match the checks in `Path::received_on`.
// Here, we update the remote port number to match the source port on the
// datagram that was received. This ensures that we send subsequent
// packets back to the right place.
path.borrow_mut().update_port(remote.port());
if path.borrow().is_primary() {
// Update when the path was last regarded as valid.
path.borrow_mut().update(now);
return;
}
if let Some(old_path) = self.select_primary(path) {
// Need to probe the old path if the peer migrates.
old_path.borrow_mut().probe();
// TODO(mt) - suppress probing if the path was valid within 3PTO.
}
}
/// Select a path to send on. This will select the first path that has
/// probes to send, then fall back to the primary path.
pub fn select_path(&self) -> Option<PathRef> {
self.paths
.iter()
.find_map(|p| {
if p.borrow().has_probe() {
Some(Rc::clone(p))
} else {
None
}
})
.or_else(|| self.primary.as_ref().map(Rc::clone))
}
/// A `PATH_RESPONSE` was received.
pub fn path_response(&mut self, response: [u8; 8], now: Instant) {
for p in &self.paths {
if p.borrow_mut().path_response(response, now) {
// The response was accepted. If this path is one we intend
// to migrate to, then migrate.
if self
.migration_target
.as_ref()
.map_or(false, |target| Rc::ptr_eq(target, p))
{
let primary = self.migration_target.take();
let _ = self.select_primary(&primary.unwrap());
}
break;
}
}
}
/// Write out any `RETIRE_CONNECTION_ID` frames that are outstanding.
pub fn write_frames(
&mut self,
builder: &mut PacketBuilder,
tokens: &mut Vec<RecoveryToken>,
stats: &mut FrameStats,
) -> Res<()> {
while let Some(seqno) = self.to_retire.pop() {
if builder.remaining() < 1 + Encoder::varint_len(seqno) {
self.to_retire.push(seqno);
break;
}
builder.encode_varint(FRAME_TYPE_RETIRE_CONNECTION_ID);
builder.encode_varint(seqno);
if builder.len() > builder.limit() {
return Err(Error::InternalError(20));
}
tokens.push(RecoveryToken::RetireConnectionId(seqno));
stats.retire_connection_id += 1;
}
Ok(())
}
pub fn lost_retire_cid(&mut self, lost: u64) {
self.to_retire.push(lost);
}
pub fn acked_retire_cid(&mut self, acked: u64) {
self.to_retire.retain(|&seqno| seqno != acked);
}
}
/// The state of a path with respect to address validation.
#[derive(Debug)]
enum ProbeState {
/// The path was last valid at the indicated time.
Valid,
/// The path was previously valid, but a new probe is needed.
ProbeNeeded { probe_count: usize },
/// The path hasn't been validated, but a probe has been sent.
Probing {
/// The number of probes that have been sent.
probe_count: usize,
/// The probe that was last sent.
data: [u8; 8],
/// Whether the probe was sent in a datagram padded to the path MTU.
mtu: bool,
/// When the probe was sent.
sent: Instant,
},
/// Validation failed the last time it was attempted.
Failed,
}
impl ProbeState {
/// Determine whether the current state requires probing.
fn probe_needed(&self) -> bool {
matches!(self, Self::ProbeNeeded { .. })
}
}
/// A network path.
///
/// Paths are used a little bit strangely by connections:
/// they need to encapsulate all the state for a path (which
/// is normal), but that information is not propagated to the
/// `Paths` instance that holds them. This is because the packet
/// processing where changes occur can't hold a reference to the
/// `Paths` instance that owns the `Path`. Any changes to the
/// path are communicated to `Paths` afterwards.
#[derive(Debug)]
#[derive(Clone, Debug, PartialEq)]
pub struct Path {
/// A local socket address.
local: SocketAddr,
/// A remote socket address.
remote: SocketAddr,
/// The connection IDs that we use when sending on this path.
/// This is only needed during the handshake.
local_cid: Option<ConnectionId>,
/// The current connection ID that we are using and its details.
remote_cid: Option<RemoteConnectionIdEntry>,
/// Whether this is the primary path.
primary: bool,
/// Whether the current path is considered valid.
state: ProbeState,
/// For a path that is not validated, this is `None`. For a validated
/// path, the time that the path was last valid.
validated: Option<Instant>,
/// A path challenge was received and PATH_RESPONSE has not been sent.
challenge: Option<[u8; 8]>,
/// 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.
received_bytes: usize,
/// The number of bytes sent on this path.
sent_bytes: usize,
local_cids: Vec<ConnectionId>,
remote_cid: ConnectionId,
reset_token: Option<[u8; 16]>,
}
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 {
/// Create a path from addresses and connection IDs.
pub fn new(
local: SocketAddr,
remote: SocketAddr,
local_cid: ConnectionId,
remote_cid: ConnectionId,
) -> Self {
Self {
local,
remote,
local_cid: None,
remote_cid: None,
primary: false,
state: ProbeState::ProbeNeeded { probe_count: 0 },
validated: None,
challenge: None,
received_bytes: 0,
sent_bytes: 0,
local_cids: vec![local_cid],
remote_cid,
reset_token: None,
}
}
/// Whether this path is the primary or current path for the connection.
pub fn is_primary(&self) -> bool {
self.primary
pub fn received_on(&self, d: &Datagram) -> bool {
self.local == d.destination() && self.remote == d.source()
}
/// Whether this path is a temporary one.
pub fn is_temporary(&self) -> bool {
self.remote_cid.is_none()
}
/// 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(
&mut self,
local_cid: Option<ConnectionId>,
remote_cid: RemoteConnectionIdEntry,
) {
if self.local_cid.is_none() {
self.local_cid = local_cid;
}
self.remote_cid.replace(remote_cid);
}
/// Determine if this path was the one that the provided datagram was received on.
/// This uses the full local socket address, but ignores the port number on the peer
/// if `flexible` is true, allowing for NAT rebinding that retains the same IP.
fn received_on(&self, local: SocketAddr, remote: SocketAddr, flexible: bool) -> bool {
self.local == local
&& self.remote.ip() == remote.ip()
&& (flexible || self.remote.port() == remote.port())
}
/// Update the remote port number. Any flexibility we allow in `received_on`
/// need to be adjusted at this point.
fn update_port(&mut self, port: u16) {
self.remote.set_port(port);
}
/// Set whether this path is primary.
fn set_primary(&mut self, primary: bool) {
qtrace!([self], "Make primary {}", primary);
self.primary = primary;
}
/// Set the current path as valid. This updates the time that the path was
/// last validated and cancels any path validation.
pub fn set_valid(&mut self, now: Instant) {
qdebug!([self], "Path validated {:?}", now);
self.state = ProbeState::Valid;
self.validated = Some(now);
}
/// Update the last use of this path, if it is valid.
/// This will keep the path active slightly longer.
pub fn update(&mut self, now: Instant) {
if self.validated.is_some() {
self.validated = Some(now);
}
}
/// Get the path MTU. This is currently a fixed value.
pub fn mtu(&self) -> usize {
match self.local.ip() {
IpAddr::V4(_) => PATH_MTU_V4,
IpAddr::V6(_) => PATH_MTU_V6,
if self.local.is_ipv4() {
PATH_MTU_V4
} else {
PATH_MTU_V6 // IPv6
}
}
/// Add a connection ID to the local set.
pub fn add_local_cid(&mut self, cid: ConnectionId) {
self.local_cids.push(cid);
}
/// Determine if the given connection ID is valid.
pub fn valid_local_cid(&self, cid: &ConnectionIdRef) -> bool {
self.local_cids.iter().any(|c| c == cid)
}
/// Get the first local connection ID.
/// Only do this for the primary path during the handshake.
pub fn local_cid(&self) -> &ConnectionId {
self.local_cid.as_ref().unwrap()
self.local_cids.first().as_ref().unwrap()
}
/// Set the remote connection ID based on the peer's choice.
/// This is only valid during the handshake.
pub fn set_remote_cid(&mut self, cid: &ConnectionIdRef) {
self.remote_cid
.as_mut()
.unwrap()
.update_cid(ConnectionId::from(cid));
self.remote_cid = ConnectionId::from(cid);
}
/// Access the remote connection ID.
pub fn remote_cid(&self) -> &ConnectionId {
self.remote_cid.as_ref().unwrap().connection_id()
&self.remote_cid
}
/// Set the stateless reset token for the connection ID that is currently in use.
/// Panics if the sequence number is non-zero as this is only necessary during
/// the handshake; all other connection IDs are initialized with a token.
pub fn set_reset_token(&mut self, token: [u8; 16]) {
self.remote_cid
.as_mut()
.unwrap()
.set_stateless_reset_token(token);
self.reset_token = Some(token);
}
/// Determine if the provided token is a stateless reset token.
pub fn is_stateless_reset(&self, token: &[u8; 16]) -> bool {
self.remote_cid
.as_ref()
.map_or(false, |rcid| rcid.is_stateless_reset(token))
/// Access the reset token.
pub fn reset_token(&self) -> Option<&[u8; 16]> {
self.reset_token.as_ref()
}
/// Make a datagram.
@ -545,202 +106,4 @@ impl Path {
pub fn remote_address(&self) -> SocketAddr {
self.remote
}
/// Whether the path has been validated.
pub fn is_valid(&self) -> bool {
self.validated.is_some()
}
/// Handle a `PATH_RESPONSE` frame. Returns true if the response was accepted.
pub fn path_response(&mut self, response: [u8; 8], now: Instant) -> bool {
if let ProbeState::Probing { data, mtu, .. } = &mut self.state {
if response == *data {
let need_full_probe = !*mtu;
self.set_valid(now);
if need_full_probe {
qdebug!([self], "Sub-MTU probe successful, reset probe count");
self.probe();
}
true
} else {
false
}
} else {
false
}
}
/// The path has been challenged. This generates a response.
/// This only generates a single response at a time.
pub fn challenged(&mut self, challenge: [u8; 8]) {
self.challenge = Some(challenge.to_owned());
}
/// At the next opportunity, send a probe.
/// If the probe count has been exhausted already, marks the path as failed.
fn probe(&mut self) {
let probe_count = match &self.state {
ProbeState::Probing { probe_count, .. } => *probe_count + 1,
ProbeState::ProbeNeeded { probe_count, .. } => *probe_count,
_ => 0,
};
self.state = if probe_count >= MAX_PATH_PROBES {
qinfo!([self], "Probing failed");
ProbeState::Failed
} else {
qdebug!([self], "Initiating probe");
ProbeState::ProbeNeeded { probe_count }
};
}
/// Returns true if this path have any probing frames to send.
pub fn has_probe(&self) -> bool {
self.challenge.is_some() || self.state.probe_needed()
}
pub fn write_frames(
&mut self,
builder: &mut PacketBuilder,
stats: &mut FrameStats,
mtu: bool, // Whether the packet we're writing into will be a full MTU.
now: Instant,
) -> Res<bool> {
if builder.remaining() < 9 {
return Ok(false);
}
// Send PATH_RESPONSE.
let resp_sent = if let Some(challenge) = self.challenge.take() {
qtrace!([self], "Responding to path challenge {}", hex(&challenge));
builder.encode_varint(FRAME_TYPE_PATH_RESPONSE);
builder.encode(&challenge[..]);
if builder.len() > builder.limit() {
return Err(Error::InternalError(21));
}
// These frames are not retransmitted in the usual fashion.
// There is no token, therefore we need to count `all` specially.
stats.path_response += 1;
stats.all += 1;
if builder.remaining() < 9 {
return Ok(true);
}
true
} else {
false
};
// Send PATH_CHALLENGE.
if let ProbeState::ProbeNeeded { probe_count } = self.state {
qtrace!([self], "Initiating path challenge {}", probe_count);
let data = <[u8; 8]>::try_from(&random(8)[..]).unwrap();
builder.encode_varint(FRAME_TYPE_PATH_CHALLENGE);
builder.encode(&data);
if builder.len() > builder.limit() {
return Err(Error::InternalError(22));
}
// As above, no recovery token.
stats.path_challenge += 1;
stats.all += 1;
self.state = ProbeState::Probing {
probe_count,
data,
mtu,
sent: now,
};
Ok(true)
} else {
Ok(resp_sent)
}
}
/// Process a timer for this path.
/// This returns true if the path is viable and can be kept alive.
pub fn process_timeout(&mut self, now: Instant, pto: Duration) -> bool {
if let ProbeState::Probing { sent, .. } = &self.state {
if now >= *sent + pto {
self.probe();
}
}
if let ProbeState::Failed = self.state {
// Retire failed paths immediately.
false
} else if self.primary {
// Keep valid primary paths otherwise.
true
} else if let ProbeState::Valid = self.state {
// Retire validated, non-primary paths.
// Allow more than `MAX_PATH_PROBES` times the PTO so that an old
// path remains around until after a previous path fails.
let count = u32::try_from(MAX_PATH_PROBES + 1).unwrap();
self.validated.unwrap() + (pto * count) > now
} else {
// Keep paths that are being actively probed.
true
}
}
/// Return the next time that this path needs servicing.
/// This only considers retransmissions of probes, not cleanup of the path.
/// If there is no other activity, then there is no real need to schedule a
/// timer to cleanup old paths.
pub fn next_timeout(&self, pto: Duration) -> Option<Instant> {
if let ProbeState::Probing { sent, .. } = &self.state {
Some(*sent + pto)
} else {
None
}
}
/// Record received bytes for the path.
pub fn add_received(&mut self, count: usize) {
self.received_bytes = self.received_bytes.saturating_add(count);
}
/// Record sent bytes for the path.
pub fn add_sent(&mut self, count: usize) {
self.sent_bytes = self.sent_bytes.saturating_add(count);
}
/// Get the number of bytes that can be written to this path.
pub fn amplification_limit(&self) -> usize {
if matches!(self.state, ProbeState::Failed) {
0
} else if self.is_valid() {
usize::MAX
} else {
self.received_bytes
.checked_mul(3)
.map_or(usize::MAX, |limit| {
let budget = if limit == 0 {
// If we have received absolutely nothing thus far, then this endpoint
// is the one initiating communication on this path. Allow enough space for probing.
self.mtu() * 5
} else {
limit
};
budget.saturating_sub(self.sent_bytes)
})
}
}
}
impl Display for Path {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.is_primary() {
write!(f, "pri-")?; // primary
}
if !self.is_valid() {
write!(f, "unv-")?; // unvalidated
}
write!(f, "path")?;
if let Some(entry) = self.remote_cid.as_ref() {
write!(f, ":{}", entry.connection_id())?;
}
write!(f, " {}->{}", self.local, self.remote)?;
Ok(())
}
}

40
third_party/rust/neqo-transport/src/qlog.rs поставляемый
Просмотреть файл

@ -7,7 +7,7 @@
// Functions that handle capturing QLOG traces.
use std::convert::TryFrom;
use std::ops::{Deref, RangeInclusive};
use std::ops::RangeInclusive;
use std::string::String;
use std::time::Duration;
@ -16,10 +16,9 @@ use qlog::{self, event::Event, PacketHeader, QuicFrame};
use neqo_common::{hex, qinfo, qlog::NeqoQlog, Decoder};
use crate::connection::State;
use crate::frame::{CloseError, Frame};
use crate::frame::{self, Frame};
use crate::packet::{DecryptedPacket, PacketNumber, PacketType, PublicPacket};
use crate::path::PathRef;
use crate::stream_id::StreamType as NeqoStreamType;
use crate::path::Path;
use crate::tparams::{self, TransportParametersHandler};
use crate::tracking::SentPacket;
use crate::QuicVersion;
@ -84,31 +83,30 @@ pub fn connection_tparams_set(qlog: &mut NeqoQlog, tph: &TransportParametersHand
})
}
pub fn server_connection_started(qlog: &mut NeqoQlog, path: &PathRef) {
pub fn server_connection_started(qlog: &mut NeqoQlog, path: &Path) {
connection_started(qlog, path)
}
pub fn client_connection_started(qlog: &mut NeqoQlog, path: &PathRef) {
pub fn client_connection_started(qlog: &mut NeqoQlog, path: &Path) {
connection_started(qlog, path)
}
fn connection_started(qlog: &mut NeqoQlog, path: &PathRef) {
fn connection_started(qlog: &mut NeqoQlog, path: &Path) {
qlog.add_event(|| {
let p = path.deref().borrow();
Some(Event::connection_started(
if p.local_address().ip().is_ipv4() {
if path.local_address().ip().is_ipv4() {
"ipv4".into()
} else {
"ipv6".into()
},
format!("{}", p.local_address().ip()),
format!("{}", p.remote_address().ip()),
format!("{}", path.local_address().ip()),
format!("{}", path.remote_address().ip()),
Some("QUIC".into()),
p.local_address().port().into(),
p.remote_address().port().into(),
path.local_address().port().into(),
path.remote_address().port().into(),
Some(format!("{:x}", QuicVersion::default().as_u32())),
Some(format!("{}", p.local_cid())),
Some(format!("{}", p.remote_cid())),
Some(format!("{}", path.local_cid())),
Some(format!("{}", path.remote_cid())),
))
})
}
@ -373,8 +371,8 @@ fn frame_to_qlogframe(frame: &Frame) -> QuicFrame {
maximum_streams,
} => QuicFrame::max_streams(
match stream_type {
NeqoStreamType::BiDi => qlog::StreamType::Bidirectional,
NeqoStreamType::UniDi => qlog::StreamType::Unidirectional,
frame::StreamType::BiDi => qlog::StreamType::Bidirectional,
frame::StreamType::UniDi => qlog::StreamType::Unidirectional,
},
maximum_streams.as_u64().to_string(),
),
@ -391,8 +389,8 @@ fn frame_to_qlogframe(frame: &Frame) -> QuicFrame {
stream_limit,
} => QuicFrame::streams_blocked(
match stream_type {
NeqoStreamType::BiDi => qlog::StreamType::Bidirectional,
NeqoStreamType::UniDi => qlog::StreamType::Unidirectional,
frame::StreamType::BiDi => qlog::StreamType::Bidirectional,
frame::StreamType::UniDi => qlog::StreamType::Unidirectional,
},
stream_limit.as_u64().to_string(),
),
@ -419,8 +417,8 @@ fn frame_to_qlogframe(frame: &Frame) -> QuicFrame {
reason_phrase,
} => QuicFrame::connection_close(
match error_code {
CloseError::Transport(_) => qlog::ErrorSpace::TransportError,
CloseError::Application(_) => qlog::ErrorSpace::ApplicationError,
frame::CloseError::Transport(_) => qlog::ErrorSpace::TransportError,
frame::CloseError::Application(_) => qlog::ErrorSpace::ApplicationError,
},
error_code.code(),
0,

Просмотреть файл

@ -19,7 +19,6 @@ use smallvec::{smallvec, SmallVec};
use neqo_common::{qdebug, qinfo, qlog::NeqoQlog, qtrace};
use crate::cc::CongestionControlAlgorithm;
use crate::cid::ConnectionIdEntry;
use crate::connection::LOCAL_IDLE_TIMEOUT;
use crate::crypto::CryptoRecoveryToken;
use crate::flow_mgr::FlowControlRecoveryToken;
@ -53,8 +52,6 @@ pub enum RecoveryToken {
Flow(FlowControlRecoveryToken),
HandshakeDone,
NewToken(usize),
NewConnectionId(ConnectionIdEntry<[u8; 16]>),
RetireConnectionId(u64),
}
#[derive(Debug)]
@ -162,13 +159,9 @@ impl Default for RttVals {
/// `SendProfile` tells a sender how to send packets.
#[derive(Debug)]
pub struct SendProfile {
/// The limit on the size of the packet.
limit: usize,
/// Whether this is a PTO, and what space the PTO is for.
pto: Option<PNSpace>,
/// What spaces should be probed.
probe: PNSpaceSet,
/// Whether pacing is active.
paced: bool,
}
@ -589,14 +582,13 @@ impl PtoState {
}
/// Generate a sending profile, indicating what space it should be from.
/// This takes a packet from the supply if one remains, or returns `None`.
pub fn send_profile(&mut self, mtu: usize) -> Option<SendProfile> {
/// This takes a packet from the supply or returns an ack-only profile if it can't.
pub fn send_profile(&mut self, mtu: usize) -> SendProfile {
if self.packets > 0 {
// This is a PTO, so ignore the limit.
self.packets -= 1;
Some(SendProfile::new_pto(self.space, mtu, self.probe))
SendProfile::new_pto(self.space, mtu, self.probe)
} else {
None
SendProfile::new_limited(0)
}
}
}
@ -771,8 +763,7 @@ impl LossRecovery {
// This must happen after on_packets_lost. If in recovery, this could
// take us out, and then lost packets will start a new recovery period
// when it shouldn't.
self.packet_sender
.on_packets_acked(&acked_packets, self.rtt_vals.min_rtt, now);
self.packet_sender.on_packets_acked(&acked_packets);
self.pto_state = None;
@ -1001,22 +992,13 @@ impl LossRecovery {
/// Check how packets should be sent, based on whether there is a PTO,
/// what the current congestion window is, and what the pacer says.
#[allow(clippy::option_if_let_else, clippy::unknown_clippy_lints)]
pub fn send_profile(
&mut self,
now: Instant,
mtu: usize,
amplification_limit: usize,
) -> SendProfile {
pub fn send_profile(&mut self, now: Instant, mtu: usize) -> SendProfile {
qdebug!([self], "get send profile {:?}", now);
if let Some(profile) = self
.pto_state
.as_mut()
.and_then(|pto| pto.send_profile(mtu))
{
profile
if let Some(pto) = self.pto_state.as_mut() {
pto.send_profile(mtu)
} else {
let limit = min(self.cwnd_avail(), amplification_limit);
if limit > mtu {
let cwnd = self.cwnd_avail();
if cwnd > mtu {
// More than an MTU available; we might need to pace.
if self.next_paced().map_or(false, |t| t > now) {
SendProfile::new_paced()
@ -1029,7 +1011,7 @@ impl LossRecovery {
// result in a PING being sent in every active space.
SendProfile::new_pto(PNSpace::Initial, mtu, PNSpaceSet::all())
} else {
SendProfile::new_limited(limit)
SendProfile::new_limited(cwnd)
}
}
}
@ -1479,33 +1461,10 @@ mod tests {
// expired should result in setting a PTO state.
let expected_pto = pn_time(2) + (INITIAL_RTT * 3) + MAX_ACK_DELAY;
lr.discard(PNSpace::Handshake, expected_pto);
let profile = lr.send_profile(expected_pto, 10000, 10000);
let profile = lr.send_profile(expected_pto, 10000);
assert!(profile.pto.is_some());
assert!(!profile.should_probe(PNSpace::Initial));
assert!(!profile.should_probe(PNSpace::Handshake));
assert!(profile.should_probe(PNSpace::ApplicationData));
}
#[test]
fn no_pto_if_amplification_limited() {
let mut lr = LossRecovery::new(CongestionControlAlgorithm::NewReno, StatsCell::default());
lr.start_pacer(now());
lr.on_packet_sent(SentPacket::new(
PacketType::Initial,
0,
now(),
true,
Vec::new(),
ON_SENT_SIZE,
));
let expected_pto = now() + (INITIAL_RTT * 3);
assert_eq!(lr.pto_time(PNSpace::Initial), Some(expected_pto));
let profile = lr.send_profile(expected_pto, 10000, 10);
assert!(profile.ack_only(PNSpace::Initial));
assert!(profile.pto.is_none());
assert!(!profile.should_probe(PNSpace::Initial));
assert!(!profile.should_probe(PNSpace::Handshake));
assert!(!profile.should_probe(PNSpace::ApplicationData));
}
}

Просмотреть файл

@ -536,7 +536,7 @@ impl SendStream {
(length, false)
}
pub fn write_frame(&mut self, builder: &mut PacketBuilder) -> Res<Option<RecoveryToken>> {
pub fn write_frame(&mut self, builder: &mut PacketBuilder) -> Option<RecoveryToken> {
let id = self.stream_id;
let final_size = self.final_size();
if let Some((offset, data)) = self.next_bytes() {
@ -549,14 +549,14 @@ impl SendStream {
};
if overhead > builder.remaining() {
qtrace!("SendStream::write_frame no space for header");
return Ok(None);
return None;
}
let (length, fill) = Self::length_and_fill(data.len(), builder.remaining() - overhead);
let fin = final_size.map_or(false, |fs| fs == offset + u64::try_from(length).unwrap());
if length == 0 && !fin {
qtrace!("SendStream::write_frame no data, no fin");
return Ok(None);
return None;
}
// Write the stream out.
@ -571,19 +571,15 @@ impl SendStream {
builder.encode_vvec(&data[..length]);
}
if builder.len() > builder.limit() {
return Err(Error::InternalError(23));
}
self.mark_as_sent(offset, length, fin);
Ok(Some(RecoveryToken::Stream(StreamRecoveryToken {
Some(RecoveryToken::Stream(StreamRecoveryToken {
id,
offset,
length,
fin,
})))
}))
} else {
Ok(None)
None
}
}
@ -872,14 +868,13 @@ impl SendStreams {
builder: &mut PacketBuilder,
tokens: &mut Vec<RecoveryToken>,
stats: &mut FrameStats,
) -> Res<()> {
) {
for (_, stream) in self {
if let Some(t) = stream.write_frame(builder)? {
if let Some(t) = stream.write_frame(builder) {
tokens.push(t);
stats.stream += 1;
}
}
Ok(())
}
}
@ -1320,8 +1315,7 @@ mod tests {
// Write a small frame: no fin.
let written = builder.len();
builder.set_limit(written + 6);
ss.write_frames(&mut builder, &mut tokens, &mut FrameStats::default())
.unwrap();
ss.write_frames(&mut builder, &mut tokens, &mut FrameStats::default());
assert_eq!(builder.len(), written + 6);
assert_eq!(tokens.len(), 1);
let f1_token = tokens.remove(0);
@ -1330,8 +1324,7 @@ mod tests {
// Write the rest: fin.
let written = builder.len();
builder.set_limit(written + 200);
ss.write_frames(&mut builder, &mut tokens, &mut FrameStats::default())
.unwrap();
ss.write_frames(&mut builder, &mut tokens, &mut FrameStats::default());
assert_eq!(builder.len(), written + 10);
assert_eq!(tokens.len(), 1);
let f2_token = tokens.remove(0);
@ -1339,8 +1332,7 @@ mod tests {
// Should be no more data to frame.
let written = builder.len();
ss.write_frames(&mut builder, &mut tokens, &mut FrameStats::default())
.unwrap();
ss.write_frames(&mut builder, &mut tokens, &mut FrameStats::default());
assert_eq!(builder.len(), written);
assert!(tokens.is_empty());
@ -1354,8 +1346,7 @@ mod tests {
// Next frame should not set fin even though stream has fin but frame
// does not include end of stream
let written = builder.len();
ss.write_frames(&mut builder, &mut tokens, &mut FrameStats::default())
.unwrap();
ss.write_frames(&mut builder, &mut tokens, &mut FrameStats::default());
assert_eq!(builder.len(), written + 7); // Needs a length this time.
assert_eq!(tokens.len(), 1);
let f4_token = tokens.remove(0);
@ -1370,8 +1361,7 @@ mod tests {
// Next frame should set fin because it includes end of stream
let written = builder.len();
ss.write_frames(&mut builder, &mut tokens, &mut FrameStats::default())
.unwrap();
ss.write_frames(&mut builder, &mut tokens, &mut FrameStats::default());
assert_eq!(builder.len(), written + 10);
assert_eq!(tokens.len(), 1);
let f5_token = tokens.remove(0);
@ -1394,22 +1384,19 @@ mod tests {
let mut tokens = Vec::new();
let mut builder = PacketBuilder::short(Encoder::new(), false, &[]);
ss.write_frames(&mut builder, &mut tokens, &mut FrameStats::default())
.unwrap();
ss.write_frames(&mut builder, &mut tokens, &mut FrameStats::default());
let f1_token = tokens.remove(0);
assert!(matches!(&f1_token, RecoveryToken::Stream(x) if x.offset == 0));
assert!(matches!(&f1_token, RecoveryToken::Stream(x) if x.length == 10));
assert!(matches!(&f1_token, RecoveryToken::Stream(x) if !x.fin));
// Should be no more data to frame
ss.write_frames(&mut builder, &mut tokens, &mut FrameStats::default())
.unwrap();
ss.write_frames(&mut builder, &mut tokens, &mut FrameStats::default());
assert!(tokens.is_empty());
ss.get_mut(StreamId::from(0)).unwrap().close();
ss.write_frames(&mut builder, &mut tokens, &mut FrameStats::default())
.unwrap();
ss.write_frames(&mut builder, &mut tokens, &mut FrameStats::default());
let f2_token = tokens.remove(0);
assert!(matches!(&f2_token, RecoveryToken::Stream(x) if x.offset == 10));
assert!(matches!(&f2_token, RecoveryToken::Stream(x) if x.length == 0));
@ -1423,8 +1410,7 @@ mod tests {
}
// Next frame should set fin
ss.write_frames(&mut builder, &mut tokens, &mut FrameStats::default())
.unwrap();
ss.write_frames(&mut builder, &mut tokens, &mut FrameStats::default());
let f3_token = tokens.remove(0);
assert!(matches!(&f3_token, RecoveryToken::Stream(x) if x.offset == 10));
assert!(matches!(&f3_token, RecoveryToken::Stream(x) if x.length == 0));
@ -1438,8 +1424,7 @@ mod tests {
}
// Next frame should set fin and include all data
ss.write_frames(&mut builder, &mut tokens, &mut FrameStats::default())
.unwrap();
ss.write_frames(&mut builder, &mut tokens, &mut FrameStats::default());
let f4_token = tokens.remove(0);
assert!(matches!(&f4_token, RecoveryToken::Stream(x) if x.offset == 0));
assert!(matches!(&f4_token, RecoveryToken::Stream(x) if x.length == 10));
@ -1566,7 +1551,7 @@ mod tests {
// No frame should be sent here.
let mut builder = PacketBuilder::short(Encoder::new(), false, &[]);
assert!(s.write_frame(&mut builder).unwrap().is_none());
assert!(s.write_frame(&mut builder).is_none());
}
/// Create a `SendStream` and force it into a state where it believes that
@ -1606,7 +1591,7 @@ mod tests {
let mut builder = PacketBuilder::short(Encoder::new(), false, &[]);
let header_len = builder.len();
builder.set_limit(header_len + space);
let token = s.write_frame(&mut builder).unwrap();
let token = s.write_frame(&mut builder);
qtrace!("STREAM frame: {}", hex_with_len(&builder[header_len..]));
token.is_some()
}
@ -1700,7 +1685,7 @@ mod tests {
let mut builder = PacketBuilder::short(Encoder::new(), false, &[]);
let header_len = builder.len();
builder.set_limit(header_len + DATA16384.len() + 2);
let token = s.write_frame(&mut builder).unwrap();
let token = s.write_frame(&mut builder);
assert!(token.is_some());
// Expect STREAM + FIN only.
assert_eq!(&builder[header_len..header_len + 2], &[0b1001, 0]);
@ -1715,7 +1700,7 @@ mod tests {
let mut builder = PacketBuilder::short(Encoder::new(), false, &[]);
let header_len = builder.len();
builder.set_limit(header_len + DATA16384.len() + 3);
let token = s.write_frame(&mut builder).unwrap();
let token = s.write_frame(&mut builder);
assert!(token.is_some());
// Expect STREAM + LEN + FIN.
assert_eq!(
@ -1741,7 +1726,7 @@ mod tests {
let mut builder = PacketBuilder::short(Encoder::new(), false, &[]);
let header_len = builder.len();
builder.set_limit(header_len + 66);
let token = s.write_frame(&mut builder).unwrap();
let token = s.write_frame(&mut builder);
assert!(token.is_some());
// Expect STREAM + FIN only.
assert_eq!(&builder[header_len..header_len + 2], &[0b1001, 0]);
@ -1752,7 +1737,7 @@ mod tests {
let mut builder = PacketBuilder::short(Encoder::new(), false, &[]);
let header_len = builder.len();
builder.set_limit(header_len + 67);
let token = s.write_frame(&mut builder).unwrap();
let token = s.write_frame(&mut builder);
assert!(token.is_some());
// Expect STREAM + LEN, not FIN.
assert_eq!(&builder[header_len..header_len + 3], &[0b1010, 0, 63]);

Просмотреть файл

@ -9,7 +9,7 @@
#![allow(clippy::module_name_repetitions)]
use crate::cc::{
ClassicCongestionControl, CongestionControl, CongestionControlAlgorithm, Cubic, NewReno,
ClassicCongestionControl, CongestionControl, CongestionControlAlgorithm, NewReno,
MAX_DATAGRAM_SIZE,
};
use crate::pace::Pacer;
@ -46,9 +46,6 @@ impl PacketSender {
CongestionControlAlgorithm::NewReno => {
Box::new(ClassicCongestionControl::new(NewReno::default()))
}
CongestionControlAlgorithm::Cubic => {
Box::new(ClassicCongestionControl::new(Cubic::default()))
}
},
pacer: None,
}
@ -70,8 +67,8 @@ impl PacketSender {
}
// 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);
pub fn on_packets_acked(&mut self, acked_pkts: &[SentPacket]) {
self.cc.on_packets_acked(acked_pkts);
}
pub fn on_packets_lost(

167
third_party/rust/neqo-transport/src/server.rs поставляемый
Просмотреть файл

@ -14,10 +14,9 @@ use neqo_crypto::{AntiReplay, Cipher, ZeroRttCheckResult, ZeroRttChecker};
pub use crate::addr_valid::ValidateAddress;
use crate::addr_valid::{AddressValidation, AddressValidationResult};
use crate::cid::{ConnectionId, ConnectionIdDecoder, ConnectionIdGenerator, ConnectionIdRef};
use crate::cid::{ConnectionId, ConnectionIdDecoder, ConnectionIdManager, ConnectionIdRef};
use crate::connection::{Connection, Output, State};
use crate::packet::{PacketBuilder, PacketType, PublicPacket};
use crate::tparams::PreferredAddress;
use crate::{ConnectionParameters, QuicVersion, Res};
use std::cell::RefCell;
@ -43,6 +42,7 @@ const TIMER_GRANULARITY: Duration = Duration::from_millis(10);
const TIMER_CAPACITY: usize = 16384;
type StateRef = Rc<RefCell<ServerConnectionState>>;
type CidMgr = Rc<RefCell<dyn ConnectionIdManager>>;
type ConnectionTableRef = Rc<RefCell<HashMap<ConnectionId, StateRef>>>;
#[derive(Debug)]
@ -127,10 +127,8 @@ pub struct Server {
anti_replay: AntiReplay,
/// A function for determining if 0-RTT can be accepted.
zero_rtt_checker: ServerZeroRttChecker,
/// A connection ID generator.
cid_generator: Rc<RefCell<dyn ConnectionIdGenerator>>,
/// The preferred address(es).
preferred_address: Option<PreferredAddress>,
/// A connection ID manager.
cid_manager: CidMgr,
/// Connection parameters.
conn_params: ConnectionParameters,
/// Active connection attempts, keyed by `AttemptKey`. Initial packets with
@ -160,7 +158,7 @@ impl Server {
/// * `zero_rtt_checker` determines whether 0-RTT should be accepted. This
/// will be passed the value of the `extra` argument that was passed to
/// `Connection::send_ticket` to see if it is OK.
/// * `cid_generator` is responsible for generating connection IDs and parsing them;
/// * `cid_manager` is responsible for generating connection IDs and parsing them;
/// connection IDs produced by the manager cannot be zero-length.
pub fn new(
now: Instant,
@ -168,7 +166,7 @@ impl Server {
protocols: &[impl AsRef<str>],
anti_replay: AntiReplay,
zero_rtt_checker: Box<dyn ZeroRttChecker>,
cid_generator: Rc<RefCell<dyn ConnectionIdGenerator>>,
cid_manager: CidMgr,
conn_params: ConnectionParameters,
) -> Res<Self> {
let validation = AddressValidation::new(now, ValidateAddress::Never)?;
@ -178,8 +176,7 @@ impl Server {
ciphers: Vec::new(),
anti_replay,
zero_rtt_checker: ServerZeroRttChecker::new(zero_rtt_checker),
cid_generator,
preferred_address: None,
cid_manager,
conn_params,
active_attempts: HashMap::default(),
connections: Rc::default(),
@ -207,11 +204,6 @@ impl Server {
self.ciphers = Vec::from(ciphers.as_ref());
}
/// Set a preferred address.
pub fn set_preferred_address(&mut self, spa: PreferredAddress) {
self.preferred_address = Some(spa);
}
fn remove_timer(&mut self, c: &StateRef) {
let last = c.borrow().last_timer;
self.timers.remove(last, |t| Rc::ptr_eq(t, c));
@ -303,23 +295,19 @@ impl Server {
qerror!([self], "unable to generate token, dropping packet");
return None;
};
if let Some(new_dcid) = self.cid_generator.borrow_mut().generate_cid() {
let packet = PacketBuilder::retry(
initial.quic_version,
&initial.src_cid,
&new_dcid,
&token,
&initial.dst_cid,
);
if let Ok(p) = packet {
let retry = Datagram::new(dgram.destination(), dgram.source(), p);
Some(retry)
} else {
qerror!([self], "unable to encode retry, dropping packet");
None
}
let new_dcid = self.cid_manager.borrow_mut().generate_cid();
let packet = PacketBuilder::retry(
initial.quic_version,
&initial.src_cid,
&new_dcid,
&token,
&initial.dst_cid,
);
if let Ok(p) = packet {
let retry = Datagram::new(dgram.destination(), dgram.source(), p);
Some(retry)
} else {
qerror!([self], "no connection ID for retry, dropping packet");
qerror!([self], "unable to encode retry, dropping packet");
None
}
}
@ -399,25 +387,6 @@ impl Server {
}
}
fn setup_connection(
&mut self,
c: &mut Connection,
attempt_key: &AttemptKey,
initial: InitialDetails,
orig_dcid: Option<ConnectionId>,
) {
let zcheck = self.zero_rtt_checker.clone();
if c.server_enable_0rtt(&self.anti_replay, zcheck).is_err() {
qwarn!([self], "Unable to enable 0-RTT");
}
if let Some(odcid) = orig_dcid {
// There was a retry, so set the connection IDs for.
c.set_retry_cids(odcid, initial.src_cid, initial.dst_cid);
}
c.set_validation(Rc::clone(&self.address_validation));
c.set_qlog(self.create_qlog_trace(attempt_key));
}
fn accept_connection(
&mut self,
attempt_key: AttemptKey,
@ -430,9 +399,9 @@ impl Server {
// The internal connection ID manager that we use is not used directly.
// Instead, wrap it so that we can save connection IDs.
let cid_mgr = Rc::new(RefCell::new(ServerConnectionIdGenerator {
let cid_mgr = Rc::new(RefCell::new(ServerConnectionIdManager {
c: Weak::new(),
cid_generator: Rc::clone(&self.cid_generator),
cid_manager: Rc::clone(&self.cid_manager),
connections: Rc::clone(&self.connections),
saved_cids: Vec::new(),
}));
@ -441,11 +410,20 @@ impl Server {
&self.certs,
&self.protocols,
Rc::clone(&cid_mgr) as _,
self.conn_params.clone().quic_version(initial.quic_version),
&self.conn_params.clone().quic_version(initial.quic_version),
);
if let Ok(mut c) = sconn {
self.setup_connection(&mut c, &attempt_key, initial, orig_dcid);
let zcheck = self.zero_rtt_checker.clone();
if c.server_enable_0rtt(&self.anti_replay, zcheck).is_err() {
qwarn!([self], "Unable to enable 0-RTT");
}
if let Some(odcid) = orig_dcid {
// There was a retry, so set the connection IDs for.
c.set_retry_cids(odcid, initial.src_cid, initial.dst_cid);
}
c.set_validation(Rc::clone(&self.address_validation));
c.set_qlog(self.create_qlog_trace(&attempt_key));
let c = Rc::new(RefCell::new(ServerConnectionState {
c,
last_timer: now,
@ -461,39 +439,12 @@ impl Server {
}
}
/// Handle 0-RTT packets that were sent with the client's choice of connection ID.
/// Most 0-RTT will arrive this way. A client can usually send 1-RTT after it
/// receives a connection ID from the server.
fn handle_0rtt(
&mut self,
dgram: Datagram,
dcid: ConnectionId,
now: Instant,
) -> Option<Datagram> {
let attempt_key = AttemptKey {
remote_address: dgram.source(),
odcid: dcid,
};
if let Some(c) = self.active_attempts.get(&attempt_key) {
qdebug!(
[self],
"Handle 0-RTT for existing connection attempt {:?}",
attempt_key
);
let c = Rc::clone(c);
self.process_connection(c, Some(dgram), now)
} else {
qdebug!([self], "Dropping 0-RTT for unknown connection");
None
}
}
fn process_input(&mut self, dgram: Datagram, now: Instant) -> Option<Datagram> {
qtrace!("Process datagram: {}", hex(&dgram[..]));
// This is only looking at the first packet header in the datagram.
// All packets in the datagram are routed to the same connection.
let res = PublicPacket::decode(&dgram[..], self.cid_generator.borrow().as_decoder());
let res = PublicPacket::decode(&dgram[..], self.cid_manager.borrow().as_decoder());
let (packet, _remainder) = match res {
Ok(res) => res,
_ => {
@ -513,25 +464,17 @@ impl Server {
return None;
}
if dgram.len() < MIN_INITIAL_PACKET_SIZE {
qtrace!([self], "Bogus packet: too short");
return None;
}
match packet.packet_type() {
PacketType::Initial => {
if dgram.len() < MIN_INITIAL_PACKET_SIZE {
qdebug!([self], "Drop initial: too short");
return None;
}
// Copy values from `packet` because they are currently still borrowing from `dgram`.
let initial = InitialDetails::new(&packet);
self.handle_initial(initial, dgram, now)
}
PacketType::ZeroRtt => {
let dcid = ConnectionId::from(packet.dcid());
self.handle_0rtt(dgram, dcid, now)
}
PacketType::OtherVersion => {
if dgram.len() < MIN_INITIAL_PACKET_SIZE {
qdebug!([self], "Unsupported version: too short");
return None;
}
let vn = PacketBuilder::version_negotiation(packet.scid(), packet.dcid());
Some(Datagram::new(dgram.destination(), dgram.source(), vn))
}
@ -638,18 +581,18 @@ impl PartialEq for ActiveConnectionRef {
impl Eq for ActiveConnectionRef {}
struct ServerConnectionIdGenerator {
struct ServerConnectionIdManager {
c: Weak<RefCell<ServerConnectionState>>,
connections: ConnectionTableRef,
cid_generator: Rc<RefCell<dyn ConnectionIdGenerator>>,
cid_manager: CidMgr,
saved_cids: Vec<ConnectionId>,
}
impl ServerConnectionIdGenerator {
impl ServerConnectionIdManager {
pub fn set_connection(&mut self, c: StateRef) {
let saved = std::mem::replace(&mut self.saved_cids, Vec::with_capacity(0));
for cid in saved {
qtrace!("ServerConnectionIdGenerator inserting saved cid {}", cid);
qtrace!("ServerConnectionIdManager inserting saved cid {}", cid);
self.insert_cid(cid, Rc::clone(&c));
}
self.c = Rc::downgrade(&c);
@ -661,28 +604,24 @@ impl ServerConnectionIdGenerator {
}
}
impl ConnectionIdDecoder for ServerConnectionIdGenerator {
impl ConnectionIdDecoder for ServerConnectionIdManager {
fn decode_cid<'a>(&self, dec: &mut Decoder<'a>) -> Option<ConnectionIdRef<'a>> {
self.cid_generator.borrow_mut().decode_cid(dec)
self.cid_manager.borrow_mut().decode_cid(dec)
}
}
impl ConnectionIdGenerator for ServerConnectionIdGenerator {
fn generate_cid(&mut self) -> Option<ConnectionId> {
let maybe_cid = self.cid_generator.borrow_mut().generate_cid();
if let Some(cid) = maybe_cid {
if let Some(rc) = self.c.upgrade() {
self.insert_cid(cid.clone(), rc);
} else {
// This function can be called before the connection is set.
// So save any connection IDs until that hookup happens.
qtrace!("ServerConnectionIdGenerator saving cid {}", cid);
self.saved_cids.push(cid.clone());
}
Some(cid)
impl ConnectionIdManager for ServerConnectionIdManager {
fn generate_cid(&mut self) -> ConnectionId {
let cid = self.cid_manager.borrow_mut().generate_cid();
if let Some(rc) = self.c.upgrade() {
self.insert_cid(cid.clone(), rc);
} else {
None
// This function can be called before the connection is set.
// So save any connection IDs until that hookup happens.
qtrace!("ServerConnectionIdManager saving cid {}", cid);
self.saved_cids.push(cid.clone());
}
cid
}
fn as_decoder(&self) -> &dyn ConnectionIdDecoder {

Просмотреть файл

@ -10,15 +10,10 @@ use std::ops::AddAssign;
use neqo_common::Role;
#[derive(PartialEq, Debug, Copy, Clone, PartialOrd, Eq, Ord, Hash)]
use crate::connection::{LOCAL_STREAM_LIMIT_BIDI, LOCAL_STREAM_LIMIT_UNI};
use crate::frame::StreamType;
/// The type of stream, either Bi-Directional or Uni-Directional.
pub enum StreamType {
BiDi,
UniDi,
}
pub(crate) struct StreamIndexes {
pub struct StreamIndexes {
pub local_max_stream_uni: StreamIndex,
pub local_max_stream_bidi: StreamIndex,
pub local_next_stream_uni: StreamIndex,
@ -30,10 +25,10 @@ pub(crate) struct StreamIndexes {
}
impl StreamIndexes {
pub fn new(local_max_stream_bidi: StreamIndex, local_max_stream_uni: StreamIndex) -> Self {
pub fn new() -> Self {
Self {
local_max_stream_bidi,
local_max_stream_uni,
local_max_stream_bidi: StreamIndex::new(LOCAL_STREAM_LIMIT_BIDI),
local_max_stream_uni: StreamIndex::new(LOCAL_STREAM_LIMIT_UNI),
local_next_stream_uni: StreamIndex::new(0),
local_next_stream_bidi: StreamIndex::new(0),
remote_max_stream_bidi: StreamIndex::new(0),
@ -131,7 +126,7 @@ impl ::std::fmt::Display for StreamId {
pub struct StreamIndex(u64);
impl StreamIndex {
pub const fn new(val: u64) -> Self {
pub fn new(val: u64) -> Self {
Self(val)
}

382
third_party/rust/neqo-transport/src/tparams.rs поставляемый
Просмотреть файл

@ -6,22 +6,20 @@
// Transport parameters. See -transport section 7.3.
use crate::cid::{
ConnectionId, ConnectionIdEntry, CONNECTION_ID_SEQNO_PREFERRED, MAX_CONNECTION_ID_LEN,
};
#![allow(dead_code)]
use crate::{Error, Res};
use neqo_common::{hex, qdebug, qinfo, qtrace, Decoder, Encoder};
use neqo_crypto::constants::{TLS_HS_CLIENT_HELLO, TLS_HS_ENCRYPTED_EXTENSIONS};
use neqo_crypto::ext::{ExtensionHandler, ExtensionHandlerResult, ExtensionWriterResult};
use neqo_crypto::{HandshakeMessage, ZeroRttCheckResult, ZeroRttChecker};
use std::cell::RefCell;
use std::collections::HashMap;
use std::convert::TryFrom;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
use std::rc::Rc;
struct PreferredAddress {
// TODO(ekr@rtfm.com): Implement.
}
pub type TransportParameterId = u64;
macro_rules! tpids {
{ $($n:ident = $v:expr),+ $(,)? } => {
@ -49,60 +47,11 @@ tpids! {
GREASE_QUIC_BIT = 0x2ab2,
}
#[derive(Clone, Debug)]
pub struct PreferredAddress {
v4: Option<SocketAddr>,
v6: Option<SocketAddr>,
}
impl PreferredAddress {
/// Make a new preferred address configuration.
///
/// # Panics
/// If neither address is provided, or if either address is of the wrong type.
#[must_use]
pub fn new(v4: Option<SocketAddr>, v6: Option<SocketAddr>) -> Self {
assert!(v4.is_some() || v6.is_some());
if let Some(a) = v4 {
if let IpAddr::V4(addr) = a.ip() {
assert!(!addr.is_unspecified());
} else {
panic!("invalid address type for v4 address");
}
assert_ne!(a.port(), 0);
}
if let Some(a) = v6 {
if let IpAddr::V6(addr) = a.ip() {
assert!(!addr.is_unspecified());
} else {
panic!("invalid address type for v6 address");
}
assert_ne!(a.port(), 0);
}
Self { v4, v6 }
}
#[must_use]
pub fn ipv4(&self) -> Option<SocketAddr> {
self.v4
}
#[must_use]
pub fn ipv6(&self) -> Option<SocketAddr> {
self.v6
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum TransportParameter {
Bytes(Vec<u8>),
Integer(u64),
Empty,
PreferredAddress {
v4: Option<SocketAddr>,
v6: Option<SocketAddr>,
cid: ConnectionId,
srt: [u8; 16],
},
}
impl TransportParameter {
@ -121,85 +70,18 @@ impl TransportParameter {
Self::Empty => {
enc.encode_varint(0_u64);
}
Self::PreferredAddress { v4, v6, cid, srt } => {
enc.encode_vvec_with(|enc_inner| {
if let Some(v4) = v4 {
debug_assert!(v4.is_ipv4());
if let IpAddr::V4(a) = v4.ip() {
enc_inner.encode(&a.octets()[..]);
} else {
unreachable!();
}
enc_inner.encode_uint(2, v4.port());
} else {
enc_inner.encode(&[0; 6]);
}
if let Some(v6) = v6 {
debug_assert!(v6.is_ipv6());
if let IpAddr::V6(a) = v6.ip() {
enc_inner.encode(&a.octets()[..]);
} else {
unreachable!();
}
enc_inner.encode_uint(2, v6.port());
} else {
enc_inner.encode(&[0; 18]);
}
enc_inner.encode_vec(1, &cid[..]);
enc_inner.encode(&srt[..]);
});
}
};
}
fn decode_preferred_address(d: &mut Decoder) -> Res<Self> {
// IPv4 address (maybe)
let v4ip =
Ipv4Addr::from(<[u8; 4]>::try_from(d.decode(4).ok_or(Error::NoMoreData)?).unwrap());
let v4port = u16::try_from(d.decode_uint(2).ok_or(Error::NoMoreData)?).unwrap();
// Can't have non-zero IP and zero port, or vice versa.
if v4ip.is_unspecified() ^ (v4port == 0) {
return Err(Error::TransportParameterError);
}
let v4 = if v4port == 0 {
None
} else {
Some(SocketAddr::new(IpAddr::V4(v4ip), v4port))
};
// IPv6 address (mostly the same as v4)
let v6ip =
Ipv6Addr::from(<[u8; 16]>::try_from(d.decode(16).ok_or(Error::NoMoreData)?).unwrap());
let v6port = u16::try_from(d.decode_uint(2).ok_or(Error::NoMoreData)?).unwrap();
if v6ip.is_unspecified() ^ (v6port == 0) {
return Err(Error::TransportParameterError);
}
let v6 = if v6port == 0 {
None
} else {
Some(SocketAddr::new(IpAddr::V6(v6ip), v6port))
};
// Need either v4 or v6 to be present.
if v4.is_none() && v6.is_none() {
return Err(Error::TransportParameterError);
}
// Connection ID (non-zero length)
let cid = ConnectionId::from(d.decode_vec(1).ok_or(Error::NoMoreData)?);
if cid.len() == 0 || cid.len() > MAX_CONNECTION_ID_LEN {
return Err(Error::TransportParameterError);
}
// Stateless reset token
let srtbuf = d.decode(16).ok_or(Error::NoMoreData)?;
let srt = <[u8; 16]>::try_from(srtbuf).unwrap();
Ok(Self::PreferredAddress { v4, v6, cid, srt })
}
fn decode(dec: &mut Decoder) -> Res<Option<(TransportParameterId, Self)>> {
let tp = dec.decode_varint().ok_or(Error::NoMoreData)?;
let content = dec.decode_vvec().ok_or(Error::NoMoreData)?;
let tp = match dec.decode_varint() {
Some(v) => v,
_ => return Err(Error::NoMoreData),
};
let content = match dec.decode_vvec() {
Some(v) => v,
_ => return Err(Error::NoMoreData),
};
qtrace!("TP {:x} length {:x}", tp, content.len());
let mut d = Decoder::from(content);
let value = match tp {
@ -242,9 +124,6 @@ impl TransportParameter {
},
DISABLE_MIGRATION | GREASE_QUIC_BIT => Self::Empty,
PREFERRED_ADDRESS => Self::decode_preferred_address(&mut d)?,
// Skip.
_ => return Ok(None),
};
@ -400,12 +279,11 @@ impl TransportParameters {
| ACK_DELAY_EXPONENT
| MAX_ACK_DELAY
| ACTIVE_CONNECTION_ID_LIMIT
| PREFERRED_ADDRESS
) {
continue;
}
if let Some(v_self) = self.params.get(k) {
match (v_self, v_rem) {
match self.params.get(k) {
Some(v_self) => match (v_self, v_rem) {
(TransportParameter::Integer(i_self), TransportParameter::Integer(i_rem)) => {
if *i_self < *i_rem {
return false;
@ -413,31 +291,13 @@ impl TransportParameters {
}
(TransportParameter::Empty, TransportParameter::Empty) => {}
_ => return false,
}
} else {
return false;
},
_ => return false,
}
}
true
}
/// Get the preferred address in a usable form.
#[must_use]
pub fn get_preferred_address(&self) -> Option<(PreferredAddress, ConnectionIdEntry<[u8; 16]>)> {
if let Some(TransportParameter::PreferredAddress { v4, v6, cid, srt }) =
self.params.get(&PREFERRED_ADDRESS)
{
Some((
PreferredAddress::new(*v4, *v6),
ConnectionIdEntry::new(CONNECTION_ID_SEQNO_PREFERRED, cid.clone(), *srt),
))
} else {
None
}
}
#[cfg(test)]
#[must_use]
fn was_sent(&self, tp: TransportParameterId) -> bool {
self.params.contains_key(&tp)
}
@ -595,214 +455,6 @@ mod tests {
let tps2 = TransportParameters::decode(&mut enc.as_decoder()).expect("Couldn't decode");
}
fn make_spa() -> TransportParameter {
TransportParameter::PreferredAddress {
v4: Some(SocketAddr::new(
IpAddr::V4(Ipv4Addr::from(0xc000_0201)),
443,
)),
v6: Some(SocketAddr::new(
IpAddr::V6(Ipv6Addr::from(0xfe80_0000_0000_0000_0000_0000_0000_0001)),
443,
)),
cid: ConnectionId::from(&[1, 2, 3, 4, 5]),
srt: [3; 16],
}
}
#[test]
fn preferred_address_encode_decode() {
const ENCODED: &[u8] = &[
0x0d, 0x2e, 0xc0, 0x00, 0x02, 0x01, 0x01, 0xbb, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0xbb, 0x05, 0x01,
0x02, 0x03, 0x04, 0x05, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
];
let spa = make_spa();
let mut enc = Encoder::new();
spa.encode(&mut enc, PREFERRED_ADDRESS);
assert_eq!(&enc[..], ENCODED);
let mut dec = enc.as_decoder();
let (id, decoded) = TransportParameter::decode(&mut dec).unwrap().unwrap();
assert_eq!(id, PREFERRED_ADDRESS);
assert_eq!(decoded, spa);
}
fn mutate_spa<F>(wrecker: F) -> TransportParameter
where
F: FnOnce(&mut Option<SocketAddr>, &mut Option<SocketAddr>, &mut ConnectionId),
{
let mut spa = make_spa();
if let TransportParameter::PreferredAddress {
ref mut v4,
ref mut v6,
ref mut cid,
..
} = &mut spa
{
wrecker(v4, v6, cid);
} else {
unreachable!();
}
spa
}
/// This takes a `TransportParameter::PreferredAddress` that has been mutilated.
/// It then encodes it, working from the knowledge that the `encode` function
/// doesn't care about validity, and decodes it. The result should be failure.
fn assert_invalid_spa(spa: TransportParameter) {
let mut enc = Encoder::new();
spa.encode(&mut enc, PREFERRED_ADDRESS);
assert_eq!(
TransportParameter::decode(&mut enc.as_decoder()).unwrap_err(),
Error::TransportParameterError
);
}
/// This is for those rare mutations that are acceptable.
fn assert_valid_spa(spa: TransportParameter) {
let mut enc = Encoder::new();
spa.encode(&mut enc, PREFERRED_ADDRESS);
let mut dec = enc.as_decoder();
let (id, decoded) = TransportParameter::decode(&mut dec).unwrap().unwrap();
assert_eq!(id, PREFERRED_ADDRESS);
assert_eq!(decoded, spa);
}
#[test]
fn preferred_address_zero_address() {
// Either port being zero is bad.
assert_invalid_spa(mutate_spa(|v4, _, _| {
v4.as_mut().unwrap().set_port(0);
}));
assert_invalid_spa(mutate_spa(|_, v6, _| {
v6.as_mut().unwrap().set_port(0);
}));
// Either IP being zero is bad.
assert_invalid_spa(mutate_spa(|v4, _, _| {
v4.as_mut().unwrap().set_ip(IpAddr::V4(Ipv4Addr::from(0)));
}));
assert_invalid_spa(mutate_spa(|_, v6, _| {
v6.as_mut().unwrap().set_ip(IpAddr::V6(Ipv6Addr::from(0)));
}));
// Either address being absent is OK.
assert_valid_spa(mutate_spa(|v4, _, _| {
*v4 = None;
}));
assert_valid_spa(mutate_spa(|_, v6, _| {
*v6 = None;
}));
// Both addresses being absent is bad.
assert_invalid_spa(mutate_spa(|v4, v6, _| {
*v4 = None;
*v6 = None;
}));
}
#[test]
fn preferred_address_bad_cid() {
assert_invalid_spa(mutate_spa(|_, _, cid| {
*cid = ConnectionId::from(&[]);
}));
assert_invalid_spa(mutate_spa(|_, _, cid| {
*cid = ConnectionId::from(&[0x0c; 21]);
}));
}
#[test]
fn preferred_address_truncated() {
let spa = make_spa();
let mut enc = Encoder::new();
spa.encode(&mut enc, PREFERRED_ADDRESS);
let mut dec = Decoder::from(&enc[..enc.len() - 1]);
assert_eq!(
TransportParameter::decode(&mut dec).unwrap_err(),
Error::NoMoreData
);
}
#[test]
#[should_panic]
fn preferred_address_wrong_family_v4() {
mutate_spa(|v4, _, _| {
v4.as_mut().unwrap().set_ip(IpAddr::V6(Ipv6Addr::from(0)));
})
.encode(&mut Encoder::new(), PREFERRED_ADDRESS);
}
#[test]
#[should_panic]
fn preferred_address_wrong_family_v6() {
mutate_spa(|_, v6, _| {
v6.as_mut().unwrap().set_ip(IpAddr::V4(Ipv4Addr::from(0)));
})
.encode(&mut Encoder::new(), PREFERRED_ADDRESS);
}
#[test]
#[should_panic]
fn preferred_address_neither() {
let _ = PreferredAddress::new(None, None);
}
#[test]
#[should_panic]
fn preferred_address_v4_unspecified() {
let _ = PreferredAddress::new(
Some(SocketAddr::new(IpAddr::V4(Ipv4Addr::from(0)), 443)),
None,
);
}
#[test]
#[should_panic]
fn preferred_address_v4_zero_port() {
let _ = PreferredAddress::new(
Some(SocketAddr::new(IpAddr::V4(Ipv4Addr::from(0xc000_0201)), 0)),
None,
);
}
#[test]
#[should_panic]
fn preferred_address_v6_unspecified() {
let _ = PreferredAddress::new(
None,
Some(SocketAddr::new(IpAddr::V6(Ipv6Addr::from(0)), 443)),
);
}
#[test]
#[should_panic]
fn preferred_address_v6_zero_port() {
let _ = PreferredAddress::new(
None,
Some(SocketAddr::new(IpAddr::V6(Ipv6Addr::from(1)), 0)),
);
}
#[test]
#[should_panic]
fn preferred_address_v4_is_v6() {
let _ = PreferredAddress::new(
Some(SocketAddr::new(IpAddr::V6(Ipv6Addr::from(1)), 443)),
None,
);
}
#[test]
#[should_panic]
fn preferred_address_v6_is_v4() {
let _ = PreferredAddress::new(
None,
Some(SocketAddr::new(
IpAddr::V4(Ipv4Addr::from(0xc000_0201)),
443,
)),
);
}
#[test]
fn compatible_0rtt_ignored_values() {
let mut tps_a = TransportParameters::default();

Просмотреть файл

@ -20,7 +20,6 @@ use neqo_crypto::{Epoch, TLS_EPOCH_HANDSHAKE, TLS_EPOCH_INITIAL};
use crate::packet::{PacketBuilder, PacketNumber, PacketType};
use crate::recovery::RecoveryToken;
use crate::stats::FrameStats;
use crate::{Error, Res};
use smallvec::{smallvec, SmallVec};
@ -413,8 +412,7 @@ impl RecvdPackets {
}
/// Add the packet to the tracked set.
/// Return true if the packet was the largest received so far.
pub fn set_received(&mut self, now: Instant, pn: PacketNumber, ack_eliciting: bool) -> bool {
pub fn set_received(&mut self, now: Instant, pn: PacketNumber, ack_eliciting: bool) {
let next_in_order_pn = self.ranges.front().map_or(0, |pr| pr.largest + 1);
qdebug!(
[self],
@ -427,12 +425,9 @@ impl RecvdPackets {
self.trim_ranges();
// The new addition was the largest, so update the time we use for calculating ACK delay.
let largest = if pn >= next_in_order_pn {
if pn >= next_in_order_pn {
self.largest_pn_time = Some(now);
true
} else {
false
};
}
if ack_eliciting {
self.pkts_since_last_ack += 1;
@ -456,7 +451,6 @@ impl RecvdPackets {
}
qdebug!([self], "Set ACK timer to {:?}", self.ack_time);
}
largest
}
/// Check if the packet is a duplicate.
@ -637,15 +631,9 @@ impl AckTracker {
now: Instant,
builder: &mut PacketBuilder,
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));
}
Ok(res)
) -> Option<RecoveryToken> {
self.get_mut(pn_space)
.and_then(|space| space.write_frame(now, builder, stats))
}
}
@ -855,14 +843,12 @@ 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
.write_frame(
PNSpace::Initial,
*NOW,
&mut builder,
&mut FrameStats::default(),
)
.unwrap();
let token = tracker.write_frame(
PNSpace::Initial,
*NOW,
&mut builder,
&mut FrameStats::default(),
);
assert!(token.is_some());
// Mark another packet as received so we have cause to send another ACK in that space.
@ -884,7 +870,6 @@ mod tests {
&mut builder,
&mut FrameStats::default()
)
.unwrap()
.is_none());
if let RecoveryToken::Ack(tok) = token.unwrap() {
tracker.acked(&tok); // Should be a noop.
@ -905,14 +890,12 @@ mod tests {
let mut builder = PacketBuilder::short(Encoder::new(), false, &[]);
builder.set_limit(10);
let token = tracker
.write_frame(
PNSpace::Initial,
*NOW,
&mut builder,
&mut FrameStats::default(),
)
.unwrap();
let token = tracker.write_frame(
PNSpace::Initial,
*NOW,
&mut builder,
&mut FrameStats::default(),
);
assert!(token.is_none());
assert_eq!(builder.len(), 1); // Only the short packet header has been added.
}
@ -933,14 +916,12 @@ mod tests {
let mut builder = PacketBuilder::short(Encoder::new(), false, &[]);
builder.set_limit(32);
let token = tracker
.write_frame(
PNSpace::Initial,
*NOW,
&mut builder,
&mut FrameStats::default(),
)
.unwrap();
let token = tracker.write_frame(
PNSpace::Initial,
*NOW,
&mut builder,
&mut FrameStats::default(),
);
assert!(token.is_some());
let mut dec = builder.as_decoder();

Просмотреть файл

@ -9,9 +9,9 @@
use neqo_common::{Datagram, Encoder};
use neqo_transport::{
Connection, ConnectionParameters, QuicVersion, RandomConnectionIdGenerator, State,
Connection, ConnectionParameters, FixedConnectionIdManager, QuicVersion, State,
};
use test_fixture::{self, addr, now};
use test_fixture::{self, loopback, now};
use std::cell::RefCell;
use std::rc::Rc;
@ -173,8 +173,8 @@ fn make_server(quic_version: QuicVersion) -> Connection {
Connection::new_server(
test_fixture::DEFAULT_KEYS,
test_fixture::DEFAULT_ALPN,
Rc::new(RefCell::new(RandomConnectionIdGenerator::new(5))),
ConnectionParameters::default().quic_version(quic_version),
Rc::new(RefCell::new(FixedConnectionIdManager::new(5))),
&ConnectionParameters::default().quic_version(quic_version),
)
.expect("create a default server")
}
@ -183,7 +183,7 @@ fn process_client_initial(quic_version: QuicVersion, packet: &str) {
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(loopback(), loopback(), pkt);
assert_eq!(*server.state(), State::Init);
let out = server.process(Some(dgram), now());
assert_eq!(*server.state(), State::Handshaking);

Просмотреть файл

@ -13,17 +13,15 @@ use neqo_crypto::{
constants::{TLS_AES_128_GCM_SHA256, TLS_VERSION_1_3},
hkdf,
hp::HpKey,
AllowZeroRtt, AuthenticationStatus, ResumptionToken, ZeroRttCheckResult, ZeroRttChecker,
AllowZeroRtt, AuthenticationStatus, ResumptionToken,
};
use neqo_transport::{
server::{ActiveConnectionRef, Server, ValidateAddress},
stream_id::StreamIndex,
Connection, ConnectionError, ConnectionEvent, ConnectionParameters, Error, Output, QuicVersion,
State, StreamType,
};
use test_fixture::{
self, addr, assertions, default_client, now, split_datagram, CountingConnectionIdGenerator,
Connection, ConnectionError, ConnectionEvent, ConnectionParameters, Error,
FixedConnectionIdManager, Output, QuicVersion, State, StreamType, LOCAL_STREAM_LIMIT_BIDI,
LOCAL_STREAM_LIMIT_UNI,
};
use test_fixture::{self, assertions, default_client, loopback, now, split_datagram};
use std::cell::RefCell;
use std::convert::TryFrom;
@ -40,7 +38,7 @@ fn default_server() -> Server {
test_fixture::DEFAULT_ALPN,
test_fixture::anti_replay(),
Box::new(AllowZeroRtt {}),
Rc::new(RefCell::new(CountingConnectionIdGenerator::default())),
Rc::new(RefCell::new(FixedConnectionIdManager::new(9))),
ConnectionParameters::default(),
)
.expect("should create a server")
@ -221,95 +219,14 @@ fn drop_non_initial() {
let mut bogus_data: Vec<u8> = header.into();
bogus_data.resize(1200, 66);
let bogus = Datagram::new(test_fixture::addr(), test_fixture::addr(), bogus_data);
let bogus = Datagram::new(
test_fixture::loopback(),
test_fixture::loopback(),
bogus_data,
);
assert!(server.process(Some(bogus), now()).dgram().is_none());
}
#[test]
fn drop_short_initial() {
const CID: &[u8] = &[55; 8]; // not a real connection ID
let mut server = default_server();
// This too small to be an Initial, but it is otherwise plausible.
let mut header = neqo_common::Encoder::with_capacity(1199);
header
.encode_byte(0xca)
.encode_uint(4, QuicVersion::default().as_u32())
.encode_vec(1, CID)
.encode_vec(1, CID);
let mut bogus_data: Vec<u8> = header.into();
bogus_data.resize(1199, 66);
let bogus = Datagram::new(test_fixture::addr(), test_fixture::addr(), bogus_data);
assert!(server.process(Some(bogus), now()).dgram().is_none());
}
/// Verify that the server can read 0-RTT properly. A more robust server would buffer
/// 0-RTT before the handshake begins and let 0-RTT arrive for a short periiod after
/// the handshake completes, but ours is for testing so it only allows 0-RTT while
/// the handshake is running.
#[test]
fn zero_rtt() {
let mut server = default_server();
let token = get_ticket(&mut server);
// Discharge the old connection so that we don't have to worry about it.
let mut now = now();
let t = server.process(None, now).callback();
now += t;
assert_eq!(server.process(None, now), Output::None);
assert_eq!(server.active_connections().len(), 1);
let start_time = now;
let mut client = default_client();
client.enable_resumption(now, &token).unwrap();
let mut client_send = || {
let client_stream = client.stream_create(StreamType::UniDi).unwrap();
client.stream_send(client_stream, &[1, 2, 3]).unwrap();
match client.process(None, now) {
Output::Datagram(d) => d,
Output::Callback(t) => {
// Pacing...
now += t;
client.process(None, now).dgram().unwrap()
}
Output::None => panic!(),
}
};
// Now generate a bunch of 0-RTT packets...
let c1 = client_send();
assertions::assert_coalesced_0rtt(&c1);
let c2 = client_send();
let c3 = client_send();
let c4 = client_send();
// 0-RTT packets that arrive before the handshake get dropped.
let _ = server.process(Some(c2), now);
assert!(server.active_connections().is_empty());
// Now handshake and let another 0-RTT packet in.
let shs = server.process(Some(c1), now).dgram();
let _ = server.process(Some(c3), now);
// The server will have received two STREAM frames now if it processed both packets.
let active = server.active_connections();
assert_eq!(active.len(), 1);
assert_eq!(active[0].borrow().stats().frame_rx.stream, 2);
// Complete the handshake. As the client was pacing 0-RTT packets, extend the time
// a little so that the pacer doesn't prevent the Finished from being sent.
now += now - start_time;
let cfin = client.process(shs, now).dgram();
let _ = server.process(cfin, now);
// The server will drop this last 0-RTT packet.
let _ = server.process(Some(c4), now);
let active = server.active_connections();
assert_eq!(active.len(), 1);
assert_eq!(active[0].borrow().stats().frame_rx.stream, 2);
}
#[test]
fn retry_basic() {
let mut server = default_server();
@ -367,7 +284,9 @@ fn get_ticket(server: &mut Server) -> ResumptionToken {
let dgram = server.process(None, now()).dgram();
client.process_input(dgram.unwrap(), now()); // Consume ticket, ignore output.
let ticket = client
// Calling active_connections clears the set of active connections.
assert_eq!(server.active_connections().len(), 1);
client
.events()
.find_map(|e| {
if let ConnectionEvent::ResumptionToken(token) = e {
@ -376,15 +295,7 @@ fn get_ticket(server: &mut Server) -> ResumptionToken {
None
}
})
.unwrap();
// Have the client close the connection and then let the server clean up.
client.close(now(), 0, "got a ticket");
let dgram = client.process_output(now()).dgram();
let _ = server.process(dgram, now());
// Calling active_connections clears the set of active connections.
assert_eq!(server.active_connections().len(), 1);
ticket
.unwrap()
}
// Attempt a retry with 0-RTT, and have 0-RTT packets sent with the second ClientHello.
@ -684,7 +595,7 @@ fn vn_after_retry() {
encoder.encode_vec(1, &client.odcid().unwrap()[..]);
encoder.encode_vec(1, &[]);
encoder.encode_uint(4, 0x5a5a_6a6a_u64);
let vn = Datagram::new(addr(), addr(), encoder);
let vn = Datagram::new(loopback(), loopback(), encoder);
assert_ne!(
client.process(Some(vn), now()).callback(),
@ -1031,7 +942,7 @@ fn closed() {
assert_eq!(res, Output::None);
}
fn can_create_streams(c: &mut Connection, t: StreamType, n: u64) {
fn can_create_streams(c: &mut Connection, t: StreamType, n: usize) {
for _ in 0..n {
c.stream_create(t).unwrap();
}
@ -1047,10 +958,10 @@ fn max_streams() {
test_fixture::DEFAULT_ALPN,
test_fixture::anti_replay(),
Box::new(AllowZeroRtt {}),
Rc::new(RefCell::new(CountingConnectionIdGenerator::default())),
Rc::new(RefCell::new(FixedConnectionIdManager::new(9))),
ConnectionParameters::default()
.max_streams(StreamType::BiDi, StreamIndex::new(MAX_STREAMS))
.max_streams(StreamType::UniDi, StreamIndex::new(MAX_STREAMS)),
.max_streams(StreamType::BiDi, MAX_STREAMS)
.max_streams(StreamType::UniDi, MAX_STREAMS),
)
.expect("should create a server");
@ -1058,8 +969,16 @@ fn max_streams() {
connect(&mut client, &mut server);
// Make sure that we can create MAX_STREAMS uni- and bidirectional streams.
can_create_streams(&mut client, StreamType::UniDi, MAX_STREAMS);
can_create_streams(&mut client, StreamType::BiDi, MAX_STREAMS);
can_create_streams(
&mut client,
StreamType::UniDi,
usize::try_from(MAX_STREAMS).unwrap(),
);
can_create_streams(
&mut client,
StreamType::BiDi,
usize::try_from(MAX_STREAMS).unwrap(),
);
}
#[test]
@ -1070,7 +989,7 @@ fn max_streams_default() {
test_fixture::DEFAULT_ALPN,
test_fixture::anti_replay(),
Box::new(AllowZeroRtt {}),
Rc::new(RefCell::new(CountingConnectionIdGenerator::default())),
Rc::new(RefCell::new(FixedConnectionIdManager::new(9))),
ConnectionParameters::default(),
)
.expect("should create a server");
@ -1078,47 +997,15 @@ fn max_streams_default() {
let mut client = default_client();
connect(&mut client, &mut server);
// Make sure that we can create streams up to the local limit.
let local_limit_unidi = ConnectionParameters::default().get_max_streams(StreamType::UniDi);
can_create_streams(&mut client, StreamType::UniDi, local_limit_unidi.as_u64());
let local_limit_bidi = ConnectionParameters::default().get_max_streams(StreamType::BiDi);
can_create_streams(&mut client, StreamType::BiDi, local_limit_bidi.as_u64());
}
#[derive(Debug)]
struct RejectZeroRtt {}
impl ZeroRttChecker for RejectZeroRtt {
fn check(&self, _token: &[u8]) -> ZeroRttCheckResult {
ZeroRttCheckResult::Reject
}
}
#[test]
fn max_streams_after_0rtt_rejection() {
const MAX_STREAMS: u64 = 40;
let mut server = Server::new(
now(),
test_fixture::DEFAULT_KEYS,
test_fixture::DEFAULT_ALPN,
test_fixture::anti_replay(),
Box::new(RejectZeroRtt {}),
Rc::new(RefCell::new(CountingConnectionIdGenerator::default())),
ConnectionParameters::default()
.max_streams(StreamType::BiDi, StreamIndex::new(MAX_STREAMS))
.max_streams(StreamType::UniDi, StreamIndex::new(MAX_STREAMS)),
)
.expect("should create a server");
let token = get_ticket(&mut server);
let mut client = default_client();
client.enable_resumption(now(), &token).unwrap();
let _ = client.stream_create(StreamType::BiDi).unwrap();
let dgram = client.process_output(now()).dgram();
let dgram = server.process(dgram, now()).dgram();
let dgram = client.process(dgram, now()).dgram();
assert!(dgram.is_some()); // We're far enough along to complete the test now.
// Make sure that we can create MAX_STREAMS uni- and bidirectional streams.
can_create_streams(&mut client, StreamType::UniDi, MAX_STREAMS);
can_create_streams(&mut client, StreamType::BiDi, MAX_STREAMS);
// Make sure that we can create LOCAL_STREAM_LIMIT_UNI unidirectional streams
can_create_streams(
&mut client,
StreamType::UniDi,
usize::try_from(LOCAL_STREAM_LIMIT_UNI).unwrap(),
);
can_create_streams(
&mut client,
StreamType::BiDi,
usize::try_from(LOCAL_STREAM_LIMIT_BIDI).unwrap(),
);
}