Bug 1712440 - Update neqo to version 0.4.25 r=necko-reviewers,kershaw

Differential Revision: https://phabricator.services.mozilla.com/D115758
This commit is contained in:
Dragana Damjanovic 2021-05-25 21:52:27 +00:00
parent 0e0c887b65
commit 4e508bd5ca
44 changed files with 1398 additions and 605 deletions

View File

@ -20,7 +20,7 @@ rev = "5cea1c9a3d8ed3ed2d7bdd5be3285e7821400b7f"
[source."https://github.com/mozilla/neqo"]
git = "https://github.com/mozilla/neqo"
replace-with = "vendored-sources"
tag = "v0.4.24"
tag = "v0.4.25"
[source."https://github.com/mozilla/mp4parse-rust"]
git = "https://github.com/mozilla/mp4parse-rust"

20
Cargo.lock generated
View File

@ -3317,8 +3317,8 @@ dependencies = [
[[package]]
name = "neqo-common"
version = "0.4.24"
source = "git+https://github.com/mozilla/neqo?tag=v0.4.24#9ac8581e2df27f2e5b03abeba923dad1a7b9f6c4"
version = "0.4.25"
source = "git+https://github.com/mozilla/neqo?tag=v0.4.25#13b5a13022419babba7183f768a974edfe2bd58b"
dependencies = [
"chrono",
"env_logger",
@ -3330,8 +3330,8 @@ dependencies = [
[[package]]
name = "neqo-crypto"
version = "0.4.24"
source = "git+https://github.com/mozilla/neqo?tag=v0.4.24#9ac8581e2df27f2e5b03abeba923dad1a7b9f6c4"
version = "0.4.25"
source = "git+https://github.com/mozilla/neqo?tag=v0.4.25#13b5a13022419babba7183f768a974edfe2bd58b"
dependencies = [
"bindgen",
"log",
@ -3343,8 +3343,8 @@ dependencies = [
[[package]]
name = "neqo-http3"
version = "0.4.24"
source = "git+https://github.com/mozilla/neqo?tag=v0.4.24#9ac8581e2df27f2e5b03abeba923dad1a7b9f6c4"
version = "0.4.25"
source = "git+https://github.com/mozilla/neqo?tag=v0.4.25#13b5a13022419babba7183f768a974edfe2bd58b"
dependencies = [
"log",
"neqo-common",
@ -3357,8 +3357,8 @@ dependencies = [
[[package]]
name = "neqo-qpack"
version = "0.4.24"
source = "git+https://github.com/mozilla/neqo?tag=v0.4.24#9ac8581e2df27f2e5b03abeba923dad1a7b9f6c4"
version = "0.4.25"
source = "git+https://github.com/mozilla/neqo?tag=v0.4.25#13b5a13022419babba7183f768a974edfe2bd58b"
dependencies = [
"lazy_static",
"log",
@ -3371,8 +3371,8 @@ dependencies = [
[[package]]
name = "neqo-transport"
version = "0.4.24"
source = "git+https://github.com/mozilla/neqo?tag=v0.4.24#9ac8581e2df27f2e5b03abeba923dad1a7b9f6c4"
version = "0.4.25"
source = "git+https://github.com/mozilla/neqo?tag=v0.4.25#13b5a13022419babba7183f768a974edfe2bd58b"
dependencies = [
"indexmap",
"lazy_static",

View File

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

View File

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

View File

@ -1 +1 @@
{"files":{"Cargo.toml":"0318981b584a1d90407c7ee25fee6ed384b78d1bfe5241051246a214b5e4cef8","build.rs":"a17b1bb1bd3de3fc958f72d4d1357f7bc4432faa26640c95b5fbfccf40579d67","src/codec.rs":"a20011436df6c4c5620b2fc9d45c10b8f4ce0922b8593c8bfb2355a41670687d","src/datagram.rs":"569f8d9e34d7ee17144bf63d34136ecd9778da0d337e513f338738c50284615e","src/event.rs":"f60fee9f4b09ef47ff5e4bfa21c07e45ffd5873c292f2605f24d834070127d62","src/hrtime.rs":"45a608ce9f00e2666ce95422a278c6dc0ff4e229b114e7bcf0b4c0d9dc61ad56","src/incrdecoder.rs":"896099bc2abf788d58b444e214db79a4b57156541b80faa0749c4be2f6ea7e7a","src/lib.rs":"659d5397175c1645eab07a999b6ccf0730d02d9614b58720955af83afef0f0e4","src/log.rs":"b69e492af85e65866cb6588138e8a337dd897d3ce399cb4e9fb8cc04ac042b7f","src/qlog.rs":"e59c4e6dcf9c70553dd6f58da41ff2053ea67b008cac186742140352f5044130","src/timer.rs":"147d82795f0f5c660d93ffb3249524461a34c58bef73c0f6bcbae365e7ae2f2d","tests/log.rs":"480b165b7907ec642c508b303d63005eee1427115d6973a349eaf6b2242ed18d"},"package":null}
{"files":{"Cargo.toml":"7f9bef3e34f1caf9fdc9a21d929cf282ee285384933f59d987e69dcc6f37f412","build.rs":"a17b1bb1bd3de3fc958f72d4d1357f7bc4432faa26640c95b5fbfccf40579d67","src/codec.rs":"a20011436df6c4c5620b2fc9d45c10b8f4ce0922b8593c8bfb2355a41670687d","src/datagram.rs":"569f8d9e34d7ee17144bf63d34136ecd9778da0d337e513f338738c50284615e","src/event.rs":"f60fee9f4b09ef47ff5e4bfa21c07e45ffd5873c292f2605f24d834070127d62","src/hrtime.rs":"45a608ce9f00e2666ce95422a278c6dc0ff4e229b114e7bcf0b4c0d9dc61ad56","src/incrdecoder.rs":"896099bc2abf788d58b444e214db79a4b57156541b80faa0749c4be2f6ea7e7a","src/lib.rs":"659d5397175c1645eab07a999b6ccf0730d02d9614b58720955af83afef0f0e4","src/log.rs":"b69e492af85e65866cb6588138e8a337dd897d3ce399cb4e9fb8cc04ac042b7f","src/qlog.rs":"e59c4e6dcf9c70553dd6f58da41ff2053ea67b008cac186742140352f5044130","src/timer.rs":"147d82795f0f5c660d93ffb3249524461a34c58bef73c0f6bcbae365e7ae2f2d","tests/log.rs":"480b165b7907ec642c508b303d63005eee1427115d6973a349eaf6b2242ed18d"},"package":null}

View File

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

View File

@ -1 +1 @@
{"files":{"Cargo.toml":"02499bc2814b9ecf3b851a6c50bae25e37d7865bb5c4e6b1c4199cec9cd90fe4","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":"6e1109511e79d0e178241ae9a40a9eb28d84c2c8359a16c0120d20d04dd0e18a","src/aead.rs":"0fffa96ff69a9aa04dec5c74636648cef1878565ccecc40acf76ac1c14286e05","src/aead_fuzzing.rs":"4e60d5a2ee6dedfd08602fa36318239e731244825df2cb801ca1d88f5f2a41c1","src/agent.rs":"5dd7dff9220fcef3e55794e946135d1c30a51172605c70fc4bf5b33664bf81bd","src/agentio.rs":"ef0a84367b18ad5696a74da8a2c560159586f8d7a696ab6353f36d7e3610084d","src/auth.rs":"e821dac1511691151a6e64b7c7130a07d941dffad4529b2631f20ddd07d3f20c","src/cert.rs":"db83d8f968993c72ae4bf6454d251488fc6c92b182680f3faf267e0eb058376a","src/constants.rs":"998e77bee88197a240032c1bfbddcff417a25ba82e576a0d2fe18ee9b63cefc7","src/err.rs":"fa3bc074a510a6d20fbc28412dfbd026e70986a6c5c82b2b591611bcf7c4392d","src/exp.rs":"61586662407359c1ecb8ed4987bc3c702f26ba2e203a091a51b6d6363cbd510f","src/ext.rs":"361277879194dc32f741b8d1894afe5fd3fcc8eb244f7dd5914eeb959b85717d","src/hkdf.rs":"22af86e54125b285f83e5de1aa12694c028f234e23bf6da3f7ea01324d92b74e","src/hp.rs":"0ae41f9683c90943e53b090592d8b700a681d82103a26de7cc9b040cd759ec75","src/lib.rs":"50e2862964e8372b9503c613c3be3e250dd323ff87215e931b787fa23e512f06","src/once.rs":"b9850384899a1a016e839743d3489c0d4d916e1973746ef8c89872105d7d9736","src/p11.rs":"32a25f45c21e13d9b2b1ff18a7d403f776adc014dd3a3bd0471a1ac083dbfd99","src/prio.rs":"23ef1913263769d95e7fff7a6cd3079b62e210617c406976b3e71ceec686c3fa","src/replay.rs":"143d8b9f8172b20cb579283fb2eb719a8ab80ef946eb0b0ab9fcc68776cbd905","src/result.rs":"cef34dfcb907723e195b56501132e4560e250b327783cb5e41201da5b63e9b5c","src/secrets.rs":"4882ba860581d2cd3f5275c17cb371d27252501cd19f2fd8d878cf9bca2c976c","src/selfencrypt.rs":"036a6a22bd0ce9ee849a986f6faad4a550de3551bd53de1f71a7d7d9b4206e5b","src/ssl.rs":"a09f47d6807db38c39150efad46b94414f413738a9c5b6224ae8a9c403f17387","src/time.rs":"8e95ddca8df8494d464619b01f583839f6ce4b9c8404f083258937d53ffa30fc","tests/aead.rs":"98a737643ca41b2f36f6eda5a5dcb2acd420650ef22ab0a8cbed16c423734cc7","tests/agent.rs":"d43e5b05dcc845394d3c7312974faae0fdcbc325c07c970aeb7ef30c3ade652e","tests/ext.rs":"eba9f03accdd598e38292ac88263a81b367d60d5a736a43117a3663de105ec48","tests/handshake.rs":"93c478fcd07d29691007abd6dcfcd2014c10c23b0206ba2d97d01594e4d64397","tests/hkdf.rs":"539235e9dcf2a56b72961a9a04f0080409adf6bf465bfad7c30026421b2d4326","tests/hp.rs":"e52a7d2f4387f2dfe8bfe1da5867e8e0d3eb51e171c6904e18b18c4343536af8","tests/init.rs":"baf680de62f5b06f38a112192a2e9a2ac9492f2cdbdf5f4b749ef18c94c9ac35","tests/selfencrypt.rs":"1125c858ec4e0a6994f34d162aa066cb003c61b324f268529ea04bcb641347cb"},"package":null}
{"files":{"Cargo.toml":"97510db649c0b541f1744a089443207db49e234c764a469036881eebaaec4bae","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":"641b9795c27041524924a14ab5b632683573fba02ddb92d09edaa6c96b175108","src/aead.rs":"0fffa96ff69a9aa04dec5c74636648cef1878565ccecc40acf76ac1c14286e05","src/aead_fuzzing.rs":"4e60d5a2ee6dedfd08602fa36318239e731244825df2cb801ca1d88f5f2a41c1","src/agent.rs":"5dd7dff9220fcef3e55794e946135d1c30a51172605c70fc4bf5b33664bf81bd","src/agentio.rs":"ef0a84367b18ad5696a74da8a2c560159586f8d7a696ab6353f36d7e3610084d","src/auth.rs":"e821dac1511691151a6e64b7c7130a07d941dffad4529b2631f20ddd07d3f20c","src/cert.rs":"db83d8f968993c72ae4bf6454d251488fc6c92b182680f3faf267e0eb058376a","src/constants.rs":"998e77bee88197a240032c1bfbddcff417a25ba82e576a0d2fe18ee9b63cefc7","src/err.rs":"fa3bc074a510a6d20fbc28412dfbd026e70986a6c5c82b2b591611bcf7c4392d","src/exp.rs":"61586662407359c1ecb8ed4987bc3c702f26ba2e203a091a51b6d6363cbd510f","src/ext.rs":"361277879194dc32f741b8d1894afe5fd3fcc8eb244f7dd5914eeb959b85717d","src/hkdf.rs":"22af86e54125b285f83e5de1aa12694c028f234e23bf6da3f7ea01324d92b74e","src/hp.rs":"0ae41f9683c90943e53b090592d8b700a681d82103a26de7cc9b040cd759ec75","src/lib.rs":"50e2862964e8372b9503c613c3be3e250dd323ff87215e931b787fa23e512f06","src/once.rs":"b9850384899a1a016e839743d3489c0d4d916e1973746ef8c89872105d7d9736","src/p11.rs":"32a25f45c21e13d9b2b1ff18a7d403f776adc014dd3a3bd0471a1ac083dbfd99","src/prio.rs":"23ef1913263769d95e7fff7a6cd3079b62e210617c406976b3e71ceec686c3fa","src/replay.rs":"143d8b9f8172b20cb579283fb2eb719a8ab80ef946eb0b0ab9fcc68776cbd905","src/result.rs":"cef34dfcb907723e195b56501132e4560e250b327783cb5e41201da5b63e9b5c","src/secrets.rs":"4882ba860581d2cd3f5275c17cb371d27252501cd19f2fd8d878cf9bca2c976c","src/selfencrypt.rs":"036a6a22bd0ce9ee849a986f6faad4a550de3551bd53de1f71a7d7d9b4206e5b","src/ssl.rs":"a09f47d6807db38c39150efad46b94414f413738a9c5b6224ae8a9c403f17387","src/time.rs":"8e95ddca8df8494d464619b01f583839f6ce4b9c8404f083258937d53ffa30fc","tests/aead.rs":"98a737643ca41b2f36f6eda5a5dcb2acd420650ef22ab0a8cbed16c423734cc7","tests/agent.rs":"d43e5b05dcc845394d3c7312974faae0fdcbc325c07c970aeb7ef30c3ade652e","tests/ext.rs":"eba9f03accdd598e38292ac88263a81b367d60d5a736a43117a3663de105ec48","tests/handshake.rs":"93c478fcd07d29691007abd6dcfcd2014c10c23b0206ba2d97d01594e4d64397","tests/hkdf.rs":"539235e9dcf2a56b72961a9a04f0080409adf6bf465bfad7c30026421b2d4326","tests/hp.rs":"e52a7d2f4387f2dfe8bfe1da5867e8e0d3eb51e171c6904e18b18c4343536af8","tests/init.rs":"baf680de62f5b06f38a112192a2e9a2ac9492f2cdbdf5f4b749ef18c94c9ac35","tests/selfencrypt.rs":"1125c858ec4e0a6994f34d162aa066cb003c61b324f268529ea04bcb641347cb"},"package":null}

View File

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

View File

@ -74,7 +74,7 @@ fn setup_clang() {
eprintln!("warning: Building without a gecko setup is not likely to work.");
eprintln!(" A working libclang is needed to build neqo.");
eprintln!(" Either LIBCLANG_PATH or MOZBUILD_STATE_PATH needs to be set.");
eprintln!("");
eprintln!();
eprintln!(" We recommend checking out https://github.com/mozilla/gecko-dev");
eprintln!(" Then run `./mach bootstrap` which will retrieve clang.");
eprintln!(" Make sure to export MOZBUILD_STATE_PATH when building.");

View File

@ -1 +1 @@
{"files":{"Cargo.toml":"040f15bffe9f3c0de617f632eeeb31aecebfec8e6a6441db93a47702d9787d2e","src/client_events.rs":"87e7c323ec2ceb759da7d6f2bf24e774f6f2e3833f63454d09cc09f47fccfa8e","src/connection.rs":"eb1e58ca22429b2f5b441f9a99e26c68c76d7d67b11ece250b95fe81e3e39f3e","src/connection_client.rs":"0db4e303c05df1b84e9aaab920e1ebbd34ecbd6f9b5a556f94a282e2d04d1c24","src/connection_server.rs":"884016ac4a0e43e0b14146b057c4d8d906f5aac94d47e05312298f76b0818e85","src/control_stream_local.rs":"2e9483d79dc00a3e5ef51ea2b0f28fda2b67348996c47729947c70a8be3007ed","src/control_stream_remote.rs":"1dfac4956a7d6971e2cef2c83963d838e73aa3bf3286b7bde97099978c41d527","src/hframe.rs":"d6d8365a333e33d41ae37d2e774f5dffb7a9676ddfb50de56bb98a50bfe49cbb","src/lib.rs":"4c26d5652fb749f07b8dad31ef2417b8daf9f71ae18e0c18b1bc22093a06290a","src/push_controller.rs":"fcf7873e8c41560b16d443e77f897e5e8b3fccb55f35f6d9997bac935e7744c3","src/push_stream.rs":"5f3a5c6d72c0a8e4d1c1c5041125f7aff9a65950fa60c77f42cd4b35c768f585","src/qlog.rs":"29c0e3c4c9571eb7fe905967edeb1c4bc236b1e35a0e0f11a4a847f1d246681d","src/recv_message.rs":"f42a73276c02c506d6046c578fc8ccef42debf1178742379c80e2fdcc2b43afb","src/send_message.rs":"7276268e5b190a1dc019d74034dad8a3c0c4b8cb409a135c733c038f9ccff6ff","src/server.rs":"99147a014be95976af54eddf3874f67a22970dc194d239685bc83a5adef3dd8c","src/server_connection_events.rs":"762ddb87f700abe91ae1ae78ebbb87a88da0c0a341748b0751b01099870e9985","src/server_events.rs":"c8cdd838129ef6b1e77ba63608d20f6d297bca4e67f26a077d1c015a5e2a3b82","src/settings.rs":"467bd6991ec30e483195855f997f0bea3671a15b1a5b82e271f62ae424e0b2c3","src/stream_type_reader.rs":"aacb2e865f79b3ac55a887fd670f2286d8ffef94f7d8b3ecfa7e0cccbfa9ec04","tests/httpconn.rs":"1a97a80f7abe11c6ba0bd9b41003be6b293049164daa21e907365d93b00a782f"},"package":null}
{"files":{"Cargo.toml":"a2ed040c308920faf9344073474708a553606b89b3f85556699c24ee68814c90","src/client_events.rs":"87e7c323ec2ceb759da7d6f2bf24e774f6f2e3833f63454d09cc09f47fccfa8e","src/connection.rs":"eb1e58ca22429b2f5b441f9a99e26c68c76d7d67b11ece250b95fe81e3e39f3e","src/connection_client.rs":"0db4e303c05df1b84e9aaab920e1ebbd34ecbd6f9b5a556f94a282e2d04d1c24","src/connection_server.rs":"884016ac4a0e43e0b14146b057c4d8d906f5aac94d47e05312298f76b0818e85","src/control_stream_local.rs":"2e9483d79dc00a3e5ef51ea2b0f28fda2b67348996c47729947c70a8be3007ed","src/control_stream_remote.rs":"1dfac4956a7d6971e2cef2c83963d838e73aa3bf3286b7bde97099978c41d527","src/hframe.rs":"d6d8365a333e33d41ae37d2e774f5dffb7a9676ddfb50de56bb98a50bfe49cbb","src/lib.rs":"4c26d5652fb749f07b8dad31ef2417b8daf9f71ae18e0c18b1bc22093a06290a","src/push_controller.rs":"fcf7873e8c41560b16d443e77f897e5e8b3fccb55f35f6d9997bac935e7744c3","src/push_stream.rs":"5f3a5c6d72c0a8e4d1c1c5041125f7aff9a65950fa60c77f42cd4b35c768f585","src/qlog.rs":"29c0e3c4c9571eb7fe905967edeb1c4bc236b1e35a0e0f11a4a847f1d246681d","src/recv_message.rs":"f42a73276c02c506d6046c578fc8ccef42debf1178742379c80e2fdcc2b43afb","src/send_message.rs":"7276268e5b190a1dc019d74034dad8a3c0c4b8cb409a135c733c038f9ccff6ff","src/server.rs":"99147a014be95976af54eddf3874f67a22970dc194d239685bc83a5adef3dd8c","src/server_connection_events.rs":"762ddb87f700abe91ae1ae78ebbb87a88da0c0a341748b0751b01099870e9985","src/server_events.rs":"c8cdd838129ef6b1e77ba63608d20f6d297bca4e67f26a077d1c015a5e2a3b82","src/settings.rs":"467bd6991ec30e483195855f997f0bea3671a15b1a5b82e271f62ae424e0b2c3","src/stream_type_reader.rs":"aacb2e865f79b3ac55a887fd670f2286d8ffef94f7d8b3ecfa7e0cccbfa9ec04","tests/httpconn.rs":"1a97a80f7abe11c6ba0bd9b41003be6b293049164daa21e907365d93b00a782f"},"package":null}

View File

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

View File

@ -1 +1 @@
{"files":{"Cargo.toml":"0e67735c9071e6d4d08e61995b2b4dd7e6e26833dc3be371786b0a8e80a2ad05","src/decoder.rs":"8e9dd33b9908e73cd131dc8c572537a2c32617d3e859dd29adbb37c89c19ae4c","src/decoder_instructions.rs":"16c5f6dbf5b80f343f3aec0a1ab17e6e8c2d23a312bc99369990712657db7360","src/encoder.rs":"9462988c5e8d8533a7341632dbec47cf634c660336798d0350a37e15b40088b2","src/encoder_instructions.rs":"56474d63efdc441e012efa23ed447a38503581442a49c1306e1b4f7acade79d7","src/header_block.rs":"4a2755ebea44cf214fe881cd2674ec244306cc89099172a3105ca7f0d36847ef","src/huffman.rs":"e275c4b6dfd8503fc710c0fcb38f1be1bafbd8577b310026aad0e284653bebdd","src/huffman_decode_helper.rs":"2970c57f052878b727c2f764490c54184f5c2608e1d6aa961c3b01509e290122","src/huffman_table.rs":"06fea766a6276ac56c7ee0326faed800a742c15fda1f33bf2513e6cc6a5e6d27","src/lib.rs":"55a0c2717d6fbb84733f6cfd31cbbc05f2c89eeff250a122a983ed8c31e86a92","src/prefix.rs":"72c587c40aef4ed38cf13b2de91091d671611679be2a9da6f0b24abafaf50dc5","src/qlog.rs":"7618085e27bb3fb1f4d1c73ba501b9a293723293c4020b7cc4129676eb278131","src/qpack_send_buf.rs":"c59309ca5a14bf6ca5e9206a5c1add78843b9d125b83b15767754c884ded58b0","src/reader.rs":"d01015cd7f1100c916bc81a9ca171b102699a93706fe9d3d9b3e4fb26a68882b","src/static_table.rs":"fda9d5c6f38f94b0bf92d3afdf8432dce6e27e189736596e16727090c77b78ec","src/stats.rs":"624dfa3b40858c304097bb0ce5b1be1bb4d7916b1abfc222f1aa705907009730","src/table.rs":"f7091bdd9ad1f8fe3b2298a7dbfd3d285c212d69569cda54f9bcf251cb758a21"},"package":null}
{"files":{"Cargo.toml":"92c4ec92146cf8663581a9c34c652ee63d4dbb6e1b35687a5fc8132ce05ef1e3","src/decoder.rs":"8e9dd33b9908e73cd131dc8c572537a2c32617d3e859dd29adbb37c89c19ae4c","src/decoder_instructions.rs":"16c5f6dbf5b80f343f3aec0a1ab17e6e8c2d23a312bc99369990712657db7360","src/encoder.rs":"9462988c5e8d8533a7341632dbec47cf634c660336798d0350a37e15b40088b2","src/encoder_instructions.rs":"56474d63efdc441e012efa23ed447a38503581442a49c1306e1b4f7acade79d7","src/header_block.rs":"4a2755ebea44cf214fe881cd2674ec244306cc89099172a3105ca7f0d36847ef","src/huffman.rs":"e275c4b6dfd8503fc710c0fcb38f1be1bafbd8577b310026aad0e284653bebdd","src/huffman_decode_helper.rs":"2970c57f052878b727c2f764490c54184f5c2608e1d6aa961c3b01509e290122","src/huffman_table.rs":"06fea766a6276ac56c7ee0326faed800a742c15fda1f33bf2513e6cc6a5e6d27","src/lib.rs":"55a0c2717d6fbb84733f6cfd31cbbc05f2c89eeff250a122a983ed8c31e86a92","src/prefix.rs":"72c587c40aef4ed38cf13b2de91091d671611679be2a9da6f0b24abafaf50dc5","src/qlog.rs":"7618085e27bb3fb1f4d1c73ba501b9a293723293c4020b7cc4129676eb278131","src/qpack_send_buf.rs":"c59309ca5a14bf6ca5e9206a5c1add78843b9d125b83b15767754c884ded58b0","src/reader.rs":"d01015cd7f1100c916bc81a9ca171b102699a93706fe9d3d9b3e4fb26a68882b","src/static_table.rs":"fda9d5c6f38f94b0bf92d3afdf8432dce6e27e189736596e16727090c77b78ec","src/stats.rs":"624dfa3b40858c304097bb0ce5b1be1bb4d7916b1abfc222f1aa705907009730","src/table.rs":"f7091bdd9ad1f8fe3b2298a7dbfd3d285c212d69569cda54f9bcf251cb758a21"},"package":null}

View File

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

File diff suppressed because one or more lines are too long

View File

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

View File

@ -0,0 +1,218 @@
// 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.
// Management of the peer's ack rate.
#![deny(clippy::pedantic)]
use crate::connection::params::ACK_RATIO_SCALE;
use crate::frame::{write_varint_frame, FRAME_TYPE_ACK_FREQUENCY};
use crate::packet::PacketBuilder;
use crate::recovery::RecoveryToken;
use crate::stats::FrameStats;
use neqo_common::qtrace;
use std::cmp::{max, min};
use std::convert::TryFrom;
use std::time::Duration;
#[derive(Debug, Clone)]
pub struct AckRate {
/// The maximum number of packets that can be received without sending an ACK.
packets: usize,
/// The maximum delay before sending an ACK.
delay: Duration,
}
impl AckRate {
pub fn new(minimum: Duration, ratio: u8, cwnd: usize, mtu: usize, rtt: Duration) -> Self {
const PACKET_RATIO: usize = ACK_RATIO_SCALE as usize;
// At worst, ask for an ACK for every other packet.
const MIN_PACKETS: usize = 2;
// At worst, require an ACK every 256 packets.
const MAX_PACKETS: usize = 256;
const RTT_RATIO: u32 = ACK_RATIO_SCALE as u32;
const MAX_DELAY: Duration = Duration::from_millis(50);
let packets = cwnd * PACKET_RATIO / mtu / usize::from(ratio);
let packets = max(MIN_PACKETS, min(packets, MAX_PACKETS)) - 1;
let delay = rtt * RTT_RATIO / u32::from(ratio);
let delay = max(minimum, min(delay, MAX_DELAY));
qtrace!("AckRate inputs: {}/{}/{}, {:?}", cwnd, mtu, ratio, rtt);
Self { packets, delay }
}
pub fn write_frame(&self, builder: &mut PacketBuilder, seqno: u64) -> bool {
write_varint_frame(
builder,
&[
FRAME_TYPE_ACK_FREQUENCY,
seqno,
u64::try_from(self.packets + 1).unwrap(),
u64::try_from(self.delay.as_micros()).unwrap(),
0,
],
)
}
/// Determine whether to send an update frame.
pub fn needs_update(&self, target: &Self) -> bool {
if self.packets != target.packets {
return true;
}
// Allow more flexibility for delays, as those can change
// by small amounts fairly easily.
let delta = target.delay / 4;
target.delay + delta < self.delay || target.delay > self.delay + delta
}
}
#[derive(Debug, Clone)]
pub struct FlexibleAckRate {
current: AckRate,
target: AckRate,
next_frame_seqno: u64,
frame_outstanding: bool,
min_ack_delay: Duration,
ratio: u8,
}
impl FlexibleAckRate {
fn new(
max_ack_delay: Duration,
min_ack_delay: Duration,
ratio: u8,
cwnd: usize,
mtu: usize,
rtt: Duration,
) -> Self {
qtrace!(
"FlexibleAckRate: {:?} {:?} {}",
max_ack_delay,
min_ack_delay,
ratio
);
let ratio = max(ACK_RATIO_SCALE, ratio); // clamp it
Self {
current: AckRate {
packets: 1,
delay: max_ack_delay,
},
target: AckRate::new(min_ack_delay, ratio, cwnd, mtu, rtt),
next_frame_seqno: 0,
frame_outstanding: false,
min_ack_delay,
ratio,
}
}
fn write_frames(
&mut self,
builder: &mut PacketBuilder,
tokens: &mut Vec<RecoveryToken>,
stats: &mut FrameStats,
) {
if !self.frame_outstanding
&& self.current.needs_update(&self.target)
&& self.target.write_frame(builder, self.next_frame_seqno)
{
qtrace!("FlexibleAckRate: write frame {:?}", self.target);
self.frame_outstanding = true;
self.next_frame_seqno += 1;
tokens.push(RecoveryToken::AckFrequency(self.target.clone()));
stats.ack_frequency += 1;
}
}
fn frame_acked(&mut self, acked: &AckRate) {
self.frame_outstanding = false;
self.current = acked.clone();
}
fn frame_lost(&mut self, _lost: &AckRate) {
self.frame_outstanding = false;
}
fn update(&mut self, cwnd: usize, mtu: usize, rtt: Duration) {
self.target = AckRate::new(self.min_ack_delay, self.ratio, cwnd, mtu, rtt);
qtrace!("FlexibleAckRate: {:?} -> {:?}", self.current, self.target);
}
fn peer_ack_delay(&self) -> Duration {
max(self.current.delay, self.target.delay)
}
}
#[derive(Debug, Clone)]
pub enum PeerAckDelay {
Fixed(Duration),
Flexible(FlexibleAckRate),
}
impl PeerAckDelay {
pub fn fixed(max_ack_delay: Duration) -> Self {
Self::Fixed(max_ack_delay)
}
pub fn flexible(
max_ack_delay: Duration,
min_ack_delay: Duration,
ratio: u8,
cwnd: usize,
mtu: usize,
rtt: Duration,
) -> Self {
Self::Flexible(FlexibleAckRate::new(
max_ack_delay,
min_ack_delay,
ratio,
cwnd,
mtu,
rtt,
))
}
pub fn write_frames(
&mut self,
builder: &mut PacketBuilder,
tokens: &mut Vec<RecoveryToken>,
stats: &mut FrameStats,
) {
if let Self::Flexible(rate) = self {
rate.write_frames(builder, tokens, stats);
}
}
pub fn frame_acked(&mut self, r: &AckRate) {
if let Self::Flexible(rate) = self {
rate.frame_acked(r);
}
}
pub fn frame_lost(&mut self, r: &AckRate) {
if let Self::Flexible(rate) = self {
rate.frame_lost(r);
}
}
pub fn max(&self) -> Duration {
match self {
Self::Flexible(rate) => rate.peer_ack_delay(),
Self::Fixed(delay) => *delay,
}
}
pub fn update(&mut self, cwnd: usize, mtu: usize, rtt: Duration) {
if let Self::Flexible(rate) = self {
rate.update(cwnd, mtu, rtt);
}
}
}
impl Default for PeerAckDelay {
fn default() -> Self {
Self::fixed(Duration::from_millis(25))
}
}

View File

@ -241,9 +241,9 @@ impl<T: WindowAdjustment> CongestionControl for ClassicCongestionControl<T> {
prev_largest_acked_sent: Option<Instant>,
pto: Duration,
lost_packets: &[SentPacket],
) {
) -> bool {
if lost_packets.is_empty() {
return;
return false;
}
for pkt in lost_packets.iter().filter(|pkt| pkt.cc_in_flight()) {
@ -257,13 +257,14 @@ impl<T: WindowAdjustment> CongestionControl for ClassicCongestionControl<T> {
qdebug!([self], "Pkts lost {}", lost_packets.len());
self.on_congestion_event(lost_packets.last().unwrap());
self.detect_persistent_congestion(
let congestion = self.on_congestion_event(lost_packets.last().unwrap());
let persistent_congestion = self.detect_persistent_congestion(
first_rtt_sample_time,
prev_largest_acked_sent,
pto,
lost_packets,
);
congestion || persistent_congestion
}
fn discard(&mut self, pkt: &SentPacket) {
@ -382,9 +383,9 @@ impl<T: WindowAdjustment> ClassicCongestionControl<T> {
prev_largest_acked_sent: Option<Instant>,
pto: Duration,
lost_packets: &[SentPacket],
) {
) -> bool {
if first_rtt_sample_time.is_none() {
return;
return false;
}
let pc_period = pto * PERSISTENT_CONG_THRESH;
@ -420,12 +421,13 @@ impl<T: WindowAdjustment> ClassicCongestionControl<T> {
&mut self.qlog,
&[QlogMetric::CongestionWindow(self.congestion_window)],
);
return;
return true;
}
} else {
start = Some(p.time_sent);
}
}
false
}
#[must_use]
@ -437,32 +439,36 @@ impl<T: WindowAdjustment> ClassicCongestionControl<T> {
}
/// Handle a congestion event.
fn on_congestion_event(&mut self, last_packet: &SentPacket) {
/// Returns true if this was a true congestion event.
fn on_congestion_event(&mut self, last_packet: &SentPacket) -> bool {
// Start a new congestion event if lost packet was sent after the start
// of the previous congestion recovery period.
if self.after_recovery_start(last_packet) {
let (cwnd, acked_bytes) = self
.cc_algorithm
.reduce_cwnd(self.congestion_window, self.acked_bytes);
self.congestion_window = max(cwnd, CWND_MIN);
self.acked_bytes = acked_bytes;
self.ssthresh = self.congestion_window;
qinfo!(
[self],
"Cong event -> recovery; cwnd {}, ssthresh {}",
self.congestion_window,
self.ssthresh
);
qlog::metrics_updated(
&mut self.qlog,
&[
QlogMetric::CongestionWindow(self.congestion_window),
QlogMetric::SsThresh(self.ssthresh),
QlogMetric::InRecovery(true),
],
);
self.set_state(State::RecoveryStart);
if !self.after_recovery_start(last_packet) {
return false;
}
let (cwnd, acked_bytes) = self
.cc_algorithm
.reduce_cwnd(self.congestion_window, self.acked_bytes);
self.congestion_window = max(cwnd, CWND_MIN);
self.acked_bytes = acked_bytes;
self.ssthresh = self.congestion_window;
qinfo!(
[self],
"Cong event -> recovery; cwnd {}, ssthresh {}",
self.congestion_window,
self.ssthresh
);
qlog::metrics_updated(
&mut self.qlog,
&[
QlogMetric::CongestionWindow(self.congestion_window),
QlogMetric::SsThresh(self.ssthresh),
QlogMetric::InRecovery(true),
],
);
self.set_state(State::RecoveryStart);
true
}
#[allow(clippy::unused_self)]

View File

@ -42,13 +42,14 @@ pub trait CongestionControl: Display + Debug {
fn on_packets_acked(&mut self, acked_pkts: &[SentPacket], min_rtt: Duration, now: Instant);
/// Returns true if the congestion window was reduced.
fn on_packets_lost(
&mut self,
first_rtt_sample_time: Option<Instant>,
prev_largest_acked_sent: Option<Instant>,
pto: Duration,
lost_packets: &[SentPacket],
);
) -> bool;
#[must_use]
fn recovery_packet(&self) -> bool;

View File

@ -45,6 +45,7 @@ use crate::packet::{
use crate::path::{Path, PathRef, Paths};
use crate::qlog;
use crate::recovery::{LossRecovery, RecoveryToken, SendProfile};
use crate::rtt::GRANULARITY;
pub use crate::send_stream::{RetransmissionPriority, TransmissionPriority};
use crate::stats::{Stats, StatsCell};
use crate::stream_id::StreamType;
@ -60,8 +61,8 @@ mod state;
use idle::IdleTimeout;
pub use idle::LOCAL_IDLE_TIMEOUT;
pub use params::ConnectionParameters;
use params::PreferredAddressConfig;
pub use params::{ConnectionParameters, ACK_RATIO_SCALE};
use saved::SavedDatagrams;
use state::StateSignaling;
pub use state::{ClosingFrame, State};
@ -1722,7 +1723,7 @@ impl Connection {
self.loss_recovery.largest_acknowledged_pn(*space),
);
// The builder will set the limit to 0 if there isn't enough space for the header.
if builder.remaining() < 2 {
if builder.is_full() {
encoder = builder.abort();
break;
}
@ -1765,36 +1766,36 @@ impl Connection {
}
self.streams
.write_frames(TransmissionPriority::Critical, builder, tokens, stats)?;
if builder.remaining() < 2 {
.write_frames(TransmissionPriority::Critical, builder, tokens, stats);
if builder.is_full() {
return Ok(());
}
self.streams
.write_frames(TransmissionPriority::Important, builder, tokens, stats)?;
if builder.remaining() < 2 {
.write_frames(TransmissionPriority::Important, builder, tokens, stats);
if builder.is_full() {
return Ok(());
}
// NEW_CONNECTION_ID and RETIRE_CONNECTION_ID.
// NEW_CONNECTION_ID, RETIRE_CONNECTION_ID, and ACK_FREQUENCY.
self.cid_manager.write_frames(builder, tokens, stats)?;
if builder.remaining() < 2 {
if builder.is_full() {
return Ok(());
}
self.paths.write_frames(builder, tokens, stats)?;
if builder.remaining() < 2 {
if builder.is_full() {
return Ok(());
}
self.streams
.write_frames(TransmissionPriority::High, builder, tokens, stats)?;
if builder.remaining() < 2 {
.write_frames(TransmissionPriority::High, builder, tokens, stats);
if builder.is_full() {
return Ok(());
}
self.streams
.write_frames(TransmissionPriority::Normal, builder, tokens, stats)?;
if builder.remaining() < 2 {
.write_frames(TransmissionPriority::Normal, builder, tokens, stats);
if builder.is_full() {
return Ok(());
}
@ -1802,16 +1803,16 @@ impl Connection {
// Both of these are only used for resumption and so can be relatively low priority.
self.crypto
.write_frame(PacketNumberSpace::ApplicationData, builder, tokens, stats)?;
if builder.remaining() < 2 {
if builder.is_full() {
return Ok(());
}
self.new_token.write_frames(builder, tokens, stats)?;
if builder.remaining() < 2 {
if builder.is_full() {
return Ok(());
}
self.streams
.write_frames(TransmissionPriority::Low, builder, tokens, stats)?;
.write_frames(TransmissionPriority::Low, builder, tokens, stats);
Ok(())
}
@ -1953,7 +1954,7 @@ impl Connection {
self.loss_recovery.largest_acknowledged_pn(*space),
);
// The builder will set the limit to 0 if there isn't enough space for the header.
if builder.remaining() < 2 {
if builder.is_full() {
encoder = builder.abort();
break;
}
@ -1963,7 +1964,7 @@ impl Connection {
builder.set_limit(profile.limit() - aead_expansion);
builder.enable_padding(needs_padding);
debug_assert!(builder.limit() <= 2048);
if builder.remaining() < 2 {
if builder.is_full() {
encoder = builder.abort();
break;
}
@ -2139,8 +2140,19 @@ impl Connection {
.borrow_mut()
.set_reset_token(reset_token);
let mad = Duration::from_millis(remote.get_integer(tparams::MAX_ACK_DELAY));
self.paths.primary().borrow_mut().set_max_ack_delay(mad);
let max_ad = Duration::from_millis(remote.get_integer(tparams::MAX_ACK_DELAY));
let min_ad = if remote.has_value(tparams::MIN_ACK_DELAY) {
Some(Duration::from_micros(
remote.get_integer(tparams::MIN_ACK_DELAY),
))
} else {
None
};
self.paths.primary().borrow_mut().set_ack_delay(
max_ad,
min_ad,
self.conn_params.get_ack_ratio(),
);
let max_active_cids = remote.get_integer(tparams::ACTIVE_CONNECTION_ID_LIMIT);
self.cid_manager.set_limit(max_active_cids);
@ -2303,6 +2315,10 @@ impl Connection {
// prepare to resend them.
self.stats.borrow_mut().frame_rx.ping += 1;
self.crypto.resend_unacked(space);
if space == PacketNumberSpace::ApplicationData {
// Send an ACK immediately if we might not otherwise do so.
self.acks.immediate_ack(now);
}
}
Frame::Ack {
largest_acknowledged,
@ -2415,6 +2431,20 @@ impl Connection {
self.discard_keys(PacketNumberSpace::Handshake, now);
self.migrate_to_preferred_address(now)?;
}
Frame::AckFrequency {
seqno,
tolerance,
delay,
ignore_order,
} => {
self.stats.borrow_mut().frame_rx.ack_frequency += 1;
let delay = Duration::from_micros(delay);
if delay < GRANULARITY {
return Err(Error::ProtocolViolation);
}
self.acks
.ack_freq(seqno, tolerance - 1, delay, ignore_order);
}
_ => unreachable!("All other frames are for streams"),
};
@ -2430,18 +2460,17 @@ impl Connection {
qdebug!([self], "Lost: {:?}", token);
if token.is_stream() {
self.streams.lost(token);
} else {
match token {
RecoveryToken::Ack(_) => {}
RecoveryToken::Crypto(ct) => self.crypto.lost(&ct),
RecoveryToken::HandshakeDone => self.state_signaling.handshake_done(),
RecoveryToken::NewToken(seqno) => self.new_token.lost(*seqno),
RecoveryToken::NewConnectionId(ncid) => self.cid_manager.lost(ncid),
RecoveryToken::RetireConnectionId(seqno) => {
self.paths.lost_retire_cid(*seqno)
}
_ => unreachable!("The rest of tokens are for streams"),
}
continue;
}
match token {
RecoveryToken::Ack(_) => {}
RecoveryToken::Crypto(ct) => self.crypto.lost(&ct),
RecoveryToken::HandshakeDone => self.state_signaling.handshake_done(),
RecoveryToken::NewToken(seqno) => self.new_token.lost(*seqno),
RecoveryToken::NewConnectionId(ncid) => self.cid_manager.lost(ncid),
RecoveryToken::RetireConnectionId(seqno) => self.paths.lost_retire_cid(*seqno),
RecoveryToken::AckFrequency(rate) => self.paths.lost_ack_frequency(rate),
_ => unreachable!("All other tokens are for streams"),
}
}
}
@ -2483,19 +2512,18 @@ impl Connection {
for token in &acked.tokens {
if token.is_stream() {
self.streams.acked(token);
} else {
match token {
RecoveryToken::Ack(at) => self.acks.acked(at),
RecoveryToken::Crypto(ct) => self.crypto.acked(ct),
RecoveryToken::NewToken(seqno) => self.new_token.acked(*seqno),
RecoveryToken::NewConnectionId(entry) => self.cid_manager.acked(entry),
RecoveryToken::RetireConnectionId(seqno) => {
self.paths.acked_retire_cid(*seqno)
}
// We only worry when these are lost
RecoveryToken::HandshakeDone => (),
_ => unreachable!("These are stream RecoveryTokens"),
}
continue;
}
match token {
RecoveryToken::Ack(at) => self.acks.acked(at),
RecoveryToken::Crypto(ct) => self.crypto.acked(ct),
RecoveryToken::NewToken(seqno) => self.new_token.acked(*seqno),
RecoveryToken::NewConnectionId(entry) => self.cid_manager.acked(entry),
RecoveryToken::RetireConnectionId(seqno) => self.paths.acked_retire_cid(*seqno),
RecoveryToken::AckFrequency(rate) => self.paths.acked_ack_frequency(rate),
// We only worry when these are lost
RecoveryToken::HandshakeDone => (),
_ => unreachable!("All other tokens are for streams"),
}
}
}

View File

@ -6,14 +6,21 @@
use crate::connection::{ConnectionIdManager, Role, LOCAL_ACTIVE_CID_LIMIT, LOCAL_IDLE_TIMEOUT};
use crate::recv_stream::RECV_BUFFER_SIZE;
use crate::rtt::GRANULARITY;
use crate::stream_id::StreamType;
use crate::tparams::{self, PreferredAddress, TransportParameter, TransportParametersHandler};
use crate::tracking::DEFAULT_ACK_DELAY;
use crate::{CongestionControlAlgorithm, QuicVersion, Res};
use std::convert::TryFrom;
const LOCAL_MAX_DATA: u64 = 0x3FFF_FFFF_FFFF_FFFF; // 2^62-1
const LOCAL_STREAM_LIMIT_BIDI: u64 = 16;
const LOCAL_STREAM_LIMIT_UNI: u64 = 16;
/// See `ConnectionParameters.ack_ratio` for a discussion of this value.
pub const ACK_RATIO_SCALE: u8 = 10;
/// By default, aim to have the peer acknowledge 4 times per round trip time.
/// See `ConnectionParameters.ack_ratio` for more.
const DEFAULT_ACK_RATIO: u8 = 4 * ACK_RATIO_SCALE;
/// What to do with preferred addresses.
#[derive(Debug, Clone)]
@ -45,6 +52,13 @@ pub struct ConnectionParameters {
max_streams_bidi: u64,
/// Initial limit on unidirectional streams that this endpoint creates.
max_streams_uni: u64,
/// The ACK ratio determines how many acknowledgements we will request as a
/// fraction of both the current congestion window (expressed in packets) and
/// as a fraction of the current round trip time. This value is scaled by
/// `ACK_RATIO_SCALE`; that is, if the goal is to have at least five
/// acknowledgments every round trip, set the value to `5 * ACK_RATIO_SCALE`.
/// Values less than `ACK_RATIO_SCALE` are clamped to `ACK_RATIO_SCALE`.
ack_ratio: u8,
preferred_address: PreferredAddressConfig,
}
@ -59,6 +73,7 @@ impl Default for ConnectionParameters {
max_stream_data_uni: u64::try_from(RECV_BUFFER_SIZE).unwrap(),
max_streams_bidi: LOCAL_STREAM_LIMIT_BIDI,
max_streams_uni: LOCAL_STREAM_LIMIT_UNI,
ack_ratio: DEFAULT_ACK_RATIO,
preferred_address: PreferredAddressConfig::Default,
}
}
@ -161,6 +176,15 @@ impl ConnectionParameters {
&self.preferred_address
}
pub fn ack_ratio(mut self, ack_ratio: u8) -> Self {
self.ack_ratio = ack_ratio;
self
}
pub fn get_ack_ratio(&self) -> u8 {
self.ack_ratio
}
pub fn create_transport_parameter(
&self,
role: Role,
@ -178,6 +202,14 @@ impl ConnectionParameters {
);
tps.local.set_empty(tparams::DISABLE_MIGRATION);
tps.local.set_empty(tparams::GREASE_QUIC_BIT);
tps.local.set_integer(
tparams::MAX_ACK_DELAY,
u64::try_from(DEFAULT_ACK_DELAY.as_millis()).unwrap(),
);
tps.local.set_integer(
tparams::MIN_ACK_DELAY,
u64::try_from(GRANULARITY.as_micros()).unwrap(),
);
// set configurable parameters
tps.local

View File

@ -0,0 +1,182 @@
// 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::{ConnectionParameters, ACK_RATIO_SCALE};
use super::{
ack_bytes, connect_rtt_idle, default_client, default_server, fill_cwnd, increase_cwnd,
induce_persistent_congestion, new_client, new_server, send_something, DEFAULT_RTT,
};
use crate::stream_id::StreamType;
use std::mem;
use std::time::Duration;
use test_fixture::{addr_v4, assertions};
/// With the default RTT here (100ms) and default ratio (4), endpoints won't send
/// `ACK_FREQUENCY` as the ACK delay isn't different enough from the default.
#[test]
fn ack_rate_default() {
let mut client = default_client();
let mut server = default_server();
let _ = connect_rtt_idle(&mut client, &mut server, DEFAULT_RTT);
assert_eq!(client.stats().frame_tx.ack_frequency, 0);
assert_eq!(server.stats().frame_tx.ack_frequency, 0);
}
/// When the congestion window increases, the rate doesn't change.
#[test]
fn ack_rate_slow_start() {
let mut client = default_client();
let mut server = default_server();
let now = connect_rtt_idle(&mut client, &mut server, DEFAULT_RTT);
// Increase the congestion window a few times.
let stream = client.stream_create(StreamType::UniDi).unwrap();
let now = increase_cwnd(&mut client, &mut server, stream, now);
let now = increase_cwnd(&mut client, &mut server, stream, now);
let _ = increase_cwnd(&mut client, &mut server, stream, now);
// The client should not have sent an ACK_FREQUENCY frame, even
// though the value would have updated.
assert_eq!(client.stats().frame_tx.ack_frequency, 0);
assert_eq!(server.stats().frame_rx.ack_frequency, 0);
}
/// When the congestion window decreases, a frame is sent.
#[test]
fn ack_rate_exit_slow_start() {
let mut client = default_client();
let mut server = default_server();
let now = connect_rtt_idle(&mut client, &mut server, DEFAULT_RTT);
// Increase the congestion window a few times, enough that after a loss,
// there are enough packets in the window to increase the packet
// count in ACK_FREQUENCY frames.
let stream = client.stream_create(StreamType::UniDi).unwrap();
let now = increase_cwnd(&mut client, &mut server, stream, now);
let now = increase_cwnd(&mut client, &mut server, stream, now);
// Now fill the congestion window and drop the first packet.
let (mut pkts, mut now) = fill_cwnd(&mut client, stream, now);
pkts.remove(0);
// After acknowledging the other packets the client will notice the loss.
now += DEFAULT_RTT / 2;
let ack = ack_bytes(&mut server, stream, pkts, now);
// Receiving the ACK will cause the client to reduce its congestion window
// and to send ACK_FREQUENCY.
now += DEFAULT_RTT / 2;
assert_eq!(client.stats().frame_tx.ack_frequency, 0);
let af = client.process(Some(ack), now).dgram();
assert!(af.is_some());
assert_eq!(client.stats().frame_tx.ack_frequency, 1);
}
/// When the congestion window collapses, `ACK_FREQUENCY` is updated.
#[test]
fn ack_rate_persistent_congestion() {
// Use a configuration that results in the value being set after exiting
// the handshake.
const RTT: Duration = Duration::from_millis(3);
let mut client = new_client(ConnectionParameters::default().ack_ratio(ACK_RATIO_SCALE));
let mut server = default_server();
let now = connect_rtt_idle(&mut client, &mut server, RTT);
// The client should have sent a frame.
assert_eq!(client.stats().frame_tx.ack_frequency, 1);
// Now crash the congestion window.
let stream = client.stream_create(StreamType::UniDi).unwrap();
let (dgrams, mut now) = fill_cwnd(&mut client, stream, now);
now += RTT / 2;
mem::drop(ack_bytes(&mut server, stream, dgrams, now));
let now = induce_persistent_congestion(&mut client, &mut server, stream, now);
// The client sends a second ACK_FREQUENCY frame with an increased rate.
let af = client.process_output(now).dgram();
assert!(af.is_some());
assert_eq!(client.stats().frame_tx.ack_frequency, 2);
}
/// Validate that the configuration works for the client.
#[test]
fn ack_rate_client_one_rtt() {
// This has to be chosen so that the resulting ACK delay is between 1ms and 50ms.
// We also have to avoid values between 20..30ms (approximately). The default
// maximum ACK delay is 25ms and an ACK_FREQUENCY frame won't be sent when the
// change to the maximum ACK delay is too small.
const RTT: Duration = Duration::from_millis(3);
let mut client = new_client(ConnectionParameters::default().ack_ratio(ACK_RATIO_SCALE));
let mut server = default_server();
let mut now = connect_rtt_idle(&mut client, &mut server, RTT);
// A single packet from the client will cause the server to engage its delayed
// acknowledgment timer, which should now be equal to RTT.
let d = send_something(&mut client, now);
now += RTT / 2;
let delay = server.process(Some(d), now).callback();
assert_eq!(delay, RTT);
assert_eq!(client.stats().frame_tx.ack_frequency, 1);
}
/// Validate that the configuration works for the server.
#[test]
fn ack_rate_server_half_rtt() {
const RTT: Duration = Duration::from_millis(10);
let mut client = default_client();
let mut server = new_server(ConnectionParameters::default().ack_ratio(ACK_RATIO_SCALE * 2));
let mut now = connect_rtt_idle(&mut client, &mut server, RTT);
let d = send_something(&mut server, now);
now += RTT / 2;
let delay = client.process(Some(d), now).callback();
assert_eq!(delay, RTT / 2);
assert_eq!(server.stats().frame_tx.ack_frequency, 1);
}
/// ACK delay calculations are path-specific,
/// so check that they can be sent on new paths.
#[test]
fn migrate_ack_delay() {
// Have the client send ACK_FREQUENCY frames at a normal-ish rate.
let mut client = new_client(ConnectionParameters::default().ack_ratio(ACK_RATIO_SCALE));
let mut server = default_server();
let mut now = connect_rtt_idle(&mut client, &mut server, DEFAULT_RTT);
client
.migrate(Some(addr_v4()), Some(addr_v4()), true, now)
.unwrap();
let client1 = send_something(&mut client, now);
assertions::assert_v4_path(&client1, true); // Contains PATH_CHALLENGE.
let client2 = send_something(&mut client, now);
assertions::assert_v4_path(&client2, false); // Doesn't. Is dropped.
now += DEFAULT_RTT / 2;
server.process_input(client1, now);
let stream = client.stream_create(StreamType::UniDi).unwrap();
let now = increase_cwnd(&mut client, &mut server, stream, now);
let now = increase_cwnd(&mut client, &mut server, stream, now);
let now = increase_cwnd(&mut client, &mut server, stream, now);
// Now lose a packet and force the client to update
let (mut pkts, mut now) = fill_cwnd(&mut client, stream, now);
pkts.remove(0);
now += DEFAULT_RTT / 2;
let ack = ack_bytes(&mut server, stream, pkts, now);
// After noticing this new loss, the client sends ACK_FREQUENCY.
// It has sent a few before (as we dropped `client2`), so ignore those.
let ad_before = client.stats().frame_tx.ack_frequency;
let af = client.process(Some(ack), now).dgram();
assert!(af.is_some());
assert_eq!(client.stats().frame_tx.ack_frequency, ad_before + 1);
}

View File

@ -4,114 +4,23 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use super::super::{Connection, Output};
use super::super::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,
ack_bytes, assert_full_cwnd, connect_rtt_idle, cwnd, cwnd_avail, cwnd_packets, default_client,
default_server, fill_cwnd, induce_persistent_congestion, send_something, DEFAULT_RTT,
FORCE_IDLE_CLIENT_1RTT_PACKETS, POST_HANDSHAKE_CWND,
};
use crate::cc::{CWND_MIN, MAX_DATAGRAM_SIZE};
use crate::cc::MAX_DATAGRAM_SIZE;
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::tracking::MAX_UNACKED_PKTS;
use crate::tracking::DEFAULT_ACK_PACKET_TOLERANCE;
use neqo_common::{qdebug, qinfo, qtrace, Datagram};
use neqo_common::{qdebug, qinfo, Datagram};
use std::convert::TryFrom;
use std::mem;
use std::time::{Duration, Instant};
// Get the current congestion window for the connection.
fn cwnd(c: &Connection) -> usize {
c.paths.primary().borrow().sender().cwnd()
}
fn cwnd_avail(c: &Connection) -> usize {
c.paths.primary().borrow().sender().cwnd_avail()
}
fn induce_persistent_congestion(
client: &mut Connection,
server: &mut Connection,
mut now: Instant,
) -> Instant {
// Note: wait some arbitrary time that should be longer than pto
// timer. This is rather brittle.
now += AT_LEAST_PTO;
let mut pto_counts = [0; MAX_PTO_COUNTS];
assert_eq!(client.stats.borrow().pto_counts, pto_counts);
qtrace!([client], "first PTO");
let (c_tx_dgrams, next_now) = fill_cwnd(client, 0, now);
now = next_now;
assert_eq!(c_tx_dgrams.len(), 2); // Two PTO packets
pto_counts[0] = 1;
assert_eq!(client.stats.borrow().pto_counts, pto_counts);
qtrace!([client], "second PTO");
now += AT_LEAST_PTO * 2;
let (c_tx_dgrams, next_now) = fill_cwnd(client, 0, now);
now = next_now;
assert_eq!(c_tx_dgrams.len(), 2); // Two PTO packets
pto_counts[0] = 0;
pto_counts[1] = 1;
assert_eq!(client.stats.borrow().pto_counts, pto_counts);
qtrace!([client], "third PTO");
now += AT_LEAST_PTO * 4;
let (c_tx_dgrams, next_now) = fill_cwnd(client, 0, now);
now = next_now;
assert_eq!(c_tx_dgrams.len(), 2); // Two PTO packets
pto_counts[1] = 0;
pto_counts[2] = 1;
assert_eq!(client.stats.borrow().pto_counts, pto_counts);
// Generate ACK
let s_tx_dgram = ack_bytes(server, 0, c_tx_dgrams, now);
// An ACK for the third PTO causes persistent congestion.
for dgram in s_tx_dgram {
client.process_input(dgram, now);
}
assert_eq!(cwnd(client), CWND_MIN);
now
}
// Receive multiple packets and generate an ack-only packet.
fn ack_bytes<D>(dest: &mut Connection, stream: u64, in_dgrams: D, now: Instant) -> Vec<Datagram>
where
D: IntoIterator<Item = Datagram>,
D::IntoIter: ExactSizeIterator,
{
let mut srv_buf = [0; 4_096];
let in_dgrams = in_dgrams.into_iter();
qdebug!([dest], "ack_bytes {} datagrams", in_dgrams.len());
for dgram in in_dgrams {
dest.process_input(dgram, now);
}
loop {
let (bytes_read, _fin) = dest.stream_recv(stream, &mut srv_buf).unwrap();
qtrace!([dest], "ack_bytes read {} bytes", bytes_read);
if bytes_read == 0 {
break;
}
}
let mut tx_dgrams = Vec::new();
while let Output::Datagram(dg) = dest.process_output(now) {
tx_dgrams.push(dg);
}
assert!((tx_dgrams.len() == 1) || (tx_dgrams.len() == 2));
tx_dgrams
}
use std::time::Duration;
#[test]
/// Verify initial CWND is honored.
@ -148,7 +57,7 @@ fn cc_slow_start_to_cong_avoidance_recovery_period() {
// Server: Receive and generate ack
now += DEFAULT_RTT / 2;
let s_tx_dgram = ack_bytes(&mut server, 0, c_tx_dgrams, now);
let s_ack = ack_bytes(&mut server, 0, c_tx_dgrams, now);
assert_eq!(
server.stats().frame_tx.largest_acknowledged,
flight1_largest
@ -156,9 +65,7 @@ fn cc_slow_start_to_cong_avoidance_recovery_period() {
// Client: Process ack
now += DEFAULT_RTT / 2;
for dgram in s_tx_dgram {
client.process_input(dgram, now);
}
client.process_input(s_ack, now);
assert_eq!(
client.stats().frame_rx.largest_acknowledged,
flight1_largest
@ -172,7 +79,7 @@ fn cc_slow_start_to_cong_avoidance_recovery_period() {
// Server: Receive and generate ack again, but drop first packet
now += DEFAULT_RTT / 2;
c_tx_dgrams.remove(0);
let s_tx_dgram = ack_bytes(&mut server, 0, c_tx_dgrams, now);
let s_ack = ack_bytes(&mut server, 0, c_tx_dgrams, now);
assert_eq!(
server.stats().frame_tx.largest_acknowledged,
flight2_largest
@ -180,9 +87,7 @@ fn cc_slow_start_to_cong_avoidance_recovery_period() {
// Client: Process ack
now += DEFAULT_RTT / 2;
for dgram in s_tx_dgram {
client.process_input(dgram, now);
}
client.process_input(s_ack, now);
assert_eq!(
client.stats().frame_rx.largest_acknowledged,
flight2_largest
@ -210,20 +115,16 @@ fn cc_cong_avoidance_recovery_period_unchanged() {
let c_tx_dgrams2 = c_tx_dgrams.split_off(5);
// Server: Receive and generate ack
let s_tx_dgram = ack_bytes(&mut server, 0, c_tx_dgrams, now);
for dgram in s_tx_dgram {
client.process_input(dgram, now);
}
let s_ack = ack_bytes(&mut server, 0, c_tx_dgrams, now);
client.process_input(s_ack, now);
let cwnd1 = cwnd(&client);
// Generate ACK for more received packets
let s_tx_dgram = ack_bytes(&mut server, 0, c_tx_dgrams2, now);
let s_ack = ack_bytes(&mut server, 0, c_tx_dgrams2, now);
// ACK more packets but they were sent before end of recovery period
for dgram in s_tx_dgram {
client.process_input(dgram, now);
}
client.process_input(s_ack, now);
// cwnd should not have changed since ACKed packets were sent before
// recovery period expired
@ -284,13 +185,11 @@ fn cc_cong_avoidance_recovery_period_to_cong_avoidance() {
// Server: Receive and generate ack
now += DEFAULT_RTT / 2;
let s_tx_dgram = ack_bytes(&mut server, 0, c_tx_dgrams, now);
let s_ack = ack_bytes(&mut server, 0, c_tx_dgrams, now);
// Client: Process ack
now += DEFAULT_RTT / 2;
for dgram in s_tx_dgram {
client.process_input(dgram, now);
}
client.process_input(s_ack, now);
// Should be in CARP now.
now += DEFAULT_RTT / 2;
@ -321,25 +220,23 @@ fn cc_cong_avoidance_recovery_period_to_cong_avoidance() {
// Until we process all the packets, the congestion window remains the same.
// Note that we need the client to process ACK frames in stages, so split the
// datagrams into two, ensuring that we allow for an ACK for each batch.
let most = c_tx_dgrams.len() - MAX_UNACKED_PKTS - 1;
let s_tx_dgram = ack_bytes(&mut server, 0, c_tx_dgrams.drain(..most), now);
for dgram in s_tx_dgram {
assert_eq!(cwnd(&client), expected_cwnd);
client.process_input(dgram, now);
// make sure to fill cwnd again.
let (mut new_pkts, next_now) = fill_cwnd(&mut client, 0, now);
now = next_now;
next_c_tx_dgrams.append(&mut new_pkts);
}
let s_tx_dgram = ack_bytes(&mut server, 0, c_tx_dgrams, now);
for dgram in s_tx_dgram {
assert_eq!(cwnd(&client), expected_cwnd);
client.process_input(dgram, now);
// make sure to fill cwnd again.
let (mut new_pkts, next_now) = fill_cwnd(&mut client, 0, now);
now = next_now;
next_c_tx_dgrams.append(&mut new_pkts);
}
let most = c_tx_dgrams.len() - usize::try_from(DEFAULT_ACK_PACKET_TOLERANCE).unwrap() - 1;
let s_ack = ack_bytes(&mut server, 0, c_tx_dgrams.drain(..most), now);
assert_eq!(cwnd(&client), expected_cwnd);
client.process_input(s_ack, now);
// make sure to fill cwnd again.
let (mut new_pkts, next_now) = fill_cwnd(&mut client, 0, now);
now = next_now;
next_c_tx_dgrams.append(&mut new_pkts);
let s_ack = ack_bytes(&mut server, 0, c_tx_dgrams, now);
assert_eq!(cwnd(&client), expected_cwnd);
client.process_input(s_ack, now);
// make sure to fill cwnd again.
let (mut new_pkts, next_now) = fill_cwnd(&mut client, 0, now);
now = next_now;
next_c_tx_dgrams.append(&mut new_pkts);
expected_cwnd += MAX_DATAGRAM_SIZE;
assert_eq!(cwnd(&client), expected_cwnd);
c_tx_dgrams = next_c_tx_dgrams;
@ -354,7 +251,7 @@ fn cc_slow_start_to_persistent_congestion_no_acks() {
let now = connect_rtt_idle(&mut client, &mut server, DEFAULT_RTT);
// Create stream 0
assert_eq!(client.stream_create(StreamType::BiDi).unwrap(), 0);
let stream = client.stream_create(StreamType::BiDi).unwrap();
// Buffer up lot of data and generate packets
let (c_tx_dgrams, mut now) = fill_cwnd(&mut client, 0, now);
@ -365,7 +262,7 @@ fn cc_slow_start_to_persistent_congestion_no_acks() {
mem::drop(ack_bytes(&mut server, 0, c_tx_dgrams, now));
// ACK lost.
induce_persistent_congestion(&mut client, &mut server, now);
induce_persistent_congestion(&mut client, &mut server, stream, now);
}
#[test]
@ -376,7 +273,7 @@ fn cc_slow_start_to_persistent_congestion_some_acks() {
let now = connect_rtt_idle(&mut client, &mut server, DEFAULT_RTT);
// Create stream 0
assert_eq!(client.stream_create(StreamType::BiDi).unwrap(), 0);
let stream = client.stream_create(StreamType::BiDi).unwrap();
// Buffer up lot of data and generate packets
let (c_tx_dgrams, mut now) = fill_cwnd(&mut client, 0, now);
@ -384,18 +281,16 @@ fn cc_slow_start_to_persistent_congestion_some_acks() {
// Server: Receive and generate ack
now += Duration::from_millis(100);
let s_tx_dgram = ack_bytes(&mut server, 0, c_tx_dgrams, now);
let s_ack = ack_bytes(&mut server, 0, c_tx_dgrams, now);
now += Duration::from_millis(100);
for dgram in s_tx_dgram {
client.process_input(dgram, now);
}
client.process_input(s_ack, now);
// send bytes that will be lost
let (_, next_now) = fill_cwnd(&mut client, 0, now);
now = next_now + Duration::from_millis(100);
induce_persistent_congestion(&mut client, &mut server, now);
induce_persistent_congestion(&mut client, &mut server, stream, now);
}
#[test]
@ -407,7 +302,7 @@ fn cc_persistent_congestion_to_slow_start() {
let now = connect_rtt_idle(&mut client, &mut server, DEFAULT_RTT);
// Create stream 0
assert_eq!(client.stream_create(StreamType::BiDi).unwrap(), 0);
let stream = client.stream_create(StreamType::BiDi).unwrap();
// Buffer up lot of data and generate packets
let (c_tx_dgrams, mut now) = fill_cwnd(&mut client, 0, now);
@ -419,7 +314,7 @@ fn cc_persistent_congestion_to_slow_start() {
// ACK lost.
now = induce_persistent_congestion(&mut client, &mut server, now);
now = induce_persistent_congestion(&mut client, &mut server, stream, now);
// New part of test starts here
@ -431,13 +326,11 @@ fn cc_persistent_congestion_to_slow_start() {
// Server: Receive and generate ack
now = next_now + Duration::from_millis(100);
let s_tx_dgram = ack_bytes(&mut server, 0, c_tx_dgrams, now);
let s_ack = ack_bytes(&mut server, 0, c_tx_dgrams, now);
// No longer in CARP. (pkts acked from after start of CARP)
// Should be in slow start now.
for dgram in s_tx_dgram {
client.process_input(dgram, now);
}
client.process_input(s_ack, now);
// ACKing 2 packets should let client send 4.
let (c_tx_dgrams, _) = fill_cwnd(&mut client, 0, now);

View File

@ -7,7 +7,7 @@
use super::super::{IdleTimeout, Output, State, LOCAL_IDLE_TIMEOUT};
use super::{
connect, connect_force_idle, connect_with_rtt, default_client, default_server,
maybe_authenticate, send_something, AT_LEAST_PTO,
maybe_authenticate, send_and_receive, send_something, AT_LEAST_PTO,
};
use crate::packet::PacketBuilder;
use crate::stats::FrameStats;
@ -128,61 +128,67 @@ fn tiny_idle_timeout() {
#[test]
fn idle_send_packet1() {
const DELTA: Duration = Duration::from_millis(10);
let mut client = default_client();
let mut server = default_server();
let mut now = now();
connect_force_idle(&mut client, &mut server);
let now = now();
let timeout = client.process(None, now).callback();
assert_eq!(timeout, LOCAL_IDLE_TIMEOUT);
let res = client.process(None, now);
assert_eq!(res, Output::Callback(LOCAL_IDLE_TIMEOUT));
now += Duration::from_secs(10);
let dgram = send_and_receive(&mut client, &mut server, now);
assert!(dgram.is_none());
let stream_id = client.stream_create(StreamType::UniDi).unwrap();
assert_eq!(client.stream_send(stream_id, b"hello").unwrap(), 5);
let out = client.process(None, now + Duration::from_secs(10));
let out = server.process(out.dgram(), now + Duration::from_secs(10));
// Still connected after 39 seconds because idle timer reset by outgoing
// packet
let _ = client.process(
out.dgram(),
now + LOCAL_IDLE_TIMEOUT + Duration::from_secs(9),
);
assert!(matches!(client.state(), State::Confirmed));
// Still connected after 39 seconds because idle timer reset by the
// outgoing packet.
now += LOCAL_IDLE_TIMEOUT - DELTA;
let dgram = client.process(None, now).dgram();
assert!(dgram.is_some()); // PTO
assert!(client.state().connected());
// Not connected after 40 seconds.
let _ = client.process(None, now + LOCAL_IDLE_TIMEOUT + Duration::from_secs(10));
assert!(matches!(client.state(), State::Closed(_)));
now += DELTA;
let out = client.process(None, now);
assert!(matches!(out, Output::None));
assert!(client.state().closed());
}
#[test]
fn idle_send_packet2() {
const GAP: Duration = Duration::from_secs(10);
const DELTA: Duration = Duration::from_millis(10);
let mut client = default_client();
let mut server = default_server();
connect_force_idle(&mut client, &mut server);
let now = now();
let mut now = now();
let res = client.process(None, now);
assert_eq!(res, Output::Callback(LOCAL_IDLE_TIMEOUT));
let timeout = client.process(None, now).callback();
assert_eq!(timeout, LOCAL_IDLE_TIMEOUT);
let stream_id = client.stream_create(StreamType::UniDi).unwrap();
assert_eq!(client.stream_send(stream_id, b"hello").unwrap(), 5);
// First transmission at t=GAP.
now += GAP;
let _ = send_something(&mut client, now);
let _out = client.process(None, now + Duration::from_secs(10));
// Second transmission at t=2*GAP.
let _ = send_something(&mut client, now + GAP);
assert!((GAP * 2 + DELTA) < LOCAL_IDLE_TIMEOUT);
assert_eq!(client.stream_send(stream_id, b"there").unwrap(), 5);
let _out = client.process(None, now + Duration::from_secs(20));
// Still connected after 39 seconds.
let _ = client.process(None, now + LOCAL_IDLE_TIMEOUT + Duration::from_secs(9));
// Still connected just before GAP + LOCAL_IDLE_TIMEOUT.
now += LOCAL_IDLE_TIMEOUT - DELTA;
let dgram = client.process(None, now).dgram();
assert!(dgram.is_some()); // PTO
assert!(matches!(client.state(), State::Confirmed));
// Not connected after 40 seconds because timer not reset by second
// outgoing packet
let _ = client.process(None, now + LOCAL_IDLE_TIMEOUT + Duration::from_secs(10));
now += DELTA;
let out = client.process(None, now);
assert!(matches!(out, Output::None));
assert!(matches!(client.state(), State::Closed(_)));
}

View File

@ -6,8 +6,8 @@
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,
connect_fail, connect_force_idle, connect_rtt_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};
@ -15,10 +15,14 @@ use crate::{ConnectionError, ConnectionParameters, EmptyConnectionIdGenerator, E
use neqo_common::Datagram;
use std::cell::RefCell;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
use std::net::{IpAddr, Ipv6Addr, SocketAddr};
use std::rc::Rc;
use std::time::{Duration, Instant};
use test_fixture::{self, addr, fixture_init, now};
use test_fixture::{
self, addr, addr_v4,
assertions::{assert_v4_path, assert_v6_path},
fixture_init, now,
};
/// This should be a valid-seeming transport parameter.
/// And it should have different values to `addr` and `addr_v4`.
@ -33,10 +37,6 @@ const SAMPLE_PREFERRED_ADDRESS: &[u8] = &[
// 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)
@ -55,25 +55,6 @@ 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);
}
}
/// As these tests use a new path, that path often has a non-zero RTT.
/// Pacing can be a problem when testing that path. This skips time forward.
fn skip_pacing(c: &mut Connection, now: Instant) -> Instant {
@ -222,6 +203,23 @@ fn migrate_immediate() {
assert_v4_path(&client3, false);
}
/// RTT estimates for paths should be preserved across migrations.
#[test]
fn migrate_rtt() {
const RTT: Duration = Duration::from_millis(20);
let mut client = default_client();
let mut server = default_server();
let now = connect_rtt_idle(&mut client, &mut server, RTT);
client
.migrate(Some(addr_v4()), Some(addr_v4()), true, now)
.unwrap();
// The RTT might be increased for the new path, so allow a little flexibility.
let rtt = client.paths.rtt();
assert!(rtt > RTT);
assert!(rtt < RTT * 2);
}
#[test]
fn migrate_immediate_fail() {
let mut client = default_client();

View File

@ -10,10 +10,11 @@ use super::{
Connection, ConnectionError, ConnectionId, ConnectionIdRef, Output, State, LOCAL_IDLE_TIMEOUT,
};
use crate::addr_valid::{AddressValidation, ValidateAddress};
use crate::cc::CWND_INITIAL_PKTS;
use crate::cc::{CWND_INITIAL_PKTS, CWND_MIN};
use crate::events::ConnectionEvent;
use crate::path::PATH_MTU_V6;
use crate::recovery::ACK_ONLY_SIZE_LIMIT;
use crate::stats::MAX_PTO_COUNTS;
use crate::{ConnectionIdDecoder, ConnectionIdGenerator, ConnectionParameters, Error, StreamType};
use std::cell::RefCell;
@ -27,6 +28,7 @@ use neqo_crypto::{random, AllowZeroRtt, AuthenticationStatus, ResumptionToken};
use test_fixture::{self, addr, fixture_init, now};
// All the tests.
mod ackrate;
mod cc;
mod close;
mod fuzzing;
@ -164,7 +166,9 @@ fn handshake(
now += rtt / 2;
mem::swap(&mut a, &mut b);
}
let _ = a.process(input, now);
if let Some(d) = input {
a.process_input(d, now);
}
now
}
@ -278,21 +282,8 @@ fn connect_force_idle(client: &mut Connection, server: &mut Connection) {
connect_rtt_idle(client, server, Duration::new(0, 0));
}
/// This fills the congestion window from a single source.
/// As the pacer will interfere with this, this moves time forward
/// as `Output::Callback` is received. Because it is hard to tell
/// from the return value whether a timeout is an ACK delay, PTO, or
/// pacing, this looks at the congestion window to tell when to stop.
/// Returns a list of datagrams and the new time.
fn fill_cwnd(c: &mut Connection, stream: u64, mut now: Instant) -> (Vec<Datagram>, Instant) {
fn fill_stream(c: &mut Connection, stream: u64) {
const BLOCK_SIZE: usize = 4_096;
// Train wreck function to get the remaining congestion window on the primary path.
fn cwnd(c: &Connection) -> usize {
c.paths.primary().borrow().sender().cwnd_avail()
}
qtrace!("fill_cwnd starting cwnd: {}", cwnd(c));
loop {
let bytes_sent = c.stream_send(stream, &[0x42; BLOCK_SIZE]).unwrap();
qtrace!("fill_cwnd wrote {} bytes", bytes_sent);
@ -300,6 +291,22 @@ fn fill_cwnd(c: &mut Connection, stream: u64, mut now: Instant) -> (Vec<Datagram
break;
}
}
}
/// This fills the congestion window from a single source.
/// As the pacer will interfere with this, this moves time forward
/// as `Output::Callback` is received. Because it is hard to tell
/// from the return value whether a timeout is an ACK delay, PTO, or
/// pacing, this looks at the congestion window to tell when to stop.
/// Returns a list of datagrams and the new time.
fn fill_cwnd(c: &mut Connection, stream: u64, mut now: Instant) -> (Vec<Datagram>, Instant) {
// Train wreck function to get the remaining congestion window on the primary path.
fn cwnd(c: &Connection) -> usize {
c.paths.primary().borrow().sender().cwnd_avail()
}
qtrace!("fill_cwnd starting cwnd: {}", cwnd(c));
fill_stream(c, stream);
let mut total_dgrams = Vec::new();
loop {
@ -326,6 +333,126 @@ fn fill_cwnd(c: &mut Connection, stream: u64, mut now: Instant) -> (Vec<Datagram
(total_dgrams, now)
}
/// This function is like the combination of `fill_cwnd` and `ack_bytes`.
/// However, it acknowledges everything inline and preserves an RTT of `DEFAULT_RTT`.
fn increase_cwnd(
sender: &mut Connection,
receiver: &mut Connection,
stream: u64,
mut now: Instant,
) -> Instant {
fill_stream(sender, stream);
loop {
let pkt = sender.process_output(now);
match pkt {
Output::Datagram(dgram) => {
receiver.process_input(dgram, now + DEFAULT_RTT / 2);
}
Output::Callback(t) => {
if t < DEFAULT_RTT {
now += t;
} else {
break; // We're on PTO now.
}
}
Output::None => panic!(),
}
}
// Now acknowledge all those packets at once.
now += DEFAULT_RTT / 2;
let ack = receiver.process_output(now).dgram();
now += DEFAULT_RTT / 2;
sender.process_input(ack.unwrap(), now);
now
}
/// Receive multiple packets and generate an ack-only packet.
/// # Panics
/// The caller is responsible for ensuring that `dest` has received
/// enough data that it wants to generate an ACK. This panics if
/// no ACK frame is generated.
fn ack_bytes<D>(dest: &mut Connection, stream: u64, in_dgrams: D, now: Instant) -> Datagram
where
D: IntoIterator<Item = Datagram>,
D::IntoIter: ExactSizeIterator,
{
let mut srv_buf = [0; 4_096];
let in_dgrams = in_dgrams.into_iter();
qdebug!([dest], "ack_bytes {} datagrams", in_dgrams.len());
for dgram in in_dgrams {
dest.process_input(dgram, now);
}
loop {
let (bytes_read, _fin) = dest.stream_recv(stream, &mut srv_buf).unwrap();
qtrace!([dest], "ack_bytes read {} bytes", bytes_read);
if bytes_read == 0 {
break;
}
}
dest.process_output(now).dgram().unwrap()
}
// Get the current congestion window for the connection.
fn cwnd(c: &Connection) -> usize {
c.paths.primary().borrow().sender().cwnd()
}
fn cwnd_avail(c: &Connection) -> usize {
c.paths.primary().borrow().sender().cwnd_avail()
}
fn induce_persistent_congestion(
client: &mut Connection,
server: &mut Connection,
stream: u64,
mut now: Instant,
) -> Instant {
// Note: wait some arbitrary time that should be longer than pto
// timer. This is rather brittle.
qtrace!([client], "induce_persistent_congestion");
now += AT_LEAST_PTO;
let mut pto_counts = [0; MAX_PTO_COUNTS];
assert_eq!(client.stats.borrow().pto_counts, pto_counts);
qtrace!([client], "first PTO");
let (c_tx_dgrams, next_now) = fill_cwnd(client, stream, now);
now = next_now;
assert_eq!(c_tx_dgrams.len(), 2); // Two PTO packets
pto_counts[0] = 1;
assert_eq!(client.stats.borrow().pto_counts, pto_counts);
qtrace!([client], "second PTO");
now += AT_LEAST_PTO * 2;
let (c_tx_dgrams, next_now) = fill_cwnd(client, stream, now);
now = next_now;
assert_eq!(c_tx_dgrams.len(), 2); // Two PTO packets
pto_counts[0] = 0;
pto_counts[1] = 1;
assert_eq!(client.stats.borrow().pto_counts, pto_counts);
qtrace!([client], "third PTO");
now += AT_LEAST_PTO * 4;
let (c_tx_dgrams, next_now) = fill_cwnd(client, stream, now);
now = next_now;
assert_eq!(c_tx_dgrams.len(), 2); // Two PTO packets
pto_counts[1] = 0;
pto_counts[2] = 1;
assert_eq!(client.stats.borrow().pto_counts, pto_counts);
// An ACK for the third PTO causes persistent congestion.
let s_ack = ack_bytes(server, stream, c_tx_dgrams, now);
client.process_input(s_ack, now);
assert_eq!(cwnd(client), CWND_MIN);
now
}
/// This magic number is the size of the client's CWND after the handshake completes.
/// This is the same as the initial congestion window, because during the handshake
/// the cc is app limited and cwnd is not increased.

View File

@ -12,9 +12,10 @@ use super::{
};
use crate::path::PATH_MTU_V6;
use crate::recovery::{MAX_OUTSTANDING_UNACK, MIN_OUTSTANDING_UNACK, PTO_PACKET_COUNT};
use crate::rtt::GRANULARITY;
use crate::stats::MAX_PTO_COUNTS;
use crate::tparams::TransportParameter;
use crate::tracking::ACK_DELAY;
use crate::tracking::DEFAULT_ACK_DELAY;
use crate::StreamType;
use neqo_common::qdebug;
@ -105,7 +106,10 @@ fn pto_works_ping() {
// Nothing to do, should return callback
let cb = client.process(None, now).callback();
assert_eq!(cb, Duration::from_millis(26)); // MAX_ACK_DELAY + GRANULARITY
// The PTO timer is calculated with:
// RTT + max(rttvar * 4, GRANULARITY) + max_ack_delay
// With zero RTT and rttvar, max_ack_delay is minimum too (GRANULARITY)
assert_eq!(cb, GRANULARITY * 2);
// Process these by server, skipping pkt0
let srv0 = server.process(Some(pkt1), now).dgram();
@ -198,6 +202,8 @@ fn pto_initial() {
/// A complete handshake that involves a PTO in the Handshake space.
#[test]
fn pto_handshake_complete() {
const HALF_RTT: Duration = Duration::from_millis(10);
let mut now = now();
// start handshake
let mut client = default_client();
@ -207,22 +213,22 @@ fn pto_handshake_complete() {
let cb = client.process(None, now).callback();
assert_eq!(cb, Duration::from_millis(300));
now += Duration::from_millis(10);
now += HALF_RTT;
let pkt = server.process(pkt, now).dgram();
now += Duration::from_millis(10);
now += HALF_RTT;
let pkt = client.process(pkt, now).dgram();
let cb = client.process(None, now).callback();
// The client now has a single RTT estimate (20ms), so
// the handshake PTO is set based on that.
assert_eq!(cb, Duration::from_millis(60));
assert_eq!(cb, HALF_RTT * 6);
now += Duration::from_millis(10);
now += HALF_RTT;
let pkt = server.process(pkt, now).dgram();
assert!(pkt.is_none());
now += Duration::from_millis(10);
now += HALF_RTT;
client.authenticated(AuthenticationStatus::Ok, now);
qdebug!("---- client: SH..FIN -> FIN");
@ -231,7 +237,7 @@ fn pto_handshake_complete() {
assert_eq!(*client.state(), State::Connected);
let cb = client.process(None, now).callback();
assert_eq!(cb, Duration::from_millis(60));
assert_eq!(cb, HALF_RTT * 6);
let mut pto_counts = [0; MAX_PTO_COUNTS];
assert_eq!(client.stats.borrow().pto_counts, pto_counts);
@ -239,7 +245,7 @@ fn pto_handshake_complete() {
// Wait for PTO to expire and resend a handshake packet.
// Wait long enough that the 1-RTT PTO also fires.
qdebug!("---- client: PTO");
now += Duration::from_millis(60);
now += HALF_RTT * 6;
let pkt2 = client.process(None, now).dgram();
pto_counts[0] = 1;
@ -255,13 +261,13 @@ fn pto_handshake_complete() {
// PTO has been doubled.
let cb = client.process(None, now).callback();
assert_eq!(cb, Duration::from_millis(120));
assert_eq!(cb, HALF_RTT * 12);
// 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 += HALF_RTT;
// 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.
@ -288,12 +294,14 @@ fn pto_handshake_complete() {
assert_eq!(1, server.stats().dropped_rx - dropped_before2);
assert_eq!(server.stats().frame_rx.all, server_frames);
now += Duration::from_millis(10);
now += HALF_RTT;
// Let the client receive the ACK.
// It should now be wait to acknowledge the HANDSHAKE_DONE.
let cb = client.process(ack, now).callback();
assert_eq!(cb, ACK_DELAY);
// The default ack delay is the RTT divided by the default ACK ratio of 4.
let expected_ack_delay = HALF_RTT * 2 / 4;
assert_eq!(cb, expected_ack_delay);
// Let the ACK delay timer expire.
now += cb;
@ -304,7 +312,7 @@ fn pto_handshake_complete() {
// We don't send another PING because the handshake space is done and there
// is nothing to probe for.
assert_eq!(cb, LOCAL_IDLE_TIMEOUT - ACK_DELAY);
assert_eq!(cb, LOCAL_IDLE_TIMEOUT - expected_ack_delay);
}
/// Test that PTO in the Handshake space contains the right frames.
@ -667,7 +675,7 @@ fn ping_with_ack(fast: bool) {
trickle(&mut sender, &mut receiver, 1, now);
assert_eq!(receiver.stats().frame_tx.ping, 1);
if let Output::Callback(t) = sender.process_output(now) {
assert_eq!(t, ACK_DELAY);
assert_eq!(t, DEFAULT_ACK_DELAY);
assert!(sender.process_output(now + t).dgram().is_some());
}
assert_eq!(sender.stats().frame_tx.ack, sender_acks_before + 1);

View File

@ -40,10 +40,22 @@ fn remember_smoothed_rtt() {
let mut client = default_client();
let mut server = default_server();
let now = connect_with_rtt(&mut client, &mut server, now(), RTT1);
let mut now = connect_with_rtt(&mut client, &mut server, now(), RTT1);
assert_eq!(client.paths.rtt(), RTT1);
let token = exchange_ticket(&mut client, &mut server, now);
// We can't use exchange_ticket here because it doesn't respect RTT.
// Also, connect_with_rtt() ends with the server receiving a packet it
// wants to acknowledge; so the ticket will include an ACK frame too.
let validation = AddressValidation::new(now, ValidateAddress::NoToken).unwrap();
let validation = Rc::new(RefCell::new(validation));
server.set_validation(Rc::clone(&validation));
server.send_ticket(now, &[]).expect("can send ticket");
let ticket = server.process_output(now).dgram();
assert!(ticket.is_some());
now += RTT1 / 2;
client.process_input(ticket.unwrap(), now);
let token = get_tokens(&mut client).pop().unwrap();
let mut client = default_client();
let mut server = default_server();
client.enable_resumption(now, token).unwrap();

View File

@ -13,7 +13,7 @@ use crate::events::ConnectionEvent;
use crate::recv_stream::RECV_BUFFER_SIZE;
use crate::send_stream::{SendStreamState, SEND_BUFFER_SIZE};
use crate::tparams::{self, TransportParameter};
use crate::tracking::MAX_UNACKED_PKTS;
use crate::tracking::DEFAULT_ACK_PACKET_TOLERANCE;
use crate::ConnectionParameters;
use crate::{Error, StreamId, StreamType};
@ -83,7 +83,7 @@ fn transfer() {
let out = server.process(Some(d), now());
assert_eq!(
out.as_dgram_ref().is_some(),
(d_num + 1) % (MAX_UNACKED_PKTS + 1) == 0
(d_num + 1) % usize::try_from(DEFAULT_ACK_PACKET_TOLERANCE + 1).unwrap() == 0
);
qdebug!("Output={:0x?}", out.as_dgram_ref());
}

View File

@ -132,15 +132,14 @@ impl SenderFlowControl<()> {
builder: &mut PacketBuilder,
tokens: &mut Vec<RecoveryToken>,
stats: &mut FrameStats,
) -> Res<()> {
) {
if let Some(limit) = self.blocked_needed() {
if write_varint_frame(builder, &[FRAME_TYPE_DATA_BLOCKED, limit])? {
if write_varint_frame(builder, &[FRAME_TYPE_DATA_BLOCKED, limit]) {
stats.data_blocked += 1;
tokens.push(RecoveryToken::DataBlocked(limit));
self.blocked_sent();
}
}
Ok(())
}
}
@ -150,12 +149,12 @@ impl SenderFlowControl<StreamId> {
builder: &mut PacketBuilder,
tokens: &mut Vec<RecoveryToken>,
stats: &mut FrameStats,
) -> Res<()> {
) {
if let Some(limit) = self.blocked_needed() {
if write_varint_frame(
builder,
&[FRAME_TYPE_STREAM_DATA_BLOCKED, self.subject.as_u64(), limit],
)? {
) {
stats.stream_data_blocked += 1;
tokens.push(RecoveryToken::StreamDataBlocked {
stream_id: self.subject,
@ -164,7 +163,6 @@ impl SenderFlowControl<StreamId> {
self.blocked_sent();
}
}
Ok(())
}
}
@ -174,14 +172,14 @@ impl SenderFlowControl<StreamType> {
builder: &mut PacketBuilder,
tokens: &mut Vec<RecoveryToken>,
stats: &mut FrameStats,
) -> Res<()> {
) {
if let Some(limit) = self.blocked_needed() {
let frame = if self.subject == StreamType::BiDi {
FRAME_TYPE_STREAMS_BLOCKED_BIDI
} else {
FRAME_TYPE_STREAMS_BLOCKED_UNIDI
};
if write_varint_frame(builder, &[frame, limit])? {
if write_varint_frame(builder, &[frame, limit]) {
stats.streams_blocked += 1;
tokens.push(RecoveryToken::StreamsBlocked {
stream_type: self.subject,
@ -190,7 +188,6 @@ impl SenderFlowControl<StreamType> {
self.blocked_sent();
}
}
Ok(())
}
}
@ -287,15 +284,14 @@ impl ReceiverFlowControl<()> {
builder: &mut PacketBuilder,
tokens: &mut Vec<RecoveryToken>,
stats: &mut FrameStats,
) -> Res<()> {
) {
if let Some(max_allowed) = self.frame_needed() {
if write_varint_frame(builder, &[FRAME_TYPE_MAX_DATA, max_allowed])? {
if write_varint_frame(builder, &[FRAME_TYPE_MAX_DATA, max_allowed]) {
stats.max_data += 1;
tokens.push(RecoveryToken::MaxData(max_allowed));
self.frame_sent(max_allowed);
}
}
Ok(())
}
}
@ -305,7 +301,7 @@ impl ReceiverFlowControl<StreamId> {
builder: &mut PacketBuilder,
tokens: &mut Vec<RecoveryToken>,
stats: &mut FrameStats,
) -> Res<()> {
) {
if let Some(max_allowed) = self.frame_needed() {
if write_varint_frame(
builder,
@ -314,7 +310,7 @@ impl ReceiverFlowControl<StreamId> {
self.subject.as_u64(),
max_allowed,
],
)? {
) {
stats.max_stream_data += 1;
tokens.push(RecoveryToken::MaxStreamData {
stream_id: self.subject,
@ -323,7 +319,6 @@ impl ReceiverFlowControl<StreamId> {
self.frame_sent(max_allowed);
}
}
Ok(())
}
}
@ -333,14 +328,14 @@ impl ReceiverFlowControl<StreamType> {
builder: &mut PacketBuilder,
tokens: &mut Vec<RecoveryToken>,
stats: &mut FrameStats,
) -> Res<()> {
) {
if let Some(max_streams) = self.frame_needed() {
let frame = if self.subject == StreamType::BiDi {
FRAME_TYPE_MAX_STREAMS_BIDI
} else {
FRAME_TYPE_MAX_STREAMS_UNIDI
};
if write_varint_frame(builder, &[frame, max_streams])? {
if write_varint_frame(builder, &[frame, max_streams]) {
stats.max_streams += 1;
tokens.push(RecoveryToken::MaxStreams {
stream_type: self.subject,
@ -349,7 +344,6 @@ impl ReceiverFlowControl<StreamType> {
self.frame_sent(max_streams);
}
}
Ok(())
}
/// Retire given amount of additional data.
@ -714,9 +708,7 @@ mod test {
// consume the frame
let mut builder = PacketBuilder::short(Encoder::new(), false, &[]);
let mut tokens = Vec::new();
fc[StreamType::BiDi]
.write_frames(&mut builder, &mut tokens, &mut FrameStats::default())
.unwrap();
fc[StreamType::BiDi].write_frames(&mut builder, &mut tokens, &mut FrameStats::default());
assert_eq!(tokens.len(), 1);
// Now 9 can be a new StreamId.
@ -736,9 +728,7 @@ mod test {
fc[StreamType::UniDi].add_retired(1);
fc[StreamType::UniDi].send_flowc_update();
// consume the frame
fc[StreamType::UniDi]
.write_frames(&mut builder, &mut tokens, &mut FrameStats::default())
.unwrap();
fc[StreamType::UniDi].write_frames(&mut builder, &mut tokens, &mut FrameStats::default());
assert_eq!(tokens.len(), 2);
// Now 7 can be a new StreamId.

View File

@ -44,6 +44,8 @@ pub 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;
// draft-ietf-quic-ack-delay
pub const FRAME_TYPE_ACK_FREQUENCY: FrameType = 0xaf;
const STREAM_FRAME_BIT_FIN: u64 = 0x01;
const STREAM_FRAME_BIT_LEN: u64 = 0x02;
@ -96,7 +98,7 @@ pub struct AckRange {
/// A lot of frames here are just a collection of varints.
/// This helper functions writes a frame like that safely, returning `true` if
/// a frame was written.
pub fn write_varint_frame(builder: &mut PacketBuilder, values: &[u64]) -> Res<bool> {
pub fn write_varint_frame(builder: &mut PacketBuilder, values: &[u64]) -> bool {
let write = builder.remaining()
>= values
.iter()
@ -106,11 +108,9 @@ pub fn write_varint_frame(builder: &mut PacketBuilder, values: &[u64]) -> Res<bo
for v in values {
builder.encode_varint(*v);
}
if builder.len() > builder.limit() {
return Err(Error::InternalError(16));
}
debug_assert!(builder.len() <= builder.limit());
};
Ok(write)
write
}
#[derive(PartialEq, Debug, Clone)]
@ -191,6 +191,18 @@ pub enum Frame<'a> {
reason_phrase: Vec<u8>,
},
HandshakeDone,
AckFrequency {
/// The current ACK frequency sequence number.
seqno: u64,
/// The number of contiguous packets that can be received without
/// acknowledging immediately.
tolerance: u64,
/// The time to delay after receiving the first packet that is
/// not immediately acknowledged.
delay: u64,
/// Ignore reordering when deciding to immediately acknowledge.
ignore_order: bool,
},
}
impl<'a> Frame<'a> {
@ -239,6 +251,7 @@ impl<'a> Frame<'a> {
FRAME_TYPE_CONNECTION_CLOSE_TRANSPORT + error_code.frame_type_bit()
}
Self::HandshakeDone => FRAME_TYPE_HANDSHAKE_DONE,
Self::AckFrequency { .. } => FRAME_TYPE_ACK_FREQUENCY,
}
}
@ -547,6 +560,25 @@ impl<'a> Frame<'a> {
})
}
FRAME_TYPE_HANDSHAKE_DONE => Ok(Self::HandshakeDone),
FRAME_TYPE_ACK_FREQUENCY => {
let seqno = dv(dec)?;
let tolerance = dv(dec)?;
if tolerance == 0 {
return Err(Error::FrameEncodingError);
}
let delay = dv(dec)?;
let ignore_order = match d(dec.decode_uint(1))? {
0 => false,
1 => true,
_ => return Err(Error::FrameEncodingError),
};
Ok(Self::AckFrequency {
seqno,
tolerance,
delay,
ignore_order,
})
}
_ => Err(Error::UnknownFrameType),
}
}
@ -854,4 +886,34 @@ mod tests {
assert!(res.is_ok());
assert_eq!(res.unwrap(), vec![5..=7, 0..=3]);
}
#[test]
fn ack_frequency() {
let f = Frame::AckFrequency {
seqno: 10,
tolerance: 5,
delay: 2000,
ignore_order: true,
};
just_dec(&f, "40af0a0547d001");
}
#[test]
fn ack_frequency_ignore_error_error() {
let enc = Encoder::from_hex("40af0a0547d003"); // ignore_order of 3
assert_eq!(
Frame::decode(&mut enc.as_decoder()).unwrap_err(),
Error::FrameEncodingError
);
}
/// Hopefully this test is eventually redundant.
#[test]
fn ack_frequency_zero_packets() {
let enc = Encoder::from_hex("40af0a000101"); // packets of 0
assert_eq!(
Frame::decode(&mut enc.as_decoder()).unwrap_err(),
Error::FrameEncodingError
);
}
}

View File

@ -9,6 +9,7 @@
use neqo_common::qinfo;
mod ackrate;
mod addr_valid;
mod cc;
mod cid;
@ -39,7 +40,9 @@ pub use self::cid::{
ConnectionId, ConnectionIdDecoder, ConnectionIdGenerator, ConnectionIdRef,
EmptyConnectionIdGenerator, RandomConnectionIdGenerator,
};
pub use self::connection::{params::ConnectionParameters, Connection, Output, State, ZeroRttState};
pub use self::connection::{
params::ConnectionParameters, params::ACK_RATIO_SCALE, Connection, Output, State, ZeroRttState,
};
pub use self::events::{ConnectionEvent, ConnectionEvents};
pub use self::frame::CloseError;
pub use self::packet::QuicVersion;

View File

@ -276,6 +276,13 @@ impl PacketBuilder {
self.limit.saturating_sub(self.encoder.len())
}
/// Returns true if the packet has no more space for frames.
#[must_use]
pub fn is_full(&self) -> bool {
// No useful frame is smaller than 2 bytes long.
self.limit < self.encoder.len() + 2
}
/// Mark the packet as needing padding (or not).
pub fn enable_padding(&mut self, needs_padding: bool) {
self.padding = needs_padding;

View File

@ -15,6 +15,7 @@ use std::net::{IpAddr, SocketAddr};
use std::rc::Rc;
use std::time::{Duration, Instant};
use crate::ackrate::{AckRate, PeerAckDelay};
use crate::cc::CongestionControlAlgorithm;
use crate::cid::{ConnectionId, ConnectionIdRef, RemoteConnectionIdEntry};
use crate::frame::{
@ -93,7 +94,7 @@ impl Paths {
.unwrap_or_else(|| {
let mut p = Path::temporary(local, remote, cc, self.qlog.clone(), now);
if let Some(primary) = self.primary.as_ref() {
p.set_initial_rtt(primary.borrow().rtt().estimate());
p.prime_rtt(primary.borrow().rtt())
}
Rc::new(RefCell::new(p))
})
@ -370,6 +371,12 @@ impl Paths {
tokens.push(RecoveryToken::RetireConnectionId(seqno));
stats.retire_connection_id += 1;
}
// Write out any ACK_FREQUENCY frames.
self.primary()
.borrow_mut()
.write_cc_frames(builder, tokens, stats);
Ok(())
}
@ -381,6 +388,14 @@ impl Paths {
self.to_retire.retain(|&seqno| seqno != acked);
}
pub fn lost_ack_frequency(&mut self, lost: &AckRate) {
self.primary().borrow_mut().lost_ack_frequency(lost);
}
pub fn acked_ack_frequency(&mut self, acked: &AckRate) {
self.primary().borrow_mut().acked_ack_frequency(acked);
}
/// Get an estimate of the RTT on the primary path.
#[cfg(test)]
pub fn rtt(&self) -> Duration {
@ -743,6 +758,24 @@ impl Path {
}
}
/// Write `ACK_FREQUENCY` frames.
pub fn write_cc_frames(
&mut self,
builder: &mut PacketBuilder,
tokens: &mut Vec<RecoveryToken>,
stats: &mut FrameStats,
) {
self.rtt.write_frames(builder, tokens, stats);
}
pub fn lost_ack_frequency(&mut self, lost: &AckRate) {
self.rtt.frame_lost(lost);
}
pub fn acked_ack_frequency(&mut self, acked: &AckRate) {
self.rtt.frame_acked(acked);
}
/// 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 {
@ -796,14 +829,33 @@ impl Path {
&self.sender
}
/// Pass on RTT configuration: the maximum acknowledgment delay of the peer.
pub fn set_max_ack_delay(&mut self, mad: Duration) {
self.rtt.set_max_ack_delay(mad);
/// Pass on RTT configuration: the maximum acknowledgment delay of the peer,
/// and maybe the minimum delay.
pub fn set_ack_delay(
&mut self,
max_ack_delay: Duration,
min_ack_delay: Option<Duration>,
ack_ratio: u8,
) {
let ack_delay = min_ack_delay.map_or_else(
|| PeerAckDelay::fixed(max_ack_delay),
|m| {
PeerAckDelay::flexible(
max_ack_delay,
m,
ack_ratio,
self.sender.cwnd(),
self.mtu(),
self.rtt.estimate(),
)
},
);
self.rtt.set_ack_delay(ack_delay);
}
/// Initialize the RTT for the path based on an existing estimate.
pub fn set_initial_rtt(&mut self, rtt: Duration) {
self.rtt.set_initial(rtt);
pub fn prime_rtt(&mut self, rtt: &RttEstimate) {
self.rtt.prime_rtt(rtt);
}
/// Record received bytes for the path.
@ -844,12 +896,15 @@ impl Path {
lost_packets: &[SentPacket],
) {
debug_assert!(self.is_primary());
self.sender.on_packets_lost(
let cwnd_reduced = self.sender.on_packets_lost(
self.rtt.first_sample_time(),
prev_largest_acked_sent,
self.rtt.pto(space), // Important: the base PTO, not adjusted.
lost_packets,
)
);
if cwnd_reduced {
self.rtt.update_ack_delay(self.sender.cwnd(), self.mtu());
}
}
/// Get the number of bytes that can be written to this path.

View File

@ -428,6 +428,7 @@ fn frame_to_qlogframe(frame: &Frame) -> QuicFrame {
Some(frame_type.to_string()),
),
Frame::HandshakeDone => QuicFrame::handshake_done(),
Frame::AckFrequency { .. } => QuicFrame::unknown(frame.get_type()),
}
}

View File

@ -18,6 +18,7 @@ use smallvec::{smallvec, SmallVec};
use neqo_common::{qdebug, qlog::NeqoQlog, qtrace, qwarn};
use crate::ackrate::AckRate;
use crate::cid::ConnectionIdEntry;
use crate::connection::LOCAL_IDLE_TIMEOUT;
use crate::crypto::CryptoRecoveryToken;
@ -78,6 +79,7 @@ pub enum RecoveryToken {
stream_type: StreamType,
max_streams: u64,
},
AckFrequency(AckRate),
}
impl RecoveryToken {

View File

@ -39,14 +39,13 @@ impl RecvStreams {
builder: &mut PacketBuilder,
tokens: &mut Vec<RecoveryToken>,
stats: &mut FrameStats,
) -> Res<()> {
) {
for stream in self.0.values_mut() {
stream.write_frame(builder, tokens, stats)?;
if builder.remaining() < 2 {
return Ok(());
stream.write_frame(builder, tokens, stats);
if builder.is_full() {
return;
}
}
Ok(())
}
pub fn insert(&mut self, id: StreamId, stream: RecvStream) {
@ -590,17 +589,17 @@ impl RecvStream {
builder: &mut PacketBuilder,
tokens: &mut Vec<RecoveryToken>,
stats: &mut FrameStats,
) -> Res<()> {
) {
match &mut self.state {
// Maybe send MAX_STREAM_DATA
RecvStreamState::Recv { fc, .. } => fc.write_frames(builder, tokens, stats)?,
RecvStreamState::Recv { fc, .. } => fc.write_frames(builder, tokens, stats),
// Maybe send STOP_SENDING
RecvStreamState::AbortReading { frame_needed, err } => {
if *frame_needed
&& write_varint_frame(
builder,
&[FRAME_TYPE_STOP_SENDING, self.stream_id.as_u64(), *err],
)?
)
{
tokens.push(RecoveryToken::StopSending {
stream_id: self.stream_id,
@ -611,7 +610,6 @@ impl RecvStream {
}
_ => {}
}
Ok(())
}
pub fn max_stream_data_lost(&mut self, maximum_data: u64) {
@ -1087,8 +1085,7 @@ mod tests {
// consume it
let mut builder = PacketBuilder::short(Encoder::new(), false, &[]);
let mut token = Vec::new();
s.write_frame(&mut builder, &mut token, &mut FrameStats::default())
.unwrap();
s.write_frame(&mut builder, &mut token, &mut FrameStats::default());
// it should be gone
s.maybe_send_flowc_update();

View File

@ -13,15 +13,16 @@ use std::time::{Duration, Instant};
use neqo_common::{qlog::NeqoQlog, qtrace};
use crate::ackrate::{AckRate, PeerAckDelay};
use crate::packet::PacketBuilder;
use crate::qlog::{self, QlogMetric};
use crate::recovery::RecoveryToken;
use crate::stats::FrameStats;
use crate::tracking::PacketNumberSpace;
/// The smallest time that the system timer (via `sleep()`, `nanosleep()`,
/// `select()`, or similar) can reliably deliver; see `neqo_common::hrtime`.
const GRANULARITY: Duration = Duration::from_millis(1);
/// The default value for the maximum time a peer can delay acknowledgment
/// of an ack-eliciting packet.
const DEFAULT_MAX_ACK_DELAY: Duration = Duration::from_millis(25);
pub const GRANULARITY: Duration = Duration::from_millis(1);
// Defined in -recovery 6.2 as 333ms but using lower value.
const INITIAL_RTT: Duration = Duration::from_millis(100);
@ -33,18 +34,10 @@ pub struct RttEstimate {
smoothed_rtt: Duration,
rttvar: Duration,
min_rtt: Duration,
max_ack_delay: Duration,
ack_delay: PeerAckDelay,
}
impl RttEstimate {
pub fn set_initial(&mut self, rtt: Duration) {
qtrace!("initial RTT={:?}", rtt);
if rtt >= GRANULARITY {
// Ignore if the value is too small.
self.init(rtt)
}
}
fn init(&mut self, rtt: Duration) {
// Only allow this when there are no samples.
debug_assert!(self.first_sample_time.is_none());
@ -54,8 +47,26 @@ impl RttEstimate {
self.rttvar = rtt / 2;
}
pub fn set_max_ack_delay(&mut self, mad: Duration) {
self.max_ack_delay = mad;
pub fn set_initial(&mut self, rtt: Duration) {
qtrace!("initial RTT={:?}", rtt);
if rtt >= GRANULARITY {
// Ignore if the value is too small.
self.init(rtt)
}
}
/// For a new path, prime the RTT based on the state of another path.
pub fn prime_rtt(&mut self, other: &Self) {
self.set_initial(other.smoothed_rtt + other.rttvar);
self.ack_delay = other.ack_delay.clone();
}
pub fn set_ack_delay(&mut self, ack_delay: PeerAckDelay) {
self.ack_delay = ack_delay;
}
pub fn update_ack_delay(&mut self, cwnd: usize, mtu: usize) {
self.ack_delay.update(cwnd, mtu, self.smoothed_rtt);
}
pub fn update(
@ -67,8 +78,9 @@ impl RttEstimate {
now: Instant,
) {
// Limit ack delay by max_ack_delay if confirmed.
let ack_delay = if confirmed && ack_delay > self.max_ack_delay {
self.max_ack_delay
let mad = self.ack_delay.max();
let ack_delay = if confirmed && ack_delay > mad {
mad
} else {
ack_delay
};
@ -117,13 +129,11 @@ impl RttEstimate {
}
pub fn pto(&self, pn_space: PacketNumberSpace) -> Duration {
self.estimate()
+ max(4 * self.rttvar, GRANULARITY)
+ if pn_space == PacketNumberSpace::ApplicationData {
self.max_ack_delay
} else {
Duration::from_millis(0)
}
let mut t = self.estimate() + max(4 * self.rttvar, GRANULARITY);
if pn_space == PacketNumberSpace::ApplicationData {
t += self.ack_delay.max()
}
t
}
/// Calculate the loss delay based on the current estimate and the last
@ -153,6 +163,23 @@ impl RttEstimate {
pub fn minimum(&self) -> Duration {
self.min_rtt
}
pub fn write_frames(
&mut self,
builder: &mut PacketBuilder,
tokens: &mut Vec<RecoveryToken>,
stats: &mut FrameStats,
) {
self.ack_delay.write_frames(builder, tokens, stats);
}
pub fn frame_lost(&mut self, lost: &AckRate) {
self.ack_delay.frame_lost(lost);
}
pub fn frame_acked(&mut self, acked: &AckRate) {
self.ack_delay.frame_acked(acked);
}
}
impl Default for RttEstimate {
@ -163,7 +190,7 @@ impl Default for RttEstimate {
smoothed_rtt: INITIAL_RTT,
rttvar: INITIAL_RTT / 2,
min_rtt: INITIAL_RTT,
max_ack_delay: DEFAULT_MAX_ACK_DELAY,
ack_delay: PeerAckDelay::default(),
}
}
}

View File

@ -679,13 +679,13 @@ impl SendStream {
builder: &mut PacketBuilder,
tokens: &mut Vec<RecoveryToken>,
stats: &mut FrameStats,
) -> Res<()> {
) {
let retransmission = if priority == self.priority {
false
} else if priority == self.priority + self.retransmission_priority {
true
} else {
return Ok(());
return;
};
let id = self.stream_id;
@ -700,14 +700,14 @@ impl SendStream {
};
if overhead > builder.remaining() {
qtrace!([self], "write_frame no space for header");
return Ok(());
return;
}
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!([self], "write_frame no data, no fin");
return Ok(());
return;
}
// Write the stream out.
@ -721,10 +721,7 @@ impl SendStream {
} else {
builder.encode_vvec(&data[..length]);
}
if builder.len() > builder.limit() {
return Err(Error::InternalError(23));
}
debug_assert!(builder.len() <= builder.limit());
self.mark_as_sent(offset, length, fin);
tokens.push(RecoveryToken::Stream(StreamRecoveryToken {
@ -735,7 +732,6 @@ impl SendStream {
}));
stats.stream += 1;
}
Ok(())
}
pub fn reset_acked(&mut self) {
@ -770,7 +766,7 @@ impl SendStream {
builder: &mut PacketBuilder,
tokens: &mut Vec<RecoveryToken>,
stats: &mut FrameStats,
) -> Res<bool> {
) -> bool {
if let SendStreamState::ResetSent {
final_size,
err,
@ -778,7 +774,7 @@ impl SendStream {
} = self.state
{
if *priority != Some(p) {
return Ok(false);
return false;
}
if write_varint_frame(
builder,
@ -788,18 +784,18 @@ impl SendStream {
err,
final_size,
],
)? {
) {
tokens.push(RecoveryToken::ResetStream {
stream_id: self.stream_id,
});
stats.reset_stream += 1;
*priority = None;
Ok(true)
true
} else {
Ok(false)
false
}
} else {
Ok(false)
false
}
}
@ -820,18 +816,14 @@ impl SendStream {
builder: &mut PacketBuilder,
tokens: &mut Vec<RecoveryToken>,
stats: &mut FrameStats,
) -> Res<()> {
) {
// Send STREAM_DATA_BLOCKED at normal priority always.
if priority == self.priority {
if let SendStreamState::Ready { fc, .. } | SendStreamState::Send { fc, .. } =
&mut self.state
{
fc.write_frames(builder, tokens, stats)
} else {
Ok(())
fc.write_frames(builder, tokens, stats);
}
} else {
Ok(())
}
}
@ -1135,15 +1127,14 @@ impl SendStreams {
builder: &mut PacketBuilder,
tokens: &mut Vec<RecoveryToken>,
stats: &mut FrameStats,
) -> Res<()> {
) {
qtrace!("write STREAM frames at priority {:?}", priority);
for stream in self.0.values_mut() {
if !stream.write_reset_frame(priority, builder, tokens, stats)? {
stream.write_blocked_frame(priority, builder, tokens, stats)?;
stream.write_stream_frame(priority, builder, tokens, stats)?;
if !stream.write_reset_frame(priority, builder, tokens, stats) {
stream.write_blocked_frame(priority, builder, tokens, stats);
stream.write_stream_frame(priority, builder, tokens, stats);
}
}
Ok(())
}
pub fn update_initial_limit(&mut self, remote: &TransportParameters) {
@ -1607,8 +1598,7 @@ mod tests {
&mut builder,
&mut tokens,
&mut FrameStats::default(),
)
.unwrap();
);
assert_eq!(builder.len(), written + 6);
assert_eq!(tokens.len(), 1);
let f1_token = tokens.remove(0);
@ -1622,8 +1612,7 @@ mod tests {
&mut builder,
&mut tokens,
&mut FrameStats::default(),
)
.unwrap();
);
assert_eq!(builder.len(), written + 10);
assert_eq!(tokens.len(), 1);
let f2_token = tokens.remove(0);
@ -1636,8 +1625,7 @@ mod tests {
&mut builder,
&mut tokens,
&mut FrameStats::default(),
)
.unwrap();
);
assert_eq!(builder.len(), written);
assert!(tokens.is_empty());
@ -1656,8 +1644,7 @@ mod tests {
&mut builder,
&mut tokens,
&mut FrameStats::default(),
)
.unwrap();
);
assert_eq!(builder.len(), written + 7); // Needs a length this time.
assert_eq!(tokens.len(), 1);
let f4_token = tokens.remove(0);
@ -1677,8 +1664,7 @@ mod tests {
&mut builder,
&mut tokens,
&mut FrameStats::default(),
)
.unwrap();
);
assert_eq!(builder.len(), written + 10);
assert_eq!(tokens.len(), 1);
let f5_token = tokens.remove(0);
@ -1705,8 +1691,7 @@ mod tests {
&mut builder,
&mut tokens,
&mut FrameStats::default(),
)
.unwrap();
);
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));
@ -1718,8 +1703,7 @@ mod tests {
&mut builder,
&mut tokens,
&mut FrameStats::default(),
)
.unwrap();
);
assert!(tokens.is_empty());
ss.get_mut(StreamId::from(0)).unwrap().close();
@ -1729,8 +1713,7 @@ mod tests {
&mut builder,
&mut tokens,
&mut FrameStats::default(),
)
.unwrap();
);
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));
@ -1749,8 +1732,7 @@ mod tests {
&mut builder,
&mut tokens,
&mut FrameStats::default(),
)
.unwrap();
);
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));
@ -1769,8 +1751,7 @@ mod tests {
&mut builder,
&mut tokens,
&mut FrameStats::default(),
)
.unwrap();
);
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));
@ -1798,8 +1779,7 @@ mod tests {
&mut builder,
&mut tokens,
&mut stats,
)
.unwrap();
);
assert_eq!(stats.stream_data_blocked, 0);
// Blocking is reported after sending the last available credit.
@ -1809,8 +1789,7 @@ mod tests {
&mut builder,
&mut tokens,
&mut stats,
)
.unwrap();
);
assert_eq!(stats.stream_data_blocked, 1);
// Now increase the stream limit and test the connection limit.
@ -1821,16 +1800,14 @@ mod tests {
// DATA_BLOCKED is not sent yet.
conn_fc
.borrow_mut()
.write_frames(&mut builder, &mut tokens, &mut stats)
.unwrap();
.write_frames(&mut builder, &mut tokens, &mut stats);
assert_eq!(stats.data_blocked, 0);
// DATA_BLOCKED is queued once bytes using all credit are sent.
s.mark_as_sent(2, 3, false);
conn_fc
.borrow_mut()
.write_frames(&mut builder, &mut tokens, &mut stats)
.unwrap();
.write_frames(&mut builder, &mut tokens, &mut stats);
assert_eq!(stats.data_blocked, 1);
}
@ -1855,8 +1832,7 @@ mod tests {
&mut builder,
&mut tokens,
&mut stats,
)
.unwrap();
);
assert_eq!(stats.stream_data_blocked, 1);
// Assert that a non-atomic write works.
@ -1873,8 +1849,7 @@ mod tests {
// Assert that DATA_BLOCKED is sent.
conn_fc
.borrow_mut()
.write_frames(&mut builder, &mut tokens, &mut stats)
.unwrap();
.write_frames(&mut builder, &mut tokens, &mut stats);
assert_eq!(stats.data_blocked, 1);
// Check that a non-atomic write works.
@ -1944,8 +1919,7 @@ mod tests {
&mut builder,
&mut tokens,
&mut stats,
)
.unwrap();
);
assert_eq!(stats.stream, 0);
}
@ -1999,8 +1973,7 @@ mod tests {
&mut builder,
&mut tokens,
&mut stats,
)
.unwrap();
);
qtrace!("STREAM frame: {}", hex_with_len(&builder[header_len..]));
stats.stream > 0
}
@ -2101,8 +2074,7 @@ mod tests {
&mut builder,
&mut tokens,
&mut stats,
)
.unwrap();
);
assert_eq!(stats.stream, 1);
// Expect STREAM + FIN only.
assert_eq!(&builder[header_len..header_len + 2], &[0b1001, 0]);
@ -2122,8 +2094,7 @@ mod tests {
&mut builder,
&mut tokens,
&mut stats,
)
.unwrap();
);
assert_eq!(stats.stream, 2);
// Expect STREAM + LEN + FIN.
assert_eq!(
@ -2156,8 +2127,7 @@ mod tests {
&mut builder,
&mut tokens,
&mut stats,
)
.unwrap();
);
assert_eq!(stats.stream, 1);
// Expect STREAM + FIN only.
assert_eq!(&builder[header_len..header_len + 2], &[0b1001, 0]);
@ -2173,8 +2143,7 @@ mod tests {
&mut builder,
&mut tokens,
&mut stats,
)
.unwrap();
);
assert_eq!(stats.stream, 2);
// Expect STREAM + LEN, not FIN.
assert_eq!(&builder[header_len..header_len + 3], &[0b1010, 0, 63]);

View File

@ -53,7 +53,6 @@ impl PacketSender {
self.cc.set_qlog(qlog);
}
#[cfg(test)]
#[must_use]
pub fn cwnd(&self) -> usize {
self.cc.cwnd()
@ -68,19 +67,20 @@ impl PacketSender {
self.cc.on_packets_acked(acked_pkts, min_rtt, now);
}
/// Called when packets are lost. Returns true if the congestion window was reduced.
pub fn on_packets_lost(
&mut self,
first_rtt_sample_time: Option<Instant>,
prev_largest_acked_sent: Option<Instant>,
pto: Duration,
lost_packets: &[SentPacket],
) {
) -> bool {
self.cc.on_packets_lost(
first_rtt_sample_time,
prev_largest_acked_sent,
pto,
lost_packets,
);
)
}
pub fn discard(&mut self, pkt: &SentPacket) {

View File

@ -47,6 +47,8 @@ pub struct FrameStats {
pub connection_close: usize,
pub handshake_done: usize,
pub new_token: usize,
pub ack_frequency: usize,
}
impl Debug for FrameStats {
@ -83,7 +85,8 @@ impl Debug for FrameStats {
self.retire_connection_id,
self.path_challenge,
self.path_response,
)
)?;
writeln!(f, " ack_frequency {} ", self.ack_frequency)
}
}

View File

@ -175,40 +175,40 @@ impl Streams {
builder: &mut PacketBuilder,
tokens: &mut Vec<RecoveryToken>,
stats: &mut FrameStats,
) -> Res<()> {
) {
// Send `DATA_BLOCKED` as necessary.
self.sender_fc
.borrow_mut()
.write_frames(builder, tokens, stats)?;
if builder.remaining() < 2 {
return Ok(());
.write_frames(builder, tokens, stats);
if builder.is_full() {
return;
}
// Send `MAX_DATA` as necessary.
self.receiver_fc
.borrow_mut()
.write_frames(builder, tokens, stats)?;
if builder.remaining() < 2 {
return Ok(());
.write_frames(builder, tokens, stats);
if builder.is_full() {
return;
}
self.recv.write_frames(builder, tokens, stats)?;
self.recv.write_frames(builder, tokens, stats);
self.remote_stream_limits[StreamType::BiDi].write_frames(builder, tokens, stats)?;
if builder.remaining() < 2 {
return Ok(());
self.remote_stream_limits[StreamType::BiDi].write_frames(builder, tokens, stats);
if builder.is_full() {
return;
}
self.remote_stream_limits[StreamType::UniDi].write_frames(builder, tokens, stats)?;
if builder.remaining() < 2 {
return Ok(());
self.remote_stream_limits[StreamType::UniDi].write_frames(builder, tokens, stats);
if builder.is_full() {
return;
}
self.local_stream_limits[StreamType::BiDi].write_frames(builder, tokens, stats)?;
if builder.remaining() < 2 {
return Ok(());
self.local_stream_limits[StreamType::BiDi].write_frames(builder, tokens, stats);
if builder.is_full() {
return;
}
self.local_stream_limits[StreamType::UniDi].write_frames(builder, tokens, stats)
self.local_stream_limits[StreamType::UniDi].write_frames(builder, tokens, stats);
}
pub fn write_frames(
@ -217,15 +217,15 @@ impl Streams {
builder: &mut PacketBuilder,
tokens: &mut Vec<RecoveryToken>,
stats: &mut FrameStats,
) -> Res<()> {
) {
if priority == TransmissionPriority::Important {
self.write_maintenance_frames(builder, tokens, stats)?;
if builder.remaining() < 2 {
return Ok(());
self.write_maintenance_frames(builder, tokens, stats);
if builder.is_full() {
return;
}
}
self.send.write_frames(priority, builder, tokens, stats)
self.send.write_frames(priority, builder, tokens, stats);
}
pub fn lost(&mut self, token: &RecoveryToken) {

View File

@ -47,6 +47,7 @@ tpids! {
INITIAL_SOURCE_CONNECTION_ID = 0x0f,
RETRY_SOURCE_CONNECTION_ID = 0x10,
GREASE_QUIC_BIT = 0x2ab2,
MIN_ACK_DELAY = 0xff02_de1a,
}
#[derive(Clone, Debug)]
@ -245,6 +246,11 @@ impl TransportParameter {
PREFERRED_ADDRESS => Self::decode_preferred_address(&mut d)?,
MIN_ACK_DELAY => match d.decode_varint() {
Some(v) if v < (1 << 24) => Self::Integer(v),
_ => return Err(Error::TransportParameterError),
},
// Skip.
_ => return Ok(None),
};
@ -310,6 +316,7 @@ impl TransportParameters {
ACK_DELAY_EXPONENT => 3,
MAX_ACK_DELAY => 25,
ACTIVE_CONNECTION_ID_LIMIT => 2,
MIN_ACK_DELAY => 0,
_ => panic!("Transport parameter not known or not an Integer"),
};
match self.params.get(&tp) {
@ -332,7 +339,8 @@ impl TransportParameters {
| MAX_UDP_PAYLOAD_SIZE
| ACK_DELAY_EXPONENT
| MAX_ACK_DELAY
| ACTIVE_CONNECTION_ID_LIMIT => {
| ACTIVE_CONNECTION_ID_LIMIT
| MIN_ACK_DELAY => {
self.set(tp, TransportParameter::Integer(value));
}
_ => panic!("Transport parameter not known"),
@ -407,7 +415,13 @@ impl TransportParameters {
if let Some(v_self) = self.params.get(k) {
match (v_self, v_rem) {
(TransportParameter::Integer(i_self), TransportParameter::Integer(i_rem)) => {
if *i_self < *i_rem {
if *k == MIN_ACK_DELAY {
// MIN_ACK_DELAY is backwards:
// it can only be reduced safely.
if *i_self > *i_rem {
return false;
}
} else if *i_self < *i_rem {
return false;
}
}
@ -436,9 +450,8 @@ impl TransportParameters {
}
}
#[cfg(test)]
#[must_use]
fn was_sent(&self, tp: TransportParameterId) -> bool {
pub fn has_value(&self, tp: TransportParameterId) -> bool {
self.params.contains_key(&tp)
}
}
@ -584,10 +597,10 @@ mod tests {
assert_eq!(tps2.get_bytes(ORIGINAL_DESTINATION_CONNECTION_ID), None);
assert_eq!(tps2.get_bytes(INITIAL_SOURCE_CONNECTION_ID), None);
assert_eq!(tps2.get_bytes(RETRY_SOURCE_CONNECTION_ID), None);
assert_eq!(tps2.was_sent(ORIGINAL_DESTINATION_CONNECTION_ID), false);
assert_eq!(tps2.was_sent(INITIAL_SOURCE_CONNECTION_ID), false);
assert_eq!(tps2.was_sent(RETRY_SOURCE_CONNECTION_ID), false);
assert_eq!(tps2.was_sent(STATELESS_RESET_TOKEN), true);
assert_eq!(tps2.has_value(ORIGINAL_DESTINATION_CONNECTION_ID), false);
assert_eq!(tps2.has_value(INITIAL_SOURCE_CONNECTION_ID), false);
assert_eq!(tps2.has_value(RETRY_SOURCE_CONNECTION_ID), false);
assert_eq!(tps2.has_value(STATELESS_RESET_TOKEN), true);
let mut enc = Encoder::default();
tps.encode(&mut enc);
@ -840,6 +853,7 @@ mod tests {
INITIAL_MAX_STREAMS_BIDI,
INITIAL_MAX_STREAMS_UNI,
MAX_UDP_PAYLOAD_SIZE,
MIN_ACK_DELAY,
];
for i in INTEGER_KEYS {
tps_a.set(*i, TransportParameter::Integer(12));
@ -849,17 +863,20 @@ mod tests {
assert!(tps_a.ok_for_0rtt(&tps_b));
assert!(tps_b.ok_for_0rtt(&tps_a));
// For each integer key, increase the value by one.
// For each integer key, choose a new value that will be accepted.
for i in INTEGER_KEYS {
let mut tps_b = tps_a.clone();
tps_b.set(*i, TransportParameter::Integer(13));
// If an increased value is remembered, then we can't attempt 0-RTT with these parameters.
// Set a safe new value; reducing MIN_ACK_DELAY instead.
let safe_value = if *i == MIN_ACK_DELAY { 11 } else { 13 };
tps_b.set(*i, TransportParameter::Integer(safe_value));
// If the new value is not safe relative to the remembered value,
// then we can't attempt 0-RTT with these parameters.
assert!(!tps_a.ok_for_0rtt(&tps_b));
// If an increased value is lower, then we can attempt 0-RTT with these parameters.
// The opposite situation is fine.
assert!(tps_b.ok_for_0rtt(&tps_a));
}
// Drop integer values and check.
// Drop integer values and check that that is OK.
for i in INTEGER_KEYS {
let mut tps_b = tps_a.clone();
tps_b.remove(*i);
@ -870,8 +887,9 @@ mod tests {
}
}
/// `ACTIVE_CONNECTION_ID_LIMIT` can't be less than 2.
#[test]
fn active_connection_id_limit_lt_2_is_error() {
fn active_connection_id_limit_min_2() {
let mut tps = TransportParameters::default();
// Intentionally set an invalid value for the ACTIVE_CONNECTION_ID_LIMIT transport parameter.

View File

@ -336,8 +336,10 @@ impl ::std::fmt::Display for PacketRange {
}
/// The ACK delay we use.
pub const ACK_DELAY: Duration = Duration::from_millis(20); // 20ms
pub const MAX_UNACKED_PKTS: usize = 1;
pub const DEFAULT_ACK_DELAY: Duration = Duration::from_millis(20); // 20ms
/// The default number of in-order packets we will receive after
/// largest acknowledged without sending an immediate acknowledgment.
pub const DEFAULT_ACK_PACKET_TOLERANCE: PacketNumber = 1;
const MAX_TRACKED_RANGES: usize = 32;
const MAX_ACKS_PER_FRAME: usize = 32;
@ -358,9 +360,22 @@ pub struct RecvdPackets {
min_tracked: PacketNumber,
/// The time we got the largest acknowledged.
largest_pn_time: Option<Instant>,
// The time that we should be sending an ACK.
/// The time that we should be sending an ACK.
ack_time: Option<Instant>,
pkts_since_last_ack: usize,
/// The current ACK frequency sequence number.
ack_frequency_seqno: u64,
/// The time to delay after receiving the first packet that is
/// not immediately acknowledged.
ack_delay: Duration,
/// The number of ack-eliciting packets that have been received, but
/// not acknowledged.
unacknowledged_count: PacketNumber,
/// The number of contiguous packets that can be received without
/// acknowledging immediately.
unacknowledged_tolerance: PacketNumber,
/// Whether we are ignoring packets that arrive out of order
/// for the purposes of generating immediate acknowledgment.
ignore_order: bool,
}
impl RecvdPackets {
@ -372,7 +387,11 @@ impl RecvdPackets {
min_tracked: 0,
largest_pn_time: None,
ack_time: None,
pkts_since_last_ack: 0,
ack_frequency_seqno: 0,
ack_delay: DEFAULT_ACK_DELAY,
unacknowledged_count: 0,
unacknowledged_tolerance: DEFAULT_ACK_PACKET_TOLERANCE,
ignore_order: false,
}
}
@ -381,6 +400,25 @@ impl RecvdPackets {
self.ack_time
}
/// Update acknowledgment delay parameters.
pub fn ack_freq(
&mut self,
seqno: u64,
tolerance: PacketNumber,
delay: Duration,
ignore_order: bool,
) {
// Yes, this means that we will overwrite values if a sequence number is
// reused, but that is better than using an `Option<PacketNumber>`
// when it will always be `Some`.
if seqno >= self.ack_frequency_seqno {
self.ack_frequency_seqno = seqno;
self.unacknowledged_tolerance = tolerance;
self.ack_delay = delay;
self.ignore_order = ignore_order;
}
}
/// Returns true if an ACK frame should be sent now.
fn ack_now(&self, now: Instant) -> bool {
match self.ack_time {
@ -433,13 +471,8 @@ 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 {
let next_in_order_pn = self.ranges.front().map_or(0, |pr| pr.largest + 1);
qdebug!(
[self],
"received {}, next in order pn: {}",
pn,
next_in_order_pn
);
let next_in_order_pn = self.ranges.front().map_or(0, |r| r.largest + 1);
qdebug!([self], "received {}, next: {}", pn, next_in_order_pn);
self.add(pn);
self.trim_ranges();
@ -453,42 +486,44 @@ impl RecvdPackets {
};
if ack_eliciting {
self.pkts_since_last_ack += 1;
self.unacknowledged_count += 1;
// Send ACK right away if out-of-order
// On the first in-order ack-eliciting packet since sending an ACK,
// set a delay.
// Count packets until we exceed MAX_UNACKED_PKTS, then remove the
// delay.
if pn != next_in_order_pn {
self.ack_time = Some(now);
} else if self.space == PacketNumberSpace::ApplicationData {
match &mut self.pkts_since_last_ack {
0 => unreachable!(),
1 => self.ack_time = Some(now + ACK_DELAY),
x if *x > MAX_UNACKED_PKTS => self.ack_time = Some(now),
_ => debug_assert!(self.ack_time.is_some()),
}
let immediate_ack = self.space != PacketNumberSpace::ApplicationData
|| (pn != next_in_order_pn && !self.ignore_order)
|| self.unacknowledged_count > self.unacknowledged_tolerance;
let ack_time = if immediate_ack {
now
} else {
self.ack_time = Some(now);
}
qdebug!([self], "Set ACK timer to {:?}", self.ack_time);
// Note that `ack_delay` can change and that won't take effect if
// we are waiting on the previous delay timer.
// If ACK delay increases, we might send an ACK a bit early;
// if ACK delay decreases, we might send an ACK a bit later.
// We could use min() here, but change is rare and the size
// of the change is very small.
self.ack_time.unwrap_or_else(|| now + self.ack_delay)
};
qdebug!([self], "Set ACK timer to {:?}", ack_time);
self.ack_time = Some(ack_time);
}
largest
}
/// If we just received a PING frame, we should immediately acknowledge.
pub fn immediate_ack(&mut self, now: Instant) {
self.ack_time = Some(now);
qdebug!([self], "immediate_ack at {:?}", now);
}
/// Check if the packet is a duplicate.
pub fn is_duplicate(&self, pn: PacketNumber) -> bool {
if pn < self.min_tracked {
return true;
}
// TODO(mt) consider a binary search or early exit.
for range in &self.ranges {
if range.contains(pn) {
return true;
}
}
false
self.ranges
.iter()
.take_while(|r| pn <= r.largest)
.any(|r| r.contains(pn))
}
/// Mark the given range as having been acknowledged.
@ -582,7 +617,7 @@ impl RecvdPackets {
// We've sent an ACK, reset the timer.
self.ack_time = None;
self.pkts_since_last_ack = 0;
self.unacknowledged_count = 0;
tokens.push(RecoveryToken::Ack(AckToken {
space: self.space,
@ -627,8 +662,32 @@ impl AckTracker {
})
}
pub fn ack_freq(
&mut self,
seqno: u64,
tolerance: PacketNumber,
delay: Duration,
ignore_order: bool,
) {
// Only ApplicationData ever delays ACK.
self.get_mut(PacketNumberSpace::ApplicationData)
.unwrap()
.ack_freq(seqno, tolerance, delay, ignore_order);
}
// Force an ACK to be generated immediately (a PING was received).
pub fn immediate_ack(&mut self, now: Instant) {
self.get_mut(PacketNumberSpace::ApplicationData)
.unwrap()
.immediate_ack(now);
}
/// Determine the earliest time that an ACK might be needed.
pub fn ack_time(&self, now: Instant) -> Option<Instant> {
for recvd in &self.spaces {
qtrace!("ack_time for {} = {:?}", recvd.space, recvd.ack_time());
}
if self.spaces.len() == 1 {
self.spaces[0].ack_time()
} else {
@ -684,21 +743,20 @@ impl Default for AckTracker {
mod tests {
use super::{
AckTracker, Duration, Instant, PacketNumberSpace, PacketNumberSpaceSet, RecoveryToken,
RecvdPackets, ACK_DELAY, MAX_TRACKED_RANGES, MAX_UNACKED_PKTS,
RecvdPackets, MAX_TRACKED_RANGES,
};
use crate::frame::Frame;
use crate::packet::PacketBuilder;
use crate::packet::{PacketBuilder, PacketNumber};
use crate::stats::FrameStats;
use lazy_static::lazy_static;
use neqo_common::Encoder;
use std::collections::HashSet;
use std::convert::TryFrom;
lazy_static! {
static ref NOW: Instant = Instant::now();
}
fn test_ack_range(pns: &[u64], nranges: usize) {
fn test_ack_range(pns: &[PacketNumber], nranges: usize) {
let mut rp = RecvdPackets::new(PacketNumberSpace::Initial); // Any space will do.
let mut packets = HashSet::new();
@ -772,22 +830,25 @@ mod tests {
#[test]
fn ack_delay() {
const COUNT: PacketNumber = 9;
const DELAY: Duration = Duration::from_millis(7);
// Only application data packets are delayed.
let mut rp = RecvdPackets::new(PacketNumberSpace::ApplicationData);
assert!(rp.ack_time().is_none());
assert!(!rp.ack_now(*NOW));
rp.ack_freq(0, COUNT, DELAY, false);
// Some packets won't cause an ACK to be needed.
let max_unacked = u64::try_from(MAX_UNACKED_PKTS).unwrap();
for num in 0..max_unacked {
rp.set_received(*NOW, num, true);
assert_eq!(Some(*NOW + ACK_DELAY), rp.ack_time());
for i in 0..COUNT {
rp.set_received(*NOW, i, true);
assert_eq!(Some(*NOW + DELAY), rp.ack_time());
assert!(!rp.ack_now(*NOW));
assert!(rp.ack_now(*NOW + ACK_DELAY));
assert!(rp.ack_now(*NOW + DELAY));
}
// Exceeding MAX_UNACKED_PKTS will move the ACK time to now.
rp.set_received(*NOW, max_unacked, true);
// Exceeding COUNT will move the ACK time to now.
rp.set_received(*NOW, COUNT, true);
assert_eq!(Some(*NOW), rp.ack_time());
assert!(rp.ack_now(*NOW));
}
@ -799,7 +860,7 @@ mod tests {
assert!(rp.ack_time().is_none());
assert!(!rp.ack_now(*NOW));
// Any packet will be acknowledged straight away.
// Any packet in these spaces is acknowledged straight away.
rp.set_received(*NOW, 0, true);
assert_eq!(Some(*NOW), rp.ack_time());
assert!(rp.ack_now(*NOW));
@ -807,26 +868,111 @@ mod tests {
}
#[test]
fn ooo_no_ack_delay() {
for space in &[
PacketNumberSpace::Initial,
PacketNumberSpace::Handshake,
PacketNumberSpace::ApplicationData,
] {
let mut rp = RecvdPackets::new(*space);
assert!(rp.ack_time().is_none());
assert!(!rp.ack_now(*NOW));
fn ooo_no_ack_delay_new() {
let mut rp = RecvdPackets::new(PacketNumberSpace::ApplicationData);
assert!(rp.ack_time().is_none());
assert!(!rp.ack_now(*NOW));
// Any OoO packet will be acknowledged straight away.
rp.set_received(*NOW, 3, true);
assert_eq!(Some(*NOW), rp.ack_time());
assert!(rp.ack_now(*NOW));
}
// Anything other than packet 0 is acknowledged immediately.
rp.set_received(*NOW, 1, true);
assert_eq!(Some(*NOW), rp.ack_time());
assert!(rp.ack_now(*NOW));
}
fn write_frame(rp: &mut RecvdPackets) {
let mut builder = PacketBuilder::short(Encoder::new(), false, &[]);
let mut stats = FrameStats::default();
let mut tokens = Vec::new();
rp.write_frame(*NOW, &mut builder, &mut tokens, &mut stats);
assert!(!tokens.is_empty());
assert_eq!(stats.ack, 1);
}
#[test]
fn ooo_no_ack_delay_fill() {
let mut rp = RecvdPackets::new(PacketNumberSpace::ApplicationData);
rp.set_received(*NOW, 1, true);
write_frame(&mut rp);
// Filling in behind the largest acknowledged causes immediate ACK.
rp.set_received(*NOW, 0, true);
assert_eq!(Some(*NOW), rp.ack_time());
assert!(rp.ack_now(*NOW));
}
#[test]
fn ooo_no_ack_delay_threshold_new() {
let mut rp = RecvdPackets::new(PacketNumberSpace::ApplicationData);
// Set tolerance to 2 and then it takes three packets.
rp.ack_freq(0, 2, Duration::from_millis(10), true);
rp.set_received(*NOW, 1, true);
assert_ne!(Some(*NOW), rp.ack_time());
rp.set_received(*NOW, 2, true);
assert_ne!(Some(*NOW), rp.ack_time());
rp.set_received(*NOW, 3, true);
assert_eq!(Some(*NOW), rp.ack_time());
}
#[test]
fn ooo_no_ack_delay_threshold_gap() {
let mut rp = RecvdPackets::new(PacketNumberSpace::ApplicationData);
rp.set_received(*NOW, 1, true);
write_frame(&mut rp);
// Set tolerance to 2 and then it takes three packets.
rp.ack_freq(0, 2, Duration::from_millis(10), true);
rp.set_received(*NOW, 3, true);
assert_ne!(Some(*NOW), rp.ack_time());
rp.set_received(*NOW, 4, true);
assert_ne!(Some(*NOW), rp.ack_time());
rp.set_received(*NOW, 5, true);
assert_eq!(Some(*NOW), rp.ack_time());
}
/// Test that an in-order packet that is not ack-eliciting doesn't
/// increase the number of packets needed to cause an ACK.
#[test]
fn non_ack_eliciting_skip() {
let mut rp = RecvdPackets::new(PacketNumberSpace::ApplicationData);
rp.ack_freq(0, 1, Duration::from_millis(10), true);
// This should be ignored.
rp.set_received(*NOW, 0, false);
assert_ne!(Some(*NOW), rp.ack_time());
// Skip 1 (it has no effect).
rp.set_received(*NOW, 2, true);
assert_ne!(Some(*NOW), rp.ack_time());
rp.set_received(*NOW, 3, true);
assert_eq!(Some(*NOW), rp.ack_time());
}
/// If a packet that is not ack-eliciting is reordered, that's fine too.
#[test]
fn non_ack_eliciting_reorder() {
let mut rp = RecvdPackets::new(PacketNumberSpace::ApplicationData);
rp.ack_freq(0, 1, Duration::from_millis(10), false);
// These are out of order, but they are not ack-eliciting.
rp.set_received(*NOW, 1, false);
assert_ne!(Some(*NOW), rp.ack_time());
rp.set_received(*NOW, 0, false);
assert_ne!(Some(*NOW), rp.ack_time());
// These are in order.
rp.set_received(*NOW, 2, true);
assert_ne!(Some(*NOW), rp.ack_time());
rp.set_received(*NOW, 3, true);
assert_eq!(Some(*NOW), rp.ack_time());
}
#[test]
fn aggregate_ack_time() {
const DELAY: Duration = Duration::from_millis(17);
let mut tracker = AckTracker::default();
tracker.ack_freq(0, 1, DELAY, false);
// This packet won't trigger an ACK.
tracker
.get_mut(PacketNumberSpace::Handshake)
@ -839,10 +985,10 @@ mod tests {
.get_mut(PacketNumberSpace::ApplicationData)
.unwrap()
.set_received(*NOW, 0, true);
assert_eq!(Some(*NOW + ACK_DELAY), tracker.ack_time(*NOW));
assert_eq!(Some(*NOW + DELAY), tracker.ack_time(*NOW));
// This should move the time forward.
let later = *NOW + ACK_DELAY.checked_div(2).unwrap();
let later = *NOW + (DELAY / 2);
tracker
.get_mut(PacketNumberSpace::Initial)
.unwrap()

View File

@ -34,6 +34,8 @@ use std::time::Duration;
/// Take a pair of connections in any state and complete the handshake.
/// The `datagram` argument is a packet that was received from the server.
/// See `connect` for what this returns.
/// # Panics
/// Only when the connection fails.
pub fn complete_connection(
client: &mut Connection,
server: &mut Server,