зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1743378 - Update to neqo version 0.5.5 r=necko-reviewers,kershaw
Differential Revision: https://phabricator.services.mozilla.com/D132562
This commit is contained in:
Родитель
58551a6795
Коммит
6fe5130c63
|
@ -10,7 +10,7 @@ rev = "029ac0d54b237f27dc7d8d4e51bc0fb076e5e852"
|
|||
[source."https://github.com/mozilla/neqo"]
|
||||
git = "https://github.com/mozilla/neqo"
|
||||
replace-with = "vendored-sources"
|
||||
tag = "v0.5.3"
|
||||
tag = "v0.5.5"
|
||||
|
||||
[source."https://github.com/mozilla/mp4parse-rust"]
|
||||
git = "https://github.com/mozilla/mp4parse-rust"
|
||||
|
|
|
@ -3243,8 +3243,8 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "neqo-common"
|
||||
version = "0.5.3"
|
||||
source = "git+https://github.com/mozilla/neqo?tag=v0.5.3#ad9439a0c90422745c326c2147edf0997f614aac"
|
||||
version = "0.5.5"
|
||||
source = "git+https://github.com/mozilla/neqo?tag=v0.5.5#cf161a560a245acd19d631cdad00ca4ffa01e1b5"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"env_logger",
|
||||
|
@ -3256,8 +3256,8 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "neqo-crypto"
|
||||
version = "0.5.3"
|
||||
source = "git+https://github.com/mozilla/neqo?tag=v0.5.3#ad9439a0c90422745c326c2147edf0997f614aac"
|
||||
version = "0.5.5"
|
||||
source = "git+https://github.com/mozilla/neqo?tag=v0.5.5#cf161a560a245acd19d631cdad00ca4ffa01e1b5"
|
||||
dependencies = [
|
||||
"bindgen",
|
||||
"log",
|
||||
|
@ -3269,9 +3269,10 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "neqo-http3"
|
||||
version = "0.5.3"
|
||||
source = "git+https://github.com/mozilla/neqo?tag=v0.5.3#ad9439a0c90422745c326c2147edf0997f614aac"
|
||||
version = "0.5.5"
|
||||
source = "git+https://github.com/mozilla/neqo?tag=v0.5.5#cf161a560a245acd19d631cdad00ca4ffa01e1b5"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"log",
|
||||
"neqo-common",
|
||||
"neqo-crypto",
|
||||
|
@ -3285,8 +3286,8 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "neqo-qpack"
|
||||
version = "0.5.3"
|
||||
source = "git+https://github.com/mozilla/neqo?tag=v0.5.3#ad9439a0c90422745c326c2147edf0997f614aac"
|
||||
version = "0.5.5"
|
||||
source = "git+https://github.com/mozilla/neqo?tag=v0.5.5#cf161a560a245acd19d631cdad00ca4ffa01e1b5"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"log",
|
||||
|
@ -3299,8 +3300,8 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "neqo-transport"
|
||||
version = "0.5.3"
|
||||
source = "git+https://github.com/mozilla/neqo?tag=v0.5.3#ad9439a0c90422745c326c2147edf0997f614aac"
|
||||
version = "0.5.5"
|
||||
source = "git+https://github.com/mozilla/neqo?tag=v0.5.5#cf161a560a245acd19d631cdad00ca4ffa01e1b5"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"lazy_static",
|
||||
|
|
|
@ -8,10 +8,10 @@ edition = "2018"
|
|||
name = "neqo_glue"
|
||||
|
||||
[dependencies]
|
||||
neqo-http3 = { tag = "v0.5.3", git = "https://github.com/mozilla/neqo" }
|
||||
neqo-transport = { tag = "v0.5.3", git = "https://github.com/mozilla/neqo" }
|
||||
neqo-common = { tag = "v0.5.3", git = "https://github.com/mozilla/neqo" }
|
||||
neqo-qpack = { tag = "v0.5.3", git = "https://github.com/mozilla/neqo" }
|
||||
neqo-http3 = { tag = "v0.5.5", git = "https://github.com/mozilla/neqo" }
|
||||
neqo-transport = { tag = "v0.5.5", git = "https://github.com/mozilla/neqo" }
|
||||
neqo-common = { tag = "v0.5.5", git = "https://github.com/mozilla/neqo" }
|
||||
neqo-qpack = { tag = "v0.5.5", git = "https://github.com/mozilla/neqo" }
|
||||
nserror = { path = "../../../xpcom/rust/nserror" }
|
||||
nsstring = { path = "../../../xpcom/rust/nsstring" }
|
||||
xpcom = { path = "../../../xpcom/rust/xpcom" }
|
||||
|
@ -25,7 +25,7 @@ static_prefs = { path = "../../../modules/libpref/init/static_prefs", optional =
|
|||
winapi = {version = "0.3", features = ["ws2def"] }
|
||||
|
||||
[dependencies.neqo-crypto]
|
||||
tag = "v0.5.3"
|
||||
tag = "v0.5.5"
|
||||
git = "https://github.com/mozilla/neqo"
|
||||
default-features = false
|
||||
features = ["gecko"]
|
||||
|
|
|
@ -5,17 +5,17 @@ authors = ["Dragana Damjanovic <dragana.damjano@gmail.com>"]
|
|||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
neqo-transport = { tag = "v0.5.3", git = "https://github.com/mozilla/neqo" }
|
||||
neqo-common = { tag = "v0.5.3", git = "https://github.com/mozilla/neqo" }
|
||||
neqo-http3 = { tag = "v0.5.3", git = "https://github.com/mozilla/neqo" }
|
||||
neqo-qpack = { tag = "v0.5.3", git = "https://github.com/mozilla/neqo" }
|
||||
neqo-transport = { tag = "v0.5.5", git = "https://github.com/mozilla/neqo" }
|
||||
neqo-common = { tag = "v0.5.5", git = "https://github.com/mozilla/neqo" }
|
||||
neqo-http3 = { tag = "v0.5.5", git = "https://github.com/mozilla/neqo" }
|
||||
neqo-qpack = { tag = "v0.5.5", git = "https://github.com/mozilla/neqo" }
|
||||
mio = "0.6.17"
|
||||
mio-extras = "2.0.5"
|
||||
log = "0.4.0"
|
||||
base64 = "0.10"
|
||||
|
||||
[dependencies.neqo-crypto]
|
||||
tag = "v0.5.3"
|
||||
tag = "v0.5.5"
|
||||
git = "https://github.com/mozilla/neqo"
|
||||
default-features = false
|
||||
features = ["gecko"]
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"23080873a43e8e60a60ef58bff3c8ce330cb6bd21a9714b34a129d74655f28da","build.rs":"a17b1bb1bd3de3fc958f72d4d1357f7bc4432faa26640c95b5fbfccf40579d67","src/codec.rs":"ee422054b6f330d303a150223fd498dc2277c70663b0c3c0dcb7f0fc14fee7d8","src/datagram.rs":"569f8d9e34d7ee17144bf63d34136ecd9778da0d337e513f338738c50284615e","src/event.rs":"f60fee9f4b09ef47ff5e4bfa21c07e45ffd5873c292f2605f24d834070127d62","src/header.rs":"b7d4eeb40952b36f71ae1f37ce82c9617af8b84c171576de4eca9d50a3071103","src/hrtime.rs":"45a608ce9f00e2666ce95422a278c6dc0ff4e229b114e7bcf0b4c0d9dc61ad56","src/incrdecoder.rs":"91dab6f99073b1a6c88ff2f2625315dadb0b00d7bb0704e13b186155fbf496e8","src/lib.rs":"e19efc395a902d1584e66cddf1c3beedbfd1a0487457ba6a18e60bcd84132be4","src/log.rs":"b69e492af85e65866cb6588138e8a337dd897d3ce399cb4e9fb8cc04ac042b7f","src/qlog.rs":"ca323c91d61810ebef2ebeb967836dda384a60a9fb492c2b8d1b235a98f2e4bf","src/timer.rs":"e63af7e7df968bf702583f263cfb63e6dca4e599bacffa2de0a6383d85333636","tests/log.rs":"480b165b7907ec642c508b303d63005eee1427115d6973a349eaf6b2242ed18d"},"package":null}
|
||||
{"files":{"Cargo.toml":"e669215a2bf5fa9ea28d92c2e61323a5cecb0ebc4107b7dd78b049de2a9214df","build.rs":"a17b1bb1bd3de3fc958f72d4d1357f7bc4432faa26640c95b5fbfccf40579d67","src/codec.rs":"ee422054b6f330d303a150223fd498dc2277c70663b0c3c0dcb7f0fc14fee7d8","src/datagram.rs":"569f8d9e34d7ee17144bf63d34136ecd9778da0d337e513f338738c50284615e","src/event.rs":"f60fee9f4b09ef47ff5e4bfa21c07e45ffd5873c292f2605f24d834070127d62","src/header.rs":"b7d4eeb40952b36f71ae1f37ce82c9617af8b84c171576de4eca9d50a3071103","src/hrtime.rs":"45a608ce9f00e2666ce95422a278c6dc0ff4e229b114e7bcf0b4c0d9dc61ad56","src/incrdecoder.rs":"91dab6f99073b1a6c88ff2f2625315dadb0b00d7bb0704e13b186155fbf496e8","src/lib.rs":"0a3679ab0bc67817097701010881e1c2f48ad1ab0700f12babc46cc59c5c788b","src/log.rs":"b69e492af85e65866cb6588138e8a337dd897d3ce399cb4e9fb8cc04ac042b7f","src/qlog.rs":"ca323c91d61810ebef2ebeb967836dda384a60a9fb492c2b8d1b235a98f2e4bf","src/timer.rs":"e63af7e7df968bf702583f263cfb63e6dca4e599bacffa2de0a6383d85333636","tests/log.rs":"480b165b7907ec642c508b303d63005eee1427115d6973a349eaf6b2242ed18d"},"package":null}
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "neqo-common"
|
||||
version = "0.5.3"
|
||||
version = "0.5.5"
|
||||
authors = ["Bobby Holley <bobbyholley@gmail.com>"]
|
||||
edition = "2018"
|
||||
license = "MIT/Apache-2.0"
|
||||
|
|
|
@ -98,3 +98,9 @@ impl ::std::fmt::Display for Role {
|
|||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum MessageType {
|
||||
Request,
|
||||
Response,
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"366920694e669ddce1b78bcc73f64d9081f988f635a250ef6d24eb60bf91721f","TODO":"ac0f1c2ebcca03f5b3c0cc56c5aedbb030a4b511e438bc07a57361c789f91e9f","bindings/bindings.toml":"d136f82a333b0ee1499e7858fdfc3d630f7ff37501a3c51028a4eeb7e2f136b4","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":"4bc26650fa85150c1a377f030e681576f92005a14f14fd8818e7ceb95c2c2a1a","src/aead.rs":"140f77ffb5016836c970c39c6c3a42db9581a14b797b9cd05386d0dd0831fe63","src/aead_fuzzing.rs":"4e60d5a2ee6dedfd08602fa36318239e731244825df2cb801ca1d88f5f2a41c1","src/agent.rs":"f83ec6be81a39f52383bec7a1fec2069055088788a989ecfd7fd155f0d5cb788","src/agentio.rs":"85a1f5295e98fcec0e884fb11e21cbc4c64de0f2ad5b6d78464ed9ac064fb495","src/auth.rs":"e821dac1511691151a6e64b7c7130a07d941dffad4529b2631f20ddd07d3f20c","src/cert.rs":"94450b248eed218b9227861ed81e557a543c0c88868fe1a434dc9c9f0f9651ae","src/constants.rs":"998e77bee88197a240032c1bfbddcff417a25ba82e576a0d2fe18ee9b63cefc7","src/ech.rs":"1b6ee298855d34310a0d65367b21fdc38678a9c37fc7e1d9579c3c8dfd753377","src/err.rs":"d4dbe63e2faba3a1f08dca015549c32550cb18907592abc3831e05e330f0a93b","src/exp.rs":"61586662407359c1ecb8ed4987bc3c702f26ba2e203a091a51b6d6363cbd510f","src/ext.rs":"361277879194dc32f741b8d1894afe5fd3fcc8eb244f7dd5914eeb959b85717d","src/hkdf.rs":"3ff432cc9d40e1dc56e9f983b54b593647c4063a5ae0f16de0a64d033ac9bd94","src/hp.rs":"46a2023c421d89fda8d09b356b648272857fd20ee5cf5829143ac88402b32e4b","src/lib.rs":"4a4aacab783b2e21d9f2f33891bc75c5a8018894c3cdf828848ccb59bf5b2d41","src/once.rs":"b9850384899a1a016e839743d3489c0d4d916e1973746ef8c89872105d7d9736","src/p11.rs":"ae054861719fdead8227220dd5a28b92882756683a436676470b672ee26b9a4e","src/prio.rs":"4224a65f25d7de9bf7d6cb18b15597a39650b3c4fcf7d184a4e4bd7f65cebccd","src/replay.rs":"c9bc0261fe1ae22e7212774c315a2669784e57762ca975a15250d4a33dbf3ea3","src/result.rs":"cef34dfcb907723e195b56501132e4560e250b327783cb5e41201da5b63e9b5c","src/secrets.rs":"48790a330994d892742048000bd12460b7eee2c3daaa444481b8527406d0a4c7","src/selfencrypt.rs":"4a9af42ccefbc77c65baedf00ef389de4fa7ed855d7ab3b60542b5931050667d","src/ssl.rs":"32e934e6dc5df4e4b4cbe96bae53921cf09a684959cb5ad3469cd65965f3164c","src/time.rs":"ddecb9f6cb6b3367852943d27fc89fd36d3c0ca0c9b5c9797494b74de2d8b5c7","tests/aead.rs":"a0fe826aa3bfcce22dbe1b06b74823cb2334331ffe6ce6152952613e9e1ccae5","tests/agent.rs":"edda258896324f0a950427fd625594bd31d772fe968a29097d6dbd76523c39c4","tests/ext.rs":"eba9f03accdd598e38292ac88263a81b367d60d5a736a43117a3663de105ec48","tests/handshake.rs":"6ea3e5b3bc889d201b55f959b658a848c0ada54c956bda087b2ac8897a24a786","tests/hkdf.rs":"47830c1ea58a02d100522bdde6fabc02bb447ccb85affa0cdc44bc25da1be32a","tests/hp.rs":"92e062538c01fa7a474225714ed238d846ceb8c8feb9d79eb05be6111b00fb1e","tests/init.rs":"fc9e392b1efa0d8efb28952f73ffc05e5348e7b2b69207b60e375c3888a252a2","tests/selfencrypt.rs":"1125c858ec4e0a6994f34d162aa066cb003c61b324f268529ea04bcb641347cb"},"package":null}
|
||||
{"files":{"Cargo.toml":"bc1c5e7a9a21903b5eb76c81420b9700828e0798544f2727e0d3a5be407fc06f","TODO":"ac0f1c2ebcca03f5b3c0cc56c5aedbb030a4b511e438bc07a57361c789f91e9f","bindings/bindings.toml":"d136f82a333b0ee1499e7858fdfc3d630f7ff37501a3c51028a4eeb7e2f136b4","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":"4bc26650fa85150c1a377f030e681576f92005a14f14fd8818e7ceb95c2c2a1a","src/aead.rs":"140f77ffb5016836c970c39c6c3a42db9581a14b797b9cd05386d0dd0831fe63","src/aead_fuzzing.rs":"4e60d5a2ee6dedfd08602fa36318239e731244825df2cb801ca1d88f5f2a41c1","src/agent.rs":"db620f07e2c0f441ce0a30a249a18ab699affb43d8209c63c4f1e5eae9f9f688","src/agentio.rs":"85a1f5295e98fcec0e884fb11e21cbc4c64de0f2ad5b6d78464ed9ac064fb495","src/auth.rs":"e821dac1511691151a6e64b7c7130a07d941dffad4529b2631f20ddd07d3f20c","src/cert.rs":"04d7328ab59a5268f2f48b3f880192bf28d42c09c362ef5906ee66e087c754d1","src/constants.rs":"998e77bee88197a240032c1bfbddcff417a25ba82e576a0d2fe18ee9b63cefc7","src/ech.rs":"1b6ee298855d34310a0d65367b21fdc38678a9c37fc7e1d9579c3c8dfd753377","src/err.rs":"d4dbe63e2faba3a1f08dca015549c32550cb18907592abc3831e05e330f0a93b","src/exp.rs":"61586662407359c1ecb8ed4987bc3c702f26ba2e203a091a51b6d6363cbd510f","src/ext.rs":"361277879194dc32f741b8d1894afe5fd3fcc8eb244f7dd5914eeb959b85717d","src/hkdf.rs":"3ff432cc9d40e1dc56e9f983b54b593647c4063a5ae0f16de0a64d033ac9bd94","src/hp.rs":"46a2023c421d89fda8d09b356b648272857fd20ee5cf5829143ac88402b32e4b","src/lib.rs":"4a4aacab783b2e21d9f2f33891bc75c5a8018894c3cdf828848ccb59bf5b2d41","src/once.rs":"b9850384899a1a016e839743d3489c0d4d916e1973746ef8c89872105d7d9736","src/p11.rs":"ae054861719fdead8227220dd5a28b92882756683a436676470b672ee26b9a4e","src/prio.rs":"4224a65f25d7de9bf7d6cb18b15597a39650b3c4fcf7d184a4e4bd7f65cebccd","src/replay.rs":"c9bc0261fe1ae22e7212774c315a2669784e57762ca975a15250d4a33dbf3ea3","src/result.rs":"cef34dfcb907723e195b56501132e4560e250b327783cb5e41201da5b63e9b5c","src/secrets.rs":"48790a330994d892742048000bd12460b7eee2c3daaa444481b8527406d0a4c7","src/selfencrypt.rs":"4a9af42ccefbc77c65baedf00ef389de4fa7ed855d7ab3b60542b5931050667d","src/ssl.rs":"32e934e6dc5df4e4b4cbe96bae53921cf09a684959cb5ad3469cd65965f3164c","src/time.rs":"ddecb9f6cb6b3367852943d27fc89fd36d3c0ca0c9b5c9797494b74de2d8b5c7","tests/aead.rs":"a0fe826aa3bfcce22dbe1b06b74823cb2334331ffe6ce6152952613e9e1ccae5","tests/agent.rs":"edda258896324f0a950427fd625594bd31d772fe968a29097d6dbd76523c39c4","tests/ext.rs":"eba9f03accdd598e38292ac88263a81b367d60d5a736a43117a3663de105ec48","tests/handshake.rs":"6ea3e5b3bc889d201b55f959b658a848c0ada54c956bda087b2ac8897a24a786","tests/hkdf.rs":"47830c1ea58a02d100522bdde6fabc02bb447ccb85affa0cdc44bc25da1be32a","tests/hp.rs":"92e062538c01fa7a474225714ed238d846ceb8c8feb9d79eb05be6111b00fb1e","tests/init.rs":"fc9e392b1efa0d8efb28952f73ffc05e5348e7b2b69207b60e375c3888a252a2","tests/selfencrypt.rs":"1125c858ec4e0a6994f34d162aa066cb003c61b324f268529ea04bcb641347cb"},"package":null}
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "neqo-crypto"
|
||||
version = "0.5.3"
|
||||
version = "0.5.5"
|
||||
authors = ["Martin Thomson <mt@lowentropy.net>"]
|
||||
edition = "2018"
|
||||
build = "build.rs"
|
||||
|
|
|
@ -710,6 +710,7 @@ impl SecretAgent {
|
|||
Ok(*Pin::into_inner(records))
|
||||
}
|
||||
|
||||
#[allow(unknown_lints, clippy::branches_sharing_code)]
|
||||
pub fn close(&mut self) {
|
||||
// It should be safe to close multiple times.
|
||||
if self.fd.is_null() {
|
||||
|
|
|
@ -65,10 +65,14 @@ fn signed_cert_timestamp(fd: *mut PRFileDesc) -> Option<Vec<u8>> {
|
|||
let sct_nss = unsafe { SSL_PeerSignedCertTimestamps(fd) };
|
||||
match NonNull::new(sct_nss as *mut SECItem) {
|
||||
Some(sct_ptr) => {
|
||||
let sct_slice = unsafe {
|
||||
slice::from_raw_parts(sct_ptr.as_ref().data, sct_ptr.as_ref().len as usize)
|
||||
};
|
||||
Some(sct_slice.to_owned())
|
||||
if unsafe { sct_ptr.as_ref().len == 0 || sct_ptr.as_ref().data.is_null() } {
|
||||
Some(Vec::new())
|
||||
} else {
|
||||
let sct_slice = unsafe {
|
||||
slice::from_raw_parts(sct_ptr.as_ref().data, sct_ptr.as_ref().len as usize)
|
||||
};
|
||||
Some(sct_slice.to_owned())
|
||||
}
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"af86a7fb67c995de07b728ec49076a265dd5d49ad34d475b1e965679d869e28c","src/client_events.rs":"2ea0ff9757e6772a18e8f85c007ba443baf83ffe8ce890f5325b1528df2d7e37","src/connection.rs":"1f32ef2e87a190c1dd040d7050673d6f1c9a82613864e946a2cbd38bd65279f8","src/connection_client.rs":"d2f8444e198b9c22cea7cc17bf57068d882ff9a16ab5bfd35f5a8af89dff306d","src/connection_server.rs":"5038c9d3a6d65fdde8a1550e9e7bd0ed465a4896e04dfa2857fa26a9b760f215","src/control_stream_local.rs":"cfb97fb2ee55edb7d3b6fde2c68f2386d32d797b1c43405af226bad740ca4fc1","src/control_stream_remote.rs":"e48df6d739971450b6aac1f7a0725f61828dc0d13c1ad5c31ac7ff331d1afedd","src/hframe.rs":"60c93883b345aec9403b41a6a651b22579d61eff9cb4e650ed92237e2d75b5b3","src/lib.rs":"297aea64fed5561e89a2e27f6f2cd5b495a157ae2385dffc22f91d9a1a09c52b","src/priority.rs":"9384efb1353c05408aa40f34eaa99ee31e995eab04011586f1748b34b370a215","src/push_controller.rs":"35c5688e053157830c55b9133f6190349826143590f13a6b55768a79320313a7","src/push_stream.rs":"281b25b7044ba876700ca713b873ff83aac688e952c21881a715390452f64005","src/qlog.rs":"7444919d5955557ade447f9457a536d9294f5bbb8a09f8fac8bbaf24c8bbad8b","src/qpack_decoder_receiver.rs":"c8e1b84b52c097a798d6fb03c8321f676f31d68bc4000041b5dcb5797f8d66ac","src/qpack_encoder_receiver.rs":"11f6bc3e9170e952d92b583659c48764c375fa66cfa97fb115298a6021e84cfb","src/recv_message.rs":"ed02ebde63a1b0fb6f5522f1be50dfa417decda1d1320149c1b437dfbeb5a06f","src/request_target.rs":"9182b641f7a7b55272e0e2e872d24a35b1207f35399221b08b899857c3e873ab","src/send_message.rs":"f3fc46e03236ddce83841734ef97f2c4b5f25b5939186ff3e8ca40d6b467e341","src/server.rs":"6b9d0254df18cc04699899f8ff5a096fe153588a56fbcc214fc5ce9eb8f6b9eb","src/server_connection_events.rs":"c53853d36f872ef624fbfcc67e4a54d364159a2c80ca273f4f87ffdf38ec04ad","src/server_events.rs":"31585865c111eb8ce031b1664cc32e221aee312dc70b96a93a32924a142e0a0e","src/settings.rs":"467bd6991ec30e483195855f997f0bea3671a15b1a5b82e271f62ae424e0b2c3","src/stream_type_reader.rs":"94014849b86fa5700d661819370f4c010ba5235a58343c2895753218c5cab999","tests/httpconn.rs":"e8e3d090aa9e28040b0f8cde39d1f3e4b7aaa56db3b741fc75fe416450047787","tests/priority.rs":"b14a7b7ee25e7b5085756f7eeac592d5641482546ffd3ca04445ffdcd79d3378"},"package":null}
|
||||
{"files":{"Cargo.toml":"f37b88a9e0ab0de81c27f5e3013b733beed9f5a2af83146275e2122a55a3de31","src/buffered_send_stream.rs":"0e4ad4914451943e49c88565661d88475ab2bbd8756e200e2a19312bafd64847","src/client_events.rs":"3dab2de2ec5fac43ff30470e0e8167fdbfede4bc467c0fcd027d809692b7465b","src/conn_params.rs":"00e3f42636f482f91cd6b86d7bebaa85a9f0407143503767fffb66a3cdfbbe43","src/connection.rs":"8d9abfc187866310244758238430efbe4c68c3db12e6edd9b746963143744766","src/connection_client.rs":"30c6d5fa8f982237a37d19097c94a664a65174fb567210824ee6154cd3fad577","src/connection_server.rs":"1e9bca9a0a8ac4532015032b1444b23f67007b8c6b3f138900f0b142cf5625d1","src/control_stream_local.rs":"49bc3b8d2da81477fa5914d80e32657e215e84ba2c40411eb18ae170ccddecd0","src/control_stream_remote.rs":"b6c4b96e0b35d54a5fee9297945c2dc6458729af2566323b13e44a621b214d72","src/features/extended_connect/mod.rs":"95e8468e62e2e7680989b6d85397775d3a99ce68390dc68708c3fb3450932e03","src/features/extended_connect/session.rs":"5f1035082891554194c89d80338ccd0991db0085bdf8be358a2c3a7ac2b52dbc","src/features/extended_connect/webtransport.rs":"721497ea16aaf55a3be4b685afe23fdbcb2c19603042898f12d93ea4e5d8aac7","src/features/mod.rs":"a981ebbd03e7bb7ea2313e883452e44f052c48f28edb7fd53a0825911b490230","src/headers_checks.rs":"4d8ce4ba54a989ecc257a26d47111ab4583cf78a3ae6c60ad106f3ad3e77ac8f","src/hframe.rs":"79311512aafe7ac5cbda068352bbce573ee99205baa06fc75db95cc3dbbf0780","src/lib.rs":"ed19bb708b711d0b69430a632d5524e93a115239d63de094ae78611e0c2af026","src/priority.rs":"89d25666fb3b35e16c58055e51d25ff746fc420820db9f30fdecfd1ed70ac020","src/push_controller.rs":"7db0c4c1e065372d3adba90662ff20c4e36adade25f64d2168616339cc5bf47d","src/qlog.rs":"44b6cdbb1d9d6ca47b793e9dbe531b8fdbd40147375f7e4c89aeab536c5d286b","src/qpack_decoder_receiver.rs":"75008d8ea5d538ee34aca4df72e58489417604ccafb61b064280782d6754dd0d","src/qpack_encoder_receiver.rs":"f95cc7d49e4d442b93d522f14ddfc581629664d84d6e13d03a520e855bbe442d","src/recv_message.rs":"3d1556087aecae432589ce55459430a19c1d35c4c819ff4b2e2869a12e6a7b3d","src/request_target.rs":"9182b641f7a7b55272e0e2e872d24a35b1207f35399221b08b899857c3e873ab","src/send_message.rs":"7a676b0f6f34f7c52e98f346d7009345fec84e7db03d11dbe2a50ae95d433369","src/server.rs":"0b3775863339f8ea9d363b83f2c667bda4b62de177bb2e93caef3830ba7c46a8","src/server_connection_events.rs":"5f3eadceb0c555ab13f68ec49af0eaa7a1ebf1fdd14e1132d77583ecd76cabbb","src/server_events.rs":"42c79731c5168df35ebd3cef448d9e38e64770b31363b2b4965679582db9f61e","src/settings.rs":"8f7e3ddd807c3d5667dad93f5b902b0e00a3bcf85e41404e1e0e5dfd2c3d5dd6","src/stream_type_reader.rs":"62fb15a5b64b9777ddfcb16c3d43031797336dca39dd70340776022431b79d77","tests/httpconn.rs":"33538bb3597e23787880f85df1a13e2942549cffdbbdbd8a8822a64f716c7620","tests/mod.rs":"fd6aee37243713e80fc526552f21f0222338cec9890409b6575a2a637b17ec1f","tests/priority.rs":"e3258878d4b692c5d80612c1d275972a99808168ccc33fdd966822a0c8776bb9","tests/send_message.rs":"673ae1d0bf2dce46c21ee8353f45f189d2cb64a2f6e137ae38da6b2262ad066e","tests/webtransport/mod.rs":"ef8f83789925502b5121fa6285bfcf6c09e2299a9360bf0ed2c8329ea0c25fc4","tests/webtransport/negotiation.rs":"866cec03df0643fa0f28a160a97112a4ab3c1ca337d8e00e81eb7eb42f67d41b","tests/webtransport/sessions.rs":"af6a336cac13bc4be1be329e9f18d48dd23c99975d20f719e3e59be6214aa9f4","tests/webtransport/streams.rs":"560b86ea59c92159b16099c26b6f042b5e8c0fc3f21983a00da677987670c4f9"},"package":null}
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "neqo-http3"
|
||||
version = "0.5.3"
|
||||
version = "0.5.5"
|
||||
authors = ["Dragana Damjanovic <dragana.damjano@gmail.com>"]
|
||||
edition = "2018"
|
||||
license = "MIT/Apache-2.0"
|
||||
|
@ -15,6 +15,7 @@ smallvec = "1.0.0"
|
|||
qlog = "0.4.0"
|
||||
sfv = "0.9.1"
|
||||
url = "2.0"
|
||||
lazy_static = "1.3.0"
|
||||
|
||||
[dev-dependencies]
|
||||
test-fixture = { path = "../test-fixture" }
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
// 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 crate::Res;
|
||||
use neqo_common::qtrace;
|
||||
use neqo_transport::{Connection, StreamId};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum BufferedStream {
|
||||
Uninitialized,
|
||||
Initialized { stream_id: StreamId, buf: Vec<u8> },
|
||||
}
|
||||
|
||||
impl Default for BufferedStream {
|
||||
fn default() -> Self {
|
||||
Self::Uninitialized
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for BufferedStream {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
write!(f, "BufferedStream {:?}", Option::<StreamId>::from(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl BufferedStream {
|
||||
#[must_use]
|
||||
pub fn new(stream_id: StreamId) -> Self {
|
||||
Self::Initialized {
|
||||
stream_id,
|
||||
buf: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// # Panics
|
||||
/// If the `BufferedStream` is initialized more than one it will panic.
|
||||
pub fn init(&mut self, stream_id: StreamId) {
|
||||
debug_assert!(&Self::Uninitialized == self);
|
||||
*self = Self::Initialized {
|
||||
stream_id,
|
||||
buf: Vec::new(),
|
||||
};
|
||||
}
|
||||
|
||||
/// # Panics
|
||||
/// This functon cannot be called before the `BufferedStream` is initialized.
|
||||
pub fn buffer(&mut self, to_buf: &[u8]) {
|
||||
if let Self::Initialized { buf, .. } = self {
|
||||
buf.extend_from_slice(to_buf);
|
||||
} else {
|
||||
debug_assert!(false, "Do not buffer date before the stream is initialized");
|
||||
}
|
||||
}
|
||||
|
||||
/// # Errors
|
||||
/// Returns `neqo_transport` errors.
|
||||
pub fn send_buffer(&mut self, conn: &mut Connection) -> Res<usize> {
|
||||
let label = ::neqo_common::log_subject!(::log::Level::Debug, self);
|
||||
let mut sent = 0;
|
||||
if let Self::Initialized { stream_id, buf } = self {
|
||||
if !buf.is_empty() {
|
||||
qtrace!([label], "sending data.");
|
||||
sent = conn.stream_send(*stream_id, &buf[..])?;
|
||||
if sent == buf.len() {
|
||||
buf.clear();
|
||||
} else {
|
||||
let b = buf.split_off(sent);
|
||||
*buf = b;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(sent)
|
||||
}
|
||||
|
||||
/// # Errors
|
||||
/// Returns `neqo_transport` errors.
|
||||
pub fn send_atomic(&mut self, conn: &mut Connection, to_send: &[u8]) -> Res<bool> {
|
||||
// First try to send anything that is in the buffer.
|
||||
self.send_buffer(conn)?;
|
||||
if let Self::Initialized { stream_id, buf } = self {
|
||||
if buf.is_empty() {
|
||||
let res = conn.stream_send_atomic(*stream_id, to_send)?;
|
||||
Ok(res)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn has_buffered_data(&self) -> bool {
|
||||
if let Self::Initialized { buf, .. } = self {
|
||||
!buf.is_empty()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&BufferedStream> for Option<StreamId> {
|
||||
fn from(stream: &BufferedStream) -> Option<StreamId> {
|
||||
if let BufferedStream::Initialized { stream_id, .. } = stream {
|
||||
Some(*stream_id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,43 +7,61 @@
|
|||
#![allow(clippy::module_name_repetitions)]
|
||||
|
||||
use crate::connection::Http3State;
|
||||
use crate::send_message::SendMessageEvents;
|
||||
use crate::Header;
|
||||
use crate::RecvMessageEvents;
|
||||
|
||||
use neqo_common::event::Provider as EventProvider;
|
||||
use crate::settings::HSettingType;
|
||||
use crate::{
|
||||
features::extended_connect::{ExtendedConnectEvents, ExtendedConnectType},
|
||||
CloseType, Http3StreamInfo, HttpRecvStreamEvents, RecvStreamEvents, SendStreamEvents,
|
||||
};
|
||||
use neqo_common::{event::Provider as EventProvider, Header};
|
||||
use neqo_crypto::ResumptionToken;
|
||||
use neqo_transport::{AppError, StreamType};
|
||||
use neqo_transport::{AppError, StreamId, StreamType};
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::collections::VecDeque;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub enum WebTransportEvent {
|
||||
Negotiated(bool),
|
||||
Session(StreamId),
|
||||
SessionClosed {
|
||||
stream_id: StreamId,
|
||||
error: Option<AppError>,
|
||||
},
|
||||
NewStream {
|
||||
stream_id: StreamId,
|
||||
session_id: StreamId,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub enum Http3ClientEvent {
|
||||
/// Response headers are received.
|
||||
HeaderReady {
|
||||
stream_id: u64,
|
||||
stream_id: StreamId,
|
||||
headers: Vec<Header>,
|
||||
interim: bool,
|
||||
fin: bool,
|
||||
},
|
||||
/// A stream can accept new data.
|
||||
DataWritable { stream_id: u64 },
|
||||
DataWritable { stream_id: StreamId },
|
||||
/// New bytes available for reading.
|
||||
DataReadable { stream_id: u64 },
|
||||
DataReadable { stream_id: StreamId },
|
||||
/// Peer reset the stream or there was an parsing error.
|
||||
Reset {
|
||||
stream_id: u64,
|
||||
stream_id: StreamId,
|
||||
error: AppError,
|
||||
local: bool,
|
||||
},
|
||||
/// Peer has sent a STOP_SENDING.
|
||||
StopSending { stream_id: u64, error: AppError },
|
||||
StopSending {
|
||||
stream_id: StreamId,
|
||||
error: AppError,
|
||||
},
|
||||
/// A new push promise.
|
||||
PushPromise {
|
||||
push_id: u64,
|
||||
request_stream_id: u64,
|
||||
request_stream_id: StreamId,
|
||||
headers: Vec<Header>,
|
||||
},
|
||||
/// A push response headers are ready.
|
||||
|
@ -76,6 +94,8 @@ pub enum Http3ClientEvent {
|
|||
GoawayReceived,
|
||||
/// Connection state change.
|
||||
StateChange(Http3State),
|
||||
/// WebTransport events
|
||||
WebTransport(WebTransportEvent),
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
|
@ -83,31 +103,33 @@ pub struct Http3ClientEvents {
|
|||
events: Rc<RefCell<VecDeque<Http3ClientEvent>>>,
|
||||
}
|
||||
|
||||
impl RecvMessageEvents for Http3ClientEvents {
|
||||
/// Add a new `HeaderReady` event.
|
||||
fn header_ready(&self, stream_id: u64, headers: Vec<Header>, interim: bool, fin: bool) {
|
||||
self.insert(Http3ClientEvent::HeaderReady {
|
||||
stream_id,
|
||||
headers,
|
||||
interim,
|
||||
fin,
|
||||
});
|
||||
}
|
||||
|
||||
impl RecvStreamEvents for Http3ClientEvents {
|
||||
/// Add a new `DataReadable` event
|
||||
fn data_readable(&self, stream_id: u64) {
|
||||
self.insert(Http3ClientEvent::DataReadable { stream_id });
|
||||
fn data_readable(&self, stream_info: Http3StreamInfo) {
|
||||
self.insert(Http3ClientEvent::DataReadable {
|
||||
stream_id: stream_info.stream_id(),
|
||||
});
|
||||
}
|
||||
|
||||
/// Add a new `Reset` event.
|
||||
fn reset(&self, stream_id: u64, error: AppError, local: bool) {
|
||||
self.remove(|evt| {
|
||||
matches!(evt,
|
||||
Http3ClientEvent::HeaderReady { stream_id: x, .. }
|
||||
| Http3ClientEvent::DataReadable { stream_id: x }
|
||||
| Http3ClientEvent::PushPromise { request_stream_id: x, .. }
|
||||
| Http3ClientEvent::Reset { stream_id: x, .. } if *x == stream_id)
|
||||
});
|
||||
fn recv_closed(&self, stream_info: Http3StreamInfo, close_type: CloseType) {
|
||||
let stream_id = stream_info.stream_id();
|
||||
let (local, error) = match close_type {
|
||||
CloseType::ResetApp(_) => {
|
||||
self.remove_recv_stream_events(stream_id);
|
||||
return;
|
||||
}
|
||||
CloseType::Done => return,
|
||||
CloseType::ResetRemote(e) => {
|
||||
self.remove_recv_stream_events(stream_id);
|
||||
(false, e)
|
||||
}
|
||||
CloseType::LocalError(e) => {
|
||||
self.remove_recv_stream_events(stream_id);
|
||||
(true, e)
|
||||
}
|
||||
};
|
||||
|
||||
self.insert(Http3ClientEvent::Reset {
|
||||
stream_id,
|
||||
error,
|
||||
|
@ -116,30 +138,79 @@ impl RecvMessageEvents for Http3ClientEvents {
|
|||
}
|
||||
}
|
||||
|
||||
impl SendMessageEvents for Http3ClientEvents {
|
||||
/// Add a new `DataWritable` event.
|
||||
fn data_writable(&self, stream_id: u64) {
|
||||
self.insert(Http3ClientEvent::DataWritable { stream_id });
|
||||
impl HttpRecvStreamEvents for Http3ClientEvents {
|
||||
/// Add a new `HeaderReady` event.
|
||||
fn header_ready(
|
||||
&self,
|
||||
stream_info: Http3StreamInfo,
|
||||
headers: Vec<Header>,
|
||||
interim: bool,
|
||||
fin: bool,
|
||||
) {
|
||||
self.insert(Http3ClientEvent::HeaderReady {
|
||||
stream_id: stream_info.stream_id(),
|
||||
headers,
|
||||
interim,
|
||||
fin,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_send_side_event(&self, stream_id: u64) {
|
||||
self.remove(|evt| {
|
||||
matches!(evt,
|
||||
Http3ClientEvent::DataWritable { stream_id: x }
|
||||
| Http3ClientEvent::StopSending { stream_id: x, .. } if *x == stream_id)
|
||||
impl SendStreamEvents for Http3ClientEvents {
|
||||
/// Add a new `DataWritable` event.
|
||||
fn data_writable(&self, stream_info: Http3StreamInfo) {
|
||||
self.insert(Http3ClientEvent::DataWritable {
|
||||
stream_id: stream_info.stream_id(),
|
||||
});
|
||||
}
|
||||
|
||||
/// Add a new `StopSending` event
|
||||
fn stop_sending(&self, stream_id: u64, error: AppError) {
|
||||
// The stream has received a STOP_SENDING frame, we should remove any DataWritable event.
|
||||
self.remove_send_side_event(stream_id);
|
||||
self.insert(Http3ClientEvent::StopSending { stream_id, error });
|
||||
fn send_closed(&self, stream_info: Http3StreamInfo, close_type: CloseType) {
|
||||
let stream_id = stream_info.stream_id();
|
||||
self.remove_send_stream_events(stream_id);
|
||||
if let CloseType::ResetRemote(error) = close_type {
|
||||
self.insert(Http3ClientEvent::StopSending { stream_id, error });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ExtendedConnectEvents for Http3ClientEvents {
|
||||
fn session_start(&self, connect_type: ExtendedConnectType, stream_id: StreamId) {
|
||||
if connect_type == ExtendedConnectType::WebTransport {
|
||||
self.insert(Http3ClientEvent::WebTransport(WebTransportEvent::Session(
|
||||
stream_id,
|
||||
)));
|
||||
} else {
|
||||
unreachable!("There is only ExtendedConnectType::WebTransport.");
|
||||
}
|
||||
}
|
||||
|
||||
fn session_end(
|
||||
&self,
|
||||
connect_type: ExtendedConnectType,
|
||||
stream_id: StreamId,
|
||||
error: Option<AppError>,
|
||||
) {
|
||||
if connect_type == ExtendedConnectType::WebTransport {
|
||||
self.insert(Http3ClientEvent::WebTransport(
|
||||
WebTransportEvent::SessionClosed { stream_id, error },
|
||||
));
|
||||
} else {
|
||||
unreachable!("There are no other types.");
|
||||
}
|
||||
}
|
||||
|
||||
fn extended_connect_new_stream(&self, stream_info: Http3StreamInfo) {
|
||||
self.insert(Http3ClientEvent::WebTransport(
|
||||
WebTransportEvent::NewStream {
|
||||
stream_id: stream_info.stream_id(),
|
||||
session_id: stream_info.session_id().unwrap(),
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
impl Http3ClientEvents {
|
||||
pub fn push_promise(&self, push_id: u64, request_stream_id: u64, headers: Vec<Header>) {
|
||||
pub fn push_promise(&self, push_id: u64, request_stream_id: StreamId, headers: Vec<Header>) {
|
||||
self.insert(Http3ClientEvent::PushPromise {
|
||||
push_id,
|
||||
request_stream_id,
|
||||
|
@ -217,14 +288,20 @@ impl Http3ClientEvents {
|
|||
}
|
||||
|
||||
/// Remove all events for a stream
|
||||
pub(crate) fn remove_events_for_stream_id(&self, stream_id: u64) {
|
||||
fn remove_recv_stream_events(&self, stream_id: StreamId) {
|
||||
self.remove(|evt| {
|
||||
matches!(evt,
|
||||
Http3ClientEvent::HeaderReady { stream_id: x, .. }
|
||||
| Http3ClientEvent::DataWritable { stream_id: x }
|
||||
| Http3ClientEvent::DataReadable { stream_id: x }
|
||||
| Http3ClientEvent::PushPromise { request_stream_id: x, .. }
|
||||
| Http3ClientEvent::Reset { stream_id: x, .. }
|
||||
| Http3ClientEvent::Reset { stream_id: x, .. } if *x == stream_id)
|
||||
});
|
||||
}
|
||||
|
||||
fn remove_send_stream_events(&self, stream_id: StreamId) {
|
||||
self.remove(|evt| {
|
||||
matches!(evt,
|
||||
Http3ClientEvent::DataWritable { stream_id: x }
|
||||
| Http3ClientEvent::StopSending { stream_id: x, .. } if *x == stream_id)
|
||||
});
|
||||
}
|
||||
|
@ -247,6 +324,14 @@ impl Http3ClientEvents {
|
|||
| Http3ClientEvent::PushCanceled{ push_id: x, .. } if *x == push_id)
|
||||
});
|
||||
}
|
||||
|
||||
pub fn negotiation_done(&self, feature_type: HSettingType, negotiated: bool) {
|
||||
if feature_type == HSettingType::EnableWebTransport {
|
||||
self.insert(Http3ClientEvent::WebTransport(
|
||||
WebTransportEvent::Negotiated(negotiated),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EventProvider for Http3ClientEvents {
|
||||
|
|
|
@ -0,0 +1,119 @@
|
|||
// 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 neqo_qpack::QpackSettings;
|
||||
use neqo_transport::ConnectionParameters;
|
||||
use std::cmp::min;
|
||||
|
||||
const QPACK_MAX_TABLE_SIZE_DEFAULT: u64 = 65536;
|
||||
const QPACK_TABLE_SIZE_LIMIT: u64 = (1 << 30) - 1;
|
||||
const QPACK_MAX_BLOCKED_STREAMS_DEFAULT: u16 = 20;
|
||||
const MAX_PUSH_STREAM_DEFAULT: u64 = 0;
|
||||
const WEBTRANSPORT_DEFAULT: bool = false;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Http3Parameters {
|
||||
conn_params: ConnectionParameters,
|
||||
qpack_settings: QpackSettings,
|
||||
max_concurrent_push_streams: u64,
|
||||
webtransport: bool,
|
||||
}
|
||||
|
||||
impl Default for Http3Parameters {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
conn_params: ConnectionParameters::default(),
|
||||
qpack_settings: QpackSettings {
|
||||
max_table_size_encoder: QPACK_MAX_TABLE_SIZE_DEFAULT,
|
||||
max_table_size_decoder: QPACK_MAX_TABLE_SIZE_DEFAULT,
|
||||
max_blocked_streams: QPACK_MAX_BLOCKED_STREAMS_DEFAULT,
|
||||
},
|
||||
max_concurrent_push_streams: MAX_PUSH_STREAM_DEFAULT,
|
||||
webtransport: WEBTRANSPORT_DEFAULT,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Http3Parameters {
|
||||
#[must_use]
|
||||
pub fn get_connection_parameters(&self) -> &ConnectionParameters {
|
||||
&self.conn_params
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn connection_parameters(mut self, conn_params: ConnectionParameters) -> Self {
|
||||
self.conn_params = conn_params;
|
||||
self
|
||||
}
|
||||
|
||||
/// # Panics
|
||||
/// The table size must be smaller than 1 << 30 by the spec.
|
||||
#[must_use]
|
||||
pub fn max_table_size_encoder(mut self, mut max_table: u64) -> Self {
|
||||
assert!(max_table <= QPACK_TABLE_SIZE_LIMIT);
|
||||
max_table = min(max_table, QPACK_TABLE_SIZE_LIMIT);
|
||||
self.qpack_settings.max_table_size_encoder = max_table;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn get_max_table_size_encoder(&self) -> u64 {
|
||||
self.qpack_settings.max_table_size_encoder
|
||||
}
|
||||
|
||||
/// # Panics
|
||||
/// The table size must be smaller than 1 << 30 by the spec.
|
||||
#[must_use]
|
||||
pub fn max_table_size_decoder(mut self, mut max_table: u64) -> Self {
|
||||
assert!(max_table <= QPACK_TABLE_SIZE_LIMIT);
|
||||
max_table = min(max_table, QPACK_TABLE_SIZE_LIMIT);
|
||||
self.qpack_settings.max_table_size_decoder = max_table;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn get_max_table_size_decoder(&self) -> u64 {
|
||||
self.qpack_settings.max_table_size_decoder
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn max_blocked_streams(mut self, max_blocked: u16) -> Self {
|
||||
self.qpack_settings.max_blocked_streams = max_blocked;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn get_max_blocked_streams(&self) -> u16 {
|
||||
self.qpack_settings.max_blocked_streams
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn get_qpack_settings(&self) -> &QpackSettings {
|
||||
&self.qpack_settings
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn max_concurrent_push_streams(mut self, max_push_streams: u64) -> Self {
|
||||
self.max_concurrent_push_streams = max_push_streams;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn get_max_concurrent_push_streams(&self) -> u64 {
|
||||
self.max_concurrent_push_streams
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn webtransport(mut self, webtransport: bool) -> Self {
|
||||
self.webtransport = webtransport;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn get_webtransport(&self) -> bool {
|
||||
self.webtransport
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -6,12 +6,14 @@
|
|||
|
||||
use crate::connection::{Http3Connection, Http3State};
|
||||
use crate::hframe::HFrame;
|
||||
use crate::recv_message::{MessageType, RecvMessage};
|
||||
use crate::recv_message::{RecvMessage, RecvMessageInfo};
|
||||
use crate::send_message::SendMessage;
|
||||
use crate::server_connection_events::{Http3ServerConnEvent, Http3ServerConnEvents};
|
||||
use crate::{Error, Header, Priority, PriorityHandler, ReceiveOutput, Res};
|
||||
use neqo_common::{event::Provider, qdebug, qinfo, qtrace, Role};
|
||||
use neqo_qpack::QpackSettings;
|
||||
use crate::{
|
||||
Error, Http3Parameters, Http3StreamType, NewStreamType, Priority, PriorityHandler,
|
||||
ReceiveOutput, Res,
|
||||
};
|
||||
use neqo_common::{event::Provider, qdebug, qinfo, qtrace, Header, MessageType, Role};
|
||||
use neqo_transport::{AppError, Connection, ConnectionEvent, StreamId, StreamType};
|
||||
use std::rc::Rc;
|
||||
use std::time::Instant;
|
||||
|
@ -30,44 +32,136 @@ impl ::std::fmt::Display for Http3ServerHandler {
|
|||
}
|
||||
|
||||
impl Http3ServerHandler {
|
||||
pub(crate) fn new(qpack_settings: QpackSettings) -> Self {
|
||||
pub(crate) fn new(http3_parameters: Http3Parameters) -> Self {
|
||||
Self {
|
||||
base_handler: Http3Connection::new(qpack_settings),
|
||||
base_handler: Http3Connection::new(http3_parameters, Role::Server),
|
||||
events: Http3ServerConnEvents::default(),
|
||||
needs_processing: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Supply a response for a request.
|
||||
pub(crate) fn set_response(
|
||||
/// # Errors
|
||||
/// `InvalidStreamId` if the stream does not exist,
|
||||
/// `AlreadyClosed` if the stream has already been closed.
|
||||
/// `TransportStreamDoesNotExist` if the transport stream does not exist (this may happen if `process_output`
|
||||
/// has not been called when needed, and HTTP3 layer has not picked up the info that the stream has been closed.)
|
||||
/// `InvalidInput` if an empty buffer has been supplied.
|
||||
pub(crate) fn send_data(
|
||||
&mut self,
|
||||
stream_id: u64,
|
||||
headers: &[Header],
|
||||
stream_id: StreamId,
|
||||
data: &[u8],
|
||||
conn: &mut Connection,
|
||||
) -> Res<usize> {
|
||||
self.base_handler.stream_has_pending_data(stream_id);
|
||||
self.needs_processing = true;
|
||||
self.base_handler
|
||||
.send_streams
|
||||
.get_mut(&stream_id)
|
||||
.ok_or(Error::InvalidStreamId)?
|
||||
.send_data(conn, data)
|
||||
}
|
||||
|
||||
/// Supply response heeaders for a request.
|
||||
pub(crate) fn send_headers(
|
||||
&mut self,
|
||||
stream_id: StreamId,
|
||||
headers: &[Header],
|
||||
conn: &mut Connection,
|
||||
) -> Res<()> {
|
||||
self.base_handler
|
||||
.send_streams
|
||||
.get_mut(&stream_id)
|
||||
.ok_or(Error::InvalidStreamId)?
|
||||
.set_message(headers, Some(data))?;
|
||||
self.base_handler
|
||||
.insert_streams_have_data_to_send(stream_id);
|
||||
.http_stream()
|
||||
.ok_or(Error::InvalidStreamId)?
|
||||
.send_headers(headers, conn)?;
|
||||
self.base_handler.stream_has_pending_data(stream_id);
|
||||
self.needs_processing = true;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Reset a request.
|
||||
pub fn stream_reset(
|
||||
&mut self,
|
||||
conn: &mut Connection,
|
||||
stream_id: u64,
|
||||
app_error: AppError,
|
||||
) -> Res<()> {
|
||||
self.base_handler.stream_reset(conn, stream_id, app_error)?;
|
||||
self.events.remove_events_for_stream_id(stream_id);
|
||||
/// This is called when application is done sending a request.
|
||||
/// # Errors
|
||||
/// An error will be returned if stream does not exist.
|
||||
pub fn stream_close_send(&mut self, stream_id: StreamId, conn: &mut Connection) -> Res<()> {
|
||||
qinfo!([self], "Close sending side stream={}.", stream_id);
|
||||
self.base_handler.stream_close_send(conn, stream_id)?;
|
||||
self.base_handler.stream_has_pending_data(stream_id);
|
||||
self.needs_processing = true;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// An application may reset a stream(request).
|
||||
/// Both sides, sending and receiving side, will be closed.
|
||||
/// # Errors
|
||||
/// An error will be return if a stream does not exist.
|
||||
pub fn cancel_fetch(
|
||||
&mut self,
|
||||
stream_id: StreamId,
|
||||
error: AppError,
|
||||
conn: &mut Connection,
|
||||
) -> Res<()> {
|
||||
qinfo!([self], "cancel_fetch {} error={}.", stream_id, error);
|
||||
self.needs_processing = true;
|
||||
self.base_handler.cancel_fetch(stream_id, error, conn)
|
||||
}
|
||||
|
||||
pub fn stream_stop_sending(
|
||||
&mut self,
|
||||
stream_id: StreamId,
|
||||
error: AppError,
|
||||
conn: &mut Connection,
|
||||
) -> Res<()> {
|
||||
qinfo!([self], "stream_stop_sending {} error={}.", stream_id, error);
|
||||
self.needs_processing = true;
|
||||
self.base_handler
|
||||
.stream_stop_sending(conn, stream_id, error)
|
||||
}
|
||||
|
||||
pub fn stream_reset_send(
|
||||
&mut self,
|
||||
stream_id: StreamId,
|
||||
error: AppError,
|
||||
conn: &mut Connection,
|
||||
) -> Res<()> {
|
||||
qinfo!([self], "stream_reset_send {} error={}.", stream_id, error);
|
||||
self.needs_processing = true;
|
||||
self.base_handler.stream_reset_send(conn, stream_id, error)
|
||||
}
|
||||
|
||||
/// Accept a `WebTransport` Session request
|
||||
pub(crate) fn webtransport_session_accept(
|
||||
&mut self,
|
||||
conn: &mut Connection,
|
||||
stream_id: StreamId,
|
||||
accept: bool,
|
||||
) -> Res<()> {
|
||||
self.needs_processing = true;
|
||||
self.base_handler.webtransport_session_accept(
|
||||
conn,
|
||||
stream_id,
|
||||
Box::new(self.events.clone()),
|
||||
accept,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn webtransport_create_stream(
|
||||
&mut self,
|
||||
conn: &mut Connection,
|
||||
session_id: StreamId,
|
||||
stream_type: StreamType,
|
||||
) -> Res<StreamId> {
|
||||
self.needs_processing = true;
|
||||
self.base_handler.webtransport_create_stream_local(
|
||||
conn,
|
||||
session_id,
|
||||
stream_type,
|
||||
Box::new(self.events.clone()),
|
||||
Box::new(self.events.clone()),
|
||||
)
|
||||
}
|
||||
|
||||
/// Process HTTTP3 layer.
|
||||
pub fn process_http3(&mut self, conn: &mut Connection, now: Instant) {
|
||||
qtrace!([self], "Process http3 internal.");
|
||||
|
@ -122,23 +216,9 @@ impl Http3ServerHandler {
|
|||
while let Some(e) = conn.next_event() {
|
||||
qdebug!([self], "check_connection_events - event {:?}.", e);
|
||||
match e {
|
||||
ConnectionEvent::NewStream { stream_id } => match stream_id.stream_type() {
|
||||
StreamType::BiDi => self.base_handler.add_streams(
|
||||
stream_id.as_u64(),
|
||||
SendMessage::new(stream_id.as_u64(), Box::new(self.events.clone())),
|
||||
Box::new(RecvMessage::new(
|
||||
MessageType::Request,
|
||||
stream_id.as_u64(),
|
||||
Rc::clone(&self.base_handler.qpack_decoder),
|
||||
Box::new(self.events.clone()),
|
||||
None,
|
||||
PriorityHandler::new(false, Priority::default()),
|
||||
)),
|
||||
),
|
||||
StreamType::UniDi => self
|
||||
.base_handler
|
||||
.handle_new_unidi_stream(stream_id.as_u64(), Role::Server),
|
||||
},
|
||||
ConnectionEvent::NewStream { stream_id } => {
|
||||
self.base_handler.add_new_stream(stream_id);
|
||||
}
|
||||
ConnectionEvent::RecvStreamReadable { stream_id } => {
|
||||
self.handle_stream_readable(conn, stream_id)?;
|
||||
}
|
||||
|
@ -147,14 +227,14 @@ impl Http3ServerHandler {
|
|||
app_error,
|
||||
} => {
|
||||
self.base_handler
|
||||
.handle_stream_reset(stream_id, app_error)?;
|
||||
.handle_stream_reset(stream_id, app_error, conn)?;
|
||||
}
|
||||
ConnectionEvent::SendStreamStopSending {
|
||||
stream_id,
|
||||
app_error,
|
||||
} => self
|
||||
.base_handler
|
||||
.handle_stream_stop_sending(stream_id, app_error)?,
|
||||
.handle_stream_stop_sending(stream_id, app_error, conn)?,
|
||||
ConnectionEvent::StateChange(state) => {
|
||||
if self.base_handler.handle_state_change(conn, &state)? {
|
||||
if self.base_handler.state() == Http3State::Connected {
|
||||
|
@ -165,12 +245,16 @@ impl Http3ServerHandler {
|
|||
.connection_state_change(self.base_handler.state());
|
||||
}
|
||||
}
|
||||
ConnectionEvent::SendStreamWritable { stream_id } => {
|
||||
if let Some(s) = self.base_handler.send_streams.get_mut(&stream_id) {
|
||||
s.stream_writable();
|
||||
}
|
||||
}
|
||||
ConnectionEvent::AuthenticationNeeded
|
||||
| ConnectionEvent::EchFallbackAuthenticationNeeded { .. }
|
||||
| ConnectionEvent::ZeroRttRejected
|
||||
| ConnectionEvent::ResumptionToken(..) => return Err(Error::HttpInternal(4)),
|
||||
ConnectionEvent::SendStreamWritable { .. }
|
||||
| ConnectionEvent::SendStreamComplete { .. }
|
||||
ConnectionEvent::SendStreamComplete { .. }
|
||||
| ConnectionEvent::SendStreamCreatable { .. }
|
||||
| ConnectionEvent::Datagram { .. }
|
||||
| ConnectionEvent::OutgoingDatagramOutcome { .. }
|
||||
|
@ -180,9 +264,47 @@ impl Http3ServerHandler {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_stream_readable(&mut self, conn: &mut Connection, stream_id: u64) -> Res<()> {
|
||||
fn handle_stream_readable(&mut self, conn: &mut Connection, stream_id: StreamId) -> Res<()> {
|
||||
match self.base_handler.handle_stream_readable(conn, stream_id)? {
|
||||
ReceiveOutput::PushStream => Err(Error::HttpStreamCreation),
|
||||
ReceiveOutput::NewStream(NewStreamType::Push(_)) => Err(Error::HttpStreamCreation),
|
||||
ReceiveOutput::NewStream(NewStreamType::Http) => {
|
||||
self.base_handler.add_streams(
|
||||
stream_id,
|
||||
Box::new(SendMessage::new(
|
||||
MessageType::Response,
|
||||
Http3StreamType::Http,
|
||||
stream_id,
|
||||
self.base_handler.qpack_encoder.clone(),
|
||||
Box::new(self.events.clone()),
|
||||
)),
|
||||
Box::new(RecvMessage::new(
|
||||
&RecvMessageInfo {
|
||||
message_type: MessageType::Request,
|
||||
stream_type: Http3StreamType::Http,
|
||||
stream_id,
|
||||
header_frame_type_read: true,
|
||||
},
|
||||
Rc::clone(&self.base_handler.qpack_decoder),
|
||||
Box::new(self.events.clone()),
|
||||
None,
|
||||
PriorityHandler::new(false, Priority::default()),
|
||||
)),
|
||||
);
|
||||
let res = self.base_handler.handle_stream_readable(conn, stream_id)?;
|
||||
assert_eq!(ReceiveOutput::NoOutput, res);
|
||||
Ok(())
|
||||
}
|
||||
ReceiveOutput::NewStream(NewStreamType::WebTransportStream(session_id)) => {
|
||||
self.base_handler.webtransport_create_stream_remote(
|
||||
StreamId::from(session_id),
|
||||
stream_id,
|
||||
Box::new(self.events.clone()),
|
||||
Box::new(self.events.clone()),
|
||||
)?;
|
||||
let res = self.base_handler.handle_stream_readable(conn, stream_id)?;
|
||||
assert_eq!(ReceiveOutput::NoOutput, res);
|
||||
Ok(())
|
||||
}
|
||||
ReceiveOutput::ControlFrames(control_frames) => {
|
||||
for f in control_frames {
|
||||
match f {
|
||||
|
@ -196,7 +318,7 @@ impl Http3ServerHandler {
|
|||
HFrame::PriorityUpdatePush { element_id, priority } => {
|
||||
// TODO: check if the element_id references a promised push stream or
|
||||
// is greater than the maximum Push ID.
|
||||
self.events.priority_update(element_id, priority);
|
||||
self.events.priority_update(StreamId::from(element_id), priority);
|
||||
Ok(())
|
||||
}
|
||||
HFrame::PriorityUpdateRequest { element_id, priority } => {
|
||||
|
@ -210,7 +332,7 @@ impl Http3ServerHandler {
|
|||
return Err(Error::HttpId)
|
||||
}
|
||||
|
||||
self.events.priority_update(element_id, priority);
|
||||
self.events.priority_update(element_stream_id, priority);
|
||||
Ok(())
|
||||
}
|
||||
_ => unreachable!(
|
||||
|
@ -229,27 +351,19 @@ impl Http3ServerHandler {
|
|||
/// # Errors
|
||||
/// It returns an error if a stream does not exist or an error happen while reading a stream, e.g.
|
||||
/// early close, protocol error, etc.
|
||||
pub fn read_request_data(
|
||||
pub fn read_data(
|
||||
&mut self,
|
||||
conn: &mut Connection,
|
||||
now: Instant,
|
||||
stream_id: u64,
|
||||
stream_id: StreamId,
|
||||
buf: &mut [u8],
|
||||
) -> Res<(usize, bool)> {
|
||||
qinfo!([self], "read_data from stream {}.", stream_id);
|
||||
let recv_stream = self
|
||||
.base_handler
|
||||
.recv_streams
|
||||
.get_mut(&stream_id)
|
||||
.ok_or(Error::InvalidStreamId)?
|
||||
.http_stream()
|
||||
.ok_or(Error::InvalidStreamId)?;
|
||||
|
||||
let res = recv_stream.read_data(conn, buf);
|
||||
let res = self.base_handler.read_data(conn, stream_id, buf);
|
||||
if let Err(e) = &res {
|
||||
self.close(conn, now, e);
|
||||
} else if recv_stream.done() {
|
||||
self.base_handler.recv_streams.remove(&stream_id);
|
||||
if e.connection_error() {
|
||||
self.close(conn, now, e);
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
|
|
|
@ -5,34 +5,32 @@
|
|||
// except according to those terms.
|
||||
|
||||
use crate::hframe::HFrame;
|
||||
use crate::{Http3StreamType, RecvStream, Res};
|
||||
use crate::{BufferedStream, Http3StreamType, RecvStream, Res};
|
||||
use neqo_common::{qtrace, Encoder};
|
||||
use neqo_transport::{Connection, StreamType};
|
||||
use neqo_transport::{Connection, StreamId, StreamType};
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
use std::convert::TryFrom;
|
||||
|
||||
pub const HTTP3_UNI_STREAM_TYPE_CONTROL: u64 = 0x0;
|
||||
|
||||
// The local control stream, responsible for encoding frames and sending them
|
||||
/// The local control stream, responsible for encoding frames and sending them
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct ControlStreamLocal {
|
||||
stream_id: Option<u64>,
|
||||
buf: Vec<u8>,
|
||||
// `stream_id`s of outstanding request streams
|
||||
outstanding_priority_update: VecDeque<u64>,
|
||||
stream: BufferedStream,
|
||||
/// `stream_id`s of outstanding request streams
|
||||
outstanding_priority_update: VecDeque<StreamId>,
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for ControlStreamLocal {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
write!(f, "Local control stream {:?}", self.stream_id)
|
||||
write!(f, "Local control stream {:?}", self.stream)
|
||||
}
|
||||
}
|
||||
|
||||
impl ControlStreamLocal {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
stream_id: None,
|
||||
buf: vec![u8::try_from(HTTP3_UNI_STREAM_TYPE_CONTROL).unwrap()],
|
||||
stream: BufferedStream::default(),
|
||||
outstanding_priority_update: VecDeque::new(),
|
||||
}
|
||||
}
|
||||
|
@ -41,10 +39,10 @@ impl ControlStreamLocal {
|
|||
pub fn queue_frame(&mut self, f: &HFrame) {
|
||||
let mut enc = Encoder::default();
|
||||
f.encode(&mut enc);
|
||||
self.buf.append(&mut enc.into());
|
||||
self.stream.buffer(&enc);
|
||||
}
|
||||
|
||||
pub fn queue_update_priority(&mut self, stream_id: u64) {
|
||||
pub fn queue_update_priority(&mut self, stream_id: StreamId) {
|
||||
self.outstanding_priority_update.push_back(stream_id);
|
||||
}
|
||||
|
||||
|
@ -52,32 +50,16 @@ impl ControlStreamLocal {
|
|||
pub fn send(
|
||||
&mut self,
|
||||
conn: &mut Connection,
|
||||
recv_conn: &mut HashMap<u64, Box<dyn RecvStream>>,
|
||||
recv_conn: &mut HashMap<StreamId, Box<dyn RecvStream>>,
|
||||
) -> Res<()> {
|
||||
if let Some(stream_id) = self.stream_id {
|
||||
if !self.buf.is_empty() {
|
||||
qtrace!([self], "sending data.");
|
||||
let sent = conn.stream_send(stream_id, &self.buf[..])?;
|
||||
if sent == self.buf.len() {
|
||||
self.buf.clear();
|
||||
} else {
|
||||
let b = self.buf.split_off(sent);
|
||||
self.buf = b;
|
||||
}
|
||||
}
|
||||
// only send priority updates if all buffer data has been sent
|
||||
if self.buf.is_empty() {
|
||||
self.send_priority_update(stream_id, conn, recv_conn)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
self.stream.send_buffer(conn)?;
|
||||
self.send_priority_update(conn, recv_conn)
|
||||
}
|
||||
|
||||
fn send_priority_update(
|
||||
&mut self,
|
||||
stream_id: u64,
|
||||
conn: &mut Connection,
|
||||
recv_conn: &mut HashMap<u64, Box<dyn RecvStream>>,
|
||||
recv_conn: &mut HashMap<StreamId, Box<dyn RecvStream>>,
|
||||
) -> Res<()> {
|
||||
// send all necessary priority updates
|
||||
while let Some(update_id) = self.outstanding_priority_update.pop_front() {
|
||||
|
@ -98,7 +80,7 @@ impl ControlStreamLocal {
|
|||
if let Some(hframe) = priority_handler.maybe_encode_frame(update_id) {
|
||||
let mut enc = Encoder::new();
|
||||
hframe.encode(&mut enc);
|
||||
if conn.stream_send_atomic(stream_id, &enc)? {
|
||||
if self.stream.send_atomic(conn, &enc)? {
|
||||
priority_handler.priority_update_sent();
|
||||
} else {
|
||||
self.outstanding_priority_update.push_front(update_id);
|
||||
|
@ -112,12 +94,14 @@ impl ControlStreamLocal {
|
|||
/// Create a control stream.
|
||||
pub fn create(&mut self, conn: &mut Connection) -> Res<()> {
|
||||
qtrace!([self], "Create a control stream.");
|
||||
self.stream_id = Some(conn.stream_create(StreamType::UniDi)?);
|
||||
self.stream.init(conn.stream_create(StreamType::UniDi)?);
|
||||
self.stream
|
||||
.buffer(&[u8::try_from(HTTP3_UNI_STREAM_TYPE_CONTROL).unwrap()]);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn stream_id(&self) -> Option<u64> {
|
||||
self.stream_id
|
||||
pub fn stream_id(&self) -> Option<StreamId> {
|
||||
(&self.stream).into()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,16 +5,14 @@
|
|||
// except according to those terms.
|
||||
|
||||
use crate::hframe::{HFrame, HFrameReader};
|
||||
use crate::{
|
||||
AppError, Error, Http3StreamType, HttpRecvStream, ReceiveOutput, RecvStream, Res, ResetType,
|
||||
};
|
||||
use crate::{CloseType, Error, Http3StreamType, ReceiveOutput, RecvStream, Res, Stream};
|
||||
use neqo_common::qdebug;
|
||||
use neqo_transport::Connection;
|
||||
use neqo_transport::{Connection, StreamId};
|
||||
|
||||
/// The remote control stream is responsible only for reading frames. The frames are handled by `Http3Connection`.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct ControlStreamRemote {
|
||||
stream_id: u64,
|
||||
stream_id: StreamId,
|
||||
frame_reader: HFrameReader,
|
||||
}
|
||||
|
||||
|
@ -25,7 +23,7 @@ impl ::std::fmt::Display for ControlStreamRemote {
|
|||
}
|
||||
|
||||
impl ControlStreamRemote {
|
||||
pub fn new(stream_id: u64) -> Self {
|
||||
pub fn new(stream_id: StreamId) -> Self {
|
||||
Self {
|
||||
stream_id,
|
||||
frame_reader: HFrameReader::new(),
|
||||
|
@ -42,33 +40,27 @@ impl ControlStreamRemote {
|
|||
}
|
||||
}
|
||||
|
||||
impl Stream for ControlStreamRemote {
|
||||
fn stream_type(&self) -> Http3StreamType {
|
||||
Http3StreamType::Control
|
||||
}
|
||||
}
|
||||
|
||||
impl RecvStream for ControlStreamRemote {
|
||||
fn stream_reset(&mut self, _error: AppError, _reset_type: ResetType) -> Res<()> {
|
||||
fn reset(&mut self, _close_type: CloseType) -> Res<()> {
|
||||
Err(Error::HttpClosedCriticalStream)
|
||||
}
|
||||
|
||||
#[allow(clippy::vec_init_then_push)] // Clippy fail.
|
||||
fn receive(&mut self, conn: &mut Connection) -> Res<ReceiveOutput> {
|
||||
fn receive(&mut self, conn: &mut Connection) -> Res<(ReceiveOutput, bool)> {
|
||||
let mut control_frames = Vec::new();
|
||||
|
||||
loop {
|
||||
if let Some(f) = self.receive_single(conn)? {
|
||||
control_frames.push(f);
|
||||
} else {
|
||||
return Ok(ReceiveOutput::ControlFrames(control_frames));
|
||||
return Ok((ReceiveOutput::ControlFrames(control_frames), false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn done(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn stream_type(&self) -> Http3StreamType {
|
||||
Http3StreamType::Control
|
||||
}
|
||||
|
||||
fn http_stream(&mut self) -> Option<&mut dyn HttpRecvStream> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![allow(clippy::module_name_repetitions)]
|
||||
|
||||
pub mod session;
|
||||
pub mod webtransport;
|
||||
|
||||
use crate::client_events::Http3ClientEvents;
|
||||
use crate::features::NegotiationState;
|
||||
use crate::settings::{HSettingType, HSettings};
|
||||
use crate::{Http3StreamInfo, Http3StreamType};
|
||||
use neqo_transport::{AppError, StreamId};
|
||||
pub use session::ExtendedConnectSession;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::BTreeSet;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Debug;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub trait ExtendedConnectEvents: Debug {
|
||||
fn session_start(&self, connect_type: ExtendedConnectType, stream_id: StreamId);
|
||||
fn session_end(
|
||||
&self,
|
||||
connect_type: ExtendedConnectType,
|
||||
stream_id: StreamId,
|
||||
error: Option<AppError>,
|
||||
);
|
||||
fn extended_connect_new_stream(&self, stream_info: Http3StreamInfo);
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Copy, Clone, Eq)]
|
||||
pub enum ExtendedConnectType {
|
||||
WebTransport,
|
||||
}
|
||||
|
||||
impl ExtendedConnectType {
|
||||
#[must_use]
|
||||
#[allow(clippy::unused_self)] // This will change when we have more features using ExtendedConnectType.
|
||||
pub fn string(&self) -> &str {
|
||||
"webtransport"
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
#[allow(clippy::unused_self)] // this will change when there is more types of the extended CONNECT.
|
||||
pub fn setting_type(self) -> HSettingType {
|
||||
HSettingType::EnableWebTransport
|
||||
}
|
||||
|
||||
#[allow(clippy::unused_self)] // This will change when we have more features using ExtendedConnectType.
|
||||
#[must_use]
|
||||
pub fn get_stream_type(self, session_id: StreamId) -> Http3StreamType {
|
||||
Http3StreamType::WebTransport(session_id)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ExtendedConnectType> for HSettingType {
|
||||
fn from(_type: ExtendedConnectType) -> Self {
|
||||
// This will change when we have more features using ExtendedConnectType.
|
||||
HSettingType::EnableWebTransport
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ExtendedConnectFeature {
|
||||
connect_type: ExtendedConnectType,
|
||||
feature_negotiation: NegotiationState,
|
||||
sessions: HashMap<StreamId, Rc<RefCell<ExtendedConnectSession>>>,
|
||||
}
|
||||
|
||||
impl ExtendedConnectFeature {
|
||||
#[must_use]
|
||||
pub fn new(connect_type: ExtendedConnectType, enable: bool) -> Self {
|
||||
Self {
|
||||
feature_negotiation: NegotiationState::new(enable, HSettingType::from(connect_type)),
|
||||
connect_type,
|
||||
sessions: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_listener(&mut self, new_listener: Http3ClientEvents) {
|
||||
self.feature_negotiation.set_listener(new_listener);
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, stream_id: StreamId, session: Rc<RefCell<ExtendedConnectSession>>) {
|
||||
self.sessions.insert(stream_id, session);
|
||||
}
|
||||
|
||||
pub fn get_session(
|
||||
&mut self,
|
||||
stream_id: StreamId,
|
||||
) -> Option<Rc<RefCell<ExtendedConnectSession>>> {
|
||||
if !matches!(self.feature_negotiation, NegotiationState::Negotiated) {
|
||||
return None;
|
||||
}
|
||||
|
||||
self.sessions.get_mut(&stream_id).cloned()
|
||||
}
|
||||
|
||||
pub fn handle_settings(&mut self, settings: &HSettings) {
|
||||
self.feature_negotiation.handle_settings(settings);
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn enabled(&self) -> bool {
|
||||
self.feature_negotiation.enabled()
|
||||
}
|
||||
|
||||
pub fn remove(
|
||||
&mut self,
|
||||
stream_id: StreamId,
|
||||
) -> Option<(BTreeSet<StreamId>, BTreeSet<StreamId>)> {
|
||||
self.sessions
|
||||
.remove(&stream_id)
|
||||
.and_then(|ec| ec.borrow_mut().take_sub_streams())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,179 @@
|
|||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![allow(clippy::module_name_repetitions)]
|
||||
|
||||
use super::{ExtendedConnectEvents, ExtendedConnectType};
|
||||
use crate::{
|
||||
CloseType, Error, Http3StreamInfo, HttpRecvStreamEvents, RecvStreamEvents, SendStreamEvents,
|
||||
};
|
||||
use neqo_common::{qtrace, Header, Role};
|
||||
use neqo_transport::StreamId;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::BTreeSet;
|
||||
use std::mem;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum SessionState {
|
||||
Negotiating,
|
||||
Active(StreamId),
|
||||
Done,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ExtendedConnectSession {
|
||||
connect_type: ExtendedConnectType,
|
||||
state: SessionState,
|
||||
events: Box<dyn ExtendedConnectEvents>,
|
||||
send_streams: BTreeSet<StreamId>,
|
||||
recv_streams: BTreeSet<StreamId>,
|
||||
role: Role,
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for ExtendedConnectSession {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"ExtendedConnectSesssion for {}",
|
||||
self.connect_type.string(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl ExtendedConnectSession {
|
||||
#[must_use]
|
||||
pub fn new(
|
||||
connect_type: ExtendedConnectType,
|
||||
events: Box<dyn ExtendedConnectEvents>,
|
||||
role: Role,
|
||||
) -> Self {
|
||||
Self {
|
||||
connect_type,
|
||||
state: SessionState::Negotiating,
|
||||
events,
|
||||
send_streams: BTreeSet::new(),
|
||||
recv_streams: BTreeSet::new(),
|
||||
role,
|
||||
}
|
||||
}
|
||||
|
||||
fn close(&mut self, stream_id: StreamId, close_type: CloseType) {
|
||||
if self.state == SessionState::Done {
|
||||
return;
|
||||
}
|
||||
qtrace!("ExtendedConnect close the session");
|
||||
self.state = SessionState::Done;
|
||||
if let CloseType::ResetApp(_) = close_type {
|
||||
return;
|
||||
}
|
||||
self.events
|
||||
.session_end(self.connect_type, stream_id, close_type.error());
|
||||
}
|
||||
|
||||
pub fn negotiation_done(&mut self, stream_id: StreamId, succeeded: bool) {
|
||||
if self.state == SessionState::Done {
|
||||
return;
|
||||
}
|
||||
self.state = if succeeded {
|
||||
self.events.session_start(self.connect_type, stream_id);
|
||||
SessionState::Active(stream_id)
|
||||
} else {
|
||||
self.events.session_end(self.connect_type, stream_id, None);
|
||||
SessionState::Done
|
||||
};
|
||||
}
|
||||
|
||||
pub fn add_stream(&mut self, stream_id: StreamId) {
|
||||
if let SessionState::Active(session_id) = self.state {
|
||||
if stream_id.is_bidi() {
|
||||
self.send_streams.insert(stream_id);
|
||||
self.recv_streams.insert(stream_id);
|
||||
} else if stream_id.is_self_initiated(self.role) {
|
||||
self.send_streams.insert(stream_id);
|
||||
} else {
|
||||
self.recv_streams.insert(stream_id);
|
||||
}
|
||||
|
||||
if !stream_id.is_self_initiated(self.role) {
|
||||
self.events
|
||||
.extended_connect_new_stream(Http3StreamInfo::new(
|
||||
stream_id,
|
||||
self.connect_type.get_stream_type(session_id),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_recv_stream(&mut self, stream_id: StreamId) {
|
||||
self.recv_streams.remove(&stream_id);
|
||||
}
|
||||
|
||||
pub fn remove_send_stream(&mut self, stream_id: StreamId) {
|
||||
self.send_streams.remove(&stream_id);
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn is_active(&self) -> bool {
|
||||
matches!(self.state, SessionState::Active(_))
|
||||
}
|
||||
|
||||
pub fn take_sub_streams(&mut self) -> Option<(BTreeSet<StreamId>, BTreeSet<StreamId>)> {
|
||||
Some((
|
||||
mem::take(&mut self.recv_streams),
|
||||
mem::take(&mut self.send_streams),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl RecvStreamEvents for Rc<RefCell<ExtendedConnectSession>> {
|
||||
fn data_readable(&self, stream_info: Http3StreamInfo) {
|
||||
// A session request is not expected to receive any data. This may change in
|
||||
// the future.
|
||||
self.borrow_mut().close(
|
||||
stream_info.stream_id(),
|
||||
CloseType::LocalError(Error::HttpGeneralProtocolStream.code()),
|
||||
);
|
||||
}
|
||||
|
||||
fn recv_closed(&self, stream_info: Http3StreamInfo, close_type: CloseType) {
|
||||
self.borrow_mut().close(stream_info.stream_id(), close_type);
|
||||
}
|
||||
}
|
||||
|
||||
impl HttpRecvStreamEvents for Rc<RefCell<ExtendedConnectSession>> {
|
||||
fn header_ready(
|
||||
&self,
|
||||
stream_info: Http3StreamInfo,
|
||||
headers: Vec<Header>,
|
||||
_interim: bool,
|
||||
_fin: bool,
|
||||
) {
|
||||
qtrace!("ExtendedConnect response headers {:?}", headers);
|
||||
self.borrow_mut().negotiation_done(
|
||||
stream_info.stream_id(),
|
||||
headers
|
||||
.iter()
|
||||
.find_map(|h| {
|
||||
if h.name() == ":status" && h.value() == "200" {
|
||||
Some(())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.is_some(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl SendStreamEvents for Rc<RefCell<ExtendedConnectSession>> {
|
||||
fn data_writable(&self, _stream_info: Http3StreamInfo) {}
|
||||
|
||||
/// Add a new `StopSending` event
|
||||
fn send_closed(&self, stream_info: Http3StreamInfo, close_type: CloseType) {
|
||||
self.borrow_mut().close(stream_info.stream_id(), close_type);
|
||||
}
|
||||
}
|
202
third_party/rust/neqo-http3/src/features/extended_connect/webtransport.rs
поставляемый
Normal file
202
third_party/rust/neqo-http3/src/features/extended_connect/webtransport.rs
поставляемый
Normal file
|
@ -0,0 +1,202 @@
|
|||
// 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::ExtendedConnectSession;
|
||||
use crate::{
|
||||
CloseType, Http3StreamInfo, Http3StreamType, ReceiveOutput, RecvStream, RecvStreamEvents, Res,
|
||||
SendStream, SendStreamEvents, Stream,
|
||||
};
|
||||
use neqo_common::Encoder;
|
||||
use neqo_transport::{Connection, StreamId};
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub const WEBTRANSPORT_UNI_STREAM: u64 = 0x54;
|
||||
pub const WEBTRANSPORT_STREAM: u64 = 0x41;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct WebTransportRecvStream {
|
||||
stream_id: StreamId,
|
||||
events: Box<dyn RecvStreamEvents>,
|
||||
session: Rc<RefCell<ExtendedConnectSession>>,
|
||||
session_id: StreamId,
|
||||
fin: bool,
|
||||
}
|
||||
|
||||
impl WebTransportRecvStream {
|
||||
pub fn new(
|
||||
stream_id: StreamId,
|
||||
session_id: StreamId,
|
||||
events: Box<dyn RecvStreamEvents>,
|
||||
session: Rc<RefCell<ExtendedConnectSession>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
stream_id,
|
||||
events,
|
||||
session_id,
|
||||
session,
|
||||
fin: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_info(&self) -> Http3StreamInfo {
|
||||
Http3StreamInfo::new(self.stream_id, self.stream_type())
|
||||
}
|
||||
}
|
||||
|
||||
impl Stream for WebTransportRecvStream {
|
||||
fn stream_type(&self) -> Http3StreamType {
|
||||
Http3StreamType::WebTransport(self.session_id)
|
||||
}
|
||||
}
|
||||
|
||||
impl RecvStream for WebTransportRecvStream {
|
||||
fn receive(&mut self, _conn: &mut Connection) -> Res<(ReceiveOutput, bool)> {
|
||||
self.events.data_readable(self.get_info());
|
||||
Ok((ReceiveOutput::NoOutput, false))
|
||||
}
|
||||
|
||||
fn reset(&mut self, close_type: CloseType) -> Res<()> {
|
||||
if !matches!(close_type, CloseType::ResetApp(_)) {
|
||||
self.events.recv_closed(self.get_info(), close_type);
|
||||
}
|
||||
self.session.borrow_mut().remove_recv_stream(self.stream_id);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_data(&mut self, conn: &mut Connection, buf: &mut [u8]) -> Res<(usize, bool)> {
|
||||
let (amount, fin) = conn.stream_recv(self.stream_id, buf)?;
|
||||
self.fin = fin;
|
||||
if fin {
|
||||
self.session.borrow_mut().remove_recv_stream(self.stream_id);
|
||||
}
|
||||
Ok((amount, fin))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum WebTransportSenderStreamState {
|
||||
SendingInit { buf: Vec<u8>, fin: bool },
|
||||
SendingData,
|
||||
Done,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct WebTransportSendStream {
|
||||
stream_id: StreamId,
|
||||
state: WebTransportSenderStreamState,
|
||||
events: Box<dyn SendStreamEvents>,
|
||||
session: Rc<RefCell<ExtendedConnectSession>>,
|
||||
session_id: StreamId,
|
||||
}
|
||||
|
||||
impl WebTransportSendStream {
|
||||
pub fn new(
|
||||
stream_id: StreamId,
|
||||
session_id: StreamId,
|
||||
events: Box<dyn SendStreamEvents>,
|
||||
session: Rc<RefCell<ExtendedConnectSession>>,
|
||||
local: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
stream_id,
|
||||
state: if local {
|
||||
let mut d = Encoder::default();
|
||||
if stream_id.is_uni() {
|
||||
d.encode_varint(WEBTRANSPORT_UNI_STREAM);
|
||||
} else {
|
||||
d.encode_varint(WEBTRANSPORT_STREAM);
|
||||
}
|
||||
d.encode_varint(session_id.as_u64());
|
||||
WebTransportSenderStreamState::SendingInit {
|
||||
buf: d.into(),
|
||||
fin: false,
|
||||
}
|
||||
} else {
|
||||
WebTransportSenderStreamState::SendingData
|
||||
},
|
||||
events,
|
||||
session_id,
|
||||
session,
|
||||
}
|
||||
}
|
||||
|
||||
fn set_done(&mut self, close_type: CloseType) {
|
||||
self.state = WebTransportSenderStreamState::Done;
|
||||
self.events.send_closed(self.get_info(), close_type);
|
||||
self.session.borrow_mut().remove_send_stream(self.stream_id);
|
||||
}
|
||||
|
||||
fn get_info(&self) -> Http3StreamInfo {
|
||||
Http3StreamInfo::new(self.stream_id, self.stream_type())
|
||||
}
|
||||
}
|
||||
|
||||
impl Stream for WebTransportSendStream {
|
||||
fn stream_type(&self) -> Http3StreamType {
|
||||
Http3StreamType::WebTransport(self.session_id)
|
||||
}
|
||||
}
|
||||
|
||||
impl SendStream for WebTransportSendStream {
|
||||
fn send(&mut self, conn: &mut Connection) -> Res<()> {
|
||||
if let WebTransportSenderStreamState::SendingInit { ref mut buf, fin } = self.state {
|
||||
let sent = conn.stream_send(self.stream_id, &buf[..])?;
|
||||
if sent == buf.len() {
|
||||
if fin {
|
||||
conn.stream_close_send(self.stream_id)?;
|
||||
self.set_done(CloseType::Done);
|
||||
} else {
|
||||
self.state = WebTransportSenderStreamState::SendingData;
|
||||
}
|
||||
} else {
|
||||
let b = buf.split_off(sent);
|
||||
*buf = b;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn has_data_to_send(&self) -> bool {
|
||||
matches!(
|
||||
self.state,
|
||||
WebTransportSenderStreamState::SendingInit { .. }
|
||||
)
|
||||
}
|
||||
|
||||
fn stream_writable(&self) {
|
||||
self.events.data_writable(self.get_info());
|
||||
}
|
||||
|
||||
fn done(&self) -> bool {
|
||||
self.state == WebTransportSenderStreamState::Done
|
||||
}
|
||||
|
||||
fn send_data(&mut self, conn: &mut Connection, buf: &[u8]) -> Res<usize> {
|
||||
self.send(conn)?;
|
||||
if self.state == WebTransportSenderStreamState::SendingData {
|
||||
let sent = conn.stream_send(self.stream_id, buf)?;
|
||||
Ok(sent)
|
||||
} else {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_stop_sending(&mut self, close_type: CloseType) {
|
||||
self.set_done(close_type);
|
||||
}
|
||||
|
||||
fn close(&mut self, conn: &mut Connection) -> Res<()> {
|
||||
if let WebTransportSenderStreamState::SendingInit { ref mut fin, .. } = self.state {
|
||||
*fin = true;
|
||||
} else {
|
||||
self.state = WebTransportSenderStreamState::Done;
|
||||
conn.stream_close_send(self.stream_id)?;
|
||||
self.set_done(CloseType::Done);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
// 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 crate::{
|
||||
client_events::Http3ClientEvents,
|
||||
settings::{HSettingType, HSettings},
|
||||
};
|
||||
use neqo_common::qtrace;
|
||||
use std::fmt::Debug;
|
||||
use std::mem;
|
||||
|
||||
pub mod extended_connect;
|
||||
|
||||
/// States:
|
||||
/// - `Disable` - it is not turned on for this connection.
|
||||
/// - `Negotiating` - the feature is enabled locally, but settings from the peer
|
||||
/// have not been received yet.
|
||||
/// - `Negotiated` - the settings have been received and both sides support the feature.
|
||||
/// - `NegotiationFailed` - the settings have been received and the peer does not
|
||||
/// support the feature.
|
||||
#[derive(Debug)]
|
||||
pub enum NegotiationState {
|
||||
Disabled,
|
||||
Negotiating {
|
||||
feature_type: HSettingType,
|
||||
listener: Option<Http3ClientEvents>,
|
||||
},
|
||||
Negotiated,
|
||||
NegotiationFailed,
|
||||
}
|
||||
|
||||
impl NegotiationState {
|
||||
#[must_use]
|
||||
pub fn new(enable: bool, feature_type: HSettingType) -> Self {
|
||||
if enable {
|
||||
Self::Negotiating {
|
||||
feature_type,
|
||||
listener: None,
|
||||
}
|
||||
} else {
|
||||
Self::Disabled
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_listener(&mut self, new_listener: Http3ClientEvents) {
|
||||
if let Self::Negotiating { listener, .. } = self {
|
||||
*listener = Some(new_listener);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_settings(&mut self, settings: &HSettings) {
|
||||
if !self.locally_enabled() {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Self::Negotiating {
|
||||
feature_type,
|
||||
listener,
|
||||
} = self
|
||||
{
|
||||
qtrace!(
|
||||
"set_negotiated {:?} to {}",
|
||||
feature_type,
|
||||
settings.get(*feature_type)
|
||||
);
|
||||
let cb = mem::take(listener);
|
||||
let ft = *feature_type;
|
||||
*self = if settings.get(ft) == 1 {
|
||||
Self::Negotiated
|
||||
} else {
|
||||
Self::NegotiationFailed
|
||||
};
|
||||
if let Some(l) = cb {
|
||||
l.negotiation_done(ft, self.enabled());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn enabled(&self) -> bool {
|
||||
matches!(self, &Self::Negotiated)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn locally_enabled(&self) -> bool {
|
||||
!matches!(self, &Self::Disabled)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
// 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 crate::{Error, MessageType, Res};
|
||||
use neqo_common::Header;
|
||||
|
||||
const PSEUDO_HEADER_STATUS: u8 = 0x1;
|
||||
const PSEUDO_HEADER_METHOD: u8 = 0x2;
|
||||
const PSEUDO_HEADER_SCHEME: u8 = 0x4;
|
||||
const PSEUDO_HEADER_AUTHORITY: u8 = 0x8;
|
||||
const PSEUDO_HEADER_PATH: u8 = 0x10;
|
||||
const PSEUDO_HEADER_PROTOCOL: u8 = 0x20;
|
||||
const REGULAR_HEADER: u8 = 0x80;
|
||||
|
||||
/// Check whether the response is informational(1xx).
|
||||
/// # Errors
|
||||
/// Returns an error if response headers do not contain
|
||||
/// a status header or if the value of the header cannot be parsed.
|
||||
pub fn is_interim(headers: &[Header]) -> Res<bool> {
|
||||
let status = headers.iter().take(1).find(|h| h.name() == ":status");
|
||||
if let Some(h) = status {
|
||||
#[allow(clippy::map_err_ignore)]
|
||||
let status_code = h.value().parse::<i32>().map_err(|_| Error::InvalidHeader)?;
|
||||
Ok((100..200).contains(&status_code))
|
||||
} else {
|
||||
Err(Error::InvalidHeader)
|
||||
}
|
||||
}
|
||||
|
||||
fn track_pseudo(name: &str, state: &mut u8, message_type: MessageType) -> Res<bool> {
|
||||
let (pseudo, bit) = if name.starts_with(':') {
|
||||
if *state & REGULAR_HEADER != 0 {
|
||||
return Err(Error::InvalidHeader);
|
||||
}
|
||||
let bit = match (message_type, name) {
|
||||
(MessageType::Response, ":status") => PSEUDO_HEADER_STATUS,
|
||||
(MessageType::Request, ":method") => PSEUDO_HEADER_METHOD,
|
||||
(MessageType::Request, ":scheme") => PSEUDO_HEADER_SCHEME,
|
||||
(MessageType::Request, ":authority") => PSEUDO_HEADER_AUTHORITY,
|
||||
(MessageType::Request, ":path") => PSEUDO_HEADER_PATH,
|
||||
(MessageType::Request, ":protocol") => PSEUDO_HEADER_PROTOCOL,
|
||||
(_, _) => return Err(Error::InvalidHeader),
|
||||
};
|
||||
(true, bit)
|
||||
} else {
|
||||
(false, REGULAR_HEADER)
|
||||
};
|
||||
|
||||
if *state & bit == 0 || !pseudo {
|
||||
*state |= bit;
|
||||
Ok(pseudo)
|
||||
} else {
|
||||
Err(Error::InvalidHeader)
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if request/response headers are well formed, i.e. contain
|
||||
/// allowed pseudo headers and in a right order, etc.
|
||||
/// # Errors
|
||||
/// Returns an error if headers are not well formed.
|
||||
pub fn headers_valid(headers: &[Header], message_type: MessageType) -> Res<()> {
|
||||
let mut method_value: Option<&str> = None;
|
||||
let mut pseudo_state = 0;
|
||||
for header in headers {
|
||||
let is_pseudo = track_pseudo(header.name(), &mut pseudo_state, message_type)?;
|
||||
|
||||
let mut bytes = header.name().bytes();
|
||||
if is_pseudo {
|
||||
if header.name() == ":method" {
|
||||
method_value = Some(header.value());
|
||||
}
|
||||
let _ = bytes.next();
|
||||
}
|
||||
|
||||
if bytes.any(|b| matches!(b, 0 | 0x10 | 0x13 | 0x3a | 0x41..=0x5a)) {
|
||||
return Err(Error::InvalidHeader); // illegal characters.
|
||||
}
|
||||
}
|
||||
// Clear the regular header bit, since we only check pseudo headers below.
|
||||
pseudo_state &= !REGULAR_HEADER;
|
||||
let pseudo_header_mask = match message_type {
|
||||
MessageType::Response => PSEUDO_HEADER_STATUS,
|
||||
MessageType::Request => {
|
||||
if method_value == Some(&"CONNECT".to_string()) {
|
||||
PSEUDO_HEADER_METHOD | PSEUDO_HEADER_AUTHORITY
|
||||
} else {
|
||||
PSEUDO_HEADER_METHOD | PSEUDO_HEADER_SCHEME | PSEUDO_HEADER_PATH
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (MessageType::Request == message_type)
|
||||
&& ((pseudo_state & PSEUDO_HEADER_PROTOCOL) > 0)
|
||||
&& method_value != Some(&"CONNECT".to_string())
|
||||
{
|
||||
return Err(Error::InvalidHeader);
|
||||
}
|
||||
|
||||
if pseudo_state & pseudo_header_mask != pseudo_header_mask {
|
||||
return Err(Error::InvalidHeader);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Checks if trailers are well formed, i.e. pseudo headers are not
|
||||
/// allowed in trailers.
|
||||
/// # Errors
|
||||
/// Returns an error if trailers are not well formed.
|
||||
pub fn trailers_valid(headers: &[Header]) -> Res<()> {
|
||||
for header in headers {
|
||||
if header.name().starts_with(':') {
|
||||
return Err(Error::InvalidHeader);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
|
@ -10,7 +10,7 @@ use neqo_common::{
|
|||
IncrementalDecoderUint,
|
||||
};
|
||||
use neqo_crypto::random;
|
||||
use neqo_transport::Connection;
|
||||
use neqo_transport::{Connection, StreamId};
|
||||
use std::convert::TryFrom;
|
||||
use std::io::Write;
|
||||
use std::mem;
|
||||
|
@ -52,7 +52,7 @@ pub enum HFrame {
|
|||
header_block: Vec<u8>,
|
||||
},
|
||||
Goaway {
|
||||
stream_id: u64,
|
||||
stream_id: StreamId,
|
||||
},
|
||||
MaxPushId {
|
||||
push_id: u64,
|
||||
|
@ -116,7 +116,7 @@ impl HFrame {
|
|||
}
|
||||
Self::Goaway { stream_id } => {
|
||||
enc.encode_vvec_with(|enc_inner| {
|
||||
enc_inner.encode_varint(*stream_id);
|
||||
enc_inner.encode_varint(stream_id.as_u64());
|
||||
});
|
||||
}
|
||||
Self::MaxPushId { push_id } => {
|
||||
|
@ -186,6 +186,18 @@ impl HFrameReader {
|
|||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn new_with_type(hframe_type: u64) -> Self {
|
||||
Self {
|
||||
state: HFrameReaderState::GetLength {
|
||||
decoder: IncrementalDecoderUint::default(),
|
||||
},
|
||||
hframe_type,
|
||||
hframe_len: 0,
|
||||
payload: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn reset(&mut self) {
|
||||
self.state = HFrameReaderState::GetType {
|
||||
decoder: IncrementalDecoderUint::default(),
|
||||
|
@ -217,7 +229,7 @@ impl HFrameReader {
|
|||
pub fn receive(
|
||||
&mut self,
|
||||
conn: &mut Connection,
|
||||
stream_id: u64,
|
||||
stream_id: StreamId,
|
||||
) -> Res<(Option<HFrame>, bool)> {
|
||||
loop {
|
||||
let to_read = std::cmp::min(self.min_remaining(), MAX_READ_SIZE);
|
||||
|
@ -372,7 +384,7 @@ impl HFrameReader {
|
|||
header_block: dec.decode_remainder().to_vec(),
|
||||
},
|
||||
H3_FRAME_TYPE_GOAWAY => HFrame::Goaway {
|
||||
stream_id: dec.decode_varint().ok_or(Error::HttpFrame)?,
|
||||
stream_id: StreamId::new(dec.decode_varint().ok_or(Error::HttpFrame)?),
|
||||
},
|
||||
H3_FRAME_TYPE_MAX_PUSH_ID => HFrame::MaxPushId {
|
||||
push_id: dec.decode_varint().ok_or(Error::HttpFrame)?,
|
||||
|
@ -406,7 +418,7 @@ mod tests {
|
|||
use crate::settings::{HSetting, HSettingType};
|
||||
use crate::Priority;
|
||||
use neqo_crypto::AuthenticationStatus;
|
||||
use neqo_transport::{Connection, StreamType};
|
||||
use neqo_transport::{Connection, StreamId, StreamType};
|
||||
use std::mem;
|
||||
use test_fixture::{connect, default_client, default_server, fixture_init, now};
|
||||
|
||||
|
@ -491,7 +503,9 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_goaway_frame4() {
|
||||
let f = HFrame::Goaway { stream_id: 5 };
|
||||
let f = HFrame::Goaway {
|
||||
stream_id: StreamId::new(5),
|
||||
};
|
||||
enc_dec(&f, "070105", 0);
|
||||
}
|
||||
|
||||
|
@ -569,7 +583,7 @@ mod tests {
|
|||
pub fr: HFrameReader,
|
||||
pub conn_c: Connection,
|
||||
pub conn_s: Connection,
|
||||
pub stream_id: u64,
|
||||
pub stream_id: StreamId,
|
||||
}
|
||||
|
||||
impl HFrameReaderTest {
|
||||
|
@ -934,7 +948,9 @@ mod tests {
|
|||
test_complete_and_incomplete_frame(&buf, buf.len());
|
||||
|
||||
// H3_FRAME_TYPE_GOAWAY
|
||||
let f = HFrame::Goaway { stream_id: 5 };
|
||||
let f = HFrame::Goaway {
|
||||
stream_id: StreamId::new(5),
|
||||
};
|
||||
let mut enc = Encoder::default();
|
||||
f.encode(&mut enc);
|
||||
let buf: Vec<_> = enc.into();
|
||||
|
|
|
@ -7,16 +7,19 @@
|
|||
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
|
||||
#![warn(clippy::pedantic)]
|
||||
|
||||
mod buffered_send_stream;
|
||||
mod client_events;
|
||||
mod conn_params;
|
||||
mod connection;
|
||||
pub mod connection_client;
|
||||
mod connection_server;
|
||||
mod control_stream_local;
|
||||
mod control_stream_remote;
|
||||
pub mod features;
|
||||
mod headers_checks;
|
||||
pub mod hframe;
|
||||
mod priority;
|
||||
mod push_controller;
|
||||
mod push_stream;
|
||||
mod qlog;
|
||||
mod qpack_decoder_receiver;
|
||||
mod qpack_encoder_receiver;
|
||||
|
@ -30,20 +33,23 @@ mod settings;
|
|||
mod stream_type_reader;
|
||||
|
||||
use neqo_qpack::Error as QpackError;
|
||||
pub use neqo_transport::Output;
|
||||
use neqo_transport::{AppError, Connection, Error as TransportError};
|
||||
pub use neqo_transport::{Output, StreamId};
|
||||
use std::fmt::Debug;
|
||||
|
||||
use crate::priority::PriorityHandler;
|
||||
pub use client_events::Http3ClientEvent;
|
||||
pub use buffered_send_stream::BufferedStream;
|
||||
pub use client_events::{Http3ClientEvent, WebTransportEvent};
|
||||
pub use conn_params::Http3Parameters;
|
||||
pub use connection::Http3State;
|
||||
pub use connection_client::Http3Client;
|
||||
pub use connection_client::Http3Parameters;
|
||||
pub use hframe::{HFrame, HFrameReader};
|
||||
pub use neqo_common::Header;
|
||||
pub use neqo_common::{Header, MessageType};
|
||||
pub use priority::Priority;
|
||||
pub use server::Http3Server;
|
||||
pub use server_events::{ClientRequestStream, Http3ServerEvent};
|
||||
pub use server_events::{
|
||||
Http3OrWebTransportStream, Http3ServerEvent, WebTransportRequest, WebTransportServerEvent,
|
||||
};
|
||||
pub use settings::HttpZeroRttChecker;
|
||||
pub use stream_type_reader::NewStreamType;
|
||||
|
||||
|
@ -154,12 +160,13 @@ impl Error {
|
|||
/// # Panics
|
||||
/// On unexpected errors, in debug mode.
|
||||
#[must_use]
|
||||
pub fn map_stream_send_errors(err: &TransportError) -> Self {
|
||||
pub fn map_stream_send_errors(err: &Error) -> Self {
|
||||
match err {
|
||||
TransportError::InvalidStreamId | TransportError::FinalSizeError => {
|
||||
Self::TransportError(TransportError::InvalidStreamId)
|
||||
| Self::TransportError(TransportError::FinalSizeError) => {
|
||||
Error::TransportStreamDoesNotExist
|
||||
}
|
||||
TransportError::InvalidInput => Error::InvalidInput,
|
||||
Self::TransportError(TransportError::InvalidInput) => Error::InvalidInput,
|
||||
_ => {
|
||||
debug_assert!(false, "Unexpected error");
|
||||
Error::TransportStreamDoesNotExist
|
||||
|
@ -278,7 +285,7 @@ impl ::std::fmt::Display for Error {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum Http3StreamType {
|
||||
Control,
|
||||
Decoder,
|
||||
|
@ -286,50 +293,180 @@ pub enum Http3StreamType {
|
|||
NewStream,
|
||||
Http,
|
||||
Push,
|
||||
ExtendedConnect,
|
||||
WebTransport(StreamId),
|
||||
Unknown,
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub enum ReceiveOutput {
|
||||
NoOutput,
|
||||
PushStream,
|
||||
ControlFrames(Vec<HFrame>),
|
||||
UnblockedStreams(Vec<u64>),
|
||||
UnblockedStreams(Vec<StreamId>),
|
||||
NewStream(NewStreamType),
|
||||
}
|
||||
|
||||
pub trait RecvStream: Debug {
|
||||
/// # Errors
|
||||
/// An error may happen while reading a stream, e.g. early close, etc.
|
||||
fn stream_reset(&mut self, error: AppError, reset_type: ResetType) -> Res<()>;
|
||||
impl Default for ReceiveOutput {
|
||||
fn default() -> Self {
|
||||
Self::NoOutput
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Stream: Debug {
|
||||
fn stream_type(&self) -> Http3StreamType;
|
||||
}
|
||||
|
||||
pub trait RecvStream: Stream {
|
||||
/// The stream reads data from the corresponding quic stream and returns `ReceiveOutput`.
|
||||
/// The function also returns true as the second parameter if the stream is done and
|
||||
/// could be forgotten, i.e. removed from all records.
|
||||
/// # Errors
|
||||
/// An error may happen while reading a stream, e.g. early close, protocol error, etc.
|
||||
fn receive(&mut self, conn: &mut Connection) -> Res<ReceiveOutput>;
|
||||
fn done(&self) -> bool;
|
||||
fn stream_type(&self) -> Http3StreamType;
|
||||
fn http_stream(&mut self) -> Option<&mut dyn HttpRecvStream>;
|
||||
fn receive(&mut self, conn: &mut Connection) -> Res<(ReceiveOutput, bool)>;
|
||||
/// # Errors
|
||||
/// An error may happen while reading a stream, e.g. early close, etc.
|
||||
fn reset(&mut self, close_type: CloseType) -> Res<()>;
|
||||
/// The function allows an app to read directly from the quic stream. The function
|
||||
/// returns the number of bytes written into `buf` and true/false if the stream is
|
||||
/// completely done and can be forgotten, i.e. removed from all records.
|
||||
/// # Errors
|
||||
/// An error may happen while reading a stream, e.g. early close, protocol error, etc.
|
||||
fn read_data(&mut self, _conn: &mut Connection, _buf: &mut [u8]) -> Res<(usize, bool)> {
|
||||
Err(Error::InvalidStreamId)
|
||||
}
|
||||
|
||||
fn http_stream(&mut self) -> Option<&mut dyn HttpRecvStream> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub trait HttpRecvStream: RecvStream {
|
||||
/// This function is similar to the receive function and has the same output, i.e.
|
||||
/// a `ReceiveOutput` enum and bool. The bool is true if the stream is completely done
|
||||
/// and can be forgotten, i.e. removed from all records.
|
||||
/// # Errors
|
||||
/// An error may happen while reading a stream, e.g. early close, protocol error, etc.
|
||||
fn header_unblocked(&mut self, conn: &mut Connection) -> Res<()>;
|
||||
/// # Errors
|
||||
/// An error may happen while reading a stream, e.g. early close, protocol error, etc.
|
||||
fn read_data(&mut self, conn: &mut Connection, buf: &mut [u8]) -> Res<(usize, bool)>;
|
||||
fn header_unblocked(&mut self, conn: &mut Connection) -> Res<(ReceiveOutput, bool)>;
|
||||
|
||||
fn priority_handler_mut(&mut self) -> &mut PriorityHandler;
|
||||
|
||||
fn set_new_listener(&mut self, _conn_events: Box<dyn HttpRecvStreamEvents>) {}
|
||||
fn extended_connect_wait_for_response(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) trait RecvMessageEvents: Debug {
|
||||
fn header_ready(&self, stream_id: u64, headers: Vec<Header>, interim: bool, fin: bool);
|
||||
fn data_readable(&self, stream_id: u64);
|
||||
fn reset(&self, stream_id: u64, error: AppError, local: bool);
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||
pub struct Http3StreamInfo {
|
||||
stream_id: StreamId,
|
||||
stream_type: Http3StreamType,
|
||||
}
|
||||
|
||||
impl Http3StreamInfo {
|
||||
#[must_use]
|
||||
pub fn new(stream_id: StreamId, stream_type: Http3StreamType) -> Self {
|
||||
Self {
|
||||
stream_id,
|
||||
stream_type,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn stream_id(&self) -> StreamId {
|
||||
self.stream_id
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn session_id(&self) -> Option<StreamId> {
|
||||
if let Http3StreamType::WebTransport(session) = self.stream_type {
|
||||
Some(session)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn is_http(&self) -> bool {
|
||||
self.stream_type == Http3StreamType::Http
|
||||
}
|
||||
}
|
||||
|
||||
pub trait RecvStreamEvents: Debug {
|
||||
fn data_readable(&self, stream_info: Http3StreamInfo);
|
||||
fn recv_closed(&self, _stream_info: Http3StreamInfo, _close_type: CloseType) {}
|
||||
}
|
||||
|
||||
pub trait HttpRecvStreamEvents: RecvStreamEvents {
|
||||
fn header_ready(
|
||||
&self,
|
||||
stream_info: Http3StreamInfo,
|
||||
headers: Vec<Header>,
|
||||
interim: bool,
|
||||
fin: bool,
|
||||
);
|
||||
fn extended_connect_new_session(&self, _stream_id: StreamId, _headers: Vec<Header>) {}
|
||||
}
|
||||
|
||||
pub trait SendStream: Stream {
|
||||
/// # Errors
|
||||
/// Error my occure during sending data, e.g. protocol error, etc.
|
||||
fn send(&mut self, conn: &mut Connection) -> Res<()>;
|
||||
fn has_data_to_send(&self) -> bool;
|
||||
fn stream_writable(&self);
|
||||
fn done(&self) -> bool;
|
||||
/// # Errors
|
||||
/// Error my occure during sending data, e.g. protocol error, etc.
|
||||
fn send_data(&mut self, _conn: &mut Connection, _buf: &[u8]) -> Res<usize>;
|
||||
/// # Errors
|
||||
/// It may happen that the transport stream is already close. This is unlikely.
|
||||
fn close(&mut self, conn: &mut Connection) -> Res<()>;
|
||||
/// This function is called when sending side is closed abruptly by the peer or
|
||||
/// the application.
|
||||
fn handle_stop_sending(&mut self, close_type: CloseType);
|
||||
fn http_stream(&mut self) -> Option<&mut dyn HttpSendStream> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub trait HttpSendStream: SendStream {
|
||||
/// This function is used to supply headers to a http message. The
|
||||
/// function is used for request headers, response headers, 1xx response and
|
||||
/// trailers.
|
||||
/// # Errors
|
||||
/// This can also return an error if the underlying stream is closed.
|
||||
fn send_headers(&mut self, headers: &[Header], conn: &mut Connection) -> Res<()>;
|
||||
fn set_new_listener(&mut self, _conn_events: Box<dyn SendStreamEvents>) {}
|
||||
}
|
||||
|
||||
pub trait SendStreamEvents: Debug {
|
||||
fn send_closed(&self, _stream_info: Http3StreamInfo, _close_type: CloseType) {}
|
||||
fn data_writable(&self, _stream_info: Http3StreamInfo) {}
|
||||
}
|
||||
|
||||
/// This enum is used to mark a different type of closing a stream:
|
||||
/// `ResetApp` - the application has closed the stream.
|
||||
/// `ResetRemote` - the stream was closed by the peer.
|
||||
/// `LocalError` - There was a stream error on the stream. The stream errors are errors
|
||||
/// that do not close the complete connection, e.g. unallowed headers.
|
||||
/// `Done` - the stream was closed without an error.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ResetType {
|
||||
App,
|
||||
Remote,
|
||||
Local,
|
||||
pub enum CloseType {
|
||||
ResetApp(AppError),
|
||||
ResetRemote(AppError),
|
||||
LocalError(AppError),
|
||||
Done,
|
||||
}
|
||||
|
||||
impl CloseType {
|
||||
#[must_use]
|
||||
pub fn error(&self) -> Option<AppError> {
|
||||
match self {
|
||||
Self::ResetApp(error) | Self::ResetRemote(error) | Self::LocalError(error) => {
|
||||
Some(*error)
|
||||
}
|
||||
Self::Done => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use crate::{Error, HFrame, Header, Res};
|
||||
use neqo_transport::StreamId;
|
||||
use sfv::{BareItem, Item, ListEntry, Parser};
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt;
|
||||
|
@ -129,17 +130,17 @@ impl PriorityHandler {
|
|||
}
|
||||
|
||||
/// Returns `HFrame` if an priority update is outstanding
|
||||
pub fn maybe_encode_frame(&self, stream_id: u64) -> Option<HFrame> {
|
||||
pub fn maybe_encode_frame(&self, stream_id: StreamId) -> Option<HFrame> {
|
||||
if self.priority == self.last_send_priority {
|
||||
None
|
||||
} else if self.push_stream {
|
||||
Some(HFrame::PriorityUpdatePush {
|
||||
element_id: stream_id,
|
||||
element_id: stream_id.as_u64(),
|
||||
priority: self.priority,
|
||||
})
|
||||
} else {
|
||||
Some(HFrame::PriorityUpdateRequest {
|
||||
element_id: stream_id,
|
||||
element_id: stream_id.as_u64(),
|
||||
priority: self.priority,
|
||||
})
|
||||
}
|
||||
|
@ -150,13 +151,14 @@ impl PriorityHandler {
|
|||
mod test {
|
||||
use crate::priority::PriorityHandler;
|
||||
use crate::{HFrame, Priority};
|
||||
use neqo_transport::StreamId;
|
||||
|
||||
#[test]
|
||||
fn priority_updates_ignore_same() {
|
||||
let mut p = PriorityHandler::new(false, Priority::new(5, false));
|
||||
assert!(!p.maybe_update_priority(Priority::new(5, false)));
|
||||
// updating with the same priority -> there should not be any priority frame sent
|
||||
assert!(p.maybe_encode_frame(4).is_none());
|
||||
assert!(p.maybe_encode_frame(StreamId::new(4)).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -164,7 +166,7 @@ mod test {
|
|||
let mut p = PriorityHandler::new(false, Priority::new(5, false));
|
||||
assert!(p.maybe_update_priority(Priority::new(6, false)));
|
||||
// updating with the a different priority -> there should be a priority frame sent
|
||||
assert!(p.maybe_encode_frame(4).is_some());
|
||||
assert!(p.maybe_encode_frame(StreamId::new(4)).is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -173,7 +175,7 @@ mod test {
|
|||
assert!(p.maybe_update_priority(Priority::new(6, false)));
|
||||
assert!(p.maybe_update_priority(Priority::new(5, false)));
|
||||
// initial and last priority same -> there should not be any priority frame sent
|
||||
assert!(p.maybe_encode_frame(4).is_none());
|
||||
assert!(p.maybe_encode_frame(StreamId::new(4)).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -186,7 +188,7 @@ mod test {
|
|||
element_id: 4,
|
||||
priority: Priority::new(7, false),
|
||||
};
|
||||
assert_eq!(p.maybe_encode_frame(4), Some(expected));
|
||||
assert_eq!(p.maybe_encode_frame(StreamId::new(4)), Some(expected));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -198,6 +200,6 @@ mod test {
|
|||
element_id: 4,
|
||||
priority: Priority::new(5, true),
|
||||
};
|
||||
assert_eq!(p.maybe_encode_frame(4), Some(expected));
|
||||
assert_eq!(p.maybe_encode_frame(StreamId::new(4)), Some(expected));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,10 +6,9 @@
|
|||
use crate::client_events::{Http3ClientEvent, Http3ClientEvents};
|
||||
use crate::connection::Http3Connection;
|
||||
use crate::hframe::HFrame;
|
||||
use crate::{Error, Header, Res};
|
||||
use crate::{RecvMessageEvents, ResetType};
|
||||
use neqo_common::{qerror, qinfo, qtrace};
|
||||
use neqo_transport::{AppError, Connection};
|
||||
use crate::{CloseType, Error, Http3StreamInfo, HttpRecvStreamEvents, RecvStreamEvents, Res};
|
||||
use neqo_common::{qerror, qinfo, qtrace, Header};
|
||||
use neqo_transport::{Connection, StreamId};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::VecDeque;
|
||||
use std::convert::TryFrom;
|
||||
|
@ -35,11 +34,11 @@ enum PushState {
|
|||
headers: Vec<Header>,
|
||||
},
|
||||
OnlyPushStream {
|
||||
stream_id: u64,
|
||||
stream_id: StreamId,
|
||||
events: Vec<Http3ClientEvent>,
|
||||
},
|
||||
Active {
|
||||
stream_id: u64,
|
||||
stream_id: StreamId,
|
||||
headers: Vec<Header>,
|
||||
},
|
||||
Closed,
|
||||
|
@ -177,7 +176,7 @@ impl PushController {
|
|||
pub fn new_push_promise(
|
||||
&mut self,
|
||||
push_id: u64,
|
||||
ref_stream_id: u64,
|
||||
ref_stream_id: StreamId,
|
||||
new_headers: Vec<Header>,
|
||||
) -> Res<()> {
|
||||
qtrace!(
|
||||
|
@ -231,7 +230,7 @@ impl PushController {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn add_new_push_stream(&mut self, push_id: u64, stream_id: u64) -> Res<bool> {
|
||||
pub fn add_new_push_stream(&mut self, push_id: u64, stream_id: StreamId) -> Res<bool> {
|
||||
qtrace!(
|
||||
"A new push stream with push_id={} stream_id={}",
|
||||
push_id,
|
||||
|
@ -305,7 +304,7 @@ impl PushController {
|
|||
}
|
||||
PushState::OnlyPushStream { stream_id, .. }
|
||||
| PushState::Active { stream_id, .. } => {
|
||||
mem::drop(base_handler.stream_reset(
|
||||
mem::drop(base_handler.stream_stop_sending(
|
||||
conn,
|
||||
stream_id,
|
||||
Error::HttpRequestCancelled.code(),
|
||||
|
@ -359,7 +358,7 @@ impl PushController {
|
|||
Some(PushState::Active { stream_id, .. }) => {
|
||||
self.conn_events.remove_events_for_push_id(push_id);
|
||||
// Cancel the stream. the transport steam may already be done, so ignore an error.
|
||||
mem::drop(base_handler.stream_reset(
|
||||
mem::drop(base_handler.stream_stop_sending(
|
||||
conn,
|
||||
*stream_id,
|
||||
Error::HttpRequestCancelled.code(),
|
||||
|
@ -371,7 +370,7 @@ impl PushController {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn push_stream_reset(&mut self, push_id: u64, app_error: AppError, reset_type: ResetType) {
|
||||
pub fn push_stream_reset(&mut self, push_id: u64, close_type: CloseType) {
|
||||
qtrace!("Push stream has been reset, push_id={}", push_id);
|
||||
|
||||
if let Some(push_state) = self.push_streams.get(push_id) {
|
||||
|
@ -382,7 +381,7 @@ impl PushController {
|
|||
PushState::Active { .. } => {
|
||||
self.push_streams.close(push_id);
|
||||
self.conn_events.remove_events_for_push_id(push_id);
|
||||
if reset_type == ResetType::Local {
|
||||
if let CloseType::LocalError(app_error) = close_type {
|
||||
self.conn_events.push_reset(push_id, app_error);
|
||||
} else {
|
||||
self.conn_events.push_canceled(push_id);
|
||||
|
@ -398,7 +397,7 @@ impl PushController {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_active_stream_id(&mut self, push_id: u64) -> Option<u64> {
|
||||
pub fn get_active_stream_id(&mut self, push_id: u64) -> Option<StreamId> {
|
||||
match self.push_streams.get(push_id) {
|
||||
Some(PushState::Active { stream_id, .. }) => Some(*stream_id),
|
||||
_ => None,
|
||||
|
@ -447,6 +446,11 @@ impl PushController {
|
|||
}
|
||||
}
|
||||
|
||||
/// `RecvPushEvents` relays a push stream events to `PushController`.
|
||||
/// It informs `PushController` when a push stream is done or canceled.
|
||||
/// Also when headers or data is ready and `PushController` decide whether to post
|
||||
/// `PushHeaderReady` and `PushDataReadable` events or to postpone them if
|
||||
/// a `push_promise` has not been yet received for the stream.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct RecvPushEvents {
|
||||
push_id: u64,
|
||||
|
@ -462,8 +466,36 @@ impl RecvPushEvents {
|
|||
}
|
||||
}
|
||||
|
||||
impl RecvMessageEvents for RecvPushEvents {
|
||||
fn header_ready(&self, _stream_id: u64, headers: Vec<Header>, interim: bool, fin: bool) {
|
||||
impl RecvStreamEvents for RecvPushEvents {
|
||||
fn data_readable(&self, _stream_info: Http3StreamInfo) {
|
||||
self.push_handler.borrow_mut().new_stream_event(
|
||||
self.push_id,
|
||||
Http3ClientEvent::PushDataReadable {
|
||||
push_id: self.push_id,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn recv_closed(&self, _stream_info: Http3StreamInfo, close_type: CloseType) {
|
||||
match close_type {
|
||||
CloseType::ResetApp(_) => {}
|
||||
CloseType::ResetRemote(_) | CloseType::LocalError(_) => self
|
||||
.push_handler
|
||||
.borrow_mut()
|
||||
.push_stream_reset(self.push_id, close_type),
|
||||
CloseType::Done => self.push_handler.borrow_mut().close(self.push_id),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HttpRecvStreamEvents for RecvPushEvents {
|
||||
fn header_ready(
|
||||
&self,
|
||||
_stream_info: Http3StreamInfo,
|
||||
headers: Vec<Header>,
|
||||
interim: bool,
|
||||
fin: bool,
|
||||
) {
|
||||
self.push_handler.borrow_mut().new_stream_event(
|
||||
self.push_id,
|
||||
Http3ClientEvent::PushHeaderReady {
|
||||
|
@ -474,15 +506,4 @@ impl RecvMessageEvents for RecvPushEvents {
|
|||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn data_readable(&self, _stream_id: u64) {
|
||||
self.push_handler.borrow_mut().new_stream_event(
|
||||
self.push_id,
|
||||
Http3ClientEvent::PushDataReadable {
|
||||
push_id: self.push_id,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn reset(&self, _stream_id: u64, _error: AppError, _local: bool) {}
|
||||
}
|
||||
|
|
|
@ -1,130 +0,0 @@
|
|||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use crate::priority::PriorityHandler;
|
||||
use crate::push_controller::{PushController, RecvPushEvents};
|
||||
use crate::recv_message::{MessageType, RecvMessage};
|
||||
use crate::{Http3StreamType, HttpRecvStream, Priority, ReceiveOutput, RecvStream, Res, ResetType};
|
||||
use neqo_qpack::decoder::QPackDecoder;
|
||||
use neqo_transport::{AppError, Connection};
|
||||
use std::cell::RefCell;
|
||||
use std::fmt::Display;
|
||||
use std::rc::Rc;
|
||||
|
||||
// The `PushController` keeps information about all push streams. Each push stream is responsible for contacting the
|
||||
// `PushController` to consult it about the push state and to inform it when the push stream is done (this are signal
|
||||
// from the peer: stream has been closed or reset). `PushController` handles CANCEL_PUSH frames and canceling
|
||||
// push from applications and PUSH_PROMISE frames.
|
||||
//
|
||||
// `PushStream` is responsible for reading from a push stream.
|
||||
// It is created when a new push stream is received.
|
||||
//
|
||||
// `PushStreams` are kept in Http3Connection::recv_streams the same as a normal request/response stream.
|
||||
// Http3Connection and read_data is responsible for reading the push data.
|
||||
//
|
||||
// PushHeaderReady and PushDataReadable are posted through the `PushController` that may decide to postpone them if
|
||||
// a push_promise has not been yet received for the stream.
|
||||
//
|
||||
// `PushStream` is responsible for removing itself from the `PushController`.
|
||||
//
|
||||
// `PushStream` may be reset from the peer in the same way as a request stream. The `PushStream` informs the
|
||||
// `PushController` that will set the push state to closed and remove any push events.
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct PushStream {
|
||||
stream_id: u64,
|
||||
push_id: u64,
|
||||
response: RecvMessage,
|
||||
push_handler: Rc<RefCell<PushController>>,
|
||||
}
|
||||
|
||||
impl PushStream {
|
||||
pub fn new(
|
||||
stream_id: u64,
|
||||
push_id: u64,
|
||||
push_handler: Rc<RefCell<PushController>>,
|
||||
qpack_decoder: Rc<RefCell<QPackDecoder>>,
|
||||
priority: Priority,
|
||||
) -> Self {
|
||||
Self {
|
||||
response: RecvMessage::new(
|
||||
MessageType::Response,
|
||||
stream_id,
|
||||
qpack_decoder,
|
||||
Box::new(RecvPushEvents::new(push_id, push_handler.clone())),
|
||||
None,
|
||||
PriorityHandler::new(true, priority),
|
||||
),
|
||||
stream_id,
|
||||
push_id,
|
||||
push_handler,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for PushStream {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"Push stream {:?} push_id={}",
|
||||
self.stream_id, self.push_id
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl RecvStream for PushStream {
|
||||
fn receive(&mut self, conn: &mut Connection) -> Res<ReceiveOutput> {
|
||||
self.response.receive(conn)?;
|
||||
if self.response.done() {
|
||||
self.push_handler.borrow_mut().close(self.push_id);
|
||||
}
|
||||
Ok(ReceiveOutput::NoOutput)
|
||||
}
|
||||
|
||||
fn done(&self) -> bool {
|
||||
self.response.done()
|
||||
}
|
||||
|
||||
fn stream_reset(&mut self, app_error: AppError, reset_type: ResetType) -> Res<()> {
|
||||
match reset_type {
|
||||
ResetType::App => {}
|
||||
t => {
|
||||
self.push_handler
|
||||
.borrow_mut()
|
||||
.push_stream_reset(self.push_id, app_error, t);
|
||||
}
|
||||
}
|
||||
self.response.stream_reset(app_error, reset_type)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn stream_type(&self) -> Http3StreamType {
|
||||
Http3StreamType::Push
|
||||
}
|
||||
|
||||
fn http_stream(&mut self) -> Option<&mut dyn HttpRecvStream> {
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl HttpRecvStream for PushStream {
|
||||
fn header_unblocked(&mut self, conn: &mut Connection) -> Res<()> {
|
||||
self.receive(conn)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_data(&mut self, conn: &mut Connection, buf: &mut [u8]) -> Res<(usize, bool)> {
|
||||
let res = self.response.read_data(conn, buf);
|
||||
if self.response.done() {
|
||||
self.push_handler.borrow_mut().close(self.push_id);
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
fn priority_handler_mut(&mut self) -> &mut PriorityHandler {
|
||||
self.response.priority_handler_mut()
|
||||
}
|
||||
}
|
|
@ -9,8 +9,9 @@ use std::convert::TryFrom;
|
|||
use qlog::{self, event::Event, H3DataRecipient};
|
||||
|
||||
use neqo_common::qlog::NeqoQlog;
|
||||
use neqo_transport::StreamId;
|
||||
|
||||
pub fn h3_data_moved_up(qlog: &mut NeqoQlog, stream_id: u64, amount: usize) {
|
||||
pub fn h3_data_moved_up(qlog: &mut NeqoQlog, stream_id: StreamId, amount: usize) {
|
||||
qlog.add_event(|| {
|
||||
Some(Event::h3_data_moved(
|
||||
stream_id.to_string(),
|
||||
|
@ -23,7 +24,7 @@ pub fn h3_data_moved_up(qlog: &mut NeqoQlog, stream_id: u64, amount: usize) {
|
|||
});
|
||||
}
|
||||
|
||||
pub fn h3_data_moved_down(qlog: &mut NeqoQlog, stream_id: u64, amount: usize) {
|
||||
pub fn h3_data_moved_down(qlog: &mut NeqoQlog, stream_id: StreamId, amount: usize) {
|
||||
qlog.add_event(|| {
|
||||
Some(Event::h3_data_moved(
|
||||
stream_id.to_string(),
|
||||
|
|
|
@ -4,46 +4,41 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use crate::{
|
||||
AppError, Error, Http3StreamType, HttpRecvStream, ReceiveOutput, RecvStream, Res, ResetType,
|
||||
};
|
||||
use crate::{CloseType, Error, Http3StreamType, ReceiveOutput, RecvStream, Res, Stream};
|
||||
use neqo_qpack::QPackDecoder;
|
||||
use neqo_transport::Connection;
|
||||
use neqo_transport::{Connection, StreamId};
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DecoderRecvStream {
|
||||
stream_id: u64,
|
||||
stream_id: StreamId,
|
||||
decoder: Rc<RefCell<QPackDecoder>>,
|
||||
}
|
||||
|
||||
impl DecoderRecvStream {
|
||||
pub fn new(stream_id: u64, decoder: Rc<RefCell<QPackDecoder>>) -> Self {
|
||||
pub fn new(stream_id: StreamId, decoder: Rc<RefCell<QPackDecoder>>) -> Self {
|
||||
Self { stream_id, decoder }
|
||||
}
|
||||
}
|
||||
|
||||
impl RecvStream for DecoderRecvStream {
|
||||
fn stream_reset(&mut self, _error: AppError, _reset_type: ResetType) -> Res<()> {
|
||||
Err(Error::HttpClosedCriticalStream)
|
||||
}
|
||||
|
||||
fn receive(&mut self, conn: &mut Connection) -> Res<ReceiveOutput> {
|
||||
Ok(ReceiveOutput::UnblockedStreams(
|
||||
self.decoder.borrow_mut().receive(conn, self.stream_id)?,
|
||||
))
|
||||
}
|
||||
|
||||
fn done(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
impl Stream for DecoderRecvStream {
|
||||
fn stream_type(&self) -> Http3StreamType {
|
||||
Http3StreamType::Decoder
|
||||
}
|
||||
}
|
||||
|
||||
fn http_stream(&mut self) -> Option<&mut dyn HttpRecvStream> {
|
||||
None
|
||||
impl RecvStream for DecoderRecvStream {
|
||||
fn reset(&mut self, _close_type: CloseType) -> Res<()> {
|
||||
Err(Error::HttpClosedCriticalStream)
|
||||
}
|
||||
|
||||
fn receive(&mut self, conn: &mut Connection) -> Res<(ReceiveOutput, bool)> {
|
||||
Ok((
|
||||
ReceiveOutput::UnblockedStreams(
|
||||
self.decoder.borrow_mut().receive(conn, self.stream_id)?,
|
||||
),
|
||||
false,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,45 +4,37 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use crate::{
|
||||
AppError, Error, Http3StreamType, HttpRecvStream, ReceiveOutput, RecvStream, Res, ResetType,
|
||||
};
|
||||
use crate::{CloseType, Error, Http3StreamType, ReceiveOutput, RecvStream, Res, Stream};
|
||||
use neqo_qpack::QPackEncoder;
|
||||
use neqo_transport::Connection;
|
||||
use neqo_transport::{Connection, StreamId};
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EncoderRecvStream {
|
||||
stream_id: u64,
|
||||
stream_id: StreamId,
|
||||
encoder: Rc<RefCell<QPackEncoder>>,
|
||||
}
|
||||
|
||||
impl EncoderRecvStream {
|
||||
pub fn new(stream_id: u64, encoder: Rc<RefCell<QPackEncoder>>) -> Self {
|
||||
pub fn new(stream_id: StreamId, encoder: Rc<RefCell<QPackEncoder>>) -> Self {
|
||||
Self { stream_id, encoder }
|
||||
}
|
||||
}
|
||||
|
||||
impl RecvStream for EncoderRecvStream {
|
||||
fn stream_reset(&mut self, _error: AppError, _reset_type: ResetType) -> Res<()> {
|
||||
Err(Error::HttpClosedCriticalStream)
|
||||
}
|
||||
|
||||
fn receive(&mut self, conn: &mut Connection) -> Res<ReceiveOutput> {
|
||||
self.encoder.borrow_mut().receive(conn, self.stream_id)?;
|
||||
Ok(ReceiveOutput::NoOutput)
|
||||
}
|
||||
|
||||
fn done(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
impl Stream for EncoderRecvStream {
|
||||
fn stream_type(&self) -> Http3StreamType {
|
||||
Http3StreamType::Encoder
|
||||
}
|
||||
}
|
||||
|
||||
fn http_stream(&mut self) -> Option<&mut dyn HttpRecvStream> {
|
||||
None
|
||||
impl RecvStream for EncoderRecvStream {
|
||||
fn reset(&mut self, _close_type: CloseType) -> Res<()> {
|
||||
Err(Error::HttpClosedCriticalStream)
|
||||
}
|
||||
|
||||
fn receive(&mut self, conn: &mut Connection) -> Res<(ReceiveOutput, bool)> {
|
||||
self.encoder.borrow_mut().receive(conn, self.stream_id)?;
|
||||
Ok((ReceiveOutput::NoOutput, false))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,17 +4,17 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use crate::hframe::{HFrame, HFrameReader};
|
||||
use crate::hframe::{HFrame, HFrameReader, H3_FRAME_TYPE_HEADERS};
|
||||
use crate::push_controller::PushController;
|
||||
use crate::{
|
||||
qlog, Error, Header, Http3StreamType, HttpRecvStream, ReceiveOutput, RecvMessageEvents,
|
||||
RecvStream, Res, ResetType,
|
||||
headers_checks::{headers_valid, is_interim},
|
||||
priority::PriorityHandler,
|
||||
qlog, CloseType, Error, Http3StreamInfo, Http3StreamType, HttpRecvStream, HttpRecvStreamEvents,
|
||||
MessageType, ReceiveOutput, RecvStream, Res, Stream,
|
||||
};
|
||||
|
||||
use crate::priority::PriorityHandler;
|
||||
use neqo_common::{qdebug, qinfo, qtrace};
|
||||
use neqo_common::{qdebug, qinfo, qtrace, Header};
|
||||
use neqo_qpack::decoder::QPackDecoder;
|
||||
use neqo_transport::{AppError, Connection};
|
||||
use neqo_transport::{Connection, StreamId};
|
||||
use std::cell::RefCell;
|
||||
use std::cmp::min;
|
||||
use std::collections::VecDeque;
|
||||
|
@ -22,17 +22,12 @@ use std::convert::TryFrom;
|
|||
use std::fmt::Debug;
|
||||
use std::rc::Rc;
|
||||
|
||||
const PSEUDO_HEADER_STATUS: u8 = 0x1;
|
||||
const PSEUDO_HEADER_METHOD: u8 = 0x2;
|
||||
const PSEUDO_HEADER_SCHEME: u8 = 0x4;
|
||||
const PSEUDO_HEADER_AUTHORITY: u8 = 0x8;
|
||||
const PSEUDO_HEADER_PATH: u8 = 0x10;
|
||||
const REGULAR_HEADER: u8 = 0x80;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum MessageType {
|
||||
Request,
|
||||
Response,
|
||||
#[allow(clippy::module_name_repetitions)]
|
||||
pub struct RecvMessageInfo {
|
||||
pub message_type: MessageType,
|
||||
pub stream_type: Http3StreamType,
|
||||
pub stream_id: StreamId,
|
||||
pub header_frame_type_read: bool,
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -50,6 +45,10 @@ pub enum MessageType {
|
|||
* ClosePending : waiting for app to pick up data, after that we can delete
|
||||
* the TransactionClient.
|
||||
* Closed
|
||||
* ExtendedConnect: this request is for a WebTransport session. In this
|
||||
* state RecvMessage will not be treated as a HTTP
|
||||
* stream anymore. It is waiting to be transformed
|
||||
* into WebTransport session or to be closed.
|
||||
*/
|
||||
#[derive(Debug)]
|
||||
enum RecvMessageState {
|
||||
|
@ -60,6 +59,7 @@ enum RecvMessageState {
|
|||
WaitingForFinAfterTrailers { frame_reader: HFrameReader },
|
||||
ClosePending, // Close must first be read by application
|
||||
Closed,
|
||||
ExtendedConnect,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -72,10 +72,11 @@ struct PushInfo {
|
|||
pub(crate) struct RecvMessage {
|
||||
state: RecvMessageState,
|
||||
message_type: MessageType,
|
||||
stream_type: Http3StreamType,
|
||||
qpack_decoder: Rc<RefCell<QPackDecoder>>,
|
||||
conn_events: Box<dyn RecvMessageEvents>,
|
||||
conn_events: Box<dyn HttpRecvStreamEvents>,
|
||||
push_handler: Option<Rc<RefCell<PushController>>>,
|
||||
stream_id: u64,
|
||||
stream_id: StreamId,
|
||||
priority_handler: PriorityHandler,
|
||||
blocked_push_promise: VecDeque<PushInfo>,
|
||||
}
|
||||
|
@ -88,22 +89,26 @@ impl ::std::fmt::Display for RecvMessage {
|
|||
|
||||
impl RecvMessage {
|
||||
pub fn new(
|
||||
message_type: MessageType,
|
||||
stream_id: u64,
|
||||
message_info: &RecvMessageInfo,
|
||||
qpack_decoder: Rc<RefCell<QPackDecoder>>,
|
||||
conn_events: Box<dyn RecvMessageEvents>,
|
||||
conn_events: Box<dyn HttpRecvStreamEvents>,
|
||||
push_handler: Option<Rc<RefCell<PushController>>>,
|
||||
priority_handler: PriorityHandler,
|
||||
) -> Self {
|
||||
Self {
|
||||
state: RecvMessageState::WaitingForResponseHeaders {
|
||||
frame_reader: HFrameReader::new(),
|
||||
frame_reader: if message_info.header_frame_type_read {
|
||||
HFrameReader::new_with_type(H3_FRAME_TYPE_HEADERS)
|
||||
} else {
|
||||
HFrameReader::new()
|
||||
},
|
||||
},
|
||||
message_type,
|
||||
message_type: message_info.message_type,
|
||||
stream_type: message_info.stream_type,
|
||||
qpack_decoder,
|
||||
conn_events,
|
||||
push_handler,
|
||||
stream_id,
|
||||
stream_id: message_info.stream_id,
|
||||
priority_handler,
|
||||
blocked_push_promise: VecDeque::new(),
|
||||
}
|
||||
|
@ -151,9 +156,12 @@ impl RecvMessage {
|
|||
|
||||
fn add_headers(&mut self, mut headers: Vec<Header>, fin: bool) -> Res<()> {
|
||||
qtrace!([self], "Add new headers fin={}", fin);
|
||||
let interim = self.is_interim(&headers)?;
|
||||
self.headers_valid(&headers)?;
|
||||
if matches!(self.message_type, MessageType::Response) {
|
||||
let interim = match self.message_type {
|
||||
MessageType::Request => false,
|
||||
MessageType::Response => is_interim(&headers)?,
|
||||
};
|
||||
headers_valid(&headers, self.message_type)?;
|
||||
if self.message_type == MessageType::Response {
|
||||
headers.retain(Header::is_allowed_for_response);
|
||||
}
|
||||
|
||||
|
@ -161,12 +169,27 @@ impl RecvMessage {
|
|||
return Err(Error::HttpGeneralProtocolStream);
|
||||
}
|
||||
|
||||
self.conn_events
|
||||
.header_ready(self.stream_id, headers, interim, fin);
|
||||
let is_web_transport = self.message_type == MessageType::Request
|
||||
&& headers
|
||||
.iter()
|
||||
.any(|h| h.name() == ":method" && h.value() == "CONNECT")
|
||||
&& headers
|
||||
.iter()
|
||||
.any(|h| h.name() == ":protocol" && h.value() == "webtransport");
|
||||
if is_web_transport {
|
||||
self.conn_events
|
||||
.extended_connect_new_session(self.stream_id, headers);
|
||||
} else {
|
||||
self.conn_events
|
||||
.header_ready(self.get_stream_info(), headers, interim, fin);
|
||||
}
|
||||
|
||||
if fin {
|
||||
self.set_closed();
|
||||
} else {
|
||||
self.state = if interim {
|
||||
self.state = if is_web_transport {
|
||||
RecvMessageState::ExtendedConnect
|
||||
} else if interim {
|
||||
RecvMessageState::WaitingForResponseHeaders {
|
||||
frame_reader: HFrameReader::new(),
|
||||
}
|
||||
|
@ -192,7 +215,7 @@ impl RecvMessage {
|
|||
RecvMessageState::WaitingForData { .. }
|
||||
| RecvMessageState::WaitingForFinAfterTrailers { .. } => {
|
||||
if post_readable_event {
|
||||
self.conn_events.data_readable(self.stream_id);
|
||||
self.conn_events.data_readable(self.get_stream_info());
|
||||
}
|
||||
}
|
||||
_ => unreachable!("Closing an already closed transaction."),
|
||||
|
@ -299,7 +322,10 @@ impl RecvMessage {
|
|||
.decode_header_block(header_block, self.stream_id)?;
|
||||
if let Some(headers) = d_headers {
|
||||
self.add_headers(headers, done)?;
|
||||
if matches!(self.state, RecvMessageState::Closed) {
|
||||
if matches!(
|
||||
self.state,
|
||||
RecvMessageState::Closed | RecvMessageState::ExtendedConnect
|
||||
) {
|
||||
break Ok(());
|
||||
}
|
||||
} else {
|
||||
|
@ -309,13 +335,17 @@ impl RecvMessage {
|
|||
}
|
||||
RecvMessageState::ReadingData { .. } => {
|
||||
if post_readable_event {
|
||||
self.conn_events.data_readable(self.stream_id);
|
||||
self.conn_events.data_readable(self.get_stream_info());
|
||||
}
|
||||
break Ok(());
|
||||
}
|
||||
RecvMessageState::ClosePending | RecvMessageState::Closed => {
|
||||
panic!("Stream readable after being closed!");
|
||||
}
|
||||
RecvMessageState::ExtendedConnect => {
|
||||
// Ignore read event, this request is waiting to be picked up by a new WebTransportSession
|
||||
break Ok(());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -327,6 +357,8 @@ impl RecvMessage {
|
|||
.cancel_stream(self.stream_id);
|
||||
}
|
||||
self.state = RecvMessageState::Closed;
|
||||
self.conn_events
|
||||
.recv_closed(self.get_stream_info(), CloseType::Done);
|
||||
}
|
||||
|
||||
fn closing(&self) -> bool {
|
||||
|
@ -336,152 +368,38 @@ impl RecvMessage {
|
|||
)
|
||||
}
|
||||
|
||||
fn is_interim(&self, headers: &[Header]) -> Res<bool> {
|
||||
match self.message_type {
|
||||
MessageType::Response => {
|
||||
let status = headers.iter().find(|h| h.name() == ":status");
|
||||
if let Some(h) = status {
|
||||
#[allow(clippy::map_err_ignore)]
|
||||
let status_code = h.value().parse::<i32>().map_err(|_| Error::InvalidHeader)?;
|
||||
Ok((100..200).contains(&status_code))
|
||||
} else {
|
||||
Err(Error::InvalidHeader)
|
||||
}
|
||||
}
|
||||
MessageType::Request => Ok(false),
|
||||
}
|
||||
fn get_stream_info(&self) -> Http3StreamInfo {
|
||||
Http3StreamInfo::new(self.stream_id, Http3StreamType::Http)
|
||||
}
|
||||
}
|
||||
|
||||
fn track_pseudo(name: &str, state: &mut u8, message_type: &MessageType) -> Res<bool> {
|
||||
let (pseudo, bit) = if name.starts_with(':') {
|
||||
if *state & REGULAR_HEADER != 0 {
|
||||
return Err(Error::InvalidHeader);
|
||||
}
|
||||
let bit = match message_type {
|
||||
MessageType::Response => match name {
|
||||
":status" => PSEUDO_HEADER_STATUS,
|
||||
_ => return Err(Error::InvalidHeader),
|
||||
},
|
||||
MessageType::Request => match name {
|
||||
":method" => PSEUDO_HEADER_METHOD,
|
||||
":scheme" => PSEUDO_HEADER_SCHEME,
|
||||
":authority" => PSEUDO_HEADER_AUTHORITY,
|
||||
":path" => PSEUDO_HEADER_PATH,
|
||||
_ => return Err(Error::InvalidHeader),
|
||||
},
|
||||
};
|
||||
(true, bit)
|
||||
} else {
|
||||
(false, REGULAR_HEADER)
|
||||
};
|
||||
|
||||
if *state & bit == 0 || !pseudo {
|
||||
*state |= bit;
|
||||
Ok(pseudo)
|
||||
} else {
|
||||
Err(Error::InvalidHeader)
|
||||
}
|
||||
}
|
||||
|
||||
fn headers_valid(&self, headers: &[Header]) -> Res<()> {
|
||||
let mut method_value: Option<&str> = None;
|
||||
let mut pseudo_state = 0;
|
||||
for header in headers {
|
||||
let is_pseudo =
|
||||
Self::track_pseudo(header.name(), &mut pseudo_state, &self.message_type)?;
|
||||
|
||||
let mut bytes = header.name().bytes();
|
||||
if is_pseudo {
|
||||
if header.name() == ":method" {
|
||||
method_value = Some(header.value());
|
||||
}
|
||||
let _ = bytes.next();
|
||||
}
|
||||
|
||||
if bytes.any(|b| matches!(b, 0 | 0x10 | 0x13 | 0x3a | 0x41..=0x5a)) {
|
||||
return Err(Error::InvalidHeader); // illegal characters.
|
||||
}
|
||||
}
|
||||
// Clear the regular header bit, since we only check pseudo headers below.
|
||||
pseudo_state &= !REGULAR_HEADER;
|
||||
let pseudo_header_mask = match self.message_type {
|
||||
MessageType::Response => PSEUDO_HEADER_STATUS,
|
||||
MessageType::Request => {
|
||||
if method_value == Some(&"CONNECT".to_string()) {
|
||||
PSEUDO_HEADER_METHOD | PSEUDO_HEADER_AUTHORITY
|
||||
} else {
|
||||
PSEUDO_HEADER_METHOD | PSEUDO_HEADER_SCHEME | PSEUDO_HEADER_PATH
|
||||
}
|
||||
}
|
||||
};
|
||||
if pseudo_state & pseudo_header_mask != pseudo_header_mask {
|
||||
return Err(Error::InvalidHeader);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
impl Stream for RecvMessage {
|
||||
fn stream_type(&self) -> Http3StreamType {
|
||||
self.stream_type
|
||||
}
|
||||
}
|
||||
|
||||
impl RecvStream for RecvMessage {
|
||||
fn receive(&mut self, conn: &mut Connection) -> Res<ReceiveOutput> {
|
||||
fn receive(&mut self, conn: &mut Connection) -> Res<(ReceiveOutput, bool)> {
|
||||
self.receive_internal(conn, true)?;
|
||||
Ok(ReceiveOutput::NoOutput)
|
||||
Ok((
|
||||
ReceiveOutput::NoOutput,
|
||||
matches!(self.state, RecvMessageState::Closed),
|
||||
))
|
||||
}
|
||||
|
||||
fn done(&self) -> bool {
|
||||
matches!(self.state, RecvMessageState::Closed)
|
||||
}
|
||||
|
||||
fn stream_reset(&mut self, app_error: AppError, reset_type: ResetType) -> Res<()> {
|
||||
fn reset(&mut self, close_type: CloseType) -> Res<()> {
|
||||
if !self.closing() || !self.blocked_push_promise.is_empty() {
|
||||
self.qpack_decoder
|
||||
.borrow_mut()
|
||||
.cancel_stream(self.stream_id);
|
||||
}
|
||||
match reset_type {
|
||||
ResetType::Local => {
|
||||
self.conn_events.reset(self.stream_id, app_error, true);
|
||||
}
|
||||
ResetType::Remote => {
|
||||
self.conn_events.reset(self.stream_id, app_error, false);
|
||||
}
|
||||
ResetType::App => {}
|
||||
}
|
||||
self.conn_events
|
||||
.recv_closed(self.get_stream_info(), close_type);
|
||||
self.state = RecvMessageState::Closed;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn stream_type(&self) -> Http3StreamType {
|
||||
Http3StreamType::Http
|
||||
}
|
||||
|
||||
fn http_stream(&mut self) -> Option<&mut dyn HttpRecvStream> {
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl HttpRecvStream for RecvMessage {
|
||||
fn header_unblocked(&mut self, conn: &mut Connection) -> Res<()> {
|
||||
while let Some(p) = self.blocked_push_promise.front() {
|
||||
if let Some(headers) = self
|
||||
.qpack_decoder
|
||||
.borrow_mut()
|
||||
.decode_header_block(&p.header_block, self.stream_id)?
|
||||
{
|
||||
self.push_handler
|
||||
.as_ref()
|
||||
.ok_or(Error::HttpFrameUnexpected)?
|
||||
.borrow_mut()
|
||||
.new_push_promise(p.push_id, self.stream_id, headers)?;
|
||||
self.blocked_push_promise.pop_front();
|
||||
} else {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
self.receive_internal(conn, true)
|
||||
}
|
||||
|
||||
fn read_data(&mut self, conn: &mut Connection, buf: &mut [u8]) -> Res<(usize, bool)> {
|
||||
let mut written = 0;
|
||||
loop {
|
||||
|
@ -523,7 +441,42 @@ impl HttpRecvStream for RecvMessage {
|
|||
}
|
||||
}
|
||||
|
||||
fn http_stream(&mut self) -> Option<&mut dyn HttpRecvStream> {
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl HttpRecvStream for RecvMessage {
|
||||
fn header_unblocked(&mut self, conn: &mut Connection) -> Res<(ReceiveOutput, bool)> {
|
||||
while let Some(p) = self.blocked_push_promise.front() {
|
||||
if let Some(headers) = self
|
||||
.qpack_decoder
|
||||
.borrow_mut()
|
||||
.decode_header_block(&p.header_block, self.stream_id)?
|
||||
{
|
||||
self.push_handler
|
||||
.as_ref()
|
||||
.ok_or(Error::HttpFrameUnexpected)?
|
||||
.borrow_mut()
|
||||
.new_push_promise(p.push_id, self.stream_id, headers)?;
|
||||
self.blocked_push_promise.pop_front();
|
||||
} else {
|
||||
return Ok((ReceiveOutput::NoOutput, false));
|
||||
}
|
||||
}
|
||||
|
||||
self.receive(conn)
|
||||
}
|
||||
|
||||
fn priority_handler_mut(&mut self) -> &mut PriorityHandler {
|
||||
&mut self.priority_handler
|
||||
}
|
||||
|
||||
fn set_new_listener(&mut self, conn_events: Box<dyn HttpRecvStreamEvents>) {
|
||||
self.conn_events = conn_events;
|
||||
}
|
||||
|
||||
fn extended_connect_wait_for_response(&self) -> bool {
|
||||
matches!(self.state, RecvMessageState::ExtendedConnect)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,15 +5,19 @@
|
|||
// except according to those terms.
|
||||
|
||||
use crate::hframe::HFrame;
|
||||
use crate::qlog;
|
||||
use crate::Header;
|
||||
use crate::{Error, Res};
|
||||
use crate::{
|
||||
headers_checks::{headers_valid, is_interim, trailers_valid},
|
||||
qlog, BufferedStream, CloseType, Error, Http3StreamInfo, Http3StreamType, HttpSendStream, Res,
|
||||
SendStream, SendStreamEvents, Stream,
|
||||
};
|
||||
|
||||
use neqo_common::{qdebug, qinfo, qtrace, Encoder};
|
||||
use neqo_common::{qdebug, qinfo, qtrace, Encoder, Header, MessageType};
|
||||
use neqo_qpack::encoder::QPackEncoder;
|
||||
use neqo_transport::{AppError, Connection};
|
||||
use neqo_transport::{Connection, StreamId};
|
||||
use std::cell::RefCell;
|
||||
use std::cmp::min;
|
||||
use std::fmt::Debug;
|
||||
use std::rc::Rc;
|
||||
|
||||
const MAX_DATA_HEADER_SIZE_2: usize = (1 << 6) - 1; // Maximal amount of data with DATA frame header size 2
|
||||
const MAX_DATA_HEADER_SIZE_2_LIMIT: usize = MAX_DATA_HEADER_SIZE_2 + 3; // 63 + 3 (size of the next buffer data frame header)
|
||||
|
@ -22,246 +26,237 @@ const MAX_DATA_HEADER_SIZE_3_LIMIT: usize = MAX_DATA_HEADER_SIZE_3 + 5; // 16383
|
|||
const MAX_DATA_HEADER_SIZE_5: usize = (1 << 30) - 1; // Maximal amount of data with DATA frame header size 3
|
||||
const MAX_DATA_HEADER_SIZE_5_LIMIT: usize = MAX_DATA_HEADER_SIZE_5 + 9; // 1073741823 + 9 (size of the next buffer data frame header)
|
||||
|
||||
pub(crate) trait SendMessageEvents: Debug {
|
||||
fn data_writable(&self, stream_id: u64);
|
||||
fn remove_send_side_event(&self, stream_id: u64);
|
||||
fn stop_sending(&self, stream_id: u64, app_err: AppError);
|
||||
/// A HTTP message, request and response, consists of headers, optional data and an optional
|
||||
/// trailer header block. This state machine does not reflect what was already sent to the
|
||||
/// transport layer but only reflect what has been supplied to the `SendMessage`.It is
|
||||
/// represented by the following states:
|
||||
/// `WaitingForHeaders` - the headers have not been supplied yet. In this state only a
|
||||
/// request/response header can be added. When headers are supplied
|
||||
/// the state changes to `WaitingForData`. A response may contain
|
||||
/// multiple messages only if all but the last one are informational(1xx)
|
||||
/// responses. The informational responses can only contain headers,
|
||||
/// therefore after an informational response is received the state
|
||||
/// machine states in `WaitingForHeaders` state.
|
||||
/// `WaitingForData` - in this state, data and trailers can be supplied. This state means that
|
||||
/// a request or response header is already supplied.
|
||||
/// `TrailersSet` - trailers have been supplied. At this stage no more data or headers can be
|
||||
/// supply only a fin.
|
||||
/// `Done` - in this state no more data or headers can be added. This state is entered when the
|
||||
/// message is closed.
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum MessageState {
|
||||
WaitingForHeaders,
|
||||
WaitingForData,
|
||||
TrailersSet,
|
||||
Done,
|
||||
}
|
||||
|
||||
/*
|
||||
* SendMessage states:
|
||||
* Uninitialized
|
||||
* Initialized : Headers are present but still not encoded. A message body may be present as well.
|
||||
* The client side sends a message body using the send_body() function that directly
|
||||
* writes into a transport stream. The server side sets headers and body when
|
||||
* initializing a send message (TODO: make server use send_body as well)
|
||||
* SendingInitialMessage : sending headers and maybe message body. From here we may switch to
|
||||
* SendingData or Closed (if the app does not want to send data and
|
||||
* has already closed the send stream).
|
||||
* SendingData : We are sending request data until the app closes the stream.
|
||||
* Closed
|
||||
*/
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
enum SendMessageState {
|
||||
Uninitialized,
|
||||
Initialized {
|
||||
headers: Vec<Header>,
|
||||
data: Option<Vec<u8>>,
|
||||
fin: bool,
|
||||
},
|
||||
SendingInitialMessage {
|
||||
buf: Vec<u8>,
|
||||
fin: bool,
|
||||
},
|
||||
SendingData,
|
||||
Closed,
|
||||
}
|
||||
|
||||
impl SendMessageState {
|
||||
pub fn is_sending_closed(&self) -> bool {
|
||||
match self {
|
||||
Self::Initialized { fin, .. } | Self::SendingInitialMessage { fin, .. } => *fin,
|
||||
Self::SendingData => false,
|
||||
_ => true,
|
||||
impl MessageState {
|
||||
fn new_headers(&mut self, headers: &[Header], message_type: MessageType) -> Res<()> {
|
||||
match &self {
|
||||
Self::WaitingForHeaders => {
|
||||
// This is only a debug assertion because we expect that application will
|
||||
// do the right thing here and performing the check costs.
|
||||
debug_assert!(headers_valid(headers, message_type).is_ok());
|
||||
match message_type {
|
||||
MessageType::Request => {
|
||||
*self = Self::WaitingForData;
|
||||
}
|
||||
MessageType::Response => {
|
||||
if !is_interim(headers)? {
|
||||
*self = Self::WaitingForData;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Self::WaitingForData => {
|
||||
trailers_valid(headers)?;
|
||||
*self = Self::TrailersSet;
|
||||
Ok(())
|
||||
}
|
||||
Self::TrailersSet | Self::Done => Err(Error::InvalidInput),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn done(&self) -> bool {
|
||||
matches!(self, Self::Closed)
|
||||
fn new_data(&self) -> Res<()> {
|
||||
if &Self::WaitingForData == self {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::InvalidInput)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_state_sending_data(&self) -> bool {
|
||||
matches!(self, Self::SendingData)
|
||||
fn fin(&mut self) -> Res<()> {
|
||||
match &self {
|
||||
Self::WaitingForHeaders | Self::Done => Err(Error::InvalidInput),
|
||||
Self::WaitingForData | Self::TrailersSet => {
|
||||
*self = Self::Done;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn done(&self) -> bool {
|
||||
&Self::Done == self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct SendMessage {
|
||||
state: SendMessageState,
|
||||
stream_id: u64,
|
||||
conn_events: Box<dyn SendMessageEvents>,
|
||||
state: MessageState,
|
||||
message_type: MessageType,
|
||||
stream_type: Http3StreamType,
|
||||
stream: BufferedStream,
|
||||
encoder: Rc<RefCell<QPackEncoder>>,
|
||||
conn_events: Box<dyn SendStreamEvents>,
|
||||
}
|
||||
|
||||
impl SendMessage {
|
||||
pub fn new(stream_id: u64, conn_events: Box<dyn SendMessageEvents>) -> Self {
|
||||
qinfo!("Create a request stream_id={}", stream_id);
|
||||
Self {
|
||||
state: SendMessageState::Uninitialized,
|
||||
stream_id,
|
||||
conn_events,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_with_headers(
|
||||
stream_id: u64,
|
||||
headers: Vec<Header>,
|
||||
conn_events: Box<dyn SendMessageEvents>,
|
||||
pub fn new(
|
||||
message_type: MessageType,
|
||||
stream_type: Http3StreamType,
|
||||
stream_id: StreamId,
|
||||
encoder: Rc<RefCell<QPackEncoder>>,
|
||||
conn_events: Box<dyn SendStreamEvents>,
|
||||
) -> Self {
|
||||
qinfo!("Create a request stream_id={}", stream_id);
|
||||
Self {
|
||||
state: SendMessageState::Initialized {
|
||||
headers,
|
||||
data: None,
|
||||
fin: false,
|
||||
},
|
||||
stream_id,
|
||||
state: MessageState::WaitingForHeaders,
|
||||
message_type,
|
||||
stream_type,
|
||||
stream: BufferedStream::new(stream_id),
|
||||
encoder,
|
||||
conn_events,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_message(&mut self, headers: &[Header], data: Option<&[u8]>) -> Res<()> {
|
||||
if !matches!(self.state, SendMessageState::Uninitialized) {
|
||||
return Err(Error::AlreadyInitialized);
|
||||
}
|
||||
|
||||
self.state = SendMessageState::Initialized {
|
||||
headers: headers.to_vec(),
|
||||
data: data.map(|d| d.to_vec()),
|
||||
fin: true,
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn send_body(&mut self, conn: &mut Connection, buf: &[u8]) -> Res<usize> {
|
||||
qtrace!(
|
||||
[self],
|
||||
"send_body: state={:?} len={}",
|
||||
self.state,
|
||||
buf.len()
|
||||
);
|
||||
match self.state {
|
||||
SendMessageState::Uninitialized
|
||||
| SendMessageState::Initialized { .. }
|
||||
| SendMessageState::SendingInitialMessage { .. } => Ok(0),
|
||||
SendMessageState::SendingData => {
|
||||
let available = conn
|
||||
.stream_avail_send_space(self.stream_id)
|
||||
.map_err(|e| Error::map_stream_send_errors(&e))?;
|
||||
if available <= 2 {
|
||||
return Ok(0);
|
||||
}
|
||||
let to_send;
|
||||
if available <= MAX_DATA_HEADER_SIZE_2_LIMIT {
|
||||
// 63 + 3
|
||||
to_send = min(min(buf.len(), available - 2), MAX_DATA_HEADER_SIZE_2);
|
||||
} else if available <= MAX_DATA_HEADER_SIZE_3_LIMIT {
|
||||
// 16383 + 5
|
||||
to_send = min(min(buf.len(), available - 3), MAX_DATA_HEADER_SIZE_3);
|
||||
} else if available <= MAX_DATA_HEADER_SIZE_5 {
|
||||
// 1073741823 + 9
|
||||
to_send = min(min(buf.len(), available - 5), MAX_DATA_HEADER_SIZE_5_LIMIT);
|
||||
} else {
|
||||
to_send = min(buf.len(), available - 9);
|
||||
}
|
||||
|
||||
qinfo!(
|
||||
[self],
|
||||
"send_request_body: available={} to_send={}.",
|
||||
available,
|
||||
to_send
|
||||
);
|
||||
|
||||
let data_frame = HFrame::Data {
|
||||
len: to_send as u64,
|
||||
};
|
||||
let mut enc = Encoder::default();
|
||||
data_frame.encode(&mut enc);
|
||||
let sent_fh = conn
|
||||
.stream_send(self.stream_id, &enc)
|
||||
.map_err(|e| Error::map_stream_send_errors(&e))?;
|
||||
debug_assert_eq!(sent_fh, enc.len());
|
||||
|
||||
let sent = conn
|
||||
.stream_send(self.stream_id, &buf[..to_send])
|
||||
.map_err(|e| Error::map_stream_send_errors(&e))?;
|
||||
qlog::h3_data_moved_down(&mut conn.qlog_mut(), self.stream_id, to_send);
|
||||
Ok(sent)
|
||||
}
|
||||
SendMessageState::Closed => Err(Error::AlreadyClosed),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn done(&self) -> bool {
|
||||
self.state.done()
|
||||
}
|
||||
|
||||
pub fn stream_writable(&self) {
|
||||
if self.state.is_state_sending_data() {
|
||||
self.conn_events.data_writable(self.stream_id);
|
||||
}
|
||||
}
|
||||
|
||||
/// # Errors
|
||||
/// `ClosedCriticalStream` if the encoder stream is closed.
|
||||
/// `InternalError` if an unexpected error occurred.
|
||||
fn ensure_encoded(&mut self, conn: &mut Connection, encoder: &mut QPackEncoder) -> Res<()> {
|
||||
if let SendMessageState::Initialized { headers, data, fin } = &self.state {
|
||||
qdebug!([self], "Encoding headers");
|
||||
let header_block = encoder.encode_header_block(conn, headers, self.stream_id)?;
|
||||
let hframe = HFrame::Headers {
|
||||
header_block: header_block.to_vec(),
|
||||
};
|
||||
let mut d = Encoder::default();
|
||||
hframe.encode(&mut d);
|
||||
if let Some(buf) = data {
|
||||
qdebug!([self], "Encoding data");
|
||||
let d_frame = HFrame::Data {
|
||||
len: buf.len() as u64,
|
||||
};
|
||||
d_frame.encode(&mut d);
|
||||
d.encode(buf);
|
||||
}
|
||||
fn encode(
|
||||
encoder: &mut QPackEncoder,
|
||||
headers: &[Header],
|
||||
conn: &mut Connection,
|
||||
stream_id: StreamId,
|
||||
) -> Vec<u8> {
|
||||
qdebug!("Encoding headers");
|
||||
let header_block = encoder.encode_header_block(conn, headers, stream_id);
|
||||
let hframe = HFrame::Headers {
|
||||
header_block: header_block.to_vec(),
|
||||
};
|
||||
let mut d = Encoder::default();
|
||||
hframe.encode(&mut d);
|
||||
d.into()
|
||||
}
|
||||
|
||||
self.state = SendMessageState::SendingInitialMessage {
|
||||
buf: d.into(),
|
||||
fin: *fin,
|
||||
};
|
||||
fn stream_id(&self) -> StreamId {
|
||||
Option::<StreamId>::from(&self.stream).unwrap()
|
||||
}
|
||||
|
||||
fn get_stream_info(&self) -> Http3StreamInfo {
|
||||
Http3StreamInfo::new(self.stream_id(), Http3StreamType::Http)
|
||||
}
|
||||
}
|
||||
|
||||
impl Stream for SendMessage {
|
||||
fn stream_type(&self) -> Http3StreamType {
|
||||
self.stream_type
|
||||
}
|
||||
}
|
||||
impl SendStream for SendMessage {
|
||||
fn send_data(&mut self, conn: &mut Connection, buf: &[u8]) -> Res<usize> {
|
||||
qtrace!([self], "send_body: len={}", buf.len());
|
||||
|
||||
self.state.new_data()?;
|
||||
|
||||
self.stream.send_buffer(conn)?;
|
||||
if self.stream.has_buffered_data() {
|
||||
return Ok(0);
|
||||
}
|
||||
let available = conn
|
||||
.stream_avail_send_space(self.stream_id())
|
||||
.map_err(|e| Error::map_stream_send_errors(&e.into()))?;
|
||||
if available <= 2 {
|
||||
return Ok(0);
|
||||
}
|
||||
let to_send = if available <= MAX_DATA_HEADER_SIZE_2_LIMIT {
|
||||
// 63 + 3
|
||||
min(min(buf.len(), available - 2), MAX_DATA_HEADER_SIZE_2)
|
||||
} else if available <= MAX_DATA_HEADER_SIZE_3_LIMIT {
|
||||
// 16383 + 5
|
||||
min(min(buf.len(), available - 3), MAX_DATA_HEADER_SIZE_3)
|
||||
} else if available <= MAX_DATA_HEADER_SIZE_5 {
|
||||
// 1073741823 + 9
|
||||
min(min(buf.len(), available - 5), MAX_DATA_HEADER_SIZE_5_LIMIT)
|
||||
} else {
|
||||
min(buf.len(), available - 9)
|
||||
};
|
||||
|
||||
qinfo!(
|
||||
[self],
|
||||
"send_request_body: available={} to_send={}.",
|
||||
available,
|
||||
to_send
|
||||
);
|
||||
|
||||
let data_frame = HFrame::Data {
|
||||
len: to_send as u64,
|
||||
};
|
||||
let mut enc = Encoder::default();
|
||||
data_frame.encode(&mut enc);
|
||||
let sent_fh = self
|
||||
.stream
|
||||
.send_atomic(conn, &enc)
|
||||
.map_err(|e| Error::map_stream_send_errors(&e))?;
|
||||
debug_assert!(sent_fh);
|
||||
|
||||
let sent = self
|
||||
.stream
|
||||
.send_atomic(conn, &buf[..to_send])
|
||||
.map_err(|e| Error::map_stream_send_errors(&e))?;
|
||||
debug_assert!(sent);
|
||||
qlog::h3_data_moved_down(&mut conn.qlog_mut(), self.stream_id(), to_send);
|
||||
Ok(to_send)
|
||||
}
|
||||
|
||||
fn done(&self) -> bool {
|
||||
!self.stream.has_buffered_data() && self.state.done()
|
||||
}
|
||||
|
||||
fn stream_writable(&self) {
|
||||
if !self.stream.has_buffered_data() && !self.state.done() {
|
||||
// DataWritable is just a signal for an application to try to write more data,
|
||||
// if writing fails it is fine. Therefore we do not need to properly check
|
||||
// whether more credits are available on the transport layer.
|
||||
self.conn_events.data_writable(self.get_stream_info());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// # Errors
|
||||
/// `ClosedCriticalStream` if the encoder stream is closed.
|
||||
/// `InternalError` if an unexpected error occurred.
|
||||
/// `InvalidStreamId` if the stream does not exist,
|
||||
/// `AlreadyClosed` if the stream has already been closed.
|
||||
/// `TransportStreamDoesNotExist` if the transport stream does not exist (this may happen if `process_output`
|
||||
/// has not been called when needed, and HTTP3 layer has not picked up the info that the stream has been closed.)
|
||||
pub fn send(&mut self, conn: &mut Connection, encoder: &mut QPackEncoder) -> Res<()> {
|
||||
self.ensure_encoded(conn, encoder)?;
|
||||
fn send(&mut self, conn: &mut Connection) -> Res<()> {
|
||||
let sent = Error::map_error(self.stream.send_buffer(conn), Error::HttpInternal(5))?;
|
||||
qlog::h3_data_moved_down(&mut conn.qlog_mut(), self.stream_id(), sent);
|
||||
|
||||
let label = if ::log::log_enabled!(::log::Level::Debug) {
|
||||
format!("{}", self)
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
||||
if let SendMessageState::SendingInitialMessage { ref mut buf, fin } = self.state {
|
||||
let sent = Error::map_error(
|
||||
conn.stream_send(self.stream_id, buf),
|
||||
Error::HttpInternal(5),
|
||||
)?;
|
||||
qlog::h3_data_moved_down(&mut conn.qlog_mut(), self.stream_id, sent);
|
||||
|
||||
qtrace!([label], "{} bytes sent", sent);
|
||||
|
||||
if sent == buf.len() {
|
||||
if fin {
|
||||
Error::map_error(
|
||||
conn.stream_close_send(self.stream_id),
|
||||
Error::HttpInternal(6),
|
||||
)?;
|
||||
self.state = SendMessageState::Closed;
|
||||
qtrace!([label], "done sending request");
|
||||
} else {
|
||||
self.state = SendMessageState::SendingData;
|
||||
self.conn_events.data_writable(self.stream_id);
|
||||
qtrace!([label], "change to state SendingData");
|
||||
}
|
||||
qtrace!([self], "{} bytes sent", sent);
|
||||
if !self.stream.has_buffered_data() {
|
||||
if self.state.done() {
|
||||
Error::map_error(
|
||||
conn.stream_close_send(self.stream_id()),
|
||||
Error::HttpInternal(6),
|
||||
)?;
|
||||
qtrace!([self], "done sending request");
|
||||
} else {
|
||||
let b = buf.split_off(sent);
|
||||
*buf = b;
|
||||
// DataWritable is just a signal for an application to try to write more data,
|
||||
// if writing fails it is fine. Therefore we do not need to properly check
|
||||
// whether more credits are available on the transport layer.
|
||||
self.conn_events.data_writable(self.get_stream_info());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
@ -270,39 +265,53 @@ impl SendMessage {
|
|||
// SendMessage owns headers and sends them. It may also own data for the server side.
|
||||
// This method returns if they're still being sent. Request body (if any) is sent by
|
||||
// http client afterwards using `send_request_body` after receiving DataWritable event.
|
||||
pub fn has_data_to_send(&self) -> bool {
|
||||
matches!(
|
||||
self.state,
|
||||
SendMessageState::Initialized { .. } | SendMessageState::SendingInitialMessage { .. }
|
||||
)
|
||||
fn has_data_to_send(&self) -> bool {
|
||||
self.stream.has_buffered_data()
|
||||
}
|
||||
|
||||
pub fn close(&mut self, conn: &mut Connection) -> Res<()> {
|
||||
match self.state {
|
||||
SendMessageState::SendingInitialMessage { ref mut fin, .. }
|
||||
| SendMessageState::Initialized { ref mut fin, .. } => {
|
||||
*fin = true;
|
||||
}
|
||||
_ => {
|
||||
self.state = SendMessageState::Closed;
|
||||
conn.stream_close_send(self.stream_id)?;
|
||||
}
|
||||
fn close(&mut self, conn: &mut Connection) -> Res<()> {
|
||||
self.state.fin()?;
|
||||
if !self.stream.has_buffered_data() {
|
||||
conn.stream_close_send(self.stream_id())?;
|
||||
}
|
||||
|
||||
self.conn_events.remove_send_side_event(self.stream_id);
|
||||
self.conn_events
|
||||
.send_closed(self.get_stream_info(), CloseType::Done);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn stop_sending(&mut self, app_err: AppError) {
|
||||
if !self.state.is_sending_closed() {
|
||||
self.conn_events.remove_send_side_event(self.stream_id);
|
||||
self.conn_events.stop_sending(self.stream_id, app_err);
|
||||
fn handle_stop_sending(&mut self, close_type: CloseType) {
|
||||
if !self.state.done() {
|
||||
self.conn_events
|
||||
.send_closed(self.get_stream_info(), close_type);
|
||||
}
|
||||
}
|
||||
|
||||
fn http_stream(&mut self) -> Option<&mut dyn HttpSendStream> {
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl HttpSendStream for SendMessage {
|
||||
fn send_headers(&mut self, headers: &[Header], conn: &mut Connection) -> Res<()> {
|
||||
self.state.new_headers(headers, self.message_type)?;
|
||||
let buf = SendMessage::encode(
|
||||
&mut self.encoder.borrow_mut(),
|
||||
headers,
|
||||
conn,
|
||||
self.stream_id(),
|
||||
);
|
||||
self.stream.buffer(&buf);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_new_listener(&mut self, conn_events: Box<dyn SendStreamEvents>) {
|
||||
self.conn_events = conn_events;
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for SendMessage {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
write!(f, "SendMesage {}", self.stream_id)
|
||||
write!(f, "SendMesage {}", self.stream_id())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,16 +9,15 @@
|
|||
use crate::connection::Http3State;
|
||||
use crate::connection_server::Http3ServerHandler;
|
||||
use crate::server_connection_events::Http3ServerConnEvent;
|
||||
use crate::server_events::{ClientRequestStream, Http3ServerEvent, Http3ServerEvents};
|
||||
use crate::server_events::{
|
||||
Http3OrWebTransportStream, Http3ServerEvent, Http3ServerEvents, WebTransportRequest,
|
||||
};
|
||||
use crate::settings::HttpZeroRttChecker;
|
||||
use crate::Res;
|
||||
use crate::{Http3Parameters, Http3StreamInfo, Res};
|
||||
use neqo_common::{qtrace, Datagram};
|
||||
use neqo_crypto::{AntiReplay, Cipher, PrivateKey, PublicKey, ZeroRttChecker};
|
||||
use neqo_qpack::QpackSettings;
|
||||
use neqo_transport::server::{ActiveConnectionRef, Server, ValidateAddress};
|
||||
use neqo_transport::{
|
||||
tparams::PreferredAddress, ConnectionIdGenerator, ConnectionParameters, Output,
|
||||
};
|
||||
use neqo_transport::{tparams::PreferredAddress, ConnectionIdGenerator, Output};
|
||||
use std::cell::RefCell;
|
||||
use std::cell::RefMut;
|
||||
use std::collections::HashMap;
|
||||
|
@ -32,7 +31,7 @@ const MAX_EVENT_DATA_SIZE: usize = 1024;
|
|||
|
||||
pub struct Http3Server {
|
||||
server: Server,
|
||||
qpack_settings: QpackSettings,
|
||||
http3_parameters: Http3Parameters,
|
||||
http3_handlers: HashMap<ActiveConnectionRef, HandlerRef>,
|
||||
events: Http3ServerEvents,
|
||||
}
|
||||
|
@ -53,7 +52,7 @@ impl Http3Server {
|
|||
protocols: &[impl AsRef<str>],
|
||||
anti_replay: AntiReplay,
|
||||
cid_manager: Rc<RefCell<dyn ConnectionIdGenerator>>,
|
||||
qpack_settings: QpackSettings,
|
||||
http3_parameters: Http3Parameters,
|
||||
zero_rtt_checker: Option<Box<dyn ZeroRttChecker>>,
|
||||
) -> Res<Self> {
|
||||
Ok(Self {
|
||||
|
@ -63,11 +62,11 @@ impl Http3Server {
|
|||
protocols,
|
||||
anti_replay,
|
||||
zero_rtt_checker
|
||||
.unwrap_or_else(|| Box::new(HttpZeroRttChecker::new(qpack_settings))),
|
||||
.unwrap_or_else(|| Box::new(HttpZeroRttChecker::new(http3_parameters))),
|
||||
cid_manager,
|
||||
ConnectionParameters::default(),
|
||||
*http3_parameters.get_connection_parameters(),
|
||||
)?,
|
||||
qpack_settings,
|
||||
http3_parameters,
|
||||
http3_handlers: HashMap::new(),
|
||||
events: Http3ServerEvents::default(),
|
||||
})
|
||||
|
@ -148,59 +147,95 @@ impl Http3Server {
|
|||
active_conns
|
||||
.iter()
|
||||
.for_each(|conn| self.server.add_to_waiting(conn.clone()));
|
||||
let qpack_settings = self.qpack_settings;
|
||||
for mut conn in active_conns {
|
||||
let handler = self
|
||||
.http3_handlers
|
||||
.entry(conn.clone())
|
||||
.or_insert_with(|| Rc::new(RefCell::new(Http3ServerHandler::new(qpack_settings))));
|
||||
self.process_events(&mut conn, now);
|
||||
}
|
||||
}
|
||||
|
||||
fn process_events(&mut self, conn: &mut ActiveConnectionRef, now: Instant) {
|
||||
let mut remove = false;
|
||||
let http3_parameters = self.http3_parameters;
|
||||
{
|
||||
let handler = self.http3_handlers.entry(conn.clone()).or_insert_with(|| {
|
||||
Rc::new(RefCell::new(Http3ServerHandler::new(http3_parameters)))
|
||||
});
|
||||
handler
|
||||
.borrow_mut()
|
||||
.process_http3(&mut conn.borrow_mut(), now);
|
||||
let mut remove = false;
|
||||
{
|
||||
let mut handler_borrowed = handler.borrow_mut();
|
||||
while let Some(e) = handler_borrowed.next_event() {
|
||||
match e {
|
||||
Http3ServerConnEvent::Headers {
|
||||
stream_id,
|
||||
headers,
|
||||
fin,
|
||||
} => self.events.headers(
|
||||
ClientRequestStream::new(conn.clone(), handler.clone(), stream_id),
|
||||
headers,
|
||||
fin,
|
||||
),
|
||||
Http3ServerConnEvent::DataReadable { stream_id } => {
|
||||
prepare_data(
|
||||
stream_id,
|
||||
&mut handler_borrowed,
|
||||
&mut conn,
|
||||
handler,
|
||||
now,
|
||||
&mut self.events,
|
||||
);
|
||||
}
|
||||
Http3ServerConnEvent::StateChange(state) => {
|
||||
self.events
|
||||
.connection_state_change(conn.clone(), state.clone());
|
||||
if let Http3State::Closed { .. } = state {
|
||||
remove = true;
|
||||
}
|
||||
}
|
||||
Http3ServerConnEvent::PriorityUpdate {
|
||||
stream_id,
|
||||
priority,
|
||||
} => {
|
||||
self.events.priority_update(stream_id, priority);
|
||||
let mut handler_borrowed = handler.borrow_mut();
|
||||
while let Some(e) = handler_borrowed.next_event() {
|
||||
match e {
|
||||
Http3ServerConnEvent::Headers {
|
||||
stream_info,
|
||||
headers,
|
||||
fin,
|
||||
} => self.events.headers(
|
||||
Http3OrWebTransportStream::new(conn.clone(), handler.clone(), stream_info),
|
||||
headers,
|
||||
fin,
|
||||
),
|
||||
Http3ServerConnEvent::DataReadable { stream_info } => {
|
||||
prepare_data(
|
||||
stream_info,
|
||||
&mut handler_borrowed,
|
||||
conn,
|
||||
handler,
|
||||
now,
|
||||
&mut self.events,
|
||||
);
|
||||
}
|
||||
Http3ServerConnEvent::DataWritable { stream_info } => self
|
||||
.events
|
||||
.data_writable(conn.clone(), handler.clone(), stream_info),
|
||||
Http3ServerConnEvent::StreamReset { stream_info, error } => {
|
||||
self.events
|
||||
.stream_reset(conn.clone(), handler.clone(), stream_info, error);
|
||||
}
|
||||
Http3ServerConnEvent::StreamStopSending { stream_info, error } => {
|
||||
self.events.stream_stop_sending(
|
||||
conn.clone(),
|
||||
handler.clone(),
|
||||
stream_info,
|
||||
error,
|
||||
);
|
||||
}
|
||||
Http3ServerConnEvent::StateChange(state) => {
|
||||
self.events
|
||||
.connection_state_change(conn.clone(), state.clone());
|
||||
if let Http3State::Closed { .. } = state {
|
||||
remove = true;
|
||||
}
|
||||
}
|
||||
Http3ServerConnEvent::PriorityUpdate {
|
||||
stream_id,
|
||||
priority,
|
||||
} => {
|
||||
self.events.priority_update(stream_id, priority);
|
||||
}
|
||||
Http3ServerConnEvent::ExtendedConnect { stream_id, headers } => {
|
||||
self.events.webtransport_new_session(
|
||||
WebTransportRequest::new(conn.clone(), handler.clone(), stream_id),
|
||||
headers,
|
||||
);
|
||||
}
|
||||
Http3ServerConnEvent::ExtendedConnectClosed {
|
||||
stream_id, error, ..
|
||||
} => self.events.webtransport_session_closed(
|
||||
WebTransportRequest::new(conn.clone(), handler.clone(), stream_id),
|
||||
error,
|
||||
),
|
||||
Http3ServerConnEvent::ExtendedConnectNewStream(stream_info) => self
|
||||
.events
|
||||
.webtransport_new_stream(Http3OrWebTransportStream::new(
|
||||
conn.clone(),
|
||||
handler.clone(),
|
||||
stream_info,
|
||||
)),
|
||||
}
|
||||
}
|
||||
if remove {
|
||||
self.http3_handlers.remove(&conn.clone());
|
||||
}
|
||||
}
|
||||
if remove {
|
||||
self.http3_handlers.remove(&conn.clone());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -224,7 +259,7 @@ impl Http3Server {
|
|||
}
|
||||
}
|
||||
fn prepare_data(
|
||||
stream_id: u64,
|
||||
stream_info: Http3StreamInfo,
|
||||
handler_borrowed: &mut RefMut<Http3ServerHandler>,
|
||||
conn: &mut ActiveConnectionRef,
|
||||
handler: &HandlerRef,
|
||||
|
@ -233,18 +268,19 @@ fn prepare_data(
|
|||
) {
|
||||
loop {
|
||||
let mut data = vec![0; MAX_EVENT_DATA_SIZE];
|
||||
let res =
|
||||
handler_borrowed.read_request_data(&mut conn.borrow_mut(), now, stream_id, &mut data);
|
||||
let res = handler_borrowed.read_data(
|
||||
&mut conn.borrow_mut(),
|
||||
now,
|
||||
stream_info.stream_id(),
|
||||
&mut data,
|
||||
);
|
||||
if let Ok((amount, fin)) = res {
|
||||
if amount > 0 {
|
||||
if amount > 0 || fin {
|
||||
if amount < MAX_EVENT_DATA_SIZE {
|
||||
data.resize(amount, 0);
|
||||
}
|
||||
events.data(
|
||||
ClientRequestStream::new(conn.clone(), handler.clone(), stream_id),
|
||||
data,
|
||||
fin,
|
||||
);
|
||||
|
||||
events.data(conn.clone(), handler.clone(), stream_info, data, fin);
|
||||
}
|
||||
if amount < MAX_EVENT_DATA_SIZE || fin {
|
||||
break;
|
||||
|
@ -260,14 +296,13 @@ fn prepare_data(
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Http3Server, Http3ServerEvent, Http3State, Rc, RefCell};
|
||||
use crate::{Error, HFrame, Header, Priority};
|
||||
use crate::{Error, HFrame, Header, Http3Parameters, Priority};
|
||||
use neqo_common::event::Provider;
|
||||
use neqo_common::Encoder;
|
||||
use neqo_crypto::{AuthenticationStatus, ZeroRttCheckResult, ZeroRttChecker};
|
||||
use neqo_qpack::encoder::QPackEncoder;
|
||||
use neqo_qpack::QpackSettings;
|
||||
use neqo_qpack::{encoder::QPackEncoder, QpackSettings};
|
||||
use neqo_transport::{
|
||||
Connection, ConnectionError, ConnectionEvent, State, StreamType, ZeroRttState,
|
||||
Connection, ConnectionError, ConnectionEvent, State, StreamId, StreamType, ZeroRttState,
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
use std::mem;
|
||||
|
@ -283,7 +318,14 @@ mod tests {
|
|||
max_blocked_streams: 100,
|
||||
};
|
||||
|
||||
pub fn create_server(settings: QpackSettings) -> Http3Server {
|
||||
fn http3params(qpack_settings: QpackSettings) -> Http3Parameters {
|
||||
Http3Parameters::default()
|
||||
.max_table_size_encoder(qpack_settings.max_table_size_encoder)
|
||||
.max_table_size_decoder(qpack_settings.max_table_size_decoder)
|
||||
.max_blocked_streams(qpack_settings.max_blocked_streams)
|
||||
}
|
||||
|
||||
pub fn create_server(conn_params: Http3Parameters) -> Http3Server {
|
||||
fixture_init();
|
||||
Http3Server::new(
|
||||
now(),
|
||||
|
@ -291,7 +333,7 @@ mod tests {
|
|||
DEFAULT_ALPN,
|
||||
anti_replay(),
|
||||
Rc::new(RefCell::new(CountingConnectionIdGenerator::default())),
|
||||
settings,
|
||||
conn_params,
|
||||
None,
|
||||
)
|
||||
.expect("create a server")
|
||||
|
@ -299,7 +341,7 @@ mod tests {
|
|||
|
||||
/// Create a http3 server with default configuration.
|
||||
pub fn default_server() -> Http3Server {
|
||||
create_server(DEFAULT_SETTINGS)
|
||||
create_server(http3params(DEFAULT_SETTINGS))
|
||||
}
|
||||
|
||||
fn assert_closed(hconn: &mut Http3Server, expected: &Error) {
|
||||
|
@ -339,12 +381,12 @@ mod tests {
|
|||
assert!(!hconn.events().any(closed));
|
||||
}
|
||||
|
||||
const CLIENT_SIDE_CONTROL_STREAM_ID: u64 = 2;
|
||||
const CLIENT_SIDE_ENCODER_STREAM_ID: u64 = 6;
|
||||
const CLIENT_SIDE_DECODER_STREAM_ID: u64 = 10;
|
||||
const SERVER_SIDE_CONTROL_STREAM_ID: u64 = 3;
|
||||
const SERVER_SIDE_ENCODER_STREAM_ID: u64 = 7;
|
||||
const SERVER_SIDE_DECODER_STREAM_ID: u64 = 11;
|
||||
const CLIENT_SIDE_CONTROL_STREAM_ID: StreamId = StreamId::new(2);
|
||||
const CLIENT_SIDE_ENCODER_STREAM_ID: StreamId = StreamId::new(6);
|
||||
const CLIENT_SIDE_DECODER_STREAM_ID: StreamId = StreamId::new(10);
|
||||
const SERVER_SIDE_CONTROL_STREAM_ID: StreamId = StreamId::new(3);
|
||||
const SERVER_SIDE_ENCODER_STREAM_ID: StreamId = StreamId::new(7);
|
||||
const SERVER_SIDE_DECODER_STREAM_ID: StreamId = StreamId::new(11);
|
||||
|
||||
fn connect_transport(server: &mut Http3Server, client: &mut Connection, resume: bool) {
|
||||
let c1 = client.process(None, now()).dgram();
|
||||
|
@ -384,9 +426,9 @@ mod tests {
|
|||
match e {
|
||||
ConnectionEvent::NewStream { stream_id } => {
|
||||
assert!(
|
||||
(stream_id.as_u64() == SERVER_SIDE_CONTROL_STREAM_ID)
|
||||
|| (stream_id.as_u64() == SERVER_SIDE_ENCODER_STREAM_ID)
|
||||
|| (stream_id.as_u64() == SERVER_SIDE_DECODER_STREAM_ID)
|
||||
(stream_id == SERVER_SIDE_CONTROL_STREAM_ID)
|
||||
|| (stream_id == SERVER_SIDE_ENCODER_STREAM_ID)
|
||||
|| (stream_id == SERVER_SIDE_DECODER_STREAM_ID)
|
||||
);
|
||||
assert_eq!(stream_id.stream_type(), StreamType::UniDi);
|
||||
}
|
||||
|
@ -457,7 +499,7 @@ mod tests {
|
|||
|
||||
struct PeerConnection {
|
||||
conn: Connection,
|
||||
control_stream_id: u64,
|
||||
control_stream_id: StreamId,
|
||||
}
|
||||
|
||||
impl PeerConnection {
|
||||
|
@ -491,7 +533,7 @@ mod tests {
|
|||
);
|
||||
assert_eq!(sent, Ok(9));
|
||||
let mut encoder = QPackEncoder::new(
|
||||
QpackSettings {
|
||||
&QpackSettings {
|
||||
max_table_size_encoder: 100,
|
||||
max_table_size_decoder: 0,
|
||||
max_blocked_streams: 0,
|
||||
|
@ -499,7 +541,7 @@ mod tests {
|
|||
true,
|
||||
);
|
||||
encoder.add_send_stream(neqo_trans_conn.stream_create(StreamType::UniDi).unwrap());
|
||||
encoder.send(&mut neqo_trans_conn).unwrap();
|
||||
encoder.send_encoder_updates(&mut neqo_trans_conn).unwrap();
|
||||
let decoder_stream = neqo_trans_conn.stream_create(StreamType::UniDi).unwrap();
|
||||
sent = neqo_trans_conn.stream_send(decoder_stream, &[0x3]);
|
||||
assert_eq!(sent, Ok(1));
|
||||
|
@ -567,11 +609,11 @@ mod tests {
|
|||
assert_closed(&mut hconn, &Error::HttpFrameUnexpected);
|
||||
}
|
||||
|
||||
fn priority_update_check_id(stream_id: u64, valid: bool) {
|
||||
fn priority_update_check_id(stream_id: StreamId, valid: bool) {
|
||||
let (mut hconn, mut peer_conn) = connect();
|
||||
// send a priority update
|
||||
let frame = HFrame::PriorityUpdateRequest {
|
||||
element_id: stream_id,
|
||||
element_id: stream_id.as_u64(),
|
||||
priority: Priority::default(),
|
||||
};
|
||||
let mut e = Encoder::default();
|
||||
|
@ -590,28 +632,28 @@ mod tests {
|
|||
#[test]
|
||||
fn test_priority_update_valid_id_0() {
|
||||
// Client-Initiated, Bidirectional
|
||||
priority_update_check_id(0, true);
|
||||
priority_update_check_id(StreamId::new(0), true);
|
||||
}
|
||||
#[test]
|
||||
fn test_priority_update_invalid_id_1() {
|
||||
// Server-Initiated, Bidirectional
|
||||
priority_update_check_id(1, false);
|
||||
priority_update_check_id(StreamId::new(1), false);
|
||||
}
|
||||
#[test]
|
||||
fn test_priority_update_invalid_id_2() {
|
||||
// Client-Initiated, Unidirectional
|
||||
priority_update_check_id(2, false);
|
||||
priority_update_check_id(StreamId::new(2), false);
|
||||
}
|
||||
#[test]
|
||||
fn test_priority_update_invalid_id_3() {
|
||||
// Server-Initiated, Unidirectional
|
||||
priority_update_check_id(3, false);
|
||||
priority_update_check_id(StreamId::new(3), false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_priority_update_invalid_large_id() {
|
||||
// Server-Initiated, Unidirectional (dividable by 4)
|
||||
priority_update_check_id(1_000_000_000, false);
|
||||
// Server-Initiated, Unidirectional (divisible by 4)
|
||||
priority_update_check_id(StreamId::new(1_000_000_000), false);
|
||||
}
|
||||
|
||||
fn test_wrong_frame_on_control_stream(v: &[u8]) {
|
||||
|
@ -853,24 +895,27 @@ mod tests {
|
|||
headers_frames += 1;
|
||||
}
|
||||
Http3ServerEvent::Data {
|
||||
mut request,
|
||||
mut stream,
|
||||
data,
|
||||
fin,
|
||||
} => {
|
||||
assert_eq!(data, REQUEST_BODY);
|
||||
assert!(fin);
|
||||
request
|
||||
.set_response(
|
||||
&[
|
||||
Header::new(":status", "200"),
|
||||
Header::new("content-length", "3"),
|
||||
],
|
||||
RESPONSE_BODY,
|
||||
)
|
||||
stream
|
||||
.send_headers(&[
|
||||
Header::new(":status", "200"),
|
||||
Header::new("content-length", "3"),
|
||||
])
|
||||
.unwrap();
|
||||
stream.send_data(RESPONSE_BODY).unwrap();
|
||||
data_received += 1;
|
||||
}
|
||||
Http3ServerEvent::StateChange { .. } | Http3ServerEvent::PriorityUpdate { .. } => {}
|
||||
Http3ServerEvent::DataWritable { .. }
|
||||
| Http3ServerEvent::StreamReset { .. }
|
||||
| Http3ServerEvent::StreamStopSending { .. }
|
||||
| Http3ServerEvent::StateChange { .. }
|
||||
| Http3ServerEvent::PriorityUpdate { .. }
|
||||
| Http3ServerEvent::WebTransport(_) => {}
|
||||
}
|
||||
}
|
||||
assert_eq!(headers_frames, 1);
|
||||
|
@ -895,30 +940,33 @@ mod tests {
|
|||
while let Some(event) = hconn.next_event() {
|
||||
match event {
|
||||
Http3ServerEvent::Headers {
|
||||
mut request,
|
||||
mut stream,
|
||||
headers,
|
||||
fin,
|
||||
} => {
|
||||
check_request_header(&headers);
|
||||
assert!(!fin);
|
||||
headers_frames += 1;
|
||||
request
|
||||
stream
|
||||
.stream_stop_sending(Error::HttpNoError.code())
|
||||
.unwrap();
|
||||
request
|
||||
.set_response(
|
||||
&[
|
||||
Header::new(":status", "200"),
|
||||
Header::new("content-length", "3"),
|
||||
],
|
||||
RESPONSE_BODY,
|
||||
)
|
||||
stream
|
||||
.send_headers(&[
|
||||
Header::new(":status", "200"),
|
||||
Header::new("content-length", "3"),
|
||||
])
|
||||
.unwrap();
|
||||
stream.send_data(RESPONSE_BODY).unwrap();
|
||||
}
|
||||
Http3ServerEvent::Data { .. } => {
|
||||
panic!("We should not have a Data event");
|
||||
}
|
||||
Http3ServerEvent::StateChange { .. } | Http3ServerEvent::PriorityUpdate { .. } => {}
|
||||
Http3ServerEvent::DataWritable { .. }
|
||||
| Http3ServerEvent::StreamReset { .. }
|
||||
| Http3ServerEvent::StreamStopSending { .. }
|
||||
| Http3ServerEvent::StateChange { .. }
|
||||
| Http3ServerEvent::PriorityUpdate { .. }
|
||||
| Http3ServerEvent::WebTransport(_) => {}
|
||||
}
|
||||
}
|
||||
let out = hconn.process(None, now());
|
||||
|
@ -940,7 +988,12 @@ mod tests {
|
|||
Http3ServerEvent::Data { .. } => {
|
||||
panic!("We should not have a Data event");
|
||||
}
|
||||
Http3ServerEvent::StateChange { .. } | Http3ServerEvent::PriorityUpdate { .. } => {}
|
||||
Http3ServerEvent::DataWritable { .. }
|
||||
| Http3ServerEvent::StreamReset { .. }
|
||||
| Http3ServerEvent::StreamStopSending { .. }
|
||||
| Http3ServerEvent::StateChange { .. }
|
||||
| Http3ServerEvent::PriorityUpdate { .. }
|
||||
| Http3ServerEvent::WebTransport(_) => {}
|
||||
}
|
||||
}
|
||||
assert_eq!(headers_frames, 1);
|
||||
|
@ -965,21 +1018,26 @@ mod tests {
|
|||
while let Some(event) = hconn.next_event() {
|
||||
match event {
|
||||
Http3ServerEvent::Headers {
|
||||
mut request,
|
||||
mut stream,
|
||||
headers,
|
||||
fin,
|
||||
} => {
|
||||
check_request_header(&headers);
|
||||
assert!(!fin);
|
||||
headers_frames += 1;
|
||||
request
|
||||
.stream_reset(Error::HttpRequestRejected.code())
|
||||
stream
|
||||
.cancel_fetch(Error::HttpRequestRejected.code())
|
||||
.unwrap();
|
||||
}
|
||||
Http3ServerEvent::Data { .. } => {
|
||||
panic!("We should not have a Data event");
|
||||
}
|
||||
Http3ServerEvent::StateChange { .. } | Http3ServerEvent::PriorityUpdate { .. } => {}
|
||||
Http3ServerEvent::DataWritable { .. }
|
||||
| Http3ServerEvent::StreamReset { .. }
|
||||
| Http3ServerEvent::StreamStopSending { .. }
|
||||
| Http3ServerEvent::StateChange { .. }
|
||||
| Http3ServerEvent::PriorityUpdate { .. }
|
||||
| Http3ServerEvent::WebTransport(_) => {}
|
||||
}
|
||||
}
|
||||
let out = hconn.process(None, now());
|
||||
|
@ -1089,7 +1147,7 @@ mod tests {
|
|||
|
||||
/// Perform a handshake, then another with the token from the first.
|
||||
/// The second should always resume, but it might not always accept early data.
|
||||
fn zero_rtt_with_settings(settings: QpackSettings, zero_rtt: &ZeroRttState) {
|
||||
fn zero_rtt_with_settings(conn_params: Http3Parameters, zero_rtt: &ZeroRttState) {
|
||||
let (_, mut client) = connect();
|
||||
let token = client.events().find_map(|e| {
|
||||
if let ConnectionEvent::ResumptionToken(token) = e {
|
||||
|
@ -1100,7 +1158,7 @@ mod tests {
|
|||
});
|
||||
assert!(token.is_some());
|
||||
|
||||
let mut server = create_server(settings);
|
||||
let mut server = create_server(conn_params);
|
||||
let mut client = default_client();
|
||||
client.enable_resumption(now(), token.unwrap()).unwrap();
|
||||
|
||||
|
@ -1111,17 +1169,17 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn zero_rtt() {
|
||||
zero_rtt_with_settings(DEFAULT_SETTINGS, &ZeroRttState::AcceptedClient);
|
||||
zero_rtt_with_settings(http3params(DEFAULT_SETTINGS), &ZeroRttState::AcceptedClient);
|
||||
}
|
||||
|
||||
/// A larger QPACK decoder table size isn't an impediment to 0-RTT.
|
||||
#[test]
|
||||
fn zero_rtt_larger_decoder_table() {
|
||||
zero_rtt_with_settings(
|
||||
QpackSettings {
|
||||
http3params(QpackSettings {
|
||||
max_table_size_decoder: DEFAULT_SETTINGS.max_table_size_decoder + 1,
|
||||
..DEFAULT_SETTINGS
|
||||
},
|
||||
}),
|
||||
&ZeroRttState::AcceptedClient,
|
||||
);
|
||||
}
|
||||
|
@ -1130,10 +1188,10 @@ mod tests {
|
|||
#[test]
|
||||
fn zero_rtt_smaller_decoder_table() {
|
||||
zero_rtt_with_settings(
|
||||
QpackSettings {
|
||||
http3params(QpackSettings {
|
||||
max_table_size_decoder: DEFAULT_SETTINGS.max_table_size_decoder - 1,
|
||||
..DEFAULT_SETTINGS
|
||||
},
|
||||
}),
|
||||
&ZeroRttState::Rejected,
|
||||
);
|
||||
}
|
||||
|
@ -1142,10 +1200,10 @@ mod tests {
|
|||
#[test]
|
||||
fn zero_rtt_more_blocked_streams() {
|
||||
zero_rtt_with_settings(
|
||||
QpackSettings {
|
||||
http3params(QpackSettings {
|
||||
max_blocked_streams: DEFAULT_SETTINGS.max_blocked_streams + 1,
|
||||
..DEFAULT_SETTINGS
|
||||
},
|
||||
}),
|
||||
&ZeroRttState::AcceptedClient,
|
||||
);
|
||||
}
|
||||
|
@ -1154,10 +1212,10 @@ mod tests {
|
|||
#[test]
|
||||
fn zero_rtt_fewer_blocked_streams() {
|
||||
zero_rtt_with_settings(
|
||||
QpackSettings {
|
||||
http3params(QpackSettings {
|
||||
max_blocked_streams: DEFAULT_SETTINGS.max_blocked_streams - 1,
|
||||
..DEFAULT_SETTINGS
|
||||
},
|
||||
}),
|
||||
&ZeroRttState::Rejected,
|
||||
);
|
||||
}
|
||||
|
@ -1166,10 +1224,10 @@ mod tests {
|
|||
#[test]
|
||||
fn zero_rtt_smaller_encoder_table() {
|
||||
zero_rtt_with_settings(
|
||||
QpackSettings {
|
||||
http3params(QpackSettings {
|
||||
max_table_size_encoder: DEFAULT_SETTINGS.max_table_size_encoder - 1,
|
||||
..DEFAULT_SETTINGS
|
||||
},
|
||||
}),
|
||||
&ZeroRttState::AcceptedClient,
|
||||
);
|
||||
}
|
||||
|
@ -1196,14 +1254,19 @@ mod tests {
|
|||
let mut requests = HashMap::new();
|
||||
while let Some(event) = hconn.next_event() {
|
||||
match event {
|
||||
Http3ServerEvent::Headers { request, .. } => {
|
||||
assert!(requests.get(&request).is_none());
|
||||
requests.insert(request, 0);
|
||||
Http3ServerEvent::Headers { stream, .. } => {
|
||||
assert!(requests.get(&stream).is_none());
|
||||
requests.insert(stream, 0);
|
||||
}
|
||||
Http3ServerEvent::Data { request, .. } => {
|
||||
assert!(requests.get(&request).is_some());
|
||||
Http3ServerEvent::Data { stream, .. } => {
|
||||
assert!(requests.get(&stream).is_some());
|
||||
}
|
||||
Http3ServerEvent::StateChange { .. } | Http3ServerEvent::PriorityUpdate { .. } => {}
|
||||
Http3ServerEvent::DataWritable { .. }
|
||||
| Http3ServerEvent::StreamReset { .. }
|
||||
| Http3ServerEvent::StreamStopSending { .. }
|
||||
| Http3ServerEvent::StateChange { .. }
|
||||
| Http3ServerEvent::PriorityUpdate { .. }
|
||||
| Http3ServerEvent::WebTransport(_) => {}
|
||||
}
|
||||
}
|
||||
assert_eq!(requests.len(), 2);
|
||||
|
@ -1226,7 +1289,7 @@ mod tests {
|
|||
DEFAULT_ALPN,
|
||||
anti_replay(),
|
||||
Rc::new(RefCell::new(CountingConnectionIdGenerator::default())),
|
||||
DEFAULT_SETTINGS,
|
||||
http3params(DEFAULT_SETTINGS),
|
||||
Some(Box::new(RejectZeroRtt::default())),
|
||||
)
|
||||
.expect("create a server");
|
||||
|
|
|
@ -5,12 +5,13 @@
|
|||
// except according to those terms.
|
||||
|
||||
use crate::connection::Http3State;
|
||||
use crate::send_message::SendMessageEvents;
|
||||
use crate::RecvMessageEvents;
|
||||
use crate::{Header, Priority};
|
||||
|
||||
use crate::{
|
||||
features::extended_connect::{ExtendedConnectEvents, ExtendedConnectType},
|
||||
CloseType, Http3StreamInfo, HttpRecvStreamEvents, Priority, RecvStreamEvents, SendStreamEvents,
|
||||
};
|
||||
use neqo_common::Header;
|
||||
use neqo_transport::AppError;
|
||||
|
||||
use neqo_transport::StreamId;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::VecDeque;
|
||||
use std::rc::Rc;
|
||||
|
@ -19,23 +20,41 @@ use std::rc::Rc;
|
|||
pub(crate) enum Http3ServerConnEvent {
|
||||
/// Headers are ready.
|
||||
Headers {
|
||||
stream_id: u64,
|
||||
stream_info: Http3StreamInfo,
|
||||
headers: Vec<Header>,
|
||||
fin: bool,
|
||||
},
|
||||
PriorityUpdate {
|
||||
stream_id: u64,
|
||||
stream_id: StreamId,
|
||||
priority: Priority,
|
||||
},
|
||||
/// Request data is ready.
|
||||
DataReadable {
|
||||
stream_id: u64,
|
||||
stream_info: Http3StreamInfo,
|
||||
},
|
||||
DataWritable {
|
||||
stream_info: Http3StreamInfo,
|
||||
},
|
||||
StreamReset {
|
||||
stream_info: Http3StreamInfo,
|
||||
error: AppError,
|
||||
},
|
||||
StreamStopSending {
|
||||
stream_info: Http3StreamInfo,
|
||||
error: AppError,
|
||||
},
|
||||
//TODO: This is never used. Do we need it?
|
||||
// Peer reset the stream.
|
||||
//Reset { stream_id: u64, error: AppError },
|
||||
/// Connection state change.
|
||||
StateChange(Http3State),
|
||||
ExtendedConnect {
|
||||
stream_id: StreamId,
|
||||
headers: Vec<Header>,
|
||||
},
|
||||
ExtendedConnectClosed {
|
||||
connect_type: ExtendedConnectType,
|
||||
stream_id: StreamId,
|
||||
error: Option<AppError>,
|
||||
},
|
||||
ExtendedConnectNewStream(Http3StreamInfo),
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
|
@ -43,32 +62,78 @@ pub(crate) struct Http3ServerConnEvents {
|
|||
events: Rc<RefCell<VecDeque<Http3ServerConnEvent>>>,
|
||||
}
|
||||
|
||||
impl RecvMessageEvents for Http3ServerConnEvents {
|
||||
impl SendStreamEvents for Http3ServerConnEvents {
|
||||
fn send_closed(&self, stream_info: Http3StreamInfo, close_type: CloseType) {
|
||||
if close_type != CloseType::Done {
|
||||
self.insert(Http3ServerConnEvent::StreamStopSending {
|
||||
stream_info,
|
||||
error: close_type.error().unwrap(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn data_writable(&self, stream_info: Http3StreamInfo) {
|
||||
self.insert(Http3ServerConnEvent::DataWritable { stream_info });
|
||||
}
|
||||
}
|
||||
|
||||
impl RecvStreamEvents for Http3ServerConnEvents {
|
||||
/// Add a new `DataReadable` event
|
||||
fn data_readable(&self, stream_info: Http3StreamInfo) {
|
||||
self.insert(Http3ServerConnEvent::DataReadable { stream_info });
|
||||
}
|
||||
|
||||
fn recv_closed(&self, stream_info: Http3StreamInfo, close_type: CloseType) {
|
||||
if close_type != CloseType::Done {
|
||||
self.remove_events_for_stream_id(stream_info);
|
||||
self.insert(Http3ServerConnEvent::StreamReset {
|
||||
stream_info,
|
||||
error: close_type.error().unwrap(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HttpRecvStreamEvents for Http3ServerConnEvents {
|
||||
/// Add a new `HeaderReady` event.
|
||||
fn header_ready(&self, stream_id: u64, headers: Vec<Header>, _interim: bool, fin: bool) {
|
||||
fn header_ready(
|
||||
&self,
|
||||
stream_info: Http3StreamInfo,
|
||||
headers: Vec<Header>,
|
||||
_interim: bool,
|
||||
fin: bool,
|
||||
) {
|
||||
self.insert(Http3ServerConnEvent::Headers {
|
||||
stream_id,
|
||||
stream_info,
|
||||
headers,
|
||||
fin,
|
||||
});
|
||||
}
|
||||
|
||||
/// Add a new `DataReadable` event
|
||||
fn data_readable(&self, stream_id: u64) {
|
||||
self.insert(Http3ServerConnEvent::DataReadable { stream_id });
|
||||
fn extended_connect_new_session(&self, stream_id: StreamId, headers: Vec<Header>) {
|
||||
self.insert(Http3ServerConnEvent::ExtendedConnect { stream_id, headers });
|
||||
}
|
||||
|
||||
fn reset(&self, _stream_id: u64, _error: AppError, _local: bool) {}
|
||||
}
|
||||
|
||||
impl SendMessageEvents for Http3ServerConnEvents {
|
||||
fn data_writable(&self, _stream_id: u64) {
|
||||
// Curently not used on the server side.
|
||||
impl ExtendedConnectEvents for Http3ServerConnEvents {
|
||||
fn session_start(&self, _connect_type: ExtendedConnectType, _stream_id: StreamId) {}
|
||||
|
||||
fn session_end(
|
||||
&self,
|
||||
connect_type: ExtendedConnectType,
|
||||
stream_id: StreamId,
|
||||
error: Option<AppError>,
|
||||
) {
|
||||
self.insert(Http3ServerConnEvent::ExtendedConnectClosed {
|
||||
connect_type,
|
||||
stream_id,
|
||||
error,
|
||||
});
|
||||
}
|
||||
|
||||
fn remove_send_side_event(&self, _stream_id: u64) {}
|
||||
|
||||
fn stop_sending(&self, _stream_id: u64, _app_err: AppError) {}
|
||||
fn extended_connect_new_stream(&self, stream_info: Http3StreamInfo) {
|
||||
self.insert(Http3ServerConnEvent::ExtendedConnectNewStream(stream_info));
|
||||
}
|
||||
}
|
||||
|
||||
impl Http3ServerConnEvents {
|
||||
|
@ -95,17 +160,17 @@ impl Http3ServerConnEvents {
|
|||
self.insert(Http3ServerConnEvent::StateChange(state));
|
||||
}
|
||||
|
||||
pub fn priority_update(&self, stream_id: u64, priority: Priority) {
|
||||
pub fn priority_update(&self, stream_id: StreamId, priority: Priority) {
|
||||
self.insert(Http3ServerConnEvent::PriorityUpdate {
|
||||
stream_id,
|
||||
priority,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn remove_events_for_stream_id(&self, stream_id: u64) {
|
||||
fn remove_events_for_stream_id(&self, stream_info: Http3StreamInfo) {
|
||||
self.remove(|evt| {
|
||||
matches!(evt,
|
||||
Http3ServerConnEvent::Headers { stream_id: x, .. } | Http3ServerConnEvent::DataReadable { stream_id: x, .. } if *x == stream_id)
|
||||
Http3ServerConnEvent::Headers { stream_info: x, .. } | Http3ServerConnEvent::DataReadable { stream_info: x, .. } if *x == stream_info)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,124 +8,352 @@
|
|||
|
||||
use crate::connection::Http3State;
|
||||
use crate::connection_server::Http3ServerHandler;
|
||||
use crate::{Header, Priority, Res};
|
||||
use neqo_common::{qdebug, qinfo};
|
||||
use crate::{Http3StreamInfo, Http3StreamType, Priority, Res};
|
||||
use neqo_common::{qdebug, qinfo, Header};
|
||||
use neqo_transport::server::ActiveConnectionRef;
|
||||
use neqo_transport::{AppError, Connection};
|
||||
use neqo_transport::{AppError, Connection, StreamId, StreamType};
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::collections::VecDeque;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ClientRequestStream {
|
||||
conn: ActiveConnectionRef,
|
||||
handler: Rc<RefCell<Http3ServerHandler>>,
|
||||
stream_id: u64,
|
||||
pub struct StreamHandler {
|
||||
pub conn: ActiveConnectionRef,
|
||||
pub handler: Rc<RefCell<Http3ServerHandler>>,
|
||||
pub stream_info: Http3StreamInfo,
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for ClientRequestStream {
|
||||
impl ::std::fmt::Display for StreamHandler {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
let conn: &Connection = &self.conn.borrow();
|
||||
write!(
|
||||
f,
|
||||
"Http3 server conn={:?} stream_id={}",
|
||||
conn, self.stream_id
|
||||
)
|
||||
write!(f, "conn={} stream_info={:?}", conn, self.stream_info)
|
||||
}
|
||||
}
|
||||
|
||||
impl ClientRequestStream {
|
||||
pub(crate) fn new(
|
||||
conn: ActiveConnectionRef,
|
||||
handler: Rc<RefCell<Http3ServerHandler>>,
|
||||
stream_id: u64,
|
||||
) -> Self {
|
||||
Self {
|
||||
conn,
|
||||
handler,
|
||||
stream_id,
|
||||
}
|
||||
impl std::hash::Hash for StreamHandler {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.conn.hash(state);
|
||||
state.write_u64(self.stream_info.stream_id().as_u64());
|
||||
state.finish();
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for StreamHandler {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.conn == other.conn && self.stream_info.stream_id() == other.stream_info.stream_id()
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for StreamHandler {}
|
||||
|
||||
impl StreamHandler {
|
||||
pub fn stream_id(&self) -> StreamId {
|
||||
self.stream_info.stream_id()
|
||||
}
|
||||
|
||||
/// Supply a response to a request.
|
||||
/// Supply a response header to a request.
|
||||
/// # Errors
|
||||
/// It may return `InvalidStreamId` if a stream does not exist anymore.
|
||||
pub fn set_response(&mut self, headers: &[Header], data: &[u8]) -> Res<()> {
|
||||
qinfo!([self], "Set new response.");
|
||||
self.handler
|
||||
.borrow_mut()
|
||||
.set_response(self.stream_id, headers, data)
|
||||
pub fn send_headers(&mut self, headers: &[Header]) -> Res<()> {
|
||||
self.handler.borrow_mut().send_headers(
|
||||
self.stream_id(),
|
||||
headers,
|
||||
&mut self.conn.borrow_mut(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Request a peer to stop sending a request.
|
||||
/// Supply response data to a request.
|
||||
/// # Errors
|
||||
/// It may return `InvalidStreamId` if a stream does not exist anymore.
|
||||
pub fn send_data(&mut self, buf: &[u8]) -> Res<usize> {
|
||||
self.handler
|
||||
.borrow_mut()
|
||||
.send_data(self.stream_id(), buf, &mut self.conn.borrow_mut())
|
||||
}
|
||||
|
||||
/// Close sending side.
|
||||
/// # Errors
|
||||
/// It may return `InvalidStreamId` if a stream does not exist anymore.
|
||||
pub fn stream_close_send(&mut self) -> Res<()> {
|
||||
self.handler
|
||||
.borrow_mut()
|
||||
.stream_close_send(self.stream_id(), &mut self.conn.borrow_mut())
|
||||
}
|
||||
|
||||
/// Request a peer to stop sending a stream.
|
||||
/// # Errors
|
||||
/// It may return `InvalidStreamId` if a stream does not exist anymore.
|
||||
pub fn stream_stop_sending(&mut self, app_error: AppError) -> Res<()> {
|
||||
qdebug!(
|
||||
[self],
|
||||
"stop sending stream_id:{} error:{}.",
|
||||
self.stream_id,
|
||||
self.stream_info.stream_id(),
|
||||
app_error
|
||||
);
|
||||
self.conn
|
||||
.borrow_mut()
|
||||
.stream_stop_sending(self.stream_id, app_error)?;
|
||||
Ok(())
|
||||
self.handler.borrow_mut().stream_stop_sending(
|
||||
self.stream_info.stream_id(),
|
||||
app_error,
|
||||
&mut self.conn.borrow_mut(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Reset sending side of a stream.
|
||||
/// # Errors
|
||||
/// It may return `InvalidStreamId` if a stream does not exist anymore.
|
||||
pub fn stream_reset_send(&mut self, app_error: AppError) -> Res<()> {
|
||||
qdebug!(
|
||||
[self],
|
||||
"reset send stream_id:{} error:{}.",
|
||||
self.stream_info.stream_id(),
|
||||
app_error
|
||||
);
|
||||
self.handler.borrow_mut().stream_reset_send(
|
||||
self.stream_info.stream_id(),
|
||||
app_error,
|
||||
&mut self.conn.borrow_mut(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Reset a stream/request.
|
||||
/// # Errors
|
||||
/// It may return `InvalidStreamId` if a stream does not exist anymore
|
||||
pub fn stream_reset(&mut self, app_error: AppError) -> Res<()> {
|
||||
pub fn cancel_fetch(&mut self, app_error: AppError) -> Res<()> {
|
||||
qdebug!([self], "reset error:{}.", app_error);
|
||||
self.handler.borrow_mut().stream_reset(
|
||||
&mut self.conn.borrow_mut(),
|
||||
self.stream_id,
|
||||
self.handler.borrow_mut().cancel_fetch(
|
||||
self.stream_info.stream_id(),
|
||||
app_error,
|
||||
&mut self.conn.borrow_mut(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::hash::Hash for ClientRequestStream {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Http3OrWebTransportStream {
|
||||
stream_handler: StreamHandler,
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for Http3OrWebTransportStream {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
write!(f, "Stream server {:?}", self.stream_handler)
|
||||
}
|
||||
}
|
||||
|
||||
impl Http3OrWebTransportStream {
|
||||
pub(crate) fn new(
|
||||
conn: ActiveConnectionRef,
|
||||
handler: Rc<RefCell<Http3ServerHandler>>,
|
||||
stream_info: Http3StreamInfo,
|
||||
) -> Self {
|
||||
Self {
|
||||
stream_handler: StreamHandler {
|
||||
conn,
|
||||
handler,
|
||||
stream_info,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Supply a response header to a request.
|
||||
/// # Errors
|
||||
/// It may return `InvalidStreamId` if a stream does not exist anymore.
|
||||
pub fn send_headers(&mut self, headers: &[Header]) -> Res<()> {
|
||||
self.stream_handler.send_headers(headers)
|
||||
}
|
||||
|
||||
/// Supply response data to a request.
|
||||
/// # Errors
|
||||
/// It may return `InvalidStreamId` if a stream does not exist anymore.
|
||||
pub fn send_data(&mut self, data: &[u8]) -> Res<usize> {
|
||||
qinfo!([self], "Set new response.");
|
||||
self.stream_handler.send_data(data)
|
||||
}
|
||||
|
||||
/// Close sending side.
|
||||
/// # Errors
|
||||
/// It may return `InvalidStreamId` if a stream does not exist anymore.
|
||||
pub fn stream_close_send(&mut self) -> Res<()> {
|
||||
qinfo!([self], "Set new response.");
|
||||
self.stream_handler.stream_close_send()
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Http3OrWebTransportStream {
|
||||
type Target = StreamHandler;
|
||||
#[must_use]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.stream_handler
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for Http3OrWebTransportStream {
|
||||
fn deref_mut(&mut self) -> &mut StreamHandler {
|
||||
&mut self.stream_handler
|
||||
}
|
||||
}
|
||||
|
||||
impl std::hash::Hash for Http3OrWebTransportStream {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.conn.hash(state);
|
||||
state.write_u64(self.stream_id);
|
||||
self.stream_handler.hash(state);
|
||||
state.finish();
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for ClientRequestStream {
|
||||
impl PartialEq for Http3OrWebTransportStream {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.conn == other.conn && self.stream_id == other.stream_id
|
||||
self.stream_handler == other.stream_handler
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for ClientRequestStream {}
|
||||
impl Eq for Http3OrWebTransportStream {}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct WebTransportRequest {
|
||||
stream_handler: StreamHandler,
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for WebTransportRequest {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
write!(f, "WebTransport session {}", self.stream_handler)
|
||||
}
|
||||
}
|
||||
|
||||
impl WebTransportRequest {
|
||||
pub(crate) fn new(
|
||||
conn: ActiveConnectionRef,
|
||||
handler: Rc<RefCell<Http3ServerHandler>>,
|
||||
stream_id: StreamId,
|
||||
) -> Self {
|
||||
Self {
|
||||
stream_handler: StreamHandler {
|
||||
conn,
|
||||
handler,
|
||||
stream_info: Http3StreamInfo::new(stream_id, Http3StreamType::Http),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Respond to a `WebTransport` session request.
|
||||
/// # Errors
|
||||
/// It may return `InvalidStreamId` if a stream does not exist anymore.
|
||||
pub fn response(&mut self, accept: bool) -> Res<()> {
|
||||
qinfo!([self], "Set a response for a WebTransport session.");
|
||||
self.stream_handler
|
||||
.handler
|
||||
.borrow_mut()
|
||||
.webtransport_session_accept(
|
||||
&mut self.stream_handler.conn.borrow_mut(),
|
||||
self.stream_handler.stream_info.stream_id(),
|
||||
accept,
|
||||
)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn stream_id(&self) -> StreamId {
|
||||
self.stream_handler.stream_id()
|
||||
}
|
||||
|
||||
/// Close sending side.
|
||||
/// # Errors
|
||||
/// It may return `InvalidStreamId` if a stream does not exist anymore.
|
||||
pub fn create_stream(&mut self, stream_type: StreamType) -> Res<Http3OrWebTransportStream> {
|
||||
let session_id = self.stream_handler.stream_id();
|
||||
let id = self
|
||||
.stream_handler
|
||||
.handler
|
||||
.borrow_mut()
|
||||
.webtransport_create_stream(
|
||||
&mut self.stream_handler.conn.borrow_mut(),
|
||||
session_id,
|
||||
stream_type,
|
||||
)?;
|
||||
|
||||
Ok(Http3OrWebTransportStream::new(
|
||||
self.stream_handler.conn.clone(),
|
||||
self.stream_handler.handler.clone(),
|
||||
Http3StreamInfo::new(id, Http3StreamType::WebTransport(session_id)),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for WebTransportRequest {
|
||||
type Target = StreamHandler;
|
||||
#[must_use]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.stream_handler
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for WebTransportRequest {
|
||||
fn deref_mut(&mut self) -> &mut StreamHandler {
|
||||
&mut self.stream_handler
|
||||
}
|
||||
}
|
||||
|
||||
impl std::hash::Hash for WebTransportRequest {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.stream_handler.hash(state);
|
||||
state.finish();
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for WebTransportRequest {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.stream_handler == other.stream_handler
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for WebTransportRequest {}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum WebTransportServerEvent {
|
||||
NewSession {
|
||||
session: WebTransportRequest,
|
||||
headers: Vec<Header>,
|
||||
},
|
||||
SessionClosed {
|
||||
session: WebTransportRequest,
|
||||
error: Option<AppError>,
|
||||
},
|
||||
NewStream(Http3OrWebTransportStream),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Http3ServerEvent {
|
||||
/// Headers are ready.
|
||||
Headers {
|
||||
request: ClientRequestStream,
|
||||
stream: Http3OrWebTransportStream,
|
||||
headers: Vec<Header>,
|
||||
fin: bool,
|
||||
},
|
||||
/// Request data is ready.
|
||||
Data {
|
||||
request: ClientRequestStream,
|
||||
stream: Http3OrWebTransportStream,
|
||||
data: Vec<u8>,
|
||||
fin: bool,
|
||||
},
|
||||
DataWritable {
|
||||
stream: Http3OrWebTransportStream,
|
||||
},
|
||||
StreamReset {
|
||||
stream: Http3OrWebTransportStream,
|
||||
error: AppError,
|
||||
},
|
||||
StreamStopSending {
|
||||
stream: Http3OrWebTransportStream,
|
||||
error: AppError,
|
||||
},
|
||||
/// When individual connection change state. It is only used for tests.
|
||||
StateChange {
|
||||
conn: ActiveConnectionRef,
|
||||
state: Http3State,
|
||||
},
|
||||
PriorityUpdate {
|
||||
stream_id: u64,
|
||||
stream_id: StreamId,
|
||||
priority: Priority,
|
||||
},
|
||||
WebTransport(WebTransportServerEvent),
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
|
@ -154,9 +382,14 @@ impl Http3ServerEvents {
|
|||
}
|
||||
|
||||
/// Insert a `Headers` event.
|
||||
pub(crate) fn headers(&self, request: ClientRequestStream, headers: Vec<Header>, fin: bool) {
|
||||
pub(crate) fn headers(
|
||||
&self,
|
||||
request: Http3OrWebTransportStream,
|
||||
headers: Vec<Header>,
|
||||
fin: bool,
|
||||
) {
|
||||
self.insert(Http3ServerEvent::Headers {
|
||||
request,
|
||||
stream: request,
|
||||
headers,
|
||||
fin,
|
||||
});
|
||||
|
@ -168,14 +401,88 @@ impl Http3ServerEvents {
|
|||
}
|
||||
|
||||
/// Insert a `Data` event.
|
||||
pub(crate) fn data(&self, request: ClientRequestStream, data: Vec<u8>, fin: bool) {
|
||||
self.insert(Http3ServerEvent::Data { request, data, fin });
|
||||
pub(crate) fn data(
|
||||
&self,
|
||||
conn: ActiveConnectionRef,
|
||||
handler: Rc<RefCell<Http3ServerHandler>>,
|
||||
stream_info: Http3StreamInfo,
|
||||
data: Vec<u8>,
|
||||
fin: bool,
|
||||
) {
|
||||
self.insert(Http3ServerEvent::Data {
|
||||
stream: Http3OrWebTransportStream::new(conn, handler, stream_info),
|
||||
data,
|
||||
fin,
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn priority_update(&self, stream_id: u64, priority: Priority) {
|
||||
pub(crate) fn data_writable(
|
||||
&self,
|
||||
conn: ActiveConnectionRef,
|
||||
handler: Rc<RefCell<Http3ServerHandler>>,
|
||||
stream_info: Http3StreamInfo,
|
||||
) {
|
||||
self.insert(Http3ServerEvent::DataWritable {
|
||||
stream: Http3OrWebTransportStream::new(conn, handler, stream_info),
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn stream_reset(
|
||||
&self,
|
||||
conn: ActiveConnectionRef,
|
||||
handler: Rc<RefCell<Http3ServerHandler>>,
|
||||
stream_info: Http3StreamInfo,
|
||||
error: AppError,
|
||||
) {
|
||||
self.insert(Http3ServerEvent::StreamReset {
|
||||
stream: Http3OrWebTransportStream::new(conn, handler, stream_info),
|
||||
error,
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn stream_stop_sending(
|
||||
&self,
|
||||
conn: ActiveConnectionRef,
|
||||
handler: Rc<RefCell<Http3ServerHandler>>,
|
||||
stream_info: Http3StreamInfo,
|
||||
error: AppError,
|
||||
) {
|
||||
self.insert(Http3ServerEvent::StreamStopSending {
|
||||
stream: Http3OrWebTransportStream::new(conn, handler, stream_info),
|
||||
error,
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn priority_update(&self, stream_id: StreamId, priority: Priority) {
|
||||
self.insert(Http3ServerEvent::PriorityUpdate {
|
||||
stream_id,
|
||||
priority,
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn webtransport_new_session(
|
||||
&self,
|
||||
session: WebTransportRequest,
|
||||
headers: Vec<Header>,
|
||||
) {
|
||||
self.insert(Http3ServerEvent::WebTransport(
|
||||
WebTransportServerEvent::NewSession { session, headers },
|
||||
));
|
||||
}
|
||||
|
||||
pub(crate) fn webtransport_session_closed(
|
||||
&self,
|
||||
session: WebTransportRequest,
|
||||
error: Option<AppError>,
|
||||
) {
|
||||
self.insert(Http3ServerEvent::WebTransport(
|
||||
WebTransportServerEvent::SessionClosed { session, error },
|
||||
));
|
||||
}
|
||||
|
||||
pub(crate) fn webtransport_new_stream(&self, stream: Http3OrWebTransportStream) {
|
||||
self.insert(Http3ServerEvent::WebTransport(
|
||||
WebTransportServerEvent::NewStream(stream),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,10 +6,9 @@
|
|||
|
||||
#![allow(clippy::module_name_repetitions)]
|
||||
|
||||
use crate::{Error, Res};
|
||||
use crate::{Error, Http3Parameters, Res};
|
||||
use neqo_common::{Decoder, Encoder};
|
||||
use neqo_crypto::{ZeroRttCheckResult, ZeroRttChecker};
|
||||
use neqo_qpack::QpackSettings;
|
||||
use std::ops::Deref;
|
||||
|
||||
type SettingsType = u64;
|
||||
|
@ -21,6 +20,7 @@ const SETTINGS_ZERO_RTT_VERSION: u64 = 1;
|
|||
const SETTINGS_MAX_HEADER_LIST_SIZE: SettingsType = 0x6;
|
||||
const SETTINGS_QPACK_MAX_TABLE_CAPACITY: SettingsType = 0x1;
|
||||
const SETTINGS_QPACK_BLOCKED_STREAMS: SettingsType = 0x7;
|
||||
const SETTINGS_ENABLE_WEB_TRANSPORT: SettingsType = 0x2b60_3742;
|
||||
|
||||
pub const H3_RESERVED_SETTINGS: &[SettingsType] = &[0x2, 0x3, 0x4, 0x5];
|
||||
|
||||
|
@ -29,12 +29,15 @@ pub enum HSettingType {
|
|||
MaxHeaderListSize,
|
||||
MaxTableCapacity,
|
||||
BlockedStreams,
|
||||
EnableWebTransport,
|
||||
}
|
||||
|
||||
fn hsetting_default(setting_type: HSettingType) -> u64 {
|
||||
match setting_type {
|
||||
HSettingType::MaxHeaderListSize => 1 << 62,
|
||||
HSettingType::MaxTableCapacity | HSettingType::BlockedStreams => 0,
|
||||
HSettingType::MaxTableCapacity
|
||||
| HSettingType::BlockedStreams
|
||||
| HSettingType::EnableWebTransport => 0,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,6 +91,10 @@ impl HSettings {
|
|||
enc_inner.encode_varint(SETTINGS_QPACK_BLOCKED_STREAMS as u64);
|
||||
enc_inner.encode_varint(iter.value);
|
||||
}
|
||||
HSettingType::EnableWebTransport => {
|
||||
enc_inner.encode_varint(SETTINGS_ENABLE_WEB_TRANSPORT as u64);
|
||||
enc_inner.encode_varint(iter.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -113,6 +120,9 @@ impl HSettings {
|
|||
(Some(SETTINGS_QPACK_BLOCKED_STREAMS), Some(value)) => self
|
||||
.settings
|
||||
.push(HSetting::new(HSettingType::BlockedStreams, value)),
|
||||
(Some(SETTINGS_ENABLE_WEB_TRANSPORT), Some(value)) => self
|
||||
.settings
|
||||
.push(HSetting::new(HSettingType::EnableWebTransport, value)),
|
||||
// other supported settings here
|
||||
(Some(_), Some(_)) => {} // ignore unknown setting, it is fine.
|
||||
_ => return Err(Error::NotEnoughData),
|
||||
|
@ -129,27 +139,50 @@ impl Deref for HSettings {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<&Http3Parameters> for HSettings {
|
||||
fn from(conn_param: &Http3Parameters) -> Self {
|
||||
Self {
|
||||
settings: vec![
|
||||
HSetting {
|
||||
setting_type: HSettingType::MaxTableCapacity,
|
||||
value: conn_param.get_max_table_size_decoder(),
|
||||
},
|
||||
HSetting {
|
||||
setting_type: HSettingType::BlockedStreams,
|
||||
value: u64::from(conn_param.get_max_blocked_streams()),
|
||||
},
|
||||
HSetting {
|
||||
setting_type: HSettingType::EnableWebTransport,
|
||||
value: u64::from(conn_param.get_webtransport()),
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct HttpZeroRttChecker {
|
||||
settings: QpackSettings,
|
||||
settings: Http3Parameters,
|
||||
}
|
||||
|
||||
impl HttpZeroRttChecker {
|
||||
/// Right now we only have QPACK settings, so that is all this takes.
|
||||
#[must_use]
|
||||
pub fn new(settings: QpackSettings) -> Self {
|
||||
pub fn new(settings: Http3Parameters) -> Self {
|
||||
Self { settings }
|
||||
}
|
||||
|
||||
/// Save the settings that matter for 0-RTT.
|
||||
#[must_use]
|
||||
pub fn save(settings: QpackSettings) -> Vec<u8> {
|
||||
pub fn save(settings: &Http3Parameters) -> Vec<u8> {
|
||||
let mut enc = Encoder::new();
|
||||
enc.encode_varint(SETTINGS_ZERO_RTT_VERSION)
|
||||
.encode_varint(SETTINGS_QPACK_MAX_TABLE_CAPACITY)
|
||||
.encode_varint(settings.max_table_size_decoder)
|
||||
.encode_varint(settings.get_max_table_size_decoder())
|
||||
.encode_varint(SETTINGS_QPACK_BLOCKED_STREAMS)
|
||||
.encode_varint(settings.max_blocked_streams);
|
||||
.encode_varint(settings.get_max_blocked_streams())
|
||||
.encode_varint(SETTINGS_ENABLE_WEB_TRANSPORT)
|
||||
.encode_varint(settings.get_webtransport());
|
||||
enc.into()
|
||||
}
|
||||
}
|
||||
|
@ -174,9 +207,18 @@ impl ZeroRttChecker for HttpZeroRttChecker {
|
|||
}
|
||||
if settings.iter().all(|setting| match setting.setting_type {
|
||||
HSettingType::BlockedStreams => {
|
||||
u64::from(self.settings.max_blocked_streams) >= setting.value
|
||||
u64::from(self.settings.get_max_blocked_streams()) >= setting.value
|
||||
}
|
||||
HSettingType::MaxTableCapacity => {
|
||||
self.settings.get_max_table_size_decoder() >= setting.value
|
||||
}
|
||||
HSettingType::EnableWebTransport => {
|
||||
if setting.value > 0 {
|
||||
return false;
|
||||
}
|
||||
let value = setting.value == 1;
|
||||
self.settings.get_webtransport() || !value
|
||||
}
|
||||
HSettingType::MaxTableCapacity => self.settings.max_table_size_decoder >= setting.value,
|
||||
HSettingType::MaxHeaderListSize => true,
|
||||
}) {
|
||||
ZeroRttCheckResult::Accept
|
||||
|
|
|
@ -7,15 +7,16 @@
|
|||
#![allow(clippy::module_name_repetitions)]
|
||||
|
||||
use crate::control_stream_local::HTTP3_UNI_STREAM_TYPE_CONTROL;
|
||||
use crate::{
|
||||
AppError, Error, Http3StreamType, HttpRecvStream, ReceiveOutput, RecvStream, Res, ResetType,
|
||||
};
|
||||
use crate::hframe::H3_FRAME_TYPE_HEADERS;
|
||||
use crate::{CloseType, Error, Http3StreamType, ReceiveOutput, RecvStream, Res, Stream};
|
||||
use neqo_common::{qtrace, Decoder, IncrementalDecoderUint, Role};
|
||||
use neqo_qpack::decoder::QPACK_UNI_STREAM_TYPE_DECODER;
|
||||
use neqo_qpack::encoder::QPACK_UNI_STREAM_TYPE_ENCODER;
|
||||
use neqo_transport::Connection;
|
||||
use neqo_transport::{Connection, StreamId, StreamType};
|
||||
|
||||
pub const HTTP3_UNI_STREAM_TYPE_PUSH: u64 = 0x1;
|
||||
pub const WEBTRANSPORT_UNI_STREAM: u64 = 0x54;
|
||||
pub const WEBTRANSPORT_STREAM: u64 = 0x41;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum NewStreamType {
|
||||
|
@ -23,6 +24,8 @@ pub enum NewStreamType {
|
|||
Decoder,
|
||||
Encoder,
|
||||
Push(u64),
|
||||
WebTransportStream(u64),
|
||||
Http,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
|
@ -33,13 +36,31 @@ impl NewStreamType {
|
|||
/// # Error
|
||||
/// Push streams received by the server are not allowed and this function will return
|
||||
/// `HttpStreamCreation` error.
|
||||
fn final_stream_type(stream_type: u64, role: Role) -> Res<Option<NewStreamType>> {
|
||||
match (stream_type, role) {
|
||||
(HTTP3_UNI_STREAM_TYPE_CONTROL, _) => Ok(Some(NewStreamType::Control)),
|
||||
(QPACK_UNI_STREAM_TYPE_ENCODER, _) => Ok(Some(NewStreamType::Decoder)),
|
||||
(QPACK_UNI_STREAM_TYPE_DECODER, _) => Ok(Some(NewStreamType::Encoder)),
|
||||
(HTTP3_UNI_STREAM_TYPE_PUSH, Role::Client) => Ok(None),
|
||||
(HTTP3_UNI_STREAM_TYPE_PUSH, Role::Server) => Err(Error::HttpStreamCreation),
|
||||
fn final_stream_type(
|
||||
stream_type: u64,
|
||||
trans_stream_type: StreamType,
|
||||
role: Role,
|
||||
) -> Res<Option<NewStreamType>> {
|
||||
match (stream_type, trans_stream_type, role) {
|
||||
(HTTP3_UNI_STREAM_TYPE_CONTROL, StreamType::UniDi, _) => {
|
||||
Ok(Some(NewStreamType::Control))
|
||||
}
|
||||
(QPACK_UNI_STREAM_TYPE_ENCODER, StreamType::UniDi, _) => {
|
||||
Ok(Some(NewStreamType::Decoder))
|
||||
}
|
||||
(QPACK_UNI_STREAM_TYPE_DECODER, StreamType::UniDi, _) => {
|
||||
Ok(Some(NewStreamType::Encoder))
|
||||
}
|
||||
(HTTP3_UNI_STREAM_TYPE_PUSH, StreamType::UniDi, Role::Client)
|
||||
| (WEBTRANSPORT_UNI_STREAM, StreamType::UniDi, _)
|
||||
| (WEBTRANSPORT_STREAM, StreamType::BiDi, _) => Ok(None),
|
||||
(H3_FRAME_TYPE_HEADERS, StreamType::BiDi, Role::Server) => {
|
||||
Ok(Some(NewStreamType::Http))
|
||||
}
|
||||
(_, StreamType::BiDi, Role::Server) => Err(Error::HttpFrame),
|
||||
(HTTP3_UNI_STREAM_TYPE_PUSH, StreamType::UniDi, Role::Server)
|
||||
| (H3_FRAME_TYPE_HEADERS, StreamType::BiDi, Role::Client)
|
||||
| (_, StreamType::BiDi, Role::Client) => Err(Error::HttpStreamCreation),
|
||||
_ => Ok(Some(NewStreamType::Unknown)),
|
||||
}
|
||||
}
|
||||
|
@ -58,17 +79,18 @@ pub enum NewStreamHeadReader {
|
|||
ReadType {
|
||||
role: Role,
|
||||
reader: IncrementalDecoderUint,
|
||||
stream_id: u64,
|
||||
stream_id: StreamId,
|
||||
},
|
||||
ReadId {
|
||||
stream_type: u64,
|
||||
reader: IncrementalDecoderUint,
|
||||
stream_id: u64,
|
||||
stream_id: StreamId,
|
||||
},
|
||||
Done,
|
||||
}
|
||||
|
||||
impl NewStreamHeadReader {
|
||||
pub fn new(stream_id: u64, role: Role) -> Self {
|
||||
pub fn new(stream_id: StreamId, role: Role) -> Self {
|
||||
NewStreamHeadReader::ReadType {
|
||||
role,
|
||||
reader: IncrementalDecoderUint::default(),
|
||||
|
@ -80,7 +102,9 @@ impl NewStreamHeadReader {
|
|||
if let NewStreamHeadReader::ReadType {
|
||||
reader, stream_id, ..
|
||||
}
|
||||
| NewStreamHeadReader::ReadId { reader, stream_id } = self
|
||||
| NewStreamHeadReader::ReadId {
|
||||
reader, stream_id, ..
|
||||
} = self
|
||||
{
|
||||
loop {
|
||||
let to_read = reader.min_remaining();
|
||||
|
@ -125,7 +149,8 @@ impl NewStreamHeadReader {
|
|||
// - None - if a stream is not identified by the type only, but it needs
|
||||
// additional data from the header to produce the final type, e.g.
|
||||
// a push stream needs pushId as well.
|
||||
let final_type = NewStreamType::final_stream_type(output, *role);
|
||||
let final_type =
|
||||
NewStreamType::final_stream_type(output, stream_id.stream_type(), *role);
|
||||
match (&final_type, fin) {
|
||||
(Err(_), _) => {
|
||||
*self = NewStreamHeadReader::Done;
|
||||
|
@ -145,17 +170,23 @@ impl NewStreamHeadReader {
|
|||
*self = NewStreamHeadReader::ReadId {
|
||||
reader: IncrementalDecoderUint::default(),
|
||||
stream_id: *stream_id,
|
||||
stream_type: output,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
NewStreamHeadReader::ReadId { .. } => {
|
||||
NewStreamHeadReader::ReadId { stream_type, .. } => {
|
||||
let is_push = *stream_type == HTTP3_UNI_STREAM_TYPE_PUSH;
|
||||
*self = NewStreamHeadReader::Done;
|
||||
qtrace!("New Stream stream push_id={}", output);
|
||||
if fin {
|
||||
return Err(Error::HttpGeneralProtocol);
|
||||
}
|
||||
return Ok(Some(NewStreamType::Push(output)));
|
||||
return if is_push {
|
||||
Ok(Some(NewStreamType::Push(output)))
|
||||
} else {
|
||||
Ok(Some(NewStreamType::WebTransportStream(output)))
|
||||
};
|
||||
}
|
||||
NewStreamHeadReader::Done => {
|
||||
unreachable!("Cannot be in state NewStreamHeadReader::Done");
|
||||
|
@ -170,48 +201,53 @@ impl NewStreamHeadReader {
|
|||
| Some(NewStreamType::Encoder)
|
||||
| Some(NewStreamType::Decoder) => Err(Error::HttpClosedCriticalStream),
|
||||
None => Err(Error::HttpStreamCreation),
|
||||
Some(NewStreamType::Http) => Err(Error::HttpFrame),
|
||||
Some(NewStreamType::Unknown) => Ok(decoded),
|
||||
Some(NewStreamType::Push(_)) => {
|
||||
unreachable!("PushStream is mapped to None at this stage.")
|
||||
Some(NewStreamType::Push(_)) | Some(NewStreamType::WebTransportStream(_)) => {
|
||||
unreachable!("PushStream and WebTransport are mapped to None at this stage.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RecvStream for NewStreamHeadReader {
|
||||
fn stream_reset(&mut self, _error: AppError, _reset_type: ResetType) -> Res<()> {
|
||||
*self = NewStreamHeadReader::Done;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn receive(&mut self, conn: &mut Connection) -> Res<ReceiveOutput> {
|
||||
Ok(self
|
||||
.get_type(conn)?
|
||||
.map_or(ReceiveOutput::NoOutput, ReceiveOutput::NewStream))
|
||||
}
|
||||
|
||||
fn done(&self) -> bool {
|
||||
matches!(self, NewStreamHeadReader::Done)
|
||||
}
|
||||
}
|
||||
|
||||
impl Stream for NewStreamHeadReader {
|
||||
fn stream_type(&self) -> Http3StreamType {
|
||||
Http3StreamType::NewStream
|
||||
}
|
||||
}
|
||||
|
||||
fn http_stream(&mut self) -> Option<&mut dyn HttpRecvStream> {
|
||||
None
|
||||
impl RecvStream for NewStreamHeadReader {
|
||||
fn reset(&mut self, _close_type: CloseType) -> Res<()> {
|
||||
*self = NewStreamHeadReader::Done;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn receive(&mut self, conn: &mut Connection) -> Res<(ReceiveOutput, bool)> {
|
||||
Ok((
|
||||
self.get_type(conn)?
|
||||
.map_or(ReceiveOutput::NoOutput, ReceiveOutput::NewStream),
|
||||
self.done(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{NewStreamHeadReader, HTTP3_UNI_STREAM_TYPE_PUSH};
|
||||
use neqo_transport::{Connection, StreamType};
|
||||
use super::{
|
||||
NewStreamHeadReader, HTTP3_UNI_STREAM_TYPE_PUSH, WEBTRANSPORT_STREAM,
|
||||
WEBTRANSPORT_UNI_STREAM,
|
||||
};
|
||||
use neqo_transport::{Connection, StreamId, StreamType};
|
||||
use std::mem;
|
||||
use test_fixture::{connect, now};
|
||||
|
||||
use crate::control_stream_local::HTTP3_UNI_STREAM_TYPE_CONTROL;
|
||||
use crate::{Error, NewStreamType, ReceiveOutput, RecvStream, Res, ResetType};
|
||||
use crate::hframe::H3_FRAME_TYPE_HEADERS;
|
||||
use crate::{CloseType, Error, NewStreamType, ReceiveOutput, RecvStream, Res};
|
||||
use neqo_common::{Encoder, Role};
|
||||
use neqo_qpack::decoder::QPACK_UNI_STREAM_TYPE_DECODER;
|
||||
use neqo_qpack::encoder::QPACK_UNI_STREAM_TYPE_ENCODER;
|
||||
|
@ -219,15 +255,15 @@ mod tests {
|
|||
struct Test {
|
||||
conn_c: Connection,
|
||||
conn_s: Connection,
|
||||
stream_id: u64,
|
||||
stream_id: StreamId,
|
||||
decoder: NewStreamHeadReader,
|
||||
}
|
||||
|
||||
impl Test {
|
||||
fn new(role: Role) -> Self {
|
||||
fn new(stream_type: StreamType, role: Role) -> Self {
|
||||
let (mut conn_c, mut conn_s) = connect();
|
||||
// create a stream
|
||||
let stream_id = conn_s.stream_create(StreamType::UniDi).unwrap();
|
||||
let stream_id = conn_s.stream_create(stream_type).unwrap();
|
||||
let out = conn_s.process(None, now());
|
||||
mem::drop(conn_c.process(out.dgram(), now()));
|
||||
|
||||
|
@ -243,7 +279,7 @@ mod tests {
|
|||
&mut self,
|
||||
enc: &[u8],
|
||||
fin: bool,
|
||||
outcome: &Res<ReceiveOutput>,
|
||||
outcome: &Res<(ReceiveOutput, bool)>,
|
||||
done: bool,
|
||||
) {
|
||||
let len = enc.len() - 1;
|
||||
|
@ -255,7 +291,7 @@ mod tests {
|
|||
mem::drop(self.conn_c.process(out.dgram(), now()));
|
||||
assert_eq!(
|
||||
self.decoder.receive(&mut self.conn_c).unwrap(),
|
||||
ReceiveOutput::NoOutput
|
||||
(ReceiveOutput::NoOutput, false)
|
||||
);
|
||||
assert!(!self.decoder.done());
|
||||
}
|
||||
|
@ -275,7 +311,7 @@ mod tests {
|
|||
&mut self,
|
||||
to_encode: &[u64],
|
||||
fin: bool,
|
||||
outcome: &Res<ReceiveOutput>,
|
||||
outcome: &Res<(ReceiveOutput, bool)>,
|
||||
done: bool,
|
||||
) {
|
||||
let mut enc = Encoder::default();
|
||||
|
@ -288,48 +324,51 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn decode_stream_decoder() {
|
||||
let mut t = Test::new(Role::Client);
|
||||
let mut t = Test::new(StreamType::UniDi, Role::Client);
|
||||
t.decode(
|
||||
&[QPACK_UNI_STREAM_TYPE_DECODER],
|
||||
false,
|
||||
&Ok(ReceiveOutput::NewStream(NewStreamType::Encoder)),
|
||||
&Ok((ReceiveOutput::NewStream(NewStreamType::Encoder), true)),
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decode_stream_encoder() {
|
||||
let mut t = Test::new(Role::Client);
|
||||
let mut t = Test::new(StreamType::UniDi, Role::Client);
|
||||
t.decode(
|
||||
&[QPACK_UNI_STREAM_TYPE_ENCODER],
|
||||
false,
|
||||
&Ok(ReceiveOutput::NewStream(NewStreamType::Decoder)),
|
||||
&Ok((ReceiveOutput::NewStream(NewStreamType::Decoder), true)),
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decode_stream_control() {
|
||||
let mut t = Test::new(Role::Client);
|
||||
let mut t = Test::new(StreamType::UniDi, Role::Client);
|
||||
t.decode(
|
||||
&[HTTP3_UNI_STREAM_TYPE_CONTROL],
|
||||
false,
|
||||
&Ok(ReceiveOutput::NewStream(NewStreamType::Control)),
|
||||
&Ok((ReceiveOutput::NewStream(NewStreamType::Control), true)),
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decode_stream_push() {
|
||||
let mut t = Test::new(Role::Client);
|
||||
let mut t = Test::new(StreamType::UniDi, Role::Client);
|
||||
t.decode(
|
||||
&[HTTP3_UNI_STREAM_TYPE_PUSH, 0xaaaa_aaaa],
|
||||
false,
|
||||
&Ok(ReceiveOutput::NewStream(NewStreamType::Push(0xaaaa_aaaa))),
|
||||
&Ok((
|
||||
ReceiveOutput::NewStream(NewStreamType::Push(0xaaaa_aaaa)),
|
||||
true,
|
||||
)),
|
||||
true,
|
||||
);
|
||||
|
||||
let mut t = Test::new(Role::Server);
|
||||
let mut t = Test::new(StreamType::UniDi, Role::Server);
|
||||
t.decode(
|
||||
&[HTTP3_UNI_STREAM_TYPE_PUSH],
|
||||
false,
|
||||
|
@ -340,55 +379,175 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn decode_stream_unknown() {
|
||||
let mut t = Test::new(Role::Client);
|
||||
let mut t = Test::new(StreamType::UniDi, Role::Client);
|
||||
t.decode(
|
||||
&[0x3fff_ffff_ffff_ffff],
|
||||
false,
|
||||
&Ok(ReceiveOutput::NewStream(NewStreamType::Unknown)),
|
||||
&Ok((ReceiveOutput::NewStream(NewStreamType::Unknown), true)),
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decode_stream_http() {
|
||||
let mut t = Test::new(StreamType::BiDi, Role::Server);
|
||||
t.decode(
|
||||
&[H3_FRAME_TYPE_HEADERS],
|
||||
false,
|
||||
&Ok((ReceiveOutput::NewStream(NewStreamType::Http), true)),
|
||||
true,
|
||||
);
|
||||
|
||||
let mut t = Test::new(StreamType::UniDi, Role::Server);
|
||||
t.decode(
|
||||
&[H3_FRAME_TYPE_HEADERS], // this is the same as a HTTP3_UNI_STREAM_TYPE_PUSH which is not aallowed on the server side.
|
||||
false,
|
||||
&Err(Error::HttpStreamCreation),
|
||||
true,
|
||||
);
|
||||
|
||||
let mut t = Test::new(StreamType::BiDi, Role::Client);
|
||||
t.decode(
|
||||
&[H3_FRAME_TYPE_HEADERS],
|
||||
false,
|
||||
&Err(Error::HttpStreamCreation),
|
||||
true,
|
||||
);
|
||||
|
||||
let mut t = Test::new(StreamType::UniDi, Role::Client);
|
||||
t.decode(
|
||||
&[H3_FRAME_TYPE_HEADERS, 0xaaaa_aaaa], // this is the same as a HTTP3_UNI_STREAM_TYPE_PUSH
|
||||
false,
|
||||
&Ok((
|
||||
ReceiveOutput::NewStream(NewStreamType::Push(0xaaaa_aaaa)),
|
||||
true,
|
||||
)),
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decode_stream_wt_bidi() {
|
||||
let mut t = Test::new(StreamType::BiDi, Role::Server);
|
||||
t.decode(
|
||||
&[WEBTRANSPORT_STREAM, 0xaaaa_aaaa],
|
||||
false,
|
||||
&Ok((
|
||||
ReceiveOutput::NewStream(NewStreamType::WebTransportStream(0xaaaa_aaaa)),
|
||||
true,
|
||||
)),
|
||||
true,
|
||||
);
|
||||
|
||||
let mut t = Test::new(StreamType::UniDi, Role::Server);
|
||||
t.decode(
|
||||
&[WEBTRANSPORT_STREAM],
|
||||
false,
|
||||
&Ok((ReceiveOutput::NewStream(NewStreamType::Unknown), true)),
|
||||
true,
|
||||
);
|
||||
|
||||
let mut t = Test::new(StreamType::BiDi, Role::Client);
|
||||
t.decode(
|
||||
&[WEBTRANSPORT_STREAM, 0xaaaa_aaaa],
|
||||
false,
|
||||
&Ok((
|
||||
ReceiveOutput::NewStream(NewStreamType::WebTransportStream(0xaaaa_aaaa)),
|
||||
true,
|
||||
)),
|
||||
true,
|
||||
);
|
||||
|
||||
let mut t = Test::new(StreamType::UniDi, Role::Client);
|
||||
t.decode(
|
||||
&[WEBTRANSPORT_STREAM],
|
||||
false,
|
||||
&Ok((ReceiveOutput::NewStream(NewStreamType::Unknown), true)),
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decode_stream_wt_unidi() {
|
||||
let mut t = Test::new(StreamType::UniDi, Role::Server);
|
||||
t.decode(
|
||||
&[WEBTRANSPORT_UNI_STREAM, 0xaaaa_aaaa],
|
||||
false,
|
||||
&Ok((
|
||||
ReceiveOutput::NewStream(NewStreamType::WebTransportStream(0xaaaa_aaaa)),
|
||||
true,
|
||||
)),
|
||||
true,
|
||||
);
|
||||
|
||||
let mut t = Test::new(StreamType::BiDi, Role::Server);
|
||||
t.decode(
|
||||
&[WEBTRANSPORT_UNI_STREAM],
|
||||
false,
|
||||
&Err(Error::HttpFrame),
|
||||
true,
|
||||
);
|
||||
|
||||
let mut t = Test::new(StreamType::UniDi, Role::Client);
|
||||
t.decode(
|
||||
&[WEBTRANSPORT_UNI_STREAM, 0xaaaa_aaaa],
|
||||
false,
|
||||
&Ok((
|
||||
ReceiveOutput::NewStream(NewStreamType::WebTransportStream(0xaaaa_aaaa)),
|
||||
true,
|
||||
)),
|
||||
true,
|
||||
);
|
||||
|
||||
let mut t = Test::new(StreamType::BiDi, Role::Client);
|
||||
t.decode(
|
||||
&[WEBTRANSPORT_UNI_STREAM],
|
||||
false,
|
||||
&Err(Error::HttpStreamCreation),
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn done_decoding() {
|
||||
let mut t = Test::new(Role::Client);
|
||||
let mut t = Test::new(StreamType::UniDi, Role::Client);
|
||||
t.decode(
|
||||
&[0x3fff],
|
||||
false,
|
||||
&Ok(ReceiveOutput::NewStream(NewStreamType::Unknown)),
|
||||
&Ok((ReceiveOutput::NewStream(NewStreamType::Unknown), true)),
|
||||
true,
|
||||
);
|
||||
// NewStreamHeadReader is done, it will not continue reading from the stream.
|
||||
t.decode(
|
||||
&[QPACK_UNI_STREAM_TYPE_DECODER],
|
||||
false,
|
||||
&Ok(ReceiveOutput::NoOutput),
|
||||
&Ok((ReceiveOutput::NoOutput, true)),
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decoding_truncate() {
|
||||
let mut t = Test::new(Role::Client);
|
||||
t.decode_buffer(&[0xff], false, &Ok(ReceiveOutput::NoOutput), false);
|
||||
let mut t = Test::new(StreamType::UniDi, Role::Client);
|
||||
t.decode_buffer(&[0xff], false, &Ok((ReceiveOutput::NoOutput, false)), false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reset() {
|
||||
let mut t = Test::new(Role::Client);
|
||||
t.decoder.stream_reset(0x100, ResetType::Remote).unwrap();
|
||||
let mut t = Test::new(StreamType::UniDi, Role::Client);
|
||||
t.decoder.reset(CloseType::ResetRemote(0x100)).unwrap();
|
||||
// after a reset NewStreamHeadReader will not read more data.
|
||||
t.decode(
|
||||
&[QPACK_UNI_STREAM_TYPE_DECODER],
|
||||
false,
|
||||
&Ok(ReceiveOutput::NoOutput),
|
||||
&Ok((ReceiveOutput::NoOutput, true)),
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stream_fin_decoder() {
|
||||
let mut t = Test::new(Role::Client);
|
||||
let mut t = Test::new(StreamType::UniDi, Role::Client);
|
||||
t.decode(
|
||||
&[QPACK_UNI_STREAM_TYPE_DECODER],
|
||||
true,
|
||||
|
@ -399,7 +558,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn stream_fin_encoder() {
|
||||
let mut t = Test::new(Role::Client);
|
||||
let mut t = Test::new(StreamType::UniDi, Role::Client);
|
||||
t.decode(
|
||||
&[QPACK_UNI_STREAM_TYPE_ENCODER],
|
||||
true,
|
||||
|
@ -410,7 +569,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn stream_fin_control() {
|
||||
let mut t = Test::new(Role::Client);
|
||||
let mut t = Test::new(StreamType::UniDi, Role::Client);
|
||||
t.decode(
|
||||
&[HTTP3_UNI_STREAM_TYPE_CONTROL],
|
||||
true,
|
||||
|
@ -421,7 +580,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn stream_fin_push() {
|
||||
let mut t = Test::new(Role::Client);
|
||||
let mut t = Test::new(StreamType::UniDi, Role::Client);
|
||||
t.decode(
|
||||
&[HTTP3_UNI_STREAM_TYPE_PUSH, 0xaaaa_aaaa],
|
||||
true,
|
||||
|
@ -429,7 +588,7 @@ mod tests {
|
|||
true,
|
||||
);
|
||||
|
||||
let mut t = Test::new(Role::Client);
|
||||
let mut t = Test::new(StreamType::UniDi, Role::Client);
|
||||
t.decode(
|
||||
&[HTTP3_UNI_STREAM_TYPE_PUSH],
|
||||
true,
|
||||
|
@ -439,16 +598,83 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn stream_fin_uknown() {
|
||||
let mut t = Test::new(Role::Client);
|
||||
fn stream_fin_wt() {
|
||||
let mut t = Test::new(StreamType::UniDi, Role::Client);
|
||||
t.decode(
|
||||
&[0x3fff_ffff_ffff_ffff],
|
||||
&[WEBTRANSPORT_UNI_STREAM, 0xaaaa_aaaa],
|
||||
true,
|
||||
&Ok(ReceiveOutput::NewStream(NewStreamType::Unknown)),
|
||||
&Err(Error::HttpGeneralProtocol),
|
||||
true,
|
||||
);
|
||||
|
||||
let mut t = Test::new(Role::Client);
|
||||
let mut t = Test::new(StreamType::UniDi, Role::Client);
|
||||
t.decode(
|
||||
&[WEBTRANSPORT_UNI_STREAM],
|
||||
true,
|
||||
&Err(Error::HttpStreamCreation),
|
||||
true,
|
||||
);
|
||||
|
||||
let mut t = Test::new(StreamType::UniDi, Role::Server);
|
||||
t.decode(
|
||||
&[WEBTRANSPORT_UNI_STREAM, 0xaaaa_aaaa],
|
||||
true,
|
||||
&Err(Error::HttpGeneralProtocol),
|
||||
true,
|
||||
);
|
||||
|
||||
let mut t = Test::new(StreamType::UniDi, Role::Server);
|
||||
t.decode(
|
||||
&[WEBTRANSPORT_UNI_STREAM],
|
||||
true,
|
||||
&Err(Error::HttpStreamCreation),
|
||||
true,
|
||||
);
|
||||
|
||||
let mut t = Test::new(StreamType::BiDi, Role::Client);
|
||||
t.decode(
|
||||
&[WEBTRANSPORT_STREAM, 0xaaaa_aaaa],
|
||||
true,
|
||||
&Err(Error::HttpGeneralProtocol),
|
||||
true,
|
||||
);
|
||||
|
||||
let mut t = Test::new(StreamType::BiDi, Role::Client);
|
||||
t.decode(
|
||||
&[WEBTRANSPORT_STREAM],
|
||||
true,
|
||||
&Err(Error::HttpStreamCreation),
|
||||
true,
|
||||
);
|
||||
|
||||
let mut t = Test::new(StreamType::BiDi, Role::Server);
|
||||
t.decode(
|
||||
&[WEBTRANSPORT_STREAM, 0xaaaa_aaaa],
|
||||
true,
|
||||
&Err(Error::HttpGeneralProtocol),
|
||||
true,
|
||||
);
|
||||
|
||||
let mut t = Test::new(StreamType::BiDi, Role::Server);
|
||||
t.decode(
|
||||
&[WEBTRANSPORT_STREAM],
|
||||
true,
|
||||
&Err(Error::HttpStreamCreation),
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stream_fin_uknown() {
|
||||
let mut t = Test::new(StreamType::UniDi, Role::Client);
|
||||
t.decode(
|
||||
&[0x3fff_ffff_ffff_ffff],
|
||||
true,
|
||||
&Ok((ReceiveOutput::NewStream(NewStreamType::Unknown), true)),
|
||||
true,
|
||||
);
|
||||
|
||||
let mut t = Test::new(StreamType::UniDi, Role::Client);
|
||||
// A stream ID of 0x3fff_ffff_ffff_ffff is encoded into [0xff; 8].
|
||||
// For this test the stream type is truncated.
|
||||
// This should cause an error.
|
||||
|
|
|
@ -9,25 +9,26 @@
|
|||
use neqo_common::{event::Provider, Datagram};
|
||||
use neqo_crypto::AuthenticationStatus;
|
||||
use neqo_http3::{
|
||||
Header, Http3Client, Http3ClientEvent, Http3Server, Http3ServerEvent, Http3State, Priority,
|
||||
Header, Http3Client, Http3ClientEvent, Http3OrWebTransportStream, Http3Parameters, Http3Server,
|
||||
Http3ServerEvent, Http3State, Priority,
|
||||
};
|
||||
use neqo_transport::{ConnectionParameters, StreamType};
|
||||
use std::mem;
|
||||
use test_fixture::*;
|
||||
|
||||
const RESPONSE_DATA: &[u8] = &[0x61, 0x62, 0x63];
|
||||
|
||||
fn process_server_events(server: &mut Http3Server) {
|
||||
let mut request_found = false;
|
||||
fn receive_request(server: &mut Http3Server) -> Option<Http3OrWebTransportStream> {
|
||||
while let Some(event) = server.next_event() {
|
||||
if let Http3ServerEvent::Headers {
|
||||
mut request,
|
||||
stream,
|
||||
headers,
|
||||
fin,
|
||||
} = event
|
||||
{
|
||||
assert_eq!(
|
||||
headers,
|
||||
vec![
|
||||
&headers,
|
||||
&[
|
||||
Header::new(":method", "GET"),
|
||||
Header::new(":scheme", "https"),
|
||||
Header::new(":authority", "something.com"),
|
||||
|
@ -35,19 +36,27 @@ fn process_server_events(server: &mut Http3Server) {
|
|||
]
|
||||
);
|
||||
assert!(fin);
|
||||
request
|
||||
.set_response(
|
||||
&[
|
||||
Header::new(":status", "200"),
|
||||
Header::new("content-length", "3"),
|
||||
],
|
||||
RESPONSE_DATA,
|
||||
)
|
||||
.unwrap();
|
||||
request_found = true;
|
||||
|
||||
return Some(stream);
|
||||
}
|
||||
}
|
||||
assert!(request_found);
|
||||
None
|
||||
}
|
||||
|
||||
fn set_response(request: &mut Http3OrWebTransportStream) {
|
||||
request
|
||||
.send_headers(&[
|
||||
Header::new(":status", "200"),
|
||||
Header::new("content-length", "3"),
|
||||
])
|
||||
.unwrap();
|
||||
request.send_data(RESPONSE_DATA).unwrap();
|
||||
request.stream_close_send().unwrap();
|
||||
}
|
||||
|
||||
fn process_server_events(server: &mut Http3Server) {
|
||||
let mut request = receive_request(server).unwrap();
|
||||
set_response(&mut request);
|
||||
}
|
||||
|
||||
fn process_client_events(conn: &mut Http3Client) {
|
||||
|
@ -57,8 +66,8 @@ fn process_client_events(conn: &mut Http3Client) {
|
|||
match event {
|
||||
Http3ClientEvent::HeaderReady { headers, fin, .. } => {
|
||||
assert_eq!(
|
||||
headers,
|
||||
vec![
|
||||
&headers,
|
||||
&[
|
||||
Header::new(":status", "200"),
|
||||
Header::new("content-length", "3"),
|
||||
]
|
||||
|
@ -68,7 +77,7 @@ fn process_client_events(conn: &mut Http3Client) {
|
|||
}
|
||||
Http3ClientEvent::DataReadable { stream_id } => {
|
||||
let mut buf = [0u8; 100];
|
||||
let (amount, fin) = conn.read_response_data(now(), stream_id, &mut buf).unwrap();
|
||||
let (amount, fin) = conn.read_data(now(), stream_id, &mut buf).unwrap();
|
||||
assert!(fin);
|
||||
assert_eq!(amount, RESPONSE_DATA.len());
|
||||
assert_eq!(&buf[..RESPONSE_DATA.len()], RESPONSE_DATA);
|
||||
|
@ -81,10 +90,7 @@ fn process_client_events(conn: &mut Http3Client) {
|
|||
assert!(response_data_found);
|
||||
}
|
||||
|
||||
fn connect() -> (Http3Client, Http3Server, Option<Datagram>) {
|
||||
let mut hconn_c = default_http3_client();
|
||||
let mut hconn_s = default_http3_server();
|
||||
|
||||
fn connect_peers(hconn_c: &mut Http3Client, hconn_s: &mut Http3Server) -> Option<Datagram> {
|
||||
assert_eq!(hconn_c.state(), Http3State::Initializing);
|
||||
let out = hconn_c.process(None, now()); // Initial
|
||||
let out = hconn_s.process(out.dgram(), now()); // Initial + Handshake
|
||||
|
@ -102,7 +108,26 @@ fn connect() -> (Http3Client, Http3Server, Option<Datagram>) {
|
|||
let out = hconn_c.process(out.dgram(), now());
|
||||
// assert!(hconn_c.settings_received);
|
||||
|
||||
(hconn_c, hconn_s, out.dgram())
|
||||
out.dgram()
|
||||
}
|
||||
|
||||
fn connect() -> (Http3Client, Http3Server, Option<Datagram>) {
|
||||
let mut hconn_c = default_http3_client();
|
||||
let mut hconn_s = default_http3_server();
|
||||
|
||||
let out = connect_peers(&mut hconn_c, &mut hconn_s);
|
||||
(hconn_c, hconn_s, out)
|
||||
}
|
||||
|
||||
fn exchange_packets(client: &mut Http3Client, server: &mut Http3Server) {
|
||||
let mut out = None;
|
||||
loop {
|
||||
out = client.process(out, now()).dgram();
|
||||
out = server.process(out, now()).dgram();
|
||||
if out.is_none() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -139,3 +164,137 @@ fn test_fetch() {
|
|||
mem::drop(hconn_c.process(out.dgram(), now()));
|
||||
process_client_events(&mut hconn_c);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_103_response() {
|
||||
let (mut hconn_c, mut hconn_s, dgram) = connect();
|
||||
|
||||
let req = hconn_c
|
||||
.fetch(
|
||||
now(),
|
||||
"GET",
|
||||
&("https", "something.com", "/"),
|
||||
&[],
|
||||
Priority::default(),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(req, 0);
|
||||
hconn_c.stream_close_send(req).unwrap();
|
||||
let out = hconn_c.process(dgram, now());
|
||||
|
||||
let out = hconn_s.process(out.dgram(), now());
|
||||
mem::drop(hconn_c.process(out.dgram(), now()));
|
||||
let mut request = receive_request(&mut hconn_s).unwrap();
|
||||
|
||||
let info_headers = [
|
||||
Header::new(":status", "103"),
|
||||
Header::new("link", "</style.css>; rel=preload; as=style"),
|
||||
];
|
||||
// Send 103
|
||||
request.send_headers(&info_headers).unwrap();
|
||||
let out = hconn_s.process(None, now());
|
||||
|
||||
mem::drop(hconn_c.process(out.dgram(), now()));
|
||||
|
||||
let info_headers_event = |e| {
|
||||
matches!(e, Http3ClientEvent::HeaderReady { headers,
|
||||
interim,
|
||||
fin, .. } if !fin && interim && headers.as_ref() == info_headers)
|
||||
};
|
||||
assert!(hconn_c.events().any(info_headers_event));
|
||||
|
||||
set_response(&mut request);
|
||||
let out = hconn_s.process(None, now());
|
||||
mem::drop(hconn_c.process(out.dgram(), now()));
|
||||
process_client_events(&mut hconn_c)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_data_writable_events() {
|
||||
const STREAM_LIMIT: u64 = 5000;
|
||||
const DATA_AMOUNT: usize = 10000;
|
||||
|
||||
let mut hconn_c = http3_client_with_params(Http3Parameters::default().connection_parameters(
|
||||
ConnectionParameters::default().max_stream_data(StreamType::BiDi, false, STREAM_LIMIT),
|
||||
));
|
||||
let mut hconn_s = default_http3_server();
|
||||
|
||||
mem::drop(connect_peers(&mut hconn_c, &mut hconn_s));
|
||||
|
||||
// Create a request.
|
||||
let req = hconn_c
|
||||
.fetch(
|
||||
now(),
|
||||
"GET",
|
||||
&("https", "something.com", "/"),
|
||||
&[],
|
||||
Priority::default(),
|
||||
)
|
||||
.unwrap();
|
||||
hconn_c.stream_close_send(req).unwrap();
|
||||
exchange_packets(&mut hconn_c, &mut hconn_s);
|
||||
|
||||
let mut request = receive_request(&mut hconn_s).unwrap();
|
||||
|
||||
request
|
||||
.send_headers(&[
|
||||
Header::new(":status", "200"),
|
||||
Header::new("content-length", DATA_AMOUNT.to_string()),
|
||||
])
|
||||
.unwrap();
|
||||
|
||||
// Send a lot of data
|
||||
let buf = &[1; DATA_AMOUNT];
|
||||
let mut sent = request.send_data(buf).unwrap();
|
||||
assert!(sent < DATA_AMOUNT);
|
||||
|
||||
// Exchange packets and read the data on the client side.
|
||||
exchange_packets(&mut hconn_c, &mut hconn_s);
|
||||
let stream_id = request.stream_id();
|
||||
let mut recv_buf = [0_u8; DATA_AMOUNT];
|
||||
let (mut recvd, _) = hconn_c.read_data(now(), stream_id, &mut recv_buf).unwrap();
|
||||
assert_eq!(sent, recvd);
|
||||
exchange_packets(&mut hconn_c, &mut hconn_s);
|
||||
|
||||
let data_writable = |e| {
|
||||
matches!(
|
||||
e,
|
||||
Http3ServerEvent::DataWritable {
|
||||
stream
|
||||
} if stream.stream_id() == stream_id
|
||||
)
|
||||
};
|
||||
// Make sure we have a DataWritable event.
|
||||
assert!(hconn_s.events().any(data_writable));
|
||||
// Data can be sent again.
|
||||
let s = request.send_data(&buf[sent..]).unwrap();
|
||||
assert!(s > 0);
|
||||
sent += s;
|
||||
|
||||
// Exchange packets and read the data on the client side.
|
||||
exchange_packets(&mut hconn_c, &mut hconn_s);
|
||||
let (r, _) = hconn_c
|
||||
.read_data(now(), stream_id, &mut recv_buf[recvd..])
|
||||
.unwrap();
|
||||
recvd += r;
|
||||
exchange_packets(&mut hconn_c, &mut hconn_s);
|
||||
assert_eq!(sent, recvd);
|
||||
|
||||
// One more DataWritable event.
|
||||
assert!(hconn_s.events().any(data_writable));
|
||||
// Send more data.
|
||||
let s = request.send_data(&buf[sent..]).unwrap();
|
||||
assert!(s > 0);
|
||||
sent += s;
|
||||
assert_eq!(sent, DATA_AMOUNT);
|
||||
|
||||
exchange_packets(&mut hconn_c, &mut hconn_s);
|
||||
let (r, _) = hconn_c
|
||||
.read_data(now(), stream_id, &mut recv_buf[recvd..])
|
||||
.unwrap();
|
||||
recvd += r;
|
||||
|
||||
// Make sure all data is received by the client.
|
||||
assert_eq!(recvd, DATA_AMOUNT);
|
||||
assert_eq!(&recv_buf, buf);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
// 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.
|
||||
|
||||
mod webtransport;
|
|
@ -8,54 +8,12 @@ use neqo_common::event::Provider;
|
|||
|
||||
use neqo_crypto::AuthenticationStatus;
|
||||
use neqo_http3::{
|
||||
Header, Http3Client, Http3ClientEvent, Http3Parameters, Http3Server, Http3ServerEvent,
|
||||
Http3State, Priority,
|
||||
Header, Http3Client, Http3ClientEvent, Http3Server, Http3ServerEvent, Http3State, Priority,
|
||||
};
|
||||
use neqo_qpack::QpackSettings;
|
||||
use neqo_transport::ConnectionParameters;
|
||||
use std::cell::RefCell;
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
use std::time::Instant;
|
||||
use test_fixture::*;
|
||||
|
||||
const DEFAULT_SETTINGS: QpackSettings = QpackSettings {
|
||||
max_table_size_encoder: 65536,
|
||||
max_table_size_decoder: 65536,
|
||||
max_blocked_streams: 100,
|
||||
};
|
||||
|
||||
pub fn default_http3_client() -> Http3Client {
|
||||
fixture_init();
|
||||
Http3Client::new(
|
||||
DEFAULT_SERVER_NAME,
|
||||
Rc::new(RefCell::new(CountingConnectionIdGenerator::default())),
|
||||
addr(),
|
||||
addr(),
|
||||
ConnectionParameters::default(),
|
||||
&Http3Parameters {
|
||||
qpack_settings: DEFAULT_SETTINGS,
|
||||
max_concurrent_push_streams: 5,
|
||||
},
|
||||
now(),
|
||||
)
|
||||
.expect("create a default client")
|
||||
}
|
||||
|
||||
pub fn default_http3_server() -> Http3Server {
|
||||
Http3Server::new(
|
||||
now(),
|
||||
DEFAULT_KEYS,
|
||||
DEFAULT_ALPN_H3,
|
||||
anti_replay(),
|
||||
Rc::new(RefCell::new(CountingConnectionIdGenerator::default())),
|
||||
DEFAULT_SETTINGS,
|
||||
None,
|
||||
)
|
||||
.expect("create a server")
|
||||
}
|
||||
|
||||
fn exchange_packets(client: &mut Http3Client, server: &mut Http3Server) {
|
||||
let mut out = None;
|
||||
let mut client_data;
|
||||
|
@ -128,18 +86,18 @@ fn priority_update() {
|
|||
|
||||
match header_event {
|
||||
Http3ServerEvent::Headers {
|
||||
request: _,
|
||||
stream: _,
|
||||
headers,
|
||||
fin,
|
||||
} => {
|
||||
let expected_headers = vec![
|
||||
let expected_headers = &[
|
||||
Header::new(":method", "GET"),
|
||||
Header::new(":scheme", "https"),
|
||||
Header::new(":authority", "something.com"),
|
||||
Header::new(":path", "/"),
|
||||
Header::new("priority", "u=4,i"),
|
||||
];
|
||||
assert_eq!(headers, expected_headers);
|
||||
assert_eq!(&headers, expected_headers);
|
||||
assert!(!fin);
|
||||
}
|
||||
other => panic!("unexpected server event: {:?}", other),
|
||||
|
@ -184,7 +142,7 @@ fn priority_update_dont_send_for_cancelled_stream() {
|
|||
|
||||
let update_priority = Priority::new(6, false);
|
||||
client.priority_update(stream_id, update_priority).unwrap();
|
||||
client.stream_reset(stream_id, 11).unwrap();
|
||||
client.cancel_fetch(stream_id, 11).unwrap();
|
||||
exchange_packets(&mut client, &mut server);
|
||||
|
||||
while let Some(event) = server.next_event() {
|
||||
|
|
|
@ -0,0 +1,329 @@
|
|||
// 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 lazy_static::lazy_static;
|
||||
use neqo_common::event::Provider;
|
||||
use neqo_crypto::AuthenticationStatus;
|
||||
use neqo_http3::{
|
||||
Error, Header, Http3Client, Http3ClientEvent, Http3OrWebTransportStream, Http3Server,
|
||||
Http3ServerEvent, Priority,
|
||||
};
|
||||
use test_fixture::*;
|
||||
|
||||
const RESPONSE_DATA: &[u8] = &[0x61, 0x62, 0x63];
|
||||
|
||||
lazy_static! {
|
||||
static ref RESPONSE_HEADER_NO_DATA: Vec<Header> =
|
||||
vec![Header::new(":status", "200"), Header::new("something", "3")];
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref RESPONSE_HEADER_103: Vec<Header> =
|
||||
vec![Header::new(":status", "103"), Header::new("link", "...")];
|
||||
}
|
||||
|
||||
fn exchange_packets(client: &mut Http3Client, server: &mut Http3Server) {
|
||||
let mut out = None;
|
||||
loop {
|
||||
out = client.process(out, now()).dgram();
|
||||
out = server.process(out, now()).dgram();
|
||||
if out.is_none() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn receive_request(server: &mut Http3Server) -> Option<Http3OrWebTransportStream> {
|
||||
while let Some(event) = server.next_event() {
|
||||
if let Http3ServerEvent::Headers {
|
||||
stream,
|
||||
headers,
|
||||
fin,
|
||||
} = event
|
||||
{
|
||||
assert_eq!(
|
||||
&headers,
|
||||
&[
|
||||
Header::new(":method", "GET"),
|
||||
Header::new(":scheme", "https"),
|
||||
Header::new(":authority", "something.com"),
|
||||
Header::new(":path", "/")
|
||||
]
|
||||
);
|
||||
assert!(fin);
|
||||
return Some(stream);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn send_trailers(request: &mut Http3OrWebTransportStream) -> Result<(), Error> {
|
||||
request.send_headers(&[
|
||||
Header::new("something1", "something"),
|
||||
Header::new("something2", "3"),
|
||||
])
|
||||
}
|
||||
|
||||
fn send_informational_headers(request: &mut Http3OrWebTransportStream) -> Result<(), Error> {
|
||||
request.send_headers(&RESPONSE_HEADER_103)
|
||||
}
|
||||
|
||||
fn send_headers(request: &mut Http3OrWebTransportStream) -> Result<(), Error> {
|
||||
request.send_headers(&[
|
||||
Header::new(":status", "200"),
|
||||
Header::new("content-length", "3"),
|
||||
])
|
||||
}
|
||||
|
||||
fn process_client_events(conn: &mut Http3Client) {
|
||||
let mut response_header_found = false;
|
||||
let mut response_data_found = false;
|
||||
while let Some(event) = conn.next_event() {
|
||||
match event {
|
||||
Http3ClientEvent::HeaderReady { headers, fin, .. } => {
|
||||
assert!(
|
||||
(headers.as_ref()
|
||||
== [
|
||||
Header::new(":status", "200"),
|
||||
Header::new("content-length", "3"),
|
||||
])
|
||||
|| (headers.as_ref() == *RESPONSE_HEADER_103)
|
||||
);
|
||||
assert!(!fin);
|
||||
response_header_found = true;
|
||||
}
|
||||
Http3ClientEvent::DataReadable { stream_id } => {
|
||||
let mut buf = [0u8; 100];
|
||||
let (amount, fin) = conn.read_data(now(), stream_id, &mut buf).unwrap();
|
||||
assert!(fin);
|
||||
assert_eq!(amount, RESPONSE_DATA.len());
|
||||
assert_eq!(&buf[..RESPONSE_DATA.len()], RESPONSE_DATA);
|
||||
response_data_found = true;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
assert!(response_header_found);
|
||||
assert!(response_data_found);
|
||||
}
|
||||
|
||||
fn process_client_events_no_data(conn: &mut Http3Client) {
|
||||
let mut response_header_found = false;
|
||||
let mut fin_received = false;
|
||||
while let Some(event) = conn.next_event() {
|
||||
match event {
|
||||
Http3ClientEvent::HeaderReady { headers, fin, .. } => {
|
||||
assert_eq!(headers.as_ref(), *RESPONSE_HEADER_NO_DATA);
|
||||
fin_received = fin;
|
||||
response_header_found = true;
|
||||
}
|
||||
Http3ClientEvent::DataReadable { stream_id } => {
|
||||
let mut buf = [0u8; 100];
|
||||
let (amount, fin) = conn.read_data(now(), stream_id, &mut buf).unwrap();
|
||||
assert!(fin);
|
||||
fin_received = true;
|
||||
assert_eq!(amount, 0);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
assert!(response_header_found);
|
||||
assert!(fin_received);
|
||||
}
|
||||
|
||||
fn connect_send_and_receive_request() -> (Http3Client, Http3Server, Http3OrWebTransportStream) {
|
||||
let mut hconn_c = default_http3_client();
|
||||
let mut hconn_s = default_http3_server();
|
||||
|
||||
exchange_packets(&mut hconn_c, &mut hconn_s);
|
||||
let authentication_needed = |e| matches!(e, Http3ClientEvent::AuthenticationNeeded);
|
||||
assert!(hconn_c.events().any(authentication_needed));
|
||||
hconn_c.authenticated(AuthenticationStatus::Ok, now());
|
||||
exchange_packets(&mut hconn_c, &mut hconn_s);
|
||||
|
||||
let req = hconn_c
|
||||
.fetch(
|
||||
now(),
|
||||
"GET",
|
||||
&("https", "something.com", "/"),
|
||||
&[],
|
||||
Priority::default(),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(req, 0);
|
||||
hconn_c.stream_close_send(req).unwrap();
|
||||
exchange_packets(&mut hconn_c, &mut hconn_s);
|
||||
|
||||
let request = receive_request(&mut hconn_s).unwrap();
|
||||
|
||||
(hconn_c, hconn_s, request)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn response_trailers1() {
|
||||
let (mut hconn_c, mut hconn_s, mut request) = connect_send_and_receive_request();
|
||||
send_headers(&mut request).unwrap();
|
||||
request.send_data(RESPONSE_DATA).unwrap();
|
||||
send_trailers(&mut request).unwrap();
|
||||
request.stream_close_send().unwrap();
|
||||
exchange_packets(&mut hconn_c, &mut hconn_s);
|
||||
process_client_events(&mut hconn_c);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn response_trailers2() {
|
||||
let (mut hconn_c, mut hconn_s, mut request) = connect_send_and_receive_request();
|
||||
send_headers(&mut request).unwrap();
|
||||
request.send_data(RESPONSE_DATA).unwrap();
|
||||
exchange_packets(&mut hconn_c, &mut hconn_s);
|
||||
send_trailers(&mut request).unwrap();
|
||||
request.stream_close_send().unwrap();
|
||||
exchange_packets(&mut hconn_c, &mut hconn_s);
|
||||
process_client_events(&mut hconn_c);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn response_trailers3() {
|
||||
let (mut hconn_c, mut hconn_s, mut request) = connect_send_and_receive_request();
|
||||
send_headers(&mut request).unwrap();
|
||||
request.send_data(RESPONSE_DATA).unwrap();
|
||||
exchange_packets(&mut hconn_c, &mut hconn_s);
|
||||
send_trailers(&mut request).unwrap();
|
||||
exchange_packets(&mut hconn_c, &mut hconn_s);
|
||||
request.stream_close_send().unwrap();
|
||||
exchange_packets(&mut hconn_c, &mut hconn_s);
|
||||
process_client_events(&mut hconn_c);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn response_trailers_no_data() {
|
||||
let (mut hconn_c, mut hconn_s, mut request) = connect_send_and_receive_request();
|
||||
request.send_headers(&RESPONSE_HEADER_NO_DATA).unwrap();
|
||||
exchange_packets(&mut hconn_c, &mut hconn_s);
|
||||
send_trailers(&mut request).unwrap();
|
||||
exchange_packets(&mut hconn_c, &mut hconn_s);
|
||||
request.stream_close_send().unwrap();
|
||||
exchange_packets(&mut hconn_c, &mut hconn_s);
|
||||
process_client_events_no_data(&mut hconn_c);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_response_trailers() {
|
||||
let (mut hconn_c, mut hconn_s, mut request) = connect_send_and_receive_request();
|
||||
send_headers(&mut request).unwrap();
|
||||
request.send_data(RESPONSE_DATA).unwrap();
|
||||
exchange_packets(&mut hconn_c, &mut hconn_s);
|
||||
send_trailers(&mut request).unwrap();
|
||||
exchange_packets(&mut hconn_c, &mut hconn_s);
|
||||
|
||||
assert_eq!(send_trailers(&mut request), Err(Error::InvalidInput));
|
||||
|
||||
request.stream_close_send().unwrap();
|
||||
exchange_packets(&mut hconn_c, &mut hconn_s);
|
||||
process_client_events(&mut hconn_c);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn data_after_trailer() {
|
||||
let (mut hconn_c, mut hconn_s, mut request) = connect_send_and_receive_request();
|
||||
send_headers(&mut request).unwrap();
|
||||
request.send_data(RESPONSE_DATA).unwrap();
|
||||
exchange_packets(&mut hconn_c, &mut hconn_s);
|
||||
send_trailers(&mut request).unwrap();
|
||||
exchange_packets(&mut hconn_c, &mut hconn_s);
|
||||
|
||||
assert_eq!(request.send_data(RESPONSE_DATA), Err(Error::InvalidInput));
|
||||
|
||||
request.stream_close_send().unwrap();
|
||||
exchange_packets(&mut hconn_c, &mut hconn_s);
|
||||
process_client_events(&mut hconn_c);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trailers_after_close() {
|
||||
let (mut hconn_c, mut hconn_s, mut request) = connect_send_and_receive_request();
|
||||
send_headers(&mut request).unwrap();
|
||||
request.send_data(RESPONSE_DATA).unwrap();
|
||||
request.stream_close_send().unwrap();
|
||||
|
||||
assert_eq!(send_trailers(&mut request), Err(Error::InvalidStreamId));
|
||||
|
||||
exchange_packets(&mut hconn_c, &mut hconn_s);
|
||||
process_client_events(&mut hconn_c);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_response_headers() {
|
||||
let (mut hconn_c, mut hconn_s, mut request) = connect_send_and_receive_request();
|
||||
request.send_headers(&RESPONSE_HEADER_NO_DATA).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
request.send_headers(&RESPONSE_HEADER_NO_DATA),
|
||||
Err(Error::InvalidHeader)
|
||||
);
|
||||
|
||||
request.stream_close_send().unwrap();
|
||||
exchange_packets(&mut hconn_c, &mut hconn_s);
|
||||
process_client_events_no_data(&mut hconn_c);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn informational_after_response_headers() {
|
||||
let (mut hconn_c, mut hconn_s, mut request) = connect_send_and_receive_request();
|
||||
request.send_headers(&RESPONSE_HEADER_NO_DATA).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
send_informational_headers(&mut request),
|
||||
Err(Error::InvalidHeader)
|
||||
);
|
||||
|
||||
request.stream_close_send().unwrap();
|
||||
exchange_packets(&mut hconn_c, &mut hconn_s);
|
||||
process_client_events_no_data(&mut hconn_c);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn data_after_informational() {
|
||||
let (mut hconn_c, mut hconn_s, mut request) = connect_send_and_receive_request();
|
||||
send_informational_headers(&mut request).unwrap();
|
||||
|
||||
assert_eq!(request.send_data(RESPONSE_DATA), Err(Error::InvalidInput));
|
||||
|
||||
send_headers(&mut request).unwrap();
|
||||
request.send_data(RESPONSE_DATA).unwrap();
|
||||
request.stream_close_send().unwrap();
|
||||
exchange_packets(&mut hconn_c, &mut hconn_s);
|
||||
process_client_events(&mut hconn_c);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn non_trailers_headers_after_data() {
|
||||
let (mut hconn_c, mut hconn_s, mut request) = connect_send_and_receive_request();
|
||||
send_headers(&mut request).unwrap();
|
||||
request.send_data(RESPONSE_DATA).unwrap();
|
||||
exchange_packets(&mut hconn_c, &mut hconn_s);
|
||||
|
||||
assert_eq!(
|
||||
request.send_headers(&RESPONSE_HEADER_NO_DATA),
|
||||
Err(Error::InvalidHeader)
|
||||
);
|
||||
|
||||
request.stream_close_send().unwrap();
|
||||
exchange_packets(&mut hconn_c, &mut hconn_s);
|
||||
process_client_events(&mut hconn_c);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn data_before_headers() {
|
||||
let (mut hconn_c, mut hconn_s, mut request) = connect_send_and_receive_request();
|
||||
assert_eq!(request.send_data(RESPONSE_DATA), Err(Error::InvalidInput));
|
||||
|
||||
send_headers(&mut request).unwrap();
|
||||
request.send_data(RESPONSE_DATA).unwrap();
|
||||
request.stream_close_send().unwrap();
|
||||
exchange_packets(&mut hconn_c, &mut hconn_s);
|
||||
process_client_events(&mut hconn_c);
|
||||
}
|
|
@ -0,0 +1,521 @@
|
|||
// 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.
|
||||
|
||||
mod negotiation;
|
||||
mod sessions;
|
||||
mod streams;
|
||||
|
||||
use neqo_common::event::Provider;
|
||||
use neqo_crypto::AuthenticationStatus;
|
||||
use neqo_http3::{
|
||||
Error, Http3Client, Http3ClientEvent, Http3OrWebTransportStream, Http3Parameters, Http3Server,
|
||||
Http3ServerEvent, Http3State, WebTransportEvent, WebTransportRequest, WebTransportServerEvent,
|
||||
};
|
||||
use neqo_transport::{AppError, StreamId, StreamType};
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use test_fixture::{
|
||||
addr, anti_replay, fixture_init, now, CountingConnectionIdGenerator, DEFAULT_ALPN_H3,
|
||||
DEFAULT_KEYS, DEFAULT_SERVER_NAME,
|
||||
};
|
||||
|
||||
pub fn default_http3_client(webtransport: bool) -> Http3Client {
|
||||
fixture_init();
|
||||
Http3Client::new(
|
||||
DEFAULT_SERVER_NAME,
|
||||
Rc::new(RefCell::new(CountingConnectionIdGenerator::default())),
|
||||
addr(),
|
||||
addr(),
|
||||
Http3Parameters::default().webtransport(webtransport),
|
||||
now(),
|
||||
)
|
||||
.expect("create a default client")
|
||||
}
|
||||
|
||||
pub fn default_http3_server(webtransport: bool) -> Http3Server {
|
||||
Http3Server::new(
|
||||
now(),
|
||||
DEFAULT_KEYS,
|
||||
DEFAULT_ALPN_H3,
|
||||
anti_replay(),
|
||||
Rc::new(RefCell::new(CountingConnectionIdGenerator::default())),
|
||||
Http3Parameters::default().webtransport(webtransport),
|
||||
None,
|
||||
)
|
||||
.expect("create a server")
|
||||
}
|
||||
|
||||
fn exchange_packets(client: &mut Http3Client, server: &mut Http3Server) {
|
||||
let mut out = None;
|
||||
loop {
|
||||
out = client.process(out, now()).dgram();
|
||||
out = server.process(out, now()).dgram();
|
||||
if out.is_none() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Perform only Quic transport handshake.
|
||||
fn connect_with(client: &mut Http3Client, server: &mut Http3Server) {
|
||||
assert_eq!(client.state(), Http3State::Initializing);
|
||||
let out = client.process(None, now());
|
||||
assert_eq!(client.state(), Http3State::Initializing);
|
||||
|
||||
let out = server.process(out.dgram(), now());
|
||||
let out = client.process(out.dgram(), now());
|
||||
let out = server.process(out.dgram(), now());
|
||||
assert!(out.as_dgram_ref().is_none());
|
||||
|
||||
let authentication_needed = |e| matches!(e, Http3ClientEvent::AuthenticationNeeded);
|
||||
assert!(client.events().any(authentication_needed));
|
||||
client.authenticated(AuthenticationStatus::Ok, now());
|
||||
|
||||
let out = client.process(out.dgram(), now());
|
||||
let connected = |e| matches!(e, Http3ClientEvent::StateChange(Http3State::Connected));
|
||||
assert!(client.events().any(connected));
|
||||
|
||||
assert_eq!(client.state(), Http3State::Connected);
|
||||
|
||||
// Exchange H3 setttings
|
||||
let out = server.process(out.dgram(), now());
|
||||
let out = client.process(out.dgram(), now());
|
||||
let out = server.process(out.dgram(), now());
|
||||
let out = client.process(out.dgram(), now());
|
||||
let _ = server.process(out.dgram(), now());
|
||||
}
|
||||
|
||||
fn connect(wt_enable_client: bool, wt_enable_server: bool) -> (Http3Client, Http3Server) {
|
||||
let mut client = default_http3_client(wt_enable_client);
|
||||
let mut server = default_http3_server(wt_enable_server);
|
||||
connect_with(&mut client, &mut server);
|
||||
(client, server)
|
||||
}
|
||||
|
||||
struct WtTest {
|
||||
client: Http3Client,
|
||||
server: Http3Server,
|
||||
}
|
||||
|
||||
impl WtTest {
|
||||
pub fn new() -> Self {
|
||||
let (client, server) = connect(true, true);
|
||||
Self { client, server }
|
||||
}
|
||||
|
||||
fn negotiate_wt_session(&mut self, accept: bool) -> (StreamId, Option<WebTransportRequest>) {
|
||||
let wt_session_id = self
|
||||
.client
|
||||
.webtransport_create_session(now(), &("https", "something.com", "/"), &[])
|
||||
.unwrap();
|
||||
self.exchange_packets();
|
||||
|
||||
let mut wt_server_session = None;
|
||||
while let Some(event) = self.server.next_event() {
|
||||
match event {
|
||||
Http3ServerEvent::WebTransport(WebTransportServerEvent::NewSession {
|
||||
mut session,
|
||||
headers,
|
||||
}) => {
|
||||
assert!(
|
||||
headers
|
||||
.iter()
|
||||
.any(|h| h.name() == ":method" && h.value() == "CONNECT")
|
||||
&& headers
|
||||
.iter()
|
||||
.any(|h| h.name() == ":protocol" && h.value() == "webtransport")
|
||||
);
|
||||
session.response(accept).unwrap();
|
||||
wt_server_session = Some(session);
|
||||
}
|
||||
Http3ServerEvent::Data { .. } => {
|
||||
panic!("There should not be any data events!");
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
self.exchange_packets();
|
||||
(wt_session_id, wt_server_session)
|
||||
}
|
||||
|
||||
fn create_wt_session(&mut self) -> WebTransportRequest {
|
||||
let (wt_session_id, wt_server_session) = self.negotiate_wt_session(true);
|
||||
let wt_session_negotiated_event = |e| {
|
||||
matches!(
|
||||
e,
|
||||
Http3ClientEvent::WebTransport(WebTransportEvent::Session(stream_id)) if stream_id == wt_session_id
|
||||
)
|
||||
};
|
||||
assert!(self.client.events().any(wt_session_negotiated_event));
|
||||
|
||||
let wt_server_session = wt_server_session.unwrap();
|
||||
assert_eq!(wt_session_id, wt_server_session.stream_id());
|
||||
wt_server_session
|
||||
}
|
||||
|
||||
fn exchange_packets(&mut self) {
|
||||
let mut out = None;
|
||||
loop {
|
||||
out = self.client.process(out, now()).dgram();
|
||||
out = self.server.process(out, now()).dgram();
|
||||
if out.is_none() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cancel_session_client(&mut self, wt_stream_id: StreamId) {
|
||||
self.client
|
||||
.cancel_fetch(wt_stream_id, Error::HttpNoError.code())
|
||||
.unwrap();
|
||||
self.exchange_packets();
|
||||
}
|
||||
|
||||
fn session_closed_client(
|
||||
e: &Http3ClientEvent,
|
||||
id: StreamId,
|
||||
expected_error: &Option<AppError>,
|
||||
) -> bool {
|
||||
if let Http3ClientEvent::WebTransport(WebTransportEvent::SessionClosed {
|
||||
stream_id,
|
||||
error,
|
||||
}) = e
|
||||
{
|
||||
*stream_id == id && error == expected_error
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_session_closed_event_client(
|
||||
&mut self,
|
||||
wt_session_id: StreamId,
|
||||
expected_error: Option<AppError>,
|
||||
) {
|
||||
let mut event_found = false;
|
||||
|
||||
while let Some(event) = self.client.next_event() {
|
||||
event_found = WtTest::session_closed_client(&event, wt_session_id, &expected_error);
|
||||
if event_found {
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert!(event_found);
|
||||
}
|
||||
|
||||
pub fn cancel_session_server(&mut self, wt_session: &mut WebTransportRequest) {
|
||||
wt_session.cancel_fetch(Error::HttpNoError.code()).unwrap();
|
||||
self.exchange_packets();
|
||||
}
|
||||
|
||||
fn session_closed_server(
|
||||
e: &Http3ServerEvent,
|
||||
id: StreamId,
|
||||
expected_error: &Option<AppError>,
|
||||
) -> bool {
|
||||
if let Http3ServerEvent::WebTransport(WebTransportServerEvent::SessionClosed {
|
||||
session,
|
||||
error,
|
||||
}) = e
|
||||
{
|
||||
session.stream_id() == id && error == expected_error
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_session_closed_event_server(
|
||||
&mut self,
|
||||
wt_session: &mut WebTransportRequest,
|
||||
expected_error: Option<AppError>,
|
||||
) {
|
||||
let event = self.server.next_event().unwrap();
|
||||
assert!(WtTest::session_closed_server(
|
||||
&event,
|
||||
wt_session.stream_id(),
|
||||
&expected_error
|
||||
));
|
||||
}
|
||||
|
||||
fn create_wt_stream_client(
|
||||
&mut self,
|
||||
wt_session_id: StreamId,
|
||||
stream_type: StreamType,
|
||||
) -> StreamId {
|
||||
self.client
|
||||
.webtransport_create_stream(wt_session_id, stream_type)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn send_data_client(&mut self, wt_stream_id: StreamId, data: &[u8]) {
|
||||
assert_eq!(
|
||||
self.client.send_data(wt_stream_id, data).unwrap(),
|
||||
data.len()
|
||||
);
|
||||
self.exchange_packets();
|
||||
}
|
||||
|
||||
fn receive_data_client(
|
||||
&mut self,
|
||||
expected_stream_id: StreamId,
|
||||
new_stream: bool,
|
||||
expected_data: &[u8],
|
||||
expected_fin: bool,
|
||||
) {
|
||||
let mut new_stream_received = false;
|
||||
let mut data_received = false;
|
||||
while let Some(event) = self.client.next_event() {
|
||||
match event {
|
||||
Http3ClientEvent::WebTransport(WebTransportEvent::NewStream {
|
||||
stream_id, ..
|
||||
}) => {
|
||||
assert_eq!(stream_id, expected_stream_id);
|
||||
new_stream_received = true;
|
||||
}
|
||||
Http3ClientEvent::DataReadable { stream_id } => {
|
||||
assert_eq!(stream_id, expected_stream_id);
|
||||
let mut buf = [0; 100];
|
||||
let (amount, fin) = self.client.read_data(now(), stream_id, &mut buf).unwrap();
|
||||
assert_eq!(fin, expected_fin);
|
||||
assert_eq!(amount, expected_data.len());
|
||||
assert_eq!(&buf[..amount], expected_data);
|
||||
data_received = true;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
assert!(data_received);
|
||||
assert_eq!(new_stream, new_stream_received);
|
||||
}
|
||||
|
||||
fn close_stream_sending_client(&mut self, wt_stream_id: StreamId) {
|
||||
self.client.stream_close_send(wt_stream_id).unwrap();
|
||||
self.exchange_packets();
|
||||
}
|
||||
|
||||
fn reset_stream_client(&mut self, wt_stream_id: StreamId) {
|
||||
self.client
|
||||
.stream_reset_send(wt_stream_id, Error::HttpNoError.code())
|
||||
.unwrap();
|
||||
self.exchange_packets();
|
||||
}
|
||||
|
||||
fn receive_reset_client(&mut self, expected_stream_id: StreamId) {
|
||||
let wt_reset_event = |e| {
|
||||
matches!(
|
||||
e,
|
||||
Http3ClientEvent::Reset {
|
||||
stream_id,
|
||||
error,
|
||||
local
|
||||
} if stream_id == expected_stream_id && error == Error::HttpNoError.code() && !local
|
||||
)
|
||||
};
|
||||
assert!(self.client.events().any(wt_reset_event));
|
||||
}
|
||||
|
||||
fn stream_stop_sending_client(&mut self, stream_id: StreamId) {
|
||||
self.client
|
||||
.stream_stop_sending(stream_id, Error::HttpNoError.code())
|
||||
.unwrap();
|
||||
self.exchange_packets();
|
||||
}
|
||||
|
||||
fn receive_stop_sending_client(&mut self, expected_stream_id: StreamId) {
|
||||
let wt_stop_sending_event = |e| {
|
||||
matches!(
|
||||
e,
|
||||
Http3ClientEvent::StopSending {
|
||||
stream_id,
|
||||
error
|
||||
} if stream_id == expected_stream_id && error == Error::HttpNoError.code()
|
||||
)
|
||||
};
|
||||
assert!(self.client.events().any(wt_stop_sending_event));
|
||||
}
|
||||
|
||||
fn check_events_after_closing_session_client(
|
||||
&mut self,
|
||||
expected_reset_ids: &[StreamId],
|
||||
expected_error_stream_reset: Option<u64>,
|
||||
expected_stop_sending_ids: &[StreamId],
|
||||
expected_error_stream_stop_sending: Option<u64>,
|
||||
expected_local: bool,
|
||||
expected_session_close: Option<(StreamId, Option<u64>)>,
|
||||
) {
|
||||
let mut reset_ids_count = 0;
|
||||
let mut stop_sending_ids_count = 0;
|
||||
let mut close_event = false;
|
||||
while let Some(event) = self.client.next_event() {
|
||||
match event {
|
||||
Http3ClientEvent::Reset {
|
||||
stream_id,
|
||||
error,
|
||||
local,
|
||||
} => {
|
||||
assert!(expected_reset_ids.contains(&stream_id));
|
||||
assert_eq!(expected_error_stream_reset.unwrap(), error);
|
||||
assert_eq!(expected_local, local);
|
||||
reset_ids_count += 1;
|
||||
}
|
||||
Http3ClientEvent::StopSending { stream_id, error } => {
|
||||
assert!(expected_stop_sending_ids.contains(&stream_id));
|
||||
assert_eq!(expected_error_stream_stop_sending.unwrap(), error);
|
||||
stop_sending_ids_count += 1;
|
||||
}
|
||||
Http3ClientEvent::WebTransport(WebTransportEvent::SessionClosed {
|
||||
stream_id,
|
||||
error,
|
||||
}) => {
|
||||
close_event = true;
|
||||
assert_eq!(stream_id, expected_session_close.unwrap().0);
|
||||
assert_eq!(expected_session_close.unwrap().1, error);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
assert_eq!(reset_ids_count, expected_reset_ids.len());
|
||||
assert_eq!(stop_sending_ids_count, expected_stop_sending_ids.len());
|
||||
assert_eq!(close_event, expected_session_close.is_some());
|
||||
}
|
||||
|
||||
fn create_wt_stream_server(
|
||||
&mut self,
|
||||
wt_server_session: &mut WebTransportRequest,
|
||||
stream_type: StreamType,
|
||||
) -> Http3OrWebTransportStream {
|
||||
wt_server_session.create_stream(stream_type).unwrap()
|
||||
}
|
||||
|
||||
fn send_data_server(&mut self, wt_stream: &mut Http3OrWebTransportStream, data: &[u8]) {
|
||||
assert_eq!(wt_stream.send_data(data).unwrap(), data.len());
|
||||
self.exchange_packets();
|
||||
}
|
||||
|
||||
fn receive_data_server(
|
||||
&mut self,
|
||||
stream_id: StreamId,
|
||||
new_stream: bool,
|
||||
expected_data: &[u8],
|
||||
expected_fin: bool,
|
||||
) -> Http3OrWebTransportStream {
|
||||
self.exchange_packets();
|
||||
let mut new_stream_received = false;
|
||||
let mut data_received = false;
|
||||
let mut wt_stream = None;
|
||||
let mut stream_closed = false;
|
||||
let mut recv_data = Vec::new();
|
||||
while let Some(event) = self.server.next_event() {
|
||||
match event {
|
||||
Http3ServerEvent::WebTransport(WebTransportServerEvent::NewStream(request)) => {
|
||||
assert_eq!(stream_id, request.stream_id());
|
||||
new_stream_received = true;
|
||||
}
|
||||
Http3ServerEvent::Data {
|
||||
mut data,
|
||||
fin,
|
||||
stream,
|
||||
} => {
|
||||
recv_data.append(&mut data);
|
||||
stream_closed = fin;
|
||||
data_received = true;
|
||||
wt_stream = Some(stream);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
assert_eq!(&recv_data[..], expected_data);
|
||||
assert!(data_received);
|
||||
assert_eq!(new_stream, new_stream_received);
|
||||
assert_eq!(stream_closed, expected_fin);
|
||||
wt_stream.unwrap()
|
||||
}
|
||||
|
||||
fn close_stream_sending_server(&mut self, wt_stream: &mut Http3OrWebTransportStream) {
|
||||
wt_stream.stream_close_send().unwrap();
|
||||
self.exchange_packets();
|
||||
}
|
||||
|
||||
fn reset_stream_server(&mut self, wt_stream: &mut Http3OrWebTransportStream) {
|
||||
wt_stream
|
||||
.stream_reset_send(Error::HttpNoError.code())
|
||||
.unwrap();
|
||||
self.exchange_packets();
|
||||
}
|
||||
|
||||
fn stream_stop_sending_server(&mut self, wt_stream: &mut Http3OrWebTransportStream) {
|
||||
wt_stream
|
||||
.stream_stop_sending(Error::HttpNoError.code())
|
||||
.unwrap();
|
||||
self.exchange_packets();
|
||||
}
|
||||
|
||||
fn receive_reset_server(&mut self, expected_stream_id: StreamId, expected_error: u64) {
|
||||
let stream_reset = |e| {
|
||||
matches!(
|
||||
e,
|
||||
Http3ServerEvent::StreamReset {
|
||||
stream,
|
||||
error
|
||||
} if stream.stream_id() == expected_stream_id && error == expected_error
|
||||
)
|
||||
};
|
||||
assert!(self.server.events().any(stream_reset));
|
||||
}
|
||||
|
||||
fn receive_stop_sending_server(&mut self, expected_stream_id: StreamId, expected_error: u64) {
|
||||
let stop_sending = |e| {
|
||||
matches!(
|
||||
e,
|
||||
Http3ServerEvent::StreamStopSending {
|
||||
stream,
|
||||
error
|
||||
} if stream.stream_id() == expected_stream_id && error == expected_error
|
||||
)
|
||||
};
|
||||
assert!(self.server.events().any(stop_sending));
|
||||
}
|
||||
|
||||
fn check_events_after_closing_session_server(
|
||||
&mut self,
|
||||
expected_reset_ids: &[StreamId],
|
||||
expected_error_stream_reset: Option<u64>,
|
||||
expected_stop_sending_ids: &[StreamId],
|
||||
expected_error_stream_stop_sending: Option<u64>,
|
||||
expected_session_close: Option<(StreamId, Option<u64>)>,
|
||||
) {
|
||||
let mut reset_ids_count = 0;
|
||||
let mut stop_sending_ids_count = 0;
|
||||
let mut close_event = false;
|
||||
while let Some(event) = self.server.next_event() {
|
||||
match event {
|
||||
Http3ServerEvent::StreamReset { stream, error } => {
|
||||
assert!(expected_reset_ids.contains(&stream.stream_id()));
|
||||
assert_eq!(expected_error_stream_reset.unwrap(), error);
|
||||
reset_ids_count += 1;
|
||||
}
|
||||
Http3ServerEvent::StreamStopSending { stream, error } => {
|
||||
assert!(expected_stop_sending_ids.contains(&stream.stream_id()));
|
||||
assert_eq!(expected_error_stream_stop_sending.unwrap(), error);
|
||||
stop_sending_ids_count += 1;
|
||||
}
|
||||
Http3ServerEvent::WebTransport(WebTransportServerEvent::SessionClosed {
|
||||
session,
|
||||
error,
|
||||
}) => {
|
||||
close_event = true;
|
||||
assert_eq!(session.stream_id(), expected_session_close.unwrap().0);
|
||||
assert_eq!(expected_session_close.unwrap().1, error);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
assert_eq!(reset_ids_count, expected_reset_ids.len());
|
||||
assert_eq!(stop_sending_ids_count, expected_stop_sending_ids.len());
|
||||
assert_eq!(close_event, expected_session_close.is_some());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
// 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::{connect, default_http3_client, default_http3_server, exchange_packets};
|
||||
use neqo_common::event::Provider;
|
||||
use neqo_http3::{Http3Client, Http3ClientEvent, Http3State, WebTransportEvent};
|
||||
use std::time::Duration;
|
||||
use test_fixture::*;
|
||||
|
||||
fn check_wt_event(client: &mut Http3Client, wt_enable_client: bool, wt_enable_server: bool) {
|
||||
let wt_event = client.events().find_map(|e| {
|
||||
if let Http3ClientEvent::WebTransport(WebTransportEvent::Negotiated(neg)) = e {
|
||||
Some(neg)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
assert_eq!(wt_event.is_some(), wt_enable_client);
|
||||
if let Some(wt) = wt_event {
|
||||
assert_eq!(wt, wt_enable_client && wt_enable_server);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn negotiate_wt() {
|
||||
let (mut client, _server) = connect(true, true);
|
||||
assert!(client.webtransport_enabled());
|
||||
check_wt_event(&mut client, true, true);
|
||||
|
||||
let (mut client, _server) = connect(true, false);
|
||||
assert!(!client.webtransport_enabled());
|
||||
check_wt_event(&mut client, true, false);
|
||||
|
||||
let (mut client, _server) = connect(false, true);
|
||||
assert!(!client.webtransport_enabled());
|
||||
check_wt_event(&mut client, false, true);
|
||||
|
||||
let (mut client, _server) = connect(false, false);
|
||||
assert!(!client.webtransport_enabled());
|
||||
check_wt_event(&mut client, false, false);
|
||||
}
|
||||
|
||||
fn zero_rtt(client_org: bool, server_org: bool, client_resumed: bool, server_resumed: bool) {
|
||||
let (mut client, mut server) = connect(client_org, server_org);
|
||||
assert_eq!(client.webtransport_enabled(), client_org && server_org);
|
||||
|
||||
// exchane token
|
||||
let out = server.process(None, now());
|
||||
// We do not have a token so we need to wait for a resumption token timer to trigger.
|
||||
let _ = client.process(out.dgram(), now() + Duration::from_millis(250));
|
||||
assert_eq!(client.state(), Http3State::Connected);
|
||||
let token = client
|
||||
.events()
|
||||
.find_map(|e| {
|
||||
if let Http3ClientEvent::ResumptionToken(token) = e {
|
||||
Some(token)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let mut client = default_http3_client(client_resumed);
|
||||
let mut server = default_http3_server(server_resumed);
|
||||
client
|
||||
.enable_resumption(now(), &token)
|
||||
.expect("Set resumption token.");
|
||||
assert_eq!(client.state(), Http3State::ZeroRtt);
|
||||
|
||||
exchange_packets(&mut client, &mut server);
|
||||
|
||||
assert_eq!(&client.state(), &Http3State::Connected);
|
||||
assert_eq!(
|
||||
client.webtransport_enabled(),
|
||||
client_resumed && server_resumed
|
||||
);
|
||||
check_wt_event(&mut client, client_resumed, server_resumed);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn zero_rtt_wt_settings() {
|
||||
zero_rtt(true, true, true, true);
|
||||
zero_rtt(true, true, true, false);
|
||||
zero_rtt(true, true, false, true);
|
||||
zero_rtt(true, true, false, false);
|
||||
|
||||
zero_rtt(true, false, true, false);
|
||||
zero_rtt(true, false, true, true);
|
||||
zero_rtt(true, false, false, false);
|
||||
zero_rtt(true, false, false, true);
|
||||
|
||||
zero_rtt(false, false, false, false);
|
||||
zero_rtt(false, false, false, true);
|
||||
zero_rtt(false, false, true, false);
|
||||
zero_rtt(false, false, true, true);
|
||||
|
||||
zero_rtt(false, true, false, true);
|
||||
zero_rtt(false, true, false, false);
|
||||
zero_rtt(false, true, true, false);
|
||||
zero_rtt(false, true, true, true);
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
// 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 crate::webtransport::WtTest;
|
||||
use neqo_http3::Error;
|
||||
use std::mem;
|
||||
|
||||
#[test]
|
||||
fn wt_session() {
|
||||
let mut wt = WtTest::new();
|
||||
mem::drop(wt.create_wt_session());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wt_session_reject() {
|
||||
let mut wt = WtTest::new();
|
||||
let (wt_session_id, _wt_session) = wt.negotiate_wt_session(false);
|
||||
|
||||
wt.check_session_closed_event_client(wt_session_id, Some(Error::HttpRequestRejected.code()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wt_session_close_client() {
|
||||
let mut wt = WtTest::new();
|
||||
let mut wt_session = wt.create_wt_session();
|
||||
|
||||
wt.cancel_session_client(wt_session.stream_id());
|
||||
wt.check_session_closed_event_server(&mut wt_session, Some(Error::HttpNoError.code()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wt_session_close_server() {
|
||||
let mut wt = WtTest::new();
|
||||
let mut wt_session = wt.create_wt_session();
|
||||
|
||||
wt.cancel_session_server(&mut wt_session);
|
||||
wt.check_session_closed_event_client(wt_session.stream_id(), Some(Error::HttpNoError.code()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wt_session_close_server_close_send() {
|
||||
let mut wt = WtTest::new();
|
||||
let mut wt_session = wt.create_wt_session();
|
||||
|
||||
wt_session.stream_close_send().unwrap();
|
||||
wt.exchange_packets();
|
||||
wt.check_session_closed_event_client(
|
||||
wt_session.stream_id(),
|
||||
Some(Error::HttpGeneralProtocolStream.code()),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wt_session_close_server_stop_sending() {
|
||||
let mut wt = WtTest::new();
|
||||
let mut wt_session = wt.create_wt_session();
|
||||
|
||||
wt_session
|
||||
.stream_stop_sending(Error::HttpNoError.code())
|
||||
.unwrap();
|
||||
wt.exchange_packets();
|
||||
wt.check_session_closed_event_client(wt_session.stream_id(), Some(Error::HttpNoError.code()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wt_session_close_server_reset() {
|
||||
let mut wt = WtTest::new();
|
||||
let mut wt_session = wt.create_wt_session();
|
||||
|
||||
wt_session
|
||||
.stream_reset_send(Error::HttpNoError.code())
|
||||
.unwrap();
|
||||
wt.exchange_packets();
|
||||
wt.check_session_closed_event_client(wt_session.stream_id(), Some(Error::HttpNoError.code()));
|
||||
}
|
|
@ -0,0 +1,971 @@
|
|||
// 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 crate::webtransport::WtTest;
|
||||
use neqo_http3::Error;
|
||||
use neqo_transport::StreamType;
|
||||
use std::mem;
|
||||
|
||||
#[test]
|
||||
fn wt_client_stream_uni() {
|
||||
const BUF_CLIENT: &[u8] = &[0; 10];
|
||||
|
||||
let mut wt = WtTest::new();
|
||||
let wt_session = wt.create_wt_session();
|
||||
let wt_stream = wt.create_wt_stream_client(wt_session.stream_id(), StreamType::UniDi);
|
||||
wt.send_data_client(wt_stream, BUF_CLIENT);
|
||||
wt.receive_data_server(wt_stream, true, BUF_CLIENT, false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wt_client_stream_bidi() {
|
||||
const BUF_CLIENT: &[u8] = &[0; 10];
|
||||
const BUF_SERVER: &[u8] = &[1; 20];
|
||||
|
||||
let mut wt = WtTest::new();
|
||||
let wt_session = wt.create_wt_session();
|
||||
let wt_client_stream = wt.create_wt_stream_client(wt_session.stream_id(), StreamType::BiDi);
|
||||
wt.send_data_client(wt_client_stream, BUF_CLIENT);
|
||||
let mut wt_server_stream = wt.receive_data_server(wt_client_stream, true, BUF_CLIENT, false);
|
||||
wt.send_data_server(&mut wt_server_stream, BUF_SERVER);
|
||||
wt.receive_data_client(wt_client_stream, false, BUF_SERVER, false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wt_server_stream_uni() {
|
||||
const BUF_SERVER: &[u8] = &[2; 30];
|
||||
|
||||
let mut wt = WtTest::new();
|
||||
let mut wt_session = wt.create_wt_session();
|
||||
let mut wt_server_stream = wt.create_wt_stream_server(&mut wt_session, StreamType::UniDi);
|
||||
wt.send_data_server(&mut wt_server_stream, BUF_SERVER);
|
||||
wt.receive_data_client(wt_server_stream.stream_id(), true, BUF_SERVER, false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wt_server_stream_bidi() {
|
||||
const BUF_CLIENT: &[u8] = &[0; 10];
|
||||
const BUF_SERVER: &[u8] = &[1; 20];
|
||||
|
||||
let mut wt = WtTest::new();
|
||||
let mut wt_session = wt.create_wt_session();
|
||||
let mut wt_server_stream = wt.create_wt_stream_server(&mut wt_session, StreamType::BiDi);
|
||||
wt.send_data_server(&mut wt_server_stream, BUF_SERVER);
|
||||
wt.receive_data_client(wt_server_stream.stream_id(), true, BUF_SERVER, false);
|
||||
wt.send_data_client(wt_server_stream.stream_id(), BUF_CLIENT);
|
||||
mem::drop(wt.receive_data_server(wt_server_stream.stream_id(), false, BUF_CLIENT, false));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wt_client_stream_uni_close() {
|
||||
const BUF_CLIENT: &[u8] = &[0; 10];
|
||||
|
||||
let mut wt = WtTest::new();
|
||||
let wt_session = wt.create_wt_session();
|
||||
let wt_stream = wt.create_wt_stream_client(wt_session.stream_id(), StreamType::UniDi);
|
||||
wt.send_data_client(wt_stream, BUF_CLIENT);
|
||||
wt.close_stream_sending_client(wt_stream);
|
||||
wt.receive_data_server(wt_stream, true, BUF_CLIENT, true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wt_client_stream_bidi_close() {
|
||||
const BUF_CLIENT: &[u8] = &[0; 10];
|
||||
const BUF_SERVER: &[u8] = &[1; 20];
|
||||
|
||||
let mut wt = WtTest::new();
|
||||
let wt_session = wt.create_wt_session();
|
||||
let wt_client_stream = wt.create_wt_stream_client(wt_session.stream_id(), StreamType::BiDi);
|
||||
|
||||
wt.send_data_client(wt_client_stream, BUF_CLIENT);
|
||||
wt.close_stream_sending_client(wt_client_stream);
|
||||
|
||||
let mut wt_server_stream = wt.receive_data_server(wt_client_stream, true, BUF_CLIENT, true);
|
||||
|
||||
wt.send_data_server(&mut wt_server_stream, BUF_SERVER);
|
||||
wt.close_stream_sending_server(&mut wt_server_stream);
|
||||
wt.receive_data_client(wt_client_stream, false, BUF_SERVER, true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wt_server_stream_uni_closed() {
|
||||
const BUF_SERVER: &[u8] = &[2; 30];
|
||||
|
||||
let mut wt = WtTest::new();
|
||||
let mut wt_session = wt.create_wt_session();
|
||||
let mut wt_server_stream = wt.create_wt_stream_server(&mut wt_session, StreamType::UniDi);
|
||||
wt.send_data_server(&mut wt_server_stream, BUF_SERVER);
|
||||
wt.close_stream_sending_server(&mut wt_server_stream);
|
||||
wt.receive_data_client(wt_server_stream.stream_id(), true, BUF_SERVER, true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wt_server_stream_bidi_close() {
|
||||
const BUF_CLIENT: &[u8] = &[0; 10];
|
||||
const BUF_SERVER: &[u8] = &[1; 20];
|
||||
|
||||
let mut wt = WtTest::new();
|
||||
let mut wt_session = wt.create_wt_session();
|
||||
let mut wt_server_stream = wt.create_wt_stream_server(&mut wt_session, StreamType::BiDi);
|
||||
wt.send_data_server(&mut wt_server_stream, BUF_SERVER);
|
||||
wt.close_stream_sending_server(&mut wt_server_stream);
|
||||
wt.receive_data_client(wt_server_stream.stream_id(), true, BUF_SERVER, true);
|
||||
wt.send_data_client(wt_server_stream.stream_id(), BUF_CLIENT);
|
||||
wt.close_stream_sending_client(wt_server_stream.stream_id());
|
||||
mem::drop(wt.receive_data_server(wt_server_stream.stream_id(), false, BUF_CLIENT, true));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wt_client_stream_uni_reset() {
|
||||
const BUF_CLIENT: &[u8] = &[0; 10];
|
||||
|
||||
let mut wt = WtTest::new();
|
||||
let wt_session = wt.create_wt_session();
|
||||
let wt_stream = wt.create_wt_stream_client(wt_session.stream_id(), StreamType::UniDi);
|
||||
wt.send_data_client(wt_stream, BUF_CLIENT);
|
||||
mem::drop(wt.receive_data_server(wt_stream, true, BUF_CLIENT, false));
|
||||
wt.reset_stream_client(wt_stream);
|
||||
wt.receive_reset_server(wt_stream, Error::HttpNoError.code());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wt_server_stream_uni_reset() {
|
||||
const BUF_SERVER: &[u8] = &[2; 30];
|
||||
|
||||
let mut wt = WtTest::new();
|
||||
let mut wt_session = wt.create_wt_session();
|
||||
let mut wt_server_stream = wt.create_wt_stream_server(&mut wt_session, StreamType::UniDi);
|
||||
wt.send_data_server(&mut wt_server_stream, BUF_SERVER);
|
||||
wt.receive_data_client(wt_server_stream.stream_id(), true, BUF_SERVER, false);
|
||||
wt.reset_stream_server(&mut wt_server_stream);
|
||||
wt.receive_reset_client(wt_server_stream.stream_id());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wt_client_stream_bidi_reset() {
|
||||
const BUF_CLIENT: &[u8] = &[0; 10];
|
||||
|
||||
let mut wt = WtTest::new();
|
||||
let wt_session = wt.create_wt_session();
|
||||
let wt_client_stream = wt.create_wt_stream_client(wt_session.stream_id(), StreamType::BiDi);
|
||||
|
||||
wt.send_data_client(wt_client_stream, BUF_CLIENT);
|
||||
let mut wt_server_stream = wt.receive_data_server(wt_client_stream, true, BUF_CLIENT, false);
|
||||
|
||||
wt.reset_stream_client(wt_client_stream);
|
||||
wt.receive_reset_server(wt_server_stream.stream_id(), Error::HttpNoError.code());
|
||||
|
||||
wt.reset_stream_server(&mut wt_server_stream);
|
||||
wt.receive_reset_client(wt_client_stream);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wt_server_stream_bidi_reset() {
|
||||
const BUF_SERVER: &[u8] = &[1; 20];
|
||||
|
||||
let mut wt = WtTest::new();
|
||||
let mut wt_session = wt.create_wt_session();
|
||||
let mut wt_server_stream = wt.create_wt_stream_server(&mut wt_session, StreamType::BiDi);
|
||||
wt.send_data_server(&mut wt_server_stream, BUF_SERVER);
|
||||
wt.receive_data_client(wt_server_stream.stream_id(), true, BUF_SERVER, false);
|
||||
|
||||
wt.reset_stream_client(wt_server_stream.stream_id());
|
||||
wt.receive_reset_server(wt_server_stream.stream_id(), Error::HttpNoError.code());
|
||||
|
||||
wt.reset_stream_server(&mut wt_server_stream);
|
||||
wt.receive_reset_client(wt_server_stream.stream_id());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wt_client_stream_uni_stop_sending() {
|
||||
const BUF_CLIENT: &[u8] = &[0; 10];
|
||||
|
||||
let mut wt = WtTest::new();
|
||||
let wt_session = wt.create_wt_session();
|
||||
let wt_stream = wt.create_wt_stream_client(wt_session.stream_id(), StreamType::UniDi);
|
||||
wt.send_data_client(wt_stream, BUF_CLIENT);
|
||||
let mut wt_server_stream = wt.receive_data_server(wt_stream, true, BUF_CLIENT, false);
|
||||
wt.stream_stop_sending_server(&mut wt_server_stream);
|
||||
wt.receive_stop_sending_client(wt_stream);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wt_server_stream_uni_stop_sending() {
|
||||
const BUF_SERVER: &[u8] = &[2; 30];
|
||||
|
||||
let mut wt = WtTest::new();
|
||||
let mut wt_session = wt.create_wt_session();
|
||||
let mut wt_server_stream = wt.create_wt_stream_server(&mut wt_session, StreamType::UniDi);
|
||||
wt.send_data_server(&mut wt_server_stream, BUF_SERVER);
|
||||
wt.receive_data_client(wt_server_stream.stream_id(), true, BUF_SERVER, false);
|
||||
wt.stream_stop_sending_client(wt_server_stream.stream_id());
|
||||
wt.receive_stop_sending_server(wt_server_stream.stream_id(), Error::HttpNoError.code());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wt_client_stream_bidi_stop_sending() {
|
||||
const BUF_CLIENT: &[u8] = &[0; 10];
|
||||
|
||||
let mut wt = WtTest::new();
|
||||
let wt_session = wt.create_wt_session();
|
||||
|
||||
let wt_client_stream = wt.create_wt_stream_client(wt_session.stream_id(), StreamType::BiDi);
|
||||
|
||||
wt.send_data_client(wt_client_stream, BUF_CLIENT);
|
||||
|
||||
let mut wt_server_stream = wt.receive_data_server(wt_client_stream, true, BUF_CLIENT, false);
|
||||
|
||||
wt.stream_stop_sending_client(wt_client_stream);
|
||||
|
||||
wt.receive_stop_sending_server(wt_server_stream.stream_id(), Error::HttpNoError.code());
|
||||
wt.stream_stop_sending_server(&mut wt_server_stream);
|
||||
wt.receive_stop_sending_client(wt_server_stream.stream_id());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wt_server_stream_bidi_stop_sending() {
|
||||
const BUF_SERVER: &[u8] = &[1; 20];
|
||||
|
||||
let mut wt = WtTest::new();
|
||||
let mut wt_session = wt.create_wt_session();
|
||||
let mut wt_server_stream = wt.create_wt_stream_server(&mut wt_session, StreamType::BiDi);
|
||||
|
||||
wt.send_data_server(&mut wt_server_stream, BUF_SERVER);
|
||||
wt.receive_data_client(wt_server_stream.stream_id(), true, BUF_SERVER, false);
|
||||
wt.stream_stop_sending_client(wt_server_stream.stream_id());
|
||||
wt.receive_stop_sending_server(wt_server_stream.stream_id(), Error::HttpNoError.code());
|
||||
wt.stream_stop_sending_server(&mut wt_server_stream);
|
||||
wt.receive_stop_sending_client(wt_server_stream.stream_id());
|
||||
}
|
||||
|
||||
// For the following tests the client cancels a session. The streams are in different states:
|
||||
// 1) Both sides of a bidirectional client stream are opened.
|
||||
// 2) A client unidirectional stream is opened.
|
||||
// 3) A client unidirectional stream has been closed and both sides consumed the closing info.
|
||||
// 4) A client unidirectional stream has been closed, but only the server has consumed the closing info.
|
||||
// 5) A client unidirectional stream has been closed, but only the client has consum the closing info.
|
||||
// 6) Both sides of a bidirectional server stream are opened.
|
||||
// 7) A server unidirectional stream is opened.
|
||||
// 8) A server unidirectional stream has been closed and both sides consumed the closing info.
|
||||
// 9) A server unidirectional stream has been closed, but only the server has consumed the closing info.
|
||||
// 10) A server unidirectional stream has been closed, but only the client has consumed the closing info.
|
||||
// 11) Both sides of a bidirectional stream have been closed and consumed by both sides.
|
||||
// 12) Both sides of a bidirectional stream have been closed, but not consumed by both sides.
|
||||
// 13) Multiples open streams
|
||||
|
||||
#[test]
|
||||
fn wt_client_session_close_1() {
|
||||
const BUF: &[u8] = &[0; 10];
|
||||
|
||||
let mut wt = WtTest::new();
|
||||
let wt_session = wt.create_wt_session();
|
||||
|
||||
let bidi_from_client = wt.create_wt_stream_client(wt_session.stream_id(), StreamType::BiDi);
|
||||
wt.send_data_client(bidi_from_client, BUF);
|
||||
let _ = wt.receive_data_server(bidi_from_client, true, BUF, false);
|
||||
|
||||
wt.cancel_session_client(wt_session.stream_id());
|
||||
|
||||
wt.check_events_after_closing_session_server(
|
||||
&[bidi_from_client],
|
||||
Some(Error::HttpRequestCancelled.code()),
|
||||
&[bidi_from_client],
|
||||
Some(Error::HttpRequestCancelled.code()),
|
||||
Some((wt_session.stream_id(), Some(Error::HttpNoError.code()))),
|
||||
);
|
||||
|
||||
wt.check_events_after_closing_session_client(
|
||||
&[bidi_from_client],
|
||||
Some(Error::HttpRequestCancelled.code()),
|
||||
&[bidi_from_client],
|
||||
Some(Error::HttpRequestCancelled.code()),
|
||||
false,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wt_client_session_close_2() {
|
||||
const BUF: &[u8] = &[0; 10];
|
||||
|
||||
let mut wt = WtTest::new();
|
||||
let wt_session = wt.create_wt_session();
|
||||
|
||||
let unidi_from_client = wt.create_wt_stream_client(wt_session.stream_id(), StreamType::UniDi);
|
||||
|
||||
wt.send_data_client(unidi_from_client, BUF);
|
||||
let _ = wt.receive_data_server(unidi_from_client, true, BUF, false);
|
||||
|
||||
wt.cancel_session_client(wt_session.stream_id());
|
||||
|
||||
wt.check_events_after_closing_session_server(
|
||||
&[unidi_from_client],
|
||||
Some(Error::HttpRequestCancelled.code()),
|
||||
&[],
|
||||
None,
|
||||
Some((wt_session.stream_id(), Some(Error::HttpNoError.code()))),
|
||||
);
|
||||
|
||||
wt.check_events_after_closing_session_client(
|
||||
&[],
|
||||
None,
|
||||
&[unidi_from_client],
|
||||
Some(Error::HttpRequestCancelled.code()),
|
||||
false,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wt_client_session_close_3() {
|
||||
const BUF: &[u8] = &[0; 10];
|
||||
|
||||
let mut wt = WtTest::new();
|
||||
let wt_session = wt.create_wt_session();
|
||||
|
||||
let unidi_from_client = wt.create_wt_stream_client(wt_session.stream_id(), StreamType::UniDi);
|
||||
|
||||
wt.send_data_client(unidi_from_client, BUF);
|
||||
let _ = wt.receive_data_server(unidi_from_client, true, BUF, false);
|
||||
wt.close_stream_sending_client(unidi_from_client);
|
||||
|
||||
wt.cancel_session_client(wt_session.stream_id());
|
||||
|
||||
wt.check_events_after_closing_session_server(
|
||||
&[],
|
||||
None,
|
||||
&[],
|
||||
None,
|
||||
Some((wt_session.stream_id(), Some(Error::HttpNoError.code()))),
|
||||
);
|
||||
|
||||
wt.check_events_after_closing_session_client(&[], None, &[], None, false, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wt_client_session_close_4() {
|
||||
const BUF: &[u8] = &[0; 10];
|
||||
|
||||
let mut wt = WtTest::new();
|
||||
let wt_session = wt.create_wt_session();
|
||||
|
||||
let unidi_from_client = wt.create_wt_stream_client(wt_session.stream_id(), StreamType::UniDi);
|
||||
|
||||
wt.send_data_client(unidi_from_client, BUF);
|
||||
let mut unidi_from_client_s = wt.receive_data_server(unidi_from_client, true, BUF, false);
|
||||
wt.stream_stop_sending_server(&mut unidi_from_client_s);
|
||||
|
||||
wt.cancel_session_client(wt_session.stream_id());
|
||||
|
||||
wt.check_events_after_closing_session_server(
|
||||
&[],
|
||||
None,
|
||||
&[],
|
||||
None,
|
||||
Some((wt_session.stream_id(), Some(Error::HttpNoError.code()))),
|
||||
);
|
||||
|
||||
wt.check_events_after_closing_session_client(
|
||||
&[],
|
||||
None,
|
||||
&[unidi_from_client],
|
||||
Some(Error::HttpNoError.code()),
|
||||
false,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wt_client_session_close_5() {
|
||||
const BUF: &[u8] = &[0; 10];
|
||||
|
||||
let mut wt = WtTest::new();
|
||||
let wt_session = wt.create_wt_session();
|
||||
|
||||
let unidi_from_client = wt.create_wt_stream_client(wt_session.stream_id(), StreamType::UniDi);
|
||||
|
||||
wt.send_data_client(unidi_from_client, BUF);
|
||||
mem::drop(wt.receive_data_server(unidi_from_client, true, BUF, false));
|
||||
wt.reset_stream_client(unidi_from_client);
|
||||
|
||||
wt.cancel_session_client(wt_session.stream_id());
|
||||
|
||||
wt.check_events_after_closing_session_server(
|
||||
&[unidi_from_client],
|
||||
Some(Error::HttpNoError.code()),
|
||||
&[],
|
||||
None,
|
||||
Some((wt_session.stream_id(), Some(Error::HttpNoError.code()))),
|
||||
);
|
||||
|
||||
wt.check_events_after_closing_session_client(&[], None, &[], None, false, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wt_client_session_close_6() {
|
||||
const BUF: &[u8] = &[0; 10];
|
||||
|
||||
let mut wt = WtTest::new();
|
||||
let mut wt_session = wt.create_wt_session();
|
||||
|
||||
let mut bidi_from_server = wt.create_wt_stream_server(&mut wt_session, StreamType::BiDi);
|
||||
wt.send_data_server(&mut bidi_from_server, BUF);
|
||||
wt.receive_data_client(bidi_from_server.stream_id(), true, BUF, false);
|
||||
|
||||
wt.cancel_session_client(wt_session.stream_id());
|
||||
|
||||
wt.check_events_after_closing_session_server(
|
||||
&[bidi_from_server.stream_id()],
|
||||
Some(Error::HttpRequestCancelled.code()),
|
||||
&[bidi_from_server.stream_id()],
|
||||
Some(Error::HttpRequestCancelled.code()),
|
||||
Some((wt_session.stream_id(), Some(Error::HttpNoError.code()))),
|
||||
);
|
||||
|
||||
wt.check_events_after_closing_session_client(
|
||||
&[bidi_from_server.stream_id()],
|
||||
Some(Error::HttpRequestCancelled.code()),
|
||||
&[bidi_from_server.stream_id()],
|
||||
Some(Error::HttpRequestCancelled.code()),
|
||||
false,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wt_client_session_close_7() {
|
||||
const BUF: &[u8] = &[0; 10];
|
||||
|
||||
let mut wt = WtTest::new();
|
||||
let mut wt_session = wt.create_wt_session();
|
||||
|
||||
let mut unidi_from_server = wt.create_wt_stream_server(&mut wt_session, StreamType::UniDi);
|
||||
wt.send_data_server(&mut unidi_from_server, BUF);
|
||||
wt.receive_data_client(unidi_from_server.stream_id(), true, BUF, false);
|
||||
|
||||
wt.cancel_session_client(wt_session.stream_id());
|
||||
|
||||
wt.check_events_after_closing_session_server(
|
||||
&[],
|
||||
None,
|
||||
&[unidi_from_server.stream_id()],
|
||||
Some(Error::HttpRequestCancelled.code()),
|
||||
Some((wt_session.stream_id(), Some(Error::HttpNoError.code()))),
|
||||
);
|
||||
|
||||
wt.check_events_after_closing_session_client(
|
||||
&[unidi_from_server.stream_id()],
|
||||
Some(Error::HttpRequestCancelled.code()),
|
||||
&[],
|
||||
None,
|
||||
false,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wt_client_session_close_8() {
|
||||
const BUF: &[u8] = &[0; 10];
|
||||
|
||||
let mut wt = WtTest::new();
|
||||
let mut wt_session = wt.create_wt_session();
|
||||
|
||||
let mut unidi_server = wt.create_wt_stream_server(&mut wt_session, StreamType::UniDi);
|
||||
wt.send_data_server(&mut unidi_server, BUF);
|
||||
wt.close_stream_sending_server(&mut unidi_server);
|
||||
wt.receive_data_client(unidi_server.stream_id(), true, BUF, true);
|
||||
|
||||
wt.cancel_session_client(wt_session.stream_id());
|
||||
|
||||
wt.check_events_after_closing_session_server(
|
||||
&[],
|
||||
None,
|
||||
&[],
|
||||
None,
|
||||
Some((wt_session.stream_id(), Some(Error::HttpNoError.code()))),
|
||||
);
|
||||
|
||||
wt.check_events_after_closing_session_client(&[], None, &[], None, false, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wt_client_session_close_9() {
|
||||
const BUF: &[u8] = &[0; 10];
|
||||
|
||||
let mut wt = WtTest::new();
|
||||
let mut wt_session = wt.create_wt_session();
|
||||
|
||||
let mut unidi_server = wt.create_wt_stream_server(&mut wt_session, StreamType::UniDi);
|
||||
wt.send_data_server(&mut unidi_server, BUF);
|
||||
wt.stream_stop_sending_client(unidi_server.stream_id());
|
||||
|
||||
wt.cancel_session_client(wt_session.stream_id());
|
||||
|
||||
wt.check_events_after_closing_session_server(
|
||||
&[],
|
||||
None,
|
||||
&[unidi_server.stream_id()],
|
||||
Some(Error::HttpNoError.code()),
|
||||
Some((wt_session.stream_id(), Some(Error::HttpNoError.code()))),
|
||||
);
|
||||
|
||||
wt.check_events_after_closing_session_client(&[], None, &[], None, false, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wt_client_session_close_10() {
|
||||
const BUF: &[u8] = &[0; 10];
|
||||
|
||||
let mut wt = WtTest::new();
|
||||
let mut wt_session = wt.create_wt_session();
|
||||
|
||||
let mut unidi_server = wt.create_wt_stream_server(&mut wt_session, StreamType::UniDi);
|
||||
wt.send_data_server(&mut unidi_server, BUF);
|
||||
wt.close_stream_sending_server(&mut unidi_server);
|
||||
|
||||
wt.cancel_session_client(wt_session.stream_id());
|
||||
|
||||
wt.check_events_after_closing_session_server(
|
||||
&[],
|
||||
None,
|
||||
&[],
|
||||
None,
|
||||
Some((wt_session.stream_id(), Some(Error::HttpNoError.code()))),
|
||||
);
|
||||
|
||||
wt.check_events_after_closing_session_client(
|
||||
&[unidi_server.stream_id()],
|
||||
Some(Error::HttpRequestCancelled.code()),
|
||||
&[],
|
||||
None,
|
||||
false,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wt_client_session_close_11() {
|
||||
const BUF: &[u8] = &[0; 10];
|
||||
|
||||
let mut wt = WtTest::new();
|
||||
let mut wt_session = wt.create_wt_session();
|
||||
|
||||
let mut bidi_server = wt.create_wt_stream_server(&mut wt_session, StreamType::BiDi);
|
||||
wt.send_data_server(&mut bidi_server, BUF);
|
||||
wt.close_stream_sending_server(&mut bidi_server);
|
||||
wt.receive_data_client(bidi_server.stream_id(), true, BUF, true);
|
||||
wt.stream_stop_sending_server(&mut bidi_server);
|
||||
wt.receive_stop_sending_client(bidi_server.stream_id());
|
||||
|
||||
wt.cancel_session_client(wt_session.stream_id());
|
||||
|
||||
wt.check_events_after_closing_session_server(
|
||||
&[],
|
||||
None,
|
||||
&[],
|
||||
None,
|
||||
Some((wt_session.stream_id(), Some(Error::HttpNoError.code()))),
|
||||
);
|
||||
|
||||
wt.check_events_after_closing_session_client(&[], None, &[], None, false, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wt_client_session_close_12() {
|
||||
const BUF: &[u8] = &[0; 10];
|
||||
|
||||
let mut wt = WtTest::new();
|
||||
let mut wt_session = wt.create_wt_session();
|
||||
|
||||
let mut bidi_server = wt.create_wt_stream_server(&mut wt_session, StreamType::BiDi);
|
||||
wt.send_data_server(&mut bidi_server, BUF);
|
||||
wt.close_stream_sending_server(&mut bidi_server);
|
||||
wt.stream_stop_sending_server(&mut bidi_server);
|
||||
|
||||
wt.cancel_session_client(wt_session.stream_id());
|
||||
|
||||
wt.check_events_after_closing_session_server(
|
||||
&[],
|
||||
None,
|
||||
&[],
|
||||
None,
|
||||
Some((wt_session.stream_id(), Some(Error::HttpNoError.code()))),
|
||||
);
|
||||
|
||||
wt.check_events_after_closing_session_client(
|
||||
&[bidi_server.stream_id()],
|
||||
Some(Error::HttpRequestCancelled.code()),
|
||||
&[bidi_server.stream_id()],
|
||||
Some(Error::HttpNoError.code()),
|
||||
false,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wt_client_session_close_13() {
|
||||
const BUF: &[u8] = &[0; 10];
|
||||
|
||||
let mut wt = WtTest::new();
|
||||
let wt_session = wt.create_wt_session();
|
||||
|
||||
let bidi_client_1 = wt.create_wt_stream_client(wt_session.stream_id(), StreamType::BiDi);
|
||||
wt.send_data_client(bidi_client_1, BUF);
|
||||
let _ = wt.receive_data_server(bidi_client_1, true, BUF, false);
|
||||
let bidi_client_2 = wt.create_wt_stream_client(wt_session.stream_id(), StreamType::BiDi);
|
||||
wt.send_data_client(bidi_client_2, BUF);
|
||||
let _ = wt.receive_data_server(bidi_client_2, true, BUF, false);
|
||||
|
||||
wt.cancel_session_client(wt_session.stream_id());
|
||||
|
||||
wt.check_events_after_closing_session_server(
|
||||
&[bidi_client_1, bidi_client_2],
|
||||
Some(Error::HttpRequestCancelled.code()),
|
||||
&[bidi_client_1, bidi_client_2],
|
||||
Some(Error::HttpRequestCancelled.code()),
|
||||
Some((wt_session.stream_id(), Some(Error::HttpNoError.code()))),
|
||||
);
|
||||
|
||||
wt.check_events_after_closing_session_client(
|
||||
&[bidi_client_1, bidi_client_2],
|
||||
Some(Error::HttpRequestCancelled.code()),
|
||||
&[bidi_client_1, bidi_client_2],
|
||||
Some(Error::HttpRequestCancelled.code()),
|
||||
false,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
// For the following tests the server cancels a session. The streams are in different states:
|
||||
// 1) Both sides of a bidirectional client stream are opened.
|
||||
// 2) A client unidirectional stream is opened.
|
||||
// 3) A client unidirectional stream has been closed and consumed by both sides.
|
||||
// 4) A client unidirectional stream has been closed, but not consumed by the client.
|
||||
// 5) Both sides of a bidirectional server stream are opened.
|
||||
// 6) A server unidirectional stream is opened.
|
||||
// 7) A server unidirectional stream has been closed and consumed by both sides.
|
||||
// 8) A server unidirectional stream has been closed, but not consumed by the client.
|
||||
// 9) Both sides of a bidirectional stream have been closed and consumed by both sides.
|
||||
// 10) Both sides of a bidirectional stream have been closed, but not consumed by the client.
|
||||
// 12) Multiples open streams
|
||||
|
||||
#[test]
|
||||
fn wt_client_session_server_close_1() {
|
||||
const BUF: &[u8] = &[0; 10];
|
||||
|
||||
let mut wt = WtTest::new();
|
||||
let mut wt_session = wt.create_wt_session();
|
||||
|
||||
let bidi_client = wt.create_wt_stream_client(wt_session.stream_id(), StreamType::BiDi);
|
||||
wt.send_data_client(bidi_client, BUF);
|
||||
let _ = wt.receive_data_server(bidi_client, true, BUF, false);
|
||||
|
||||
wt.cancel_session_server(&mut wt_session);
|
||||
|
||||
wt.check_events_after_closing_session_client(
|
||||
&[bidi_client],
|
||||
Some(Error::HttpRequestCancelled.code()),
|
||||
&[bidi_client],
|
||||
Some(Error::HttpRequestCancelled.code()),
|
||||
false,
|
||||
Some((wt_session.stream_id(), Some(Error::HttpNoError.code()))),
|
||||
);
|
||||
|
||||
wt.check_events_after_closing_session_server(
|
||||
&[bidi_client],
|
||||
Some(Error::HttpRequestCancelled.code()),
|
||||
&[bidi_client],
|
||||
Some(Error::HttpRequestCancelled.code()),
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wt_client_session_server_close_2() {
|
||||
const BUF: &[u8] = &[0; 10];
|
||||
|
||||
let mut wt = WtTest::new();
|
||||
let mut wt_session = wt.create_wt_session();
|
||||
|
||||
let unidi_client = wt.create_wt_stream_client(wt_session.stream_id(), StreamType::UniDi);
|
||||
wt.send_data_client(unidi_client, BUF);
|
||||
let _ = wt.receive_data_server(unidi_client, true, BUF, false);
|
||||
|
||||
wt.cancel_session_server(&mut wt_session);
|
||||
|
||||
wt.check_events_after_closing_session_client(
|
||||
&[],
|
||||
None,
|
||||
&[unidi_client],
|
||||
Some(Error::HttpRequestCancelled.code()),
|
||||
false,
|
||||
Some((wt_session.stream_id(), Some(Error::HttpNoError.code()))),
|
||||
);
|
||||
|
||||
wt.check_events_after_closing_session_server(
|
||||
&[unidi_client],
|
||||
Some(Error::HttpRequestCancelled.code()),
|
||||
&[],
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wt_client_session_server_close_3() {
|
||||
const BUF: &[u8] = &[0; 10];
|
||||
|
||||
let mut wt = WtTest::new();
|
||||
let mut wt_session = wt.create_wt_session();
|
||||
|
||||
let unidi_client = wt.create_wt_stream_client(wt_session.stream_id(), StreamType::UniDi);
|
||||
wt.send_data_client(unidi_client, BUF);
|
||||
let mut unidi_client_s = wt.receive_data_server(unidi_client, true, BUF, false);
|
||||
wt.stream_stop_sending_server(&mut unidi_client_s);
|
||||
wt.receive_stop_sending_client(unidi_client);
|
||||
|
||||
wt.cancel_session_server(&mut wt_session);
|
||||
|
||||
wt.check_events_after_closing_session_client(
|
||||
&[],
|
||||
None,
|
||||
&[],
|
||||
None,
|
||||
false,
|
||||
Some((wt_session.stream_id(), Some(Error::HttpNoError.code()))),
|
||||
);
|
||||
|
||||
wt.check_events_after_closing_session_server(&[], None, &[], None, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wt_client_session_server_close_4() {
|
||||
const BUF: &[u8] = &[0; 10];
|
||||
|
||||
let mut wt = WtTest::new();
|
||||
let mut wt_session = wt.create_wt_session();
|
||||
|
||||
let unidi_client = wt.create_wt_stream_client(wt_session.stream_id(), StreamType::UniDi);
|
||||
wt.send_data_client(unidi_client, BUF);
|
||||
let mut unidi_client_s = wt.receive_data_server(unidi_client, true, BUF, false);
|
||||
wt.stream_stop_sending_server(&mut unidi_client_s);
|
||||
|
||||
wt.cancel_session_server(&mut wt_session);
|
||||
|
||||
wt.check_events_after_closing_session_client(
|
||||
&[],
|
||||
None,
|
||||
&[unidi_client],
|
||||
Some(Error::HttpNoError.code()),
|
||||
false,
|
||||
Some((wt_session.stream_id(), Some(Error::HttpNoError.code()))),
|
||||
);
|
||||
|
||||
wt.check_events_after_closing_session_server(&[], None, &[], None, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wt_client_session_server_close_5() {
|
||||
const BUF: &[u8] = &[0; 10];
|
||||
|
||||
let mut wt = WtTest::new();
|
||||
let mut wt_session = wt.create_wt_session();
|
||||
|
||||
let mut bidi_server = wt.create_wt_stream_server(&mut wt_session, StreamType::BiDi);
|
||||
wt.send_data_server(&mut bidi_server, BUF);
|
||||
wt.receive_data_client(bidi_server.stream_id(), true, BUF, false);
|
||||
|
||||
wt.cancel_session_server(&mut wt_session);
|
||||
|
||||
wt.check_events_after_closing_session_client(
|
||||
&[bidi_server.stream_id()],
|
||||
Some(Error::HttpRequestCancelled.code()),
|
||||
&[bidi_server.stream_id()],
|
||||
Some(Error::HttpRequestCancelled.code()),
|
||||
false,
|
||||
Some((wt_session.stream_id(), Some(Error::HttpNoError.code()))),
|
||||
);
|
||||
|
||||
wt.check_events_after_closing_session_server(
|
||||
&[bidi_server.stream_id()],
|
||||
Some(Error::HttpRequestCancelled.code()),
|
||||
&[bidi_server.stream_id()],
|
||||
Some(Error::HttpRequestCancelled.code()),
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wt_client_session_server_close_6() {
|
||||
const BUF: &[u8] = &[0; 10];
|
||||
|
||||
let mut wt = WtTest::new();
|
||||
let mut wt_session = wt.create_wt_session();
|
||||
|
||||
let mut unidi_server = wt.create_wt_stream_server(&mut wt_session, StreamType::UniDi);
|
||||
wt.send_data_server(&mut unidi_server, BUF);
|
||||
wt.receive_data_client(unidi_server.stream_id(), true, BUF, false);
|
||||
|
||||
wt.cancel_session_server(&mut wt_session);
|
||||
|
||||
wt.check_events_after_closing_session_client(
|
||||
&[unidi_server.stream_id()],
|
||||
Some(Error::HttpRequestCancelled.code()),
|
||||
&[],
|
||||
None,
|
||||
false,
|
||||
Some((wt_session.stream_id(), Some(Error::HttpNoError.code()))),
|
||||
);
|
||||
wt.check_events_after_closing_session_server(
|
||||
&[],
|
||||
None,
|
||||
&[unidi_server.stream_id()],
|
||||
Some(Error::HttpRequestCancelled.code()),
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wt_client_session_server_close_7() {
|
||||
const BUF: &[u8] = &[0; 10];
|
||||
|
||||
let mut wt = WtTest::new();
|
||||
let mut wt_session = wt.create_wt_session();
|
||||
|
||||
let mut unidi_server = wt.create_wt_stream_server(&mut wt_session, StreamType::UniDi);
|
||||
wt.send_data_server(&mut unidi_server, BUF);
|
||||
wt.close_stream_sending_server(&mut unidi_server);
|
||||
wt.receive_data_client(unidi_server.stream_id(), true, BUF, true);
|
||||
|
||||
wt.cancel_session_server(&mut wt_session);
|
||||
|
||||
// Already close stream will not have a reset event.
|
||||
wt.check_events_after_closing_session_client(
|
||||
&[],
|
||||
None,
|
||||
&[],
|
||||
None,
|
||||
false,
|
||||
Some((wt_session.stream_id(), Some(Error::HttpNoError.code()))),
|
||||
);
|
||||
|
||||
wt.check_events_after_closing_session_server(&[], None, &[], None, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wt_client_session_server_close_8() {
|
||||
const BUF: &[u8] = &[0; 10];
|
||||
|
||||
let mut wt = WtTest::new();
|
||||
let mut wt_session = wt.create_wt_session();
|
||||
|
||||
let mut unidi_server = wt.create_wt_stream_server(&mut wt_session, StreamType::UniDi);
|
||||
wt.send_data_server(&mut unidi_server, BUF);
|
||||
wt.close_stream_sending_server(&mut unidi_server);
|
||||
|
||||
wt.cancel_session_server(&mut wt_session);
|
||||
|
||||
// The stream was only closed on the server side therefore it is cancelled on the client side.
|
||||
wt.check_events_after_closing_session_client(
|
||||
&[unidi_server.stream_id()],
|
||||
Some(Error::HttpRequestCancelled.code()),
|
||||
&[],
|
||||
None,
|
||||
false,
|
||||
Some((wt_session.stream_id(), Some(Error::HttpNoError.code()))),
|
||||
);
|
||||
|
||||
wt.check_events_after_closing_session_server(&[], None, &[], None, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wt_client_session_server_close_9() {
|
||||
const BUF: &[u8] = &[0; 10];
|
||||
|
||||
let mut wt = WtTest::new();
|
||||
let mut wt_session = wt.create_wt_session();
|
||||
|
||||
let mut bidi_server = wt.create_wt_stream_server(&mut wt_session, StreamType::BiDi);
|
||||
wt.send_data_server(&mut bidi_server, BUF);
|
||||
wt.close_stream_sending_server(&mut bidi_server);
|
||||
wt.receive_data_client(bidi_server.stream_id(), true, BUF, true);
|
||||
wt.stream_stop_sending_server(&mut bidi_server);
|
||||
wt.receive_stop_sending_client(bidi_server.stream_id());
|
||||
|
||||
wt.cancel_session_server(&mut wt_session);
|
||||
|
||||
// Already close stream will not have a reset event.
|
||||
wt.check_events_after_closing_session_client(
|
||||
&[],
|
||||
None,
|
||||
&[],
|
||||
None,
|
||||
false,
|
||||
Some((wt_session.stream_id(), Some(Error::HttpNoError.code()))),
|
||||
);
|
||||
|
||||
wt.check_events_after_closing_session_server(&[], None, &[], None, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wt_client_session_server_close_10() {
|
||||
const BUF: &[u8] = &[0; 10];
|
||||
|
||||
let mut wt = WtTest::new();
|
||||
let mut wt_session = wt.create_wt_session();
|
||||
|
||||
let mut bidi_server = wt.create_wt_stream_server(&mut wt_session, StreamType::BiDi);
|
||||
wt.send_data_server(&mut bidi_server, BUF);
|
||||
wt.close_stream_sending_server(&mut bidi_server);
|
||||
wt.stream_stop_sending_server(&mut bidi_server);
|
||||
|
||||
wt.cancel_session_server(&mut wt_session);
|
||||
|
||||
wt.check_events_after_closing_session_client(
|
||||
&[bidi_server.stream_id()],
|
||||
Some(Error::HttpRequestCancelled.code()),
|
||||
&[bidi_server.stream_id()],
|
||||
Some(Error::HttpNoError.code()),
|
||||
false,
|
||||
Some((wt_session.stream_id(), Some(Error::HttpNoError.code()))),
|
||||
);
|
||||
|
||||
wt.check_events_after_closing_session_server(&[], None, &[], None, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wt_client_session_server_close_11() {
|
||||
const BUF: &[u8] = &[0; 10];
|
||||
|
||||
let mut wt = WtTest::new();
|
||||
let mut wt_session = wt.create_wt_session();
|
||||
|
||||
let bidi_client_1 = wt.create_wt_stream_client(wt_session.stream_id(), StreamType::BiDi);
|
||||
wt.send_data_client(bidi_client_1, BUF);
|
||||
let _ = wt.receive_data_server(bidi_client_1, true, BUF, false);
|
||||
let bidi_client_2 = wt.create_wt_stream_client(wt_session.stream_id(), StreamType::BiDi);
|
||||
wt.send_data_client(bidi_client_2, BUF);
|
||||
let _ = wt.receive_data_server(bidi_client_2, true, BUF, false);
|
||||
|
||||
wt.cancel_session_server(&mut wt_session);
|
||||
|
||||
wt.check_events_after_closing_session_client(
|
||||
&[bidi_client_1, bidi_client_2],
|
||||
Some(Error::HttpRequestCancelled.code()),
|
||||
&[bidi_client_1, bidi_client_2],
|
||||
Some(Error::HttpRequestCancelled.code()),
|
||||
false,
|
||||
Some((wt_session.stream_id(), Some(Error::HttpNoError.code()))),
|
||||
);
|
||||
|
||||
wt.check_events_after_closing_session_server(
|
||||
&[bidi_client_1, bidi_client_2],
|
||||
Some(Error::HttpRequestCancelled.code()),
|
||||
&[bidi_client_1, bidi_client_2],
|
||||
Some(Error::HttpRequestCancelled.code()),
|
||||
None,
|
||||
);
|
||||
}
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"26d508a0c9b82cbdaccaae625d06b30fdaa0ff679fa5124e97cb56793423cc37","src/decoder.rs":"542f03f35044efa880780f851dbe8b1794a0ee8fe537e7b24928562298ac0a7f","src/decoder_instructions.rs":"6f40dd661f28c034239b0fa34b4bbf6beaa36cbab249fd3410292e2343d69658","src/encoder.rs":"cd2c0268a5c1d63e374e971c4d6000f9c3a4c1ef9d163148a8a79ac90b8ecfe3","src/encoder_instructions.rs":"a7f1d3a4f8ae941286d0aba81037a8df3ef85e275392ef31d9938e9314c706db","src/header_block.rs":"7910ddc28b44d2065070cb2d87ab3cfbb905cce912b23d8b12b0f0add5691ceb","src/huffman.rs":"3a9edaf827343ec6e43cfd50fcc0d0077287947160ae630da5c3ddaaefedd010","src/huffman_decode_helper.rs":"2970c57f052878b727c2f764490c54184f5c2608e1d6aa961c3b01509e290122","src/huffman_table.rs":"06fea766a6276ac56c7ee0326faed800a742c15fda1f33bf2513e6cc6a5e6d27","src/lib.rs":"29c5e47f8a4cf9c0a5dfdc614594868db22bc25b9688e5efdbc041cd222a17e5","src/prefix.rs":"72c587c40aef4ed38cf13b2de91091d671611679be2a9da6f0b24abafaf50dc5","src/qlog.rs":"7618085e27bb3fb1f4d1c73ba501b9a293723293c4020b7cc4129676eb278131","src/qpack_send_buf.rs":"ca620f64e2d9c1514bbac3e968ff692d0f8f60d99e805e05c173c74c56da39ee","src/reader.rs":"a97c5d94cba1756735e389b127b9397b5ee673855cedbe7a34894e306ee64434","src/static_table.rs":"fda9d5c6f38f94b0bf92d3afdf8432dce6e27e189736596e16727090c77b78ec","src/stats.rs":"624dfa3b40858c304097bb0ce5b1be1bb4d7916b1abfc222f1aa705907009730","src/table.rs":"f7091bdd9ad1f8fe3b2298a7dbfd3d285c212d69569cda54f9bcf251cb758a21"},"package":null}
|
||||
{"files":{"Cargo.toml":"0dc3e499db364f97db58d873c8a652db66eeb608cf1b3ddd8d71560be9c0c8ce","src/decoder.rs":"6404dc3f93799aa0d3589817de9e2e2a5162d1235ff9dc66011df326e7f1f0d9","src/decoder_instructions.rs":"19d47158bc09551b449be205f5cd5ea83e6984c4e4d3e7d4b95938b09617015e","src/encoder.rs":"a902a3161fac8a0daea25ca15afb2333df9da01a8b5e5db92a82fe28cec417b8","src/encoder_instructions.rs":"a7f1d3a4f8ae941286d0aba81037a8df3ef85e275392ef31d9938e9314c706db","src/header_block.rs":"7910ddc28b44d2065070cb2d87ab3cfbb905cce912b23d8b12b0f0add5691ceb","src/huffman.rs":"3a9edaf827343ec6e43cfd50fcc0d0077287947160ae630da5c3ddaaefedd010","src/huffman_decode_helper.rs":"2970c57f052878b727c2f764490c54184f5c2608e1d6aa961c3b01509e290122","src/huffman_table.rs":"06fea766a6276ac56c7ee0326faed800a742c15fda1f33bf2513e6cc6a5e6d27","src/lib.rs":"29c5e47f8a4cf9c0a5dfdc614594868db22bc25b9688e5efdbc041cd222a17e5","src/prefix.rs":"72c587c40aef4ed38cf13b2de91091d671611679be2a9da6f0b24abafaf50dc5","src/qlog.rs":"7618085e27bb3fb1f4d1c73ba501b9a293723293c4020b7cc4129676eb278131","src/qpack_send_buf.rs":"ca620f64e2d9c1514bbac3e968ff692d0f8f60d99e805e05c173c74c56da39ee","src/reader.rs":"448729cb2fc7857914d093f9d1ca00b27f013666f834463ef6569c23f2ddf597","src/static_table.rs":"fda9d5c6f38f94b0bf92d3afdf8432dce6e27e189736596e16727090c77b78ec","src/stats.rs":"624dfa3b40858c304097bb0ce5b1be1bb4d7916b1abfc222f1aa705907009730","src/table.rs":"f7091bdd9ad1f8fe3b2298a7dbfd3d285c212d69569cda54f9bcf251cb758a21"},"package":null}
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "neqo-qpack"
|
||||
version = "0.5.3"
|
||||
version = "0.5.5"
|
||||
authors = ["Dragana Damjanovic <dragana.damjano@gmail.com>"]
|
||||
edition = "2018"
|
||||
license = "MIT/Apache-2.0"
|
||||
|
|
|
@ -13,7 +13,7 @@ use crate::stats::Stats;
|
|||
use crate::table::HeaderTable;
|
||||
use crate::{Error, QpackSettings, Res};
|
||||
use neqo_common::{qdebug, Header};
|
||||
use neqo_transport::Connection;
|
||||
use neqo_transport::{Connection, StreamId};
|
||||
use std::convert::TryFrom;
|
||||
|
||||
pub const QPACK_UNI_STREAM_TYPE_DECODER: u64 = 0x3;
|
||||
|
@ -25,10 +25,10 @@ pub struct QPackDecoder {
|
|||
acked_inserts: u64,
|
||||
max_entries: u64,
|
||||
send_buf: QpackData,
|
||||
local_stream_id: Option<u64>,
|
||||
local_stream_id: Option<StreamId>,
|
||||
max_table_size: u64,
|
||||
max_blocked_streams: usize,
|
||||
blocked_streams: Vec<(u64, u64)>, //stream_id and requested inserts count.
|
||||
blocked_streams: Vec<(StreamId, u64)>, //stream_id and requested inserts count.
|
||||
stats: Stats,
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,7 @@ impl QPackDecoder {
|
|||
/// # Panics
|
||||
/// If settings include invalid values.
|
||||
#[must_use]
|
||||
pub fn new(qpack_settings: QpackSettings) -> Self {
|
||||
pub fn new(qpack_settings: &QpackSettings) -> Self {
|
||||
qdebug!("Decoder: creating a new qpack decoder.");
|
||||
let mut send_buf = QpackData::default();
|
||||
send_buf.encode_varint(QPACK_UNI_STREAM_TYPE_DECODER);
|
||||
|
@ -75,7 +75,7 @@ impl QPackDecoder {
|
|||
/// # Errors
|
||||
/// May return: `ClosedCriticalStream` if stream has been closed or `EncoderStream`
|
||||
/// in case of any other transport error.
|
||||
pub fn receive(&mut self, conn: &mut Connection, stream_id: u64) -> Res<Vec<u64>> {
|
||||
pub fn receive(&mut self, conn: &mut Connection, stream_id: StreamId) -> Res<Vec<StreamId>> {
|
||||
let base_old = self.table.base();
|
||||
self.read_instructions(conn, stream_id)
|
||||
.map_err(|e| map_error(&e))?;
|
||||
|
@ -93,7 +93,7 @@ impl QPackDecoder {
|
|||
Ok(r)
|
||||
}
|
||||
|
||||
fn read_instructions(&mut self, conn: &mut Connection, stream_id: u64) -> Res<()> {
|
||||
fn read_instructions(&mut self, conn: &mut Connection, stream_id: StreamId) -> Res<()> {
|
||||
let mut recv = ReceiverConnWrapper::new(conn, stream_id);
|
||||
loop {
|
||||
match self.instruction_reader.read_instructions(&mut recv) {
|
||||
|
@ -147,14 +147,14 @@ impl QPackDecoder {
|
|||
self.table.set_capacity(cap)
|
||||
}
|
||||
|
||||
fn header_ack(&mut self, stream_id: u64, required_inserts: u64) {
|
||||
fn header_ack(&mut self, stream_id: StreamId, required_inserts: u64) {
|
||||
DecoderInstruction::HeaderAck { stream_id }.marshal(&mut self.send_buf);
|
||||
if required_inserts > self.acked_inserts {
|
||||
self.acked_inserts = required_inserts;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cancel_stream(&mut self, stream_id: u64) {
|
||||
pub fn cancel_stream(&mut self, stream_id: StreamId) {
|
||||
if self.table.capacity() > 0 {
|
||||
self.blocked_streams.retain(|(id, _)| *id != stream_id);
|
||||
DecoderInstruction::StreamCancellation { stream_id }.marshal(&mut self.send_buf);
|
||||
|
@ -195,7 +195,11 @@ impl QPackDecoder {
|
|||
/// May return `DecompressionFailed` if header block is incorrect or incomplete.
|
||||
/// # Panics
|
||||
/// When there is a programming error.
|
||||
pub fn decode_header_block(&mut self, buf: &[u8], stream_id: u64) -> Res<Option<Vec<Header>>> {
|
||||
pub fn decode_header_block(
|
||||
&mut self,
|
||||
buf: &[u8],
|
||||
stream_id: StreamId,
|
||||
) -> Res<Option<Vec<Header>>> {
|
||||
qdebug!([self], "decode header block.");
|
||||
let mut decoder = HeaderDecoder::new(buf);
|
||||
|
||||
|
@ -231,7 +235,7 @@ impl QPackDecoder {
|
|||
|
||||
/// # Panics
|
||||
/// When a stream has already been added.
|
||||
pub fn add_send_stream(&mut self, stream_id: u64) {
|
||||
pub fn add_send_stream(&mut self, stream_id: StreamId) {
|
||||
if self.local_stream_id.is_some() {
|
||||
panic!("Adding multiple local streams");
|
||||
}
|
||||
|
@ -239,7 +243,7 @@ impl QPackDecoder {
|
|||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn local_stream_id(&self) -> Option<u64> {
|
||||
pub fn local_stream_id(&self) -> Option<StreamId> {
|
||||
self.local_stream_id
|
||||
}
|
||||
|
||||
|
@ -265,17 +269,20 @@ fn map_error(err: &Error) -> Error {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Connection, Error, Header, QPackDecoder, Res};
|
||||
use super::{Connection, Error, QPackDecoder, Res};
|
||||
use crate::QpackSettings;
|
||||
use neqo_transport::StreamType;
|
||||
use std::convert::TryInto;
|
||||
use neqo_common::Header;
|
||||
use neqo_transport::{StreamId, StreamType};
|
||||
use std::convert::TryFrom;
|
||||
use std::mem;
|
||||
use test_fixture::now;
|
||||
|
||||
const STREAM_0: StreamId = StreamId::new(0);
|
||||
|
||||
struct TestDecoder {
|
||||
decoder: QPackDecoder,
|
||||
send_stream_id: u64,
|
||||
recv_stream_id: u64,
|
||||
send_stream_id: StreamId,
|
||||
recv_stream_id: StreamId,
|
||||
conn: Connection,
|
||||
peer_conn: Connection,
|
||||
}
|
||||
|
@ -288,7 +295,7 @@ mod tests {
|
|||
let send_stream_id = conn.stream_create(StreamType::UniDi).unwrap();
|
||||
|
||||
// create a decoder
|
||||
let mut decoder = QPackDecoder::new(QpackSettings {
|
||||
let mut decoder = QPackDecoder::new(&QpackSettings {
|
||||
max_table_size_encoder: 0,
|
||||
max_table_size_decoder: 300,
|
||||
max_blocked_streams: 100,
|
||||
|
@ -336,7 +343,7 @@ mod tests {
|
|||
decoder: &mut TestDecoder,
|
||||
header_block: &[u8],
|
||||
headers: &[Header],
|
||||
stream_id: u64,
|
||||
stream_id: StreamId,
|
||||
) {
|
||||
let decoded_headers = decoder
|
||||
.decoder
|
||||
|
@ -486,7 +493,7 @@ mod tests {
|
|||
|
||||
recv_instruction(&mut decoder, second_encoder_inst, &Ok(()));
|
||||
|
||||
decode_headers(&mut decoder, header_block, &headers, 0);
|
||||
decode_headers(&mut decoder, header_block, &headers, STREAM_0);
|
||||
|
||||
send_instructions_and_check(&mut decoder, &[0x80, 0x1]);
|
||||
}
|
||||
|
@ -522,7 +529,7 @@ mod tests {
|
|||
|
||||
recv_instruction(&mut decoder, second_encoder_inst, &Ok(()));
|
||||
|
||||
decode_headers(&mut decoder, header_block, &headers, 0);
|
||||
decode_headers(&mut decoder, header_block, &headers, STREAM_0);
|
||||
|
||||
send_instructions_and_check(&mut decoder, &[0x80]);
|
||||
}
|
||||
|
@ -548,7 +555,7 @@ mod tests {
|
|||
|
||||
recv_instruction(&mut decoder, encoder_inst, &Ok(()));
|
||||
|
||||
decode_headers(&mut decoder, header_block, &headers, 0);
|
||||
decode_headers(&mut decoder, header_block, &headers, STREAM_0);
|
||||
|
||||
send_instructions_and_check(&mut decoder, &[0x03, 0x80]);
|
||||
}
|
||||
|
@ -572,7 +579,7 @@ mod tests {
|
|||
|
||||
recv_instruction(&mut decoder, encoder_inst, &Ok(()));
|
||||
|
||||
decode_headers(&mut decoder, header_block, &headers, 0);
|
||||
decode_headers(&mut decoder, header_block, &headers, STREAM_0);
|
||||
|
||||
send_instructions_and_check(&mut decoder, &[0x03, 0x80, 0x01]);
|
||||
}
|
||||
|
@ -649,7 +656,7 @@ mod tests {
|
|||
&mut decoder,
|
||||
t.header_block,
|
||||
&t.headers,
|
||||
i.try_into().unwrap(),
|
||||
StreamId::from(u64::try_from(i).unwrap()),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -727,7 +734,7 @@ mod tests {
|
|||
&mut decoder,
|
||||
t.header_block,
|
||||
&t.headers,
|
||||
i.try_into().unwrap(),
|
||||
StreamId::from(u64::try_from(i).unwrap()),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -760,11 +767,11 @@ mod tests {
|
|||
|
||||
recv_instruction(&mut decoder, ENCODER_INST, &Ok(()));
|
||||
|
||||
decode_headers(&mut decoder, HEADER_BLOCK_1, &headers, 0);
|
||||
decode_headers(&mut decoder, HEADER_BLOCK_1, &headers, STREAM_0);
|
||||
|
||||
let headers = vec![Header::new("my-headera", "my-valuea")];
|
||||
|
||||
decode_headers(&mut decoder, HEADER_BLOCK_2, &headers, 0);
|
||||
decode_headers(&mut decoder, HEADER_BLOCK_2, &headers, STREAM_0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -791,6 +798,6 @@ mod tests {
|
|||
|
||||
recv_instruction(&mut decoder, ENCODER_INST, &Ok(()));
|
||||
|
||||
decode_headers(&mut decoder, HEADER_BLOCK, &headers, 0);
|
||||
decode_headers(&mut decoder, HEADER_BLOCK, &headers, STREAM_0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,22 +11,27 @@ use crate::qpack_send_buf::QpackData;
|
|||
use crate::reader::{IntReader, ReadByte};
|
||||
use crate::Res;
|
||||
use neqo_common::{qdebug, qtrace};
|
||||
use neqo_transport::StreamId;
|
||||
use std::mem;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub enum DecoderInstruction {
|
||||
InsertCountIncrement { increment: u64 },
|
||||
HeaderAck { stream_id: u64 },
|
||||
StreamCancellation { stream_id: u64 },
|
||||
HeaderAck { stream_id: StreamId },
|
||||
StreamCancellation { stream_id: StreamId },
|
||||
NoInstruction,
|
||||
}
|
||||
|
||||
impl DecoderInstruction {
|
||||
fn get_instruction(b: u8) -> Self {
|
||||
if DECODER_HEADER_ACK.cmp_prefix(b) {
|
||||
Self::HeaderAck { stream_id: 0 }
|
||||
Self::HeaderAck {
|
||||
stream_id: StreamId::from(0),
|
||||
}
|
||||
} else if DECODER_STREAM_CANCELLATION.cmp_prefix(b) {
|
||||
Self::StreamCancellation { stream_id: 0 }
|
||||
Self::StreamCancellation {
|
||||
stream_id: StreamId::from(0),
|
||||
}
|
||||
} else if DECODER_INSERT_COUNT_INCREMENT.cmp_prefix(b) {
|
||||
Self::InsertCountIncrement { increment: 0 }
|
||||
} else {
|
||||
|
@ -40,10 +45,10 @@ impl DecoderInstruction {
|
|||
enc.encode_prefixed_encoded_int(DECODER_INSERT_COUNT_INCREMENT, *increment);
|
||||
}
|
||||
Self::HeaderAck { stream_id } => {
|
||||
enc.encode_prefixed_encoded_int(DECODER_HEADER_ACK, *stream_id);
|
||||
enc.encode_prefixed_encoded_int(DECODER_HEADER_ACK, stream_id.as_u64());
|
||||
}
|
||||
Self::StreamCancellation { stream_id } => {
|
||||
enc.encode_prefixed_encoded_int(DECODER_STREAM_CANCELLATION, *stream_id);
|
||||
enc.encode_prefixed_encoded_int(DECODER_STREAM_CANCELLATION, stream_id.as_u64());
|
||||
}
|
||||
Self::NoInstruction => {}
|
||||
}
|
||||
|
@ -102,9 +107,7 @@ impl DecoderInstructionReader {
|
|||
let val = reader.read(recv)?;
|
||||
qtrace!([self], "varint read {}", val);
|
||||
match &mut self.instruction {
|
||||
DecoderInstruction::InsertCountIncrement { increment: v }
|
||||
| DecoderInstruction::HeaderAck { stream_id: v }
|
||||
| DecoderInstruction::StreamCancellation { stream_id: v } => {
|
||||
DecoderInstruction::InsertCountIncrement { increment: v } => {
|
||||
*v = val;
|
||||
self.state = DecoderInstructionReaderState::ReadInstruction;
|
||||
break Ok(mem::replace(
|
||||
|
@ -112,6 +115,15 @@ impl DecoderInstructionReader {
|
|||
DecoderInstruction::NoInstruction,
|
||||
));
|
||||
}
|
||||
DecoderInstruction::HeaderAck { stream_id: v }
|
||||
| DecoderInstruction::StreamCancellation { stream_id: v } => {
|
||||
*v = StreamId::from(val);
|
||||
self.state = DecoderInstructionReaderState::ReadInstruction;
|
||||
break Ok(mem::replace(
|
||||
&mut self.instruction,
|
||||
DecoderInstruction::NoInstruction,
|
||||
));
|
||||
}
|
||||
DecoderInstruction::NoInstruction => {
|
||||
unreachable!("This instruction cannot be in this state.");
|
||||
}
|
||||
|
@ -128,6 +140,7 @@ mod test {
|
|||
use super::{DecoderInstruction, DecoderInstructionReader, QpackData};
|
||||
use crate::reader::test_receiver::TestReceiver;
|
||||
use crate::Error;
|
||||
use neqo_transport::StreamId;
|
||||
|
||||
fn test_encoding_decoding(instruction: DecoderInstruction) {
|
||||
let mut buf = QpackData::default();
|
||||
|
@ -146,11 +159,19 @@ mod test {
|
|||
test_encoding_decoding(DecoderInstruction::InsertCountIncrement { increment: 1 });
|
||||
test_encoding_decoding(DecoderInstruction::InsertCountIncrement { increment: 10_000 });
|
||||
|
||||
test_encoding_decoding(DecoderInstruction::HeaderAck { stream_id: 1 });
|
||||
test_encoding_decoding(DecoderInstruction::HeaderAck { stream_id: 10_000 });
|
||||
test_encoding_decoding(DecoderInstruction::HeaderAck {
|
||||
stream_id: StreamId::new(1),
|
||||
});
|
||||
test_encoding_decoding(DecoderInstruction::HeaderAck {
|
||||
stream_id: StreamId::new(10_000),
|
||||
});
|
||||
|
||||
test_encoding_decoding(DecoderInstruction::StreamCancellation { stream_id: 1 });
|
||||
test_encoding_decoding(DecoderInstruction::StreamCancellation { stream_id: 10_000 });
|
||||
test_encoding_decoding(DecoderInstruction::StreamCancellation {
|
||||
stream_id: StreamId::new(1),
|
||||
});
|
||||
test_encoding_decoding(DecoderInstruction::StreamCancellation {
|
||||
stream_id: StreamId::new(10_000),
|
||||
});
|
||||
}
|
||||
|
||||
fn test_encoding_decoding_slow_reader(instruction: DecoderInstruction) {
|
||||
|
@ -177,9 +198,11 @@ mod test {
|
|||
test_encoding_decoding_slow_reader(DecoderInstruction::InsertCountIncrement {
|
||||
increment: 10_000,
|
||||
});
|
||||
test_encoding_decoding_slow_reader(DecoderInstruction::HeaderAck { stream_id: 10_000 });
|
||||
test_encoding_decoding_slow_reader(DecoderInstruction::HeaderAck {
|
||||
stream_id: StreamId::new(10_000),
|
||||
});
|
||||
test_encoding_decoding_slow_reader(DecoderInstruction::StreamCancellation {
|
||||
stream_id: 10_000,
|
||||
stream_id: StreamId::new(10_000),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ pub struct QPackEncoder {
|
|||
// There can be multiple header blocks in one stream, headers, trailer, push stream request, etc.
|
||||
// This HashMap maps a stream ID to a list of header blocks. Each header block is a list of
|
||||
// referenced dynamic table entries.
|
||||
unacked_header_blocks: HashMap<u64, VecDeque<HashSet<u64>>>,
|
||||
unacked_header_blocks: HashMap<StreamId, VecDeque<HashSet<u64>>>,
|
||||
blocked_stream_cnt: u16,
|
||||
use_huffman: bool,
|
||||
next_capacity: Option<u64>,
|
||||
|
@ -57,7 +57,7 @@ pub struct QPackEncoder {
|
|||
|
||||
impl QPackEncoder {
|
||||
#[must_use]
|
||||
pub fn new(qpack_settings: QpackSettings, use_huffman: bool) -> Self {
|
||||
pub fn new(qpack_settings: &QpackSettings, use_huffman: bool) -> Self {
|
||||
Self {
|
||||
table: HeaderTable::new(true),
|
||||
max_table_size: qpack_settings.max_table_size_encoder,
|
||||
|
@ -114,12 +114,12 @@ impl QPackEncoder {
|
|||
/// # Errors
|
||||
/// May return: `ClosedCriticalStream` if stream has been closed or `DecoderStream`
|
||||
/// in case of any other transport error.
|
||||
pub fn receive(&mut self, conn: &mut Connection, stream_id: u64) -> Res<()> {
|
||||
pub fn receive(&mut self, conn: &mut Connection, stream_id: StreamId) -> Res<()> {
|
||||
self.read_instructions(conn, stream_id)
|
||||
.map_err(|e| map_error(&e))
|
||||
}
|
||||
|
||||
fn read_instructions(&mut self, conn: &mut Connection, stream_id: u64) -> Res<()> {
|
||||
fn read_instructions(&mut self, conn: &mut Connection, stream_id: StreamId) -> Res<()> {
|
||||
qdebug!([self], "read a new instraction");
|
||||
loop {
|
||||
let mut recv = ReceiverConnWrapper::new(conn, stream_id);
|
||||
|
@ -151,7 +151,7 @@ impl QPackEncoder {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn header_ack(&mut self, stream_id: u64) {
|
||||
fn header_ack(&mut self, stream_id: StreamId) {
|
||||
self.stats.header_acks_recv += 1;
|
||||
let mut new_acked = self.table.get_acked_inserts_cnt();
|
||||
if let Some(hb_list) = self.unacked_header_blocks.get_mut(&stream_id) {
|
||||
|
@ -175,7 +175,7 @@ impl QPackEncoder {
|
|||
}
|
||||
}
|
||||
|
||||
fn stream_cancellation(&mut self, stream_id: u64) {
|
||||
fn stream_cancellation(&mut self, stream_id: StreamId) {
|
||||
self.stats.stream_cancelled_recv += 1;
|
||||
let mut was_blocker = false;
|
||||
if let Some(mut hb_list) = self.unacked_header_blocks.remove(&stream_id) {
|
||||
|
@ -251,7 +251,7 @@ impl QPackEncoder {
|
|||
let stream_id = self.local_stream.stream_id().ok_or(Error::Internal)?;
|
||||
|
||||
let sent = conn
|
||||
.stream_send_atomic(stream_id.as_u64(), &buf)
|
||||
.stream_send_atomic(stream_id, &buf)
|
||||
.map_err(|e| map_stream_send_atomic_error(&e))?;
|
||||
if !sent {
|
||||
return Err(Error::EncoderStreamBlocked);
|
||||
|
@ -285,7 +285,7 @@ impl QPackEncoder {
|
|||
}
|
||||
let mut buf = QpackData::default();
|
||||
EncoderInstruction::Capacity { value: cap }.marshal(&mut buf, self.use_huffman);
|
||||
if !conn.stream_send_atomic(stream_id.as_u64(), &buf)? {
|
||||
if !conn.stream_send_atomic(stream_id, &buf)? {
|
||||
return Err(Error::EncoderStreamBlocked);
|
||||
}
|
||||
if self.table.set_capacity(cap).is_err() {
|
||||
|
@ -304,7 +304,7 @@ impl QPackEncoder {
|
|||
/// Sends any qpack encoder instructions.
|
||||
/// # Errors
|
||||
/// returns `EncoderStream` in case of an error.
|
||||
pub fn send(&mut self, conn: &mut Connection) -> Res<()> {
|
||||
pub fn send_encoder_updates(&mut self, conn: &mut Connection) -> Res<()> {
|
||||
match self.local_stream {
|
||||
LocalStreamState::NoStream => {
|
||||
qerror!("Send call but there is no stream yet.");
|
||||
|
@ -313,7 +313,7 @@ impl QPackEncoder {
|
|||
LocalStreamState::Uninitialized(stream_id) => {
|
||||
let mut buf = QpackData::default();
|
||||
buf.encode_varint(QPACK_UNI_STREAM_TYPE_ENCODER);
|
||||
if !conn.stream_send_atomic(stream_id.as_u64(), &buf[..])? {
|
||||
if !conn.stream_send_atomic(stream_id, &buf[..])? {
|
||||
return Err(Error::EncoderStreamBlocked);
|
||||
}
|
||||
self.local_stream = LocalStreamState::Initialized(stream_id);
|
||||
|
@ -325,7 +325,7 @@ impl QPackEncoder {
|
|||
}
|
||||
}
|
||||
|
||||
fn is_stream_blocker(&self, stream_id: u64) -> bool {
|
||||
fn is_stream_blocker(&self, stream_id: StreamId) -> bool {
|
||||
if let Some(hb_list) = self.unacked_header_blocks.get(&stream_id) {
|
||||
debug_assert!(!hb_list.is_empty());
|
||||
match hb_list.iter().flatten().max() {
|
||||
|
@ -347,21 +347,25 @@ impl QPackEncoder {
|
|||
&mut self,
|
||||
conn: &mut Connection,
|
||||
h: &[Header],
|
||||
stream_id: u64,
|
||||
) -> Res<HeaderEncoder> {
|
||||
stream_id: StreamId,
|
||||
) -> HeaderEncoder {
|
||||
qdebug!([self], "encoding headers.");
|
||||
|
||||
let mut encoder_blocked = false;
|
||||
// Try to send capacity instructions if present.
|
||||
match self.send(conn) {
|
||||
Ok(()) => {}
|
||||
Err(Error::EncoderStreamBlocked) => {
|
||||
encoder_blocked = true;
|
||||
}
|
||||
Err(e) => {
|
||||
// `InternalError`, `ClosedCriticalStream`
|
||||
return Err(e);
|
||||
}
|
||||
if self.send_encoder_updates(conn).is_err() {
|
||||
// This code doesn't try to deal with errors, it just tries
|
||||
// to write to the encoder stream AND if it can't uses
|
||||
// literal instructions.
|
||||
// The errors can be:
|
||||
// 1) `EncoderStreamBlocked` - this is an error that
|
||||
// can occur.
|
||||
// 2) `InternalError` - this is unexpected error.
|
||||
// 3) `ClosedCriticalStream` - this is error that should
|
||||
// close the HTTP/3 session.
|
||||
// The last 2 errors are ignored here and will be picked up
|
||||
// by the main loop.
|
||||
encoder_blocked = true;
|
||||
}
|
||||
|
||||
let mut encoded_h =
|
||||
|
@ -404,21 +408,27 @@ impl QPackEncoder {
|
|||
} else if can_block && !encoder_blocked {
|
||||
// Insert using an InsertWithNameLiteral instruction. This entry name does not match any name in the
|
||||
// tables therefore we cannot use any other instruction.
|
||||
match self.send_and_insert(conn, &name, &value) {
|
||||
Ok(index) => {
|
||||
encoded_h.encode_indexed_dynamic(index);
|
||||
ref_entries.insert(index);
|
||||
self.table.add_ref(index);
|
||||
}
|
||||
Err(Error::EncoderStreamBlocked) | Err(Error::DynamicTableFull) => {
|
||||
// As soon as one of the instructions cannot be written or the table is full, do not try again.
|
||||
encoder_blocked = true;
|
||||
encoded_h.encode_literal_with_name_literal(&name, &value);
|
||||
}
|
||||
Err(e) => {
|
||||
// `InternalError`, `ClosedCriticalStream`
|
||||
return Err(e);
|
||||
}
|
||||
if let Ok(index) = self.send_and_insert(conn, &name, &value) {
|
||||
encoded_h.encode_indexed_dynamic(index);
|
||||
ref_entries.insert(index);
|
||||
self.table.add_ref(index);
|
||||
} else {
|
||||
// This code doesn't try to deal with errors, it just tries
|
||||
// to write to the encoder stream AND if it can't uses
|
||||
// literal instructions.
|
||||
// The errors can be:
|
||||
// 1) `EncoderStreamBlocked` - this is an error that
|
||||
// can occur.
|
||||
// 2) `DynamicTableFull` - this is an error that
|
||||
// can occur.
|
||||
// 3) `InternalError` - this is unexpected error.
|
||||
// 4) `ClosedCriticalStream` - this is error that should
|
||||
// close the HTTP/3 session.
|
||||
// The last 2 errors are ignored here and will be picked up
|
||||
// by the main loop.
|
||||
// As soon as one of the instructions cannot be written or the table is full, do not try again.
|
||||
encoder_blocked = true;
|
||||
encoded_h.encode_literal_with_name_literal(&name, &value);
|
||||
}
|
||||
} else {
|
||||
encoded_h.encode_literal_with_name_literal(&name, &value);
|
||||
|
@ -444,15 +454,15 @@ impl QPackEncoder {
|
|||
.push_front(ref_entries);
|
||||
self.stats.dynamic_table_references += 1;
|
||||
}
|
||||
Ok(encoded_h)
|
||||
encoded_h
|
||||
}
|
||||
|
||||
/// Encoder stream has been created. Add the stream id.
|
||||
/// # Panics
|
||||
/// If a stream has already been added.
|
||||
pub fn add_send_stream(&mut self, stream_id: u64) {
|
||||
pub fn add_send_stream(&mut self, stream_id: StreamId) {
|
||||
if self.local_stream == LocalStreamState::NoStream {
|
||||
self.local_stream = LocalStreamState::Uninitialized(StreamId::new(stream_id));
|
||||
self.local_stream = LocalStreamState::Uninitialized(stream_id);
|
||||
} else {
|
||||
panic!("Adding multiple local streams");
|
||||
}
|
||||
|
@ -464,8 +474,8 @@ impl QPackEncoder {
|
|||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn local_stream_id(&self) -> Option<u64> {
|
||||
self.local_stream.stream_id().map(StreamId::as_u64)
|
||||
pub fn local_stream_id(&self) -> Option<StreamId> {
|
||||
self.local_stream.stream_id()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -504,14 +514,14 @@ fn map_stream_send_atomic_error(err: &TransportError) -> Error {
|
|||
mod tests {
|
||||
use super::{Connection, Error, Header, QPackEncoder, Res};
|
||||
use crate::QpackSettings;
|
||||
use neqo_transport::{ConnectionParameters, StreamType};
|
||||
use neqo_transport::{ConnectionParameters, StreamId, StreamType};
|
||||
use std::mem;
|
||||
use test_fixture::{configure_server, default_client, default_server, handshake, now};
|
||||
|
||||
struct TestEncoder {
|
||||
encoder: QPackEncoder,
|
||||
send_stream_id: u64,
|
||||
recv_stream_id: u64,
|
||||
send_stream_id: StreamId,
|
||||
recv_stream_id: StreamId,
|
||||
conn: Connection,
|
||||
peer_conn: Connection,
|
||||
}
|
||||
|
@ -520,7 +530,7 @@ mod tests {
|
|||
pub fn change_capacity(&mut self, capacity: u64) -> Res<()> {
|
||||
self.encoder.set_max_capacity(capacity).unwrap();
|
||||
// We will try to really change the table only when we send the change capacity instruction.
|
||||
self.encoder.send(&mut self.conn)
|
||||
self.encoder.send_encoder_updates(&mut self.conn)
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, header: &[u8], value: &[u8], inst: &[u8]) {
|
||||
|
@ -531,21 +541,20 @@ mod tests {
|
|||
|
||||
pub fn encode_header_block(
|
||||
&mut self,
|
||||
stream_id: u64,
|
||||
stream_id: StreamId,
|
||||
headers: &[Header],
|
||||
expected_encoding: &[u8],
|
||||
inst: &[u8],
|
||||
) {
|
||||
let buf = self
|
||||
.encoder
|
||||
.encode_header_block(&mut self.conn, headers, stream_id)
|
||||
.unwrap();
|
||||
.encode_header_block(&mut self.conn, headers, stream_id);
|
||||
assert_eq!(&buf[..], expected_encoding);
|
||||
self.send_instructions(inst);
|
||||
}
|
||||
|
||||
pub fn send_instructions(&mut self, encoder_instruction: &[u8]) {
|
||||
self.encoder.send(&mut self.conn).unwrap();
|
||||
self.encoder.send_encoder_updates(&mut self.conn).unwrap();
|
||||
let out = self.conn.process(None, now());
|
||||
let out2 = self.peer_conn.process(out.dgram(), now());
|
||||
mem::drop(self.conn.process(out2.dgram(), now()));
|
||||
|
@ -577,7 +586,7 @@ mod tests {
|
|||
|
||||
// create an encoder
|
||||
let mut encoder = QPackEncoder::new(
|
||||
QpackSettings {
|
||||
&QpackSettings {
|
||||
max_table_size_encoder: 1500,
|
||||
max_table_size_decoder: 0,
|
||||
max_blocked_streams: 0,
|
||||
|
@ -642,6 +651,8 @@ mod tests {
|
|||
// Indexed Header Field that refers to the first entry in the dynamic table.
|
||||
const ENCODE_INDEXED_REF_DYNAMIC: &[u8] = &[0x02, 0x00, 0x80];
|
||||
|
||||
const STREAM_1: StreamId = StreamId::new(1);
|
||||
const STREAM_2: StreamId = StreamId::new(2);
|
||||
const HEADER_ACK_STREAM_ID_1: &[u8] = &[0x81];
|
||||
const HEADER_ACK_STREAM_ID_2: &[u8] = &[0x82];
|
||||
const STREAM_CANCELED_ID_1: &[u8] = &[0x41];
|
||||
|
@ -761,8 +772,7 @@ mod tests {
|
|||
for t in &test_cases {
|
||||
let buf = encoder
|
||||
.encoder
|
||||
.encode_header_block(&mut encoder.conn, &t.headers, 1)
|
||||
.unwrap();
|
||||
.encode_header_block(&mut encoder.conn, &t.headers, STREAM_1);
|
||||
assert_eq!(&buf[..], t.header_block);
|
||||
encoder.send_instructions(t.encoder_inst);
|
||||
}
|
||||
|
@ -835,8 +845,7 @@ mod tests {
|
|||
for t in &test_cases {
|
||||
let buf = encoder
|
||||
.encoder
|
||||
.encode_header_block(&mut encoder.conn, &t.headers, 1)
|
||||
.unwrap();
|
||||
.encode_header_block(&mut encoder.conn, &t.headers, STREAM_1);
|
||||
assert_eq!(&buf[..], t.header_block);
|
||||
encoder.send_instructions(t.encoder_inst);
|
||||
}
|
||||
|
@ -903,14 +912,11 @@ mod tests {
|
|||
recv_instruction(&mut encoder, &[0x01]);
|
||||
|
||||
// send a header block
|
||||
let buf = encoder
|
||||
.encoder
|
||||
.encode_header_block(
|
||||
&mut encoder.conn,
|
||||
&[Header::new("content-length", "1234")],
|
||||
1,
|
||||
)
|
||||
.unwrap();
|
||||
let buf = encoder.encoder.encode_header_block(
|
||||
&mut encoder.conn,
|
||||
&[Header::new("content-length", "1234")],
|
||||
STREAM_1,
|
||||
);
|
||||
assert_eq!(&buf[..], ENCODE_INDEXED_REF_DYNAMIC);
|
||||
encoder.send_instructions(&[]);
|
||||
|
||||
|
@ -986,14 +992,11 @@ mod tests {
|
|||
encoder.encoder.set_max_blocked_streams(1).unwrap();
|
||||
|
||||
// send a header block, it refers to unacked entry.
|
||||
let buf = encoder
|
||||
.encoder
|
||||
.encode_header_block(
|
||||
&mut encoder.conn,
|
||||
&[Header::new("content-length", "1234")],
|
||||
1,
|
||||
)
|
||||
.unwrap();
|
||||
let buf = encoder.encoder.encode_header_block(
|
||||
&mut encoder.conn,
|
||||
&[Header::new("content-length", "1234")],
|
||||
STREAM_1,
|
||||
);
|
||||
assert_is_index_to_dynamic(&buf);
|
||||
|
||||
assert_eq!(encoder.encoder.blocked_stream_cnt(), 1);
|
||||
|
@ -1002,28 +1005,22 @@ mod tests {
|
|||
|
||||
// The next one will not use the dynamic entry because it is exceeding the max_blocked_streams
|
||||
// limit.
|
||||
let buf = encoder
|
||||
.encoder
|
||||
.encode_header_block(
|
||||
&mut encoder.conn,
|
||||
&[Header::new("content-length", "1234")],
|
||||
2,
|
||||
)
|
||||
.unwrap();
|
||||
let buf = encoder.encoder.encode_header_block(
|
||||
&mut encoder.conn,
|
||||
&[Header::new("content-length", "1234")],
|
||||
StreamId::new(2),
|
||||
);
|
||||
assert_is_index_to_static_name_only(&buf);
|
||||
|
||||
encoder.send_instructions(&[]);
|
||||
assert_eq!(encoder.encoder.blocked_stream_cnt(), 1);
|
||||
|
||||
// another header block to already blocked stream can still use the entry.
|
||||
let buf = encoder
|
||||
.encoder
|
||||
.encode_header_block(
|
||||
&mut encoder.conn,
|
||||
&[Header::new("content-length", "1234")],
|
||||
1,
|
||||
)
|
||||
.unwrap();
|
||||
let buf = encoder.encoder.encode_header_block(
|
||||
&mut encoder.conn,
|
||||
&[Header::new("content-length", "1234")],
|
||||
STREAM_1,
|
||||
);
|
||||
assert_is_index_to_dynamic(&buf);
|
||||
|
||||
assert_eq!(encoder.encoder.blocked_stream_cnt(), 1);
|
||||
|
@ -1058,30 +1055,23 @@ mod tests {
|
|||
|
||||
encoder.encoder.set_max_blocked_streams(1).unwrap();
|
||||
|
||||
let stream_id = 1;
|
||||
// send a header block, it refers to unacked entry.
|
||||
let buf = encoder
|
||||
.encoder
|
||||
.encode_header_block(
|
||||
&mut encoder.conn,
|
||||
&[Header::new("content-length", "1234")],
|
||||
stream_id,
|
||||
)
|
||||
.unwrap();
|
||||
let buf = encoder.encoder.encode_header_block(
|
||||
&mut encoder.conn,
|
||||
&[Header::new("content-length", "1234")],
|
||||
STREAM_1,
|
||||
);
|
||||
assert_is_index_to_dynamic(&buf);
|
||||
|
||||
// encode another header block for the same stream that will refer to the second entry
|
||||
// in the dynamic table.
|
||||
// This should work because the stream is already a blocked stream
|
||||
// send a header block, it refers to unacked entry.
|
||||
let buf = encoder
|
||||
.encoder
|
||||
.encode_header_block(
|
||||
&mut encoder.conn,
|
||||
&[Header::new("content-length", "12345")],
|
||||
stream_id,
|
||||
)
|
||||
.unwrap();
|
||||
let buf = encoder.encoder.encode_header_block(
|
||||
&mut encoder.conn,
|
||||
&[Header::new("content-length", "12345")],
|
||||
STREAM_1,
|
||||
);
|
||||
assert_is_index_to_dynamic(&buf);
|
||||
}
|
||||
|
||||
|
@ -1099,28 +1089,31 @@ mod tests {
|
|||
assert_eq!(encoder.encoder.blocked_stream_cnt(), 0);
|
||||
|
||||
// send a header block, that creates an new entry and refers to it.
|
||||
let buf = encoder
|
||||
.encoder
|
||||
.encode_header_block(&mut encoder.conn, &[Header::new("name1", "value1")], 1)
|
||||
.unwrap();
|
||||
let buf = encoder.encoder.encode_header_block(
|
||||
&mut encoder.conn,
|
||||
&[Header::new("name1", "value1")],
|
||||
STREAM_1,
|
||||
);
|
||||
assert_is_index_to_dynamic_post(&buf);
|
||||
|
||||
assert_eq!(encoder.encoder.blocked_stream_cnt(), 1);
|
||||
|
||||
// The next one will not create a new entry because the encoder is on max_blocked_streams limit.
|
||||
let buf = encoder
|
||||
.encoder
|
||||
.encode_header_block(&mut encoder.conn, &[Header::new("name2", "value2")], 2)
|
||||
.unwrap();
|
||||
let buf = encoder.encoder.encode_header_block(
|
||||
&mut encoder.conn,
|
||||
&[Header::new("name2", "value2")],
|
||||
STREAM_2,
|
||||
);
|
||||
assert_is_literal_value_literal_name(&buf);
|
||||
|
||||
assert_eq!(encoder.encoder.blocked_stream_cnt(), 1);
|
||||
|
||||
// another header block to already blocked stream can still create a new entry.
|
||||
let buf = encoder
|
||||
.encoder
|
||||
.encode_header_block(&mut encoder.conn, &[Header::new("name2", "value2")], 1)
|
||||
.unwrap();
|
||||
let buf = encoder.encoder.encode_header_block(
|
||||
&mut encoder.conn,
|
||||
&[Header::new("name2", "value2")],
|
||||
STREAM_1,
|
||||
);
|
||||
assert_is_index_to_dynamic_post(&buf);
|
||||
|
||||
assert_eq!(encoder.encoder.blocked_stream_cnt(), 1);
|
||||
|
@ -1140,19 +1133,21 @@ mod tests {
|
|||
assert_eq!(encoder.encoder.blocked_stream_cnt(), 0);
|
||||
|
||||
// send a header block, that creates an new entry and refers to it.
|
||||
let buf = encoder
|
||||
.encoder
|
||||
.encode_header_block(&mut encoder.conn, &[Header::new("name1", "value1")], 1)
|
||||
.unwrap();
|
||||
let buf = encoder.encoder.encode_header_block(
|
||||
&mut encoder.conn,
|
||||
&[Header::new("name1", "value1")],
|
||||
STREAM_1,
|
||||
);
|
||||
assert_is_index_to_dynamic_post(&buf);
|
||||
|
||||
assert_eq!(encoder.encoder.blocked_stream_cnt(), 1);
|
||||
|
||||
// another header block to already blocked stream can still create a new entry.
|
||||
let buf = encoder
|
||||
.encoder
|
||||
.encode_header_block(&mut encoder.conn, &[Header::new("name2", "value2")], 1)
|
||||
.unwrap();
|
||||
let buf = encoder.encoder.encode_header_block(
|
||||
&mut encoder.conn,
|
||||
&[Header::new("name2", "value2")],
|
||||
STREAM_1,
|
||||
);
|
||||
assert_is_index_to_dynamic_post(&buf);
|
||||
|
||||
assert_eq!(encoder.encoder.blocked_stream_cnt(), 1);
|
||||
|
@ -1178,19 +1173,21 @@ mod tests {
|
|||
assert_eq!(encoder.encoder.blocked_stream_cnt(), 0);
|
||||
|
||||
// send a header block, that creates an new entry and refers to it.
|
||||
let buf = encoder
|
||||
.encoder
|
||||
.encode_header_block(&mut encoder.conn, &[Header::new("name1", "value1")], 1)
|
||||
.unwrap();
|
||||
let buf = encoder.encoder.encode_header_block(
|
||||
&mut encoder.conn,
|
||||
&[Header::new("name1", "value1")],
|
||||
STREAM_1,
|
||||
);
|
||||
assert_is_index_to_dynamic_post(&buf);
|
||||
|
||||
assert_eq!(encoder.encoder.blocked_stream_cnt(), 1);
|
||||
|
||||
// another header block to already blocked stream can still create a new entry.
|
||||
let buf = encoder
|
||||
.encoder
|
||||
.encode_header_block(&mut encoder.conn, &[Header::new("name1", "value1")], 1)
|
||||
.unwrap();
|
||||
let buf = encoder.encoder.encode_header_block(
|
||||
&mut encoder.conn,
|
||||
&[Header::new("name1", "value1")],
|
||||
STREAM_1,
|
||||
);
|
||||
assert_is_index_to_dynamic(&buf);
|
||||
|
||||
assert_eq!(encoder.encoder.blocked_stream_cnt(), 1);
|
||||
|
@ -1216,19 +1213,21 @@ mod tests {
|
|||
assert_eq!(encoder.encoder.blocked_stream_cnt(), 0);
|
||||
|
||||
// send a header block, that creates an new entry and refers to it.
|
||||
let buf = encoder
|
||||
.encoder
|
||||
.encode_header_block(&mut encoder.conn, &[Header::new("name1", "value1")], 1)
|
||||
.unwrap();
|
||||
let buf = encoder.encoder.encode_header_block(
|
||||
&mut encoder.conn,
|
||||
&[Header::new("name1", "value1")],
|
||||
STREAM_1,
|
||||
);
|
||||
assert_is_index_to_dynamic_post(&buf);
|
||||
|
||||
assert_eq!(encoder.encoder.blocked_stream_cnt(), 1);
|
||||
|
||||
// header block for the next stream will create an new entry as well.
|
||||
let buf = encoder
|
||||
.encoder
|
||||
.encode_header_block(&mut encoder.conn, &[Header::new("name2", "value2")], 2)
|
||||
.unwrap();
|
||||
let buf = encoder.encoder.encode_header_block(
|
||||
&mut encoder.conn,
|
||||
&[Header::new("name2", "value2")],
|
||||
STREAM_2,
|
||||
);
|
||||
assert_is_index_to_dynamic_post(&buf);
|
||||
|
||||
assert_eq!(encoder.encoder.blocked_stream_cnt(), 2);
|
||||
|
@ -1254,19 +1253,21 @@ mod tests {
|
|||
assert_eq!(encoder.encoder.blocked_stream_cnt(), 0);
|
||||
|
||||
// send a header block, that creates an new entry and refers to it.
|
||||
let buf = encoder
|
||||
.encoder
|
||||
.encode_header_block(&mut encoder.conn, &[Header::new("name1", "value1")], 1)
|
||||
.unwrap();
|
||||
let buf = encoder.encoder.encode_header_block(
|
||||
&mut encoder.conn,
|
||||
&[Header::new("name1", "value1")],
|
||||
STREAM_1,
|
||||
);
|
||||
assert_is_index_to_dynamic_post(&buf);
|
||||
|
||||
assert_eq!(encoder.encoder.blocked_stream_cnt(), 1);
|
||||
|
||||
// header block for the next stream will create an new entry as well.
|
||||
let buf = encoder
|
||||
.encoder
|
||||
.encode_header_block(&mut encoder.conn, &[Header::new("name1", "value1")], 2)
|
||||
.unwrap();
|
||||
let buf = encoder.encoder.encode_header_block(
|
||||
&mut encoder.conn,
|
||||
&[Header::new("name1", "value1")],
|
||||
STREAM_2,
|
||||
);
|
||||
assert_is_index_to_dynamic(&buf);
|
||||
|
||||
assert_eq!(encoder.encoder.blocked_stream_cnt(), 2);
|
||||
|
@ -1294,28 +1295,31 @@ mod tests {
|
|||
assert_eq!(encoder.encoder.blocked_stream_cnt(), 0);
|
||||
|
||||
// send a header block, that creates an new entry and refers to it.
|
||||
let buf = encoder
|
||||
.encoder
|
||||
.encode_header_block(&mut encoder.conn, &[Header::new("name1", "value1")], 1)
|
||||
.unwrap();
|
||||
let buf = encoder.encoder.encode_header_block(
|
||||
&mut encoder.conn,
|
||||
&[Header::new("name1", "value1")],
|
||||
STREAM_1,
|
||||
);
|
||||
assert_is_index_to_dynamic_post(&buf);
|
||||
|
||||
assert_eq!(encoder.encoder.blocked_stream_cnt(), 1);
|
||||
|
||||
// header block for the next stream will refer to the same entry.
|
||||
let buf = encoder
|
||||
.encoder
|
||||
.encode_header_block(&mut encoder.conn, &[Header::new("name1", "value1")], 2)
|
||||
.unwrap();
|
||||
let buf = encoder.encoder.encode_header_block(
|
||||
&mut encoder.conn,
|
||||
&[Header::new("name1", "value1")],
|
||||
STREAM_2,
|
||||
);
|
||||
assert_is_index_to_dynamic(&buf);
|
||||
|
||||
assert_eq!(encoder.encoder.blocked_stream_cnt(), 2);
|
||||
|
||||
// send another header block on stream 1.
|
||||
let buf = encoder
|
||||
.encoder
|
||||
.encode_header_block(&mut encoder.conn, &[Header::new("name2", "value2")], 1)
|
||||
.unwrap();
|
||||
let buf = encoder.encoder.encode_header_block(
|
||||
&mut encoder.conn,
|
||||
&[Header::new("name2", "value2")],
|
||||
STREAM_1,
|
||||
);
|
||||
assert_is_index_to_dynamic_post(&buf);
|
||||
|
||||
assert_eq!(encoder.encoder.blocked_stream_cnt(), 2);
|
||||
|
@ -1349,14 +1353,11 @@ mod tests {
|
|||
encoder.send_instructions(HEADER_CONTENT_LENGTH_VALUE_1_NAME_LITERAL);
|
||||
|
||||
// send a header block, it refers to unacked entry.
|
||||
let buf = encoder
|
||||
.encoder
|
||||
.encode_header_block(
|
||||
&mut encoder.conn,
|
||||
&[Header::new("content-length", "1234")],
|
||||
1,
|
||||
)
|
||||
.unwrap();
|
||||
let buf = encoder.encoder.encode_header_block(
|
||||
&mut encoder.conn,
|
||||
&[Header::new("content-length", "1234")],
|
||||
STREAM_1,
|
||||
);
|
||||
assert_is_index_to_dynamic(&buf);
|
||||
|
||||
// trying to evict the entry will failed.
|
||||
|
@ -1396,14 +1397,11 @@ mod tests {
|
|||
encoder.send_instructions(HEADER_CONTENT_LENGTH_VALUE_1_NAME_LITERAL);
|
||||
|
||||
// send a header block, it refers to unacked entry.
|
||||
let buf = encoder
|
||||
.encoder
|
||||
.encode_header_block(
|
||||
&mut encoder.conn,
|
||||
&[Header::new("content-length", "1234")],
|
||||
1,
|
||||
)
|
||||
.unwrap();
|
||||
let buf = encoder.encoder.encode_header_block(
|
||||
&mut encoder.conn,
|
||||
&[Header::new("content-length", "1234")],
|
||||
STREAM_1,
|
||||
);
|
||||
assert_is_index_to_dynamic(&buf);
|
||||
|
||||
// trying to evict the entry will failed.
|
||||
|
@ -1473,14 +1471,11 @@ mod tests {
|
|||
encoder.send_instructions(HEADER_CONTENT_LENGTH_VALUE_1_NAME_LITERAL);
|
||||
|
||||
// send a header block, it refers to unacked entry.
|
||||
let buf = encoder
|
||||
.encoder
|
||||
.encode_header_block(
|
||||
&mut encoder.conn,
|
||||
&[Header::new("content-length", "1234")],
|
||||
1,
|
||||
)
|
||||
.unwrap();
|
||||
let buf = encoder.encoder.encode_header_block(
|
||||
&mut encoder.conn,
|
||||
&[Header::new("content-length", "1234")],
|
||||
STREAM_1,
|
||||
);
|
||||
assert_is_index_to_dynamic(&buf);
|
||||
|
||||
// trying to evict the entry will failed. The stream is still referring to it and
|
||||
|
@ -1514,17 +1509,14 @@ mod tests {
|
|||
// Encode a header block with 2 headers. The first header will be added to the dynamic table.
|
||||
// The second will not be added to the dynamic table, because the corresponding instruction
|
||||
// cannot be written immediately due to the flow control limit.
|
||||
let buf1 = encoder
|
||||
.encoder
|
||||
.encode_header_block(
|
||||
&mut encoder.conn,
|
||||
&[
|
||||
Header::new("something", "1234"),
|
||||
Header::new("something2", "12345678910"),
|
||||
],
|
||||
1,
|
||||
)
|
||||
.unwrap();
|
||||
let buf1 = encoder.encoder.encode_header_block(
|
||||
&mut encoder.conn,
|
||||
&[
|
||||
Header::new("something", "1234"),
|
||||
Header::new("something2", "12345678910"),
|
||||
],
|
||||
STREAM_1,
|
||||
);
|
||||
|
||||
// Assert that the first header is encoded as an index to the dynamic table (a post form).
|
||||
assert_eq!(buf1[2], 0x10);
|
||||
|
@ -1532,17 +1524,14 @@ mod tests {
|
|||
assert_eq!(buf1[3] & 0xf0, 0x20);
|
||||
|
||||
// Try to encode another header block. Here both headers will be encoded as a literal with a name literal
|
||||
let buf2 = encoder
|
||||
.encoder
|
||||
.encode_header_block(
|
||||
&mut encoder.conn,
|
||||
&[
|
||||
Header::new("something3", "1234"),
|
||||
Header::new("something4", "12345678910"),
|
||||
],
|
||||
2,
|
||||
)
|
||||
.unwrap();
|
||||
let buf2 = encoder.encoder.encode_header_block(
|
||||
&mut encoder.conn,
|
||||
&[
|
||||
Header::new("something3", "1234"),
|
||||
Header::new("something4", "12345678910"),
|
||||
],
|
||||
STREAM_2,
|
||||
);
|
||||
assert_eq!(buf2[2] & 0xf0, 0x20);
|
||||
|
||||
// Ensure that we have sent only one instruction for (String::from("something", "1234"))
|
||||
|
@ -1554,17 +1543,14 @@ mod tests {
|
|||
|
||||
// Try writing a new header block. Now, headers will be added to the dynamic table again, because
|
||||
// instructions can be sent.
|
||||
let buf3 = encoder
|
||||
.encoder
|
||||
.encode_header_block(
|
||||
&mut encoder.conn,
|
||||
&[
|
||||
Header::new("something5", "1234"),
|
||||
Header::new("something6", "12345678910"),
|
||||
],
|
||||
3,
|
||||
)
|
||||
.unwrap();
|
||||
let buf3 = encoder.encoder.encode_header_block(
|
||||
&mut encoder.conn,
|
||||
&[
|
||||
Header::new("something5", "1234"),
|
||||
Header::new("something6", "12345678910"),
|
||||
],
|
||||
StreamId::new(3),
|
||||
);
|
||||
// Assert that the first header is encoded as an index to the dynamic table (a post form).
|
||||
assert_eq!(buf3[2], 0x10);
|
||||
// Assert that the second header is encoded as a literal with a name literal
|
||||
|
@ -1584,7 +1570,7 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_do_not_evict_entry_that_are_referd_only_by_the_same_header_blocked_encoding() {
|
||||
fn test_do_not_evict_entry_that_are_referred_only_by_the_same_header_blocked_encoding() {
|
||||
let mut encoder = connect(false);
|
||||
|
||||
encoder.encoder.set_max_blocked_streams(20).unwrap();
|
||||
|
@ -1595,23 +1581,36 @@ mod tests {
|
|||
.send_and_insert(&mut encoder.conn, b"something5", b"1234")
|
||||
.unwrap();
|
||||
|
||||
encoder.encoder.send(&mut encoder.conn).unwrap();
|
||||
encoder
|
||||
.encoder
|
||||
.send_encoder_updates(&mut encoder.conn)
|
||||
.unwrap();
|
||||
let out = encoder.conn.process(None, now());
|
||||
mem::drop(encoder.peer_conn.process(out.dgram(), now()));
|
||||
// receive an insert count increment.
|
||||
recv_instruction(&mut encoder, &[0x01]);
|
||||
|
||||
assert!(encoder
|
||||
.encoder
|
||||
.encode_header_block(
|
||||
&mut encoder.conn,
|
||||
&[
|
||||
Header::new("something5", "1234"),
|
||||
Header::new("something6", "1234"),
|
||||
],
|
||||
3,
|
||||
)
|
||||
.is_ok());
|
||||
// The first header will use the table entry and the second will use the literal
|
||||
// encoding because the first entry is referred to and cannot be evicted.
|
||||
assert_eq!(
|
||||
encoder
|
||||
.encoder
|
||||
.encode_header_block(
|
||||
&mut encoder.conn,
|
||||
&[
|
||||
Header::new("something5", "1234"),
|
||||
Header::new("something6", "1234"),
|
||||
],
|
||||
StreamId::new(3),
|
||||
)
|
||||
.to_vec(),
|
||||
&[
|
||||
0x02, 0x00, 0x80, 0x27, 0x03, 0x73, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x69, 0x6e, 0x67,
|
||||
0x36, 0x04, 0x31, 0x32, 0x33, 0x34
|
||||
]
|
||||
);
|
||||
// Also check that ther is no new instruction send by the encoder.
|
||||
assert!(encoder.conn.process_output(now()).dgram().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1631,7 +1630,7 @@ mod tests {
|
|||
|
||||
// send a header block
|
||||
encoder.encode_header_block(
|
||||
1,
|
||||
StreamId::new(1),
|
||||
&[Header::new("content-length", "1234")],
|
||||
ENCODE_INDEXED_REF_DYNAMIC,
|
||||
&[],
|
||||
|
|
|
@ -8,7 +8,7 @@ use crate::huffman::decode_huffman;
|
|||
use crate::prefix::Prefix;
|
||||
use crate::{Error, Res};
|
||||
use neqo_common::{qdebug, qerror};
|
||||
use neqo_transport::Connection;
|
||||
use neqo_transport::{Connection, StreamId};
|
||||
use std::convert::TryInto;
|
||||
use std::mem;
|
||||
use std::str;
|
||||
|
@ -29,7 +29,7 @@ pub trait Reader {
|
|||
|
||||
pub(crate) struct ReceiverConnWrapper<'a> {
|
||||
conn: &'a mut Connection,
|
||||
stream_id: u64,
|
||||
stream_id: StreamId,
|
||||
}
|
||||
|
||||
impl<'a> ReadByte for ReceiverConnWrapper<'a> {
|
||||
|
@ -53,7 +53,7 @@ impl<'a> Reader for ReceiverConnWrapper<'a> {
|
|||
}
|
||||
|
||||
impl<'a> ReceiverConnWrapper<'a> {
|
||||
pub fn new(conn: &'a mut Connection, stream_id: u64) -> Self {
|
||||
pub fn new(conn: &'a mut Connection, stream_id: StreamId) -> Self {
|
||||
Self { conn, stream_id }
|
||||
}
|
||||
}
|
||||
|
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "neqo-transport"
|
||||
version = "0.5.3"
|
||||
version = "0.5.5"
|
||||
authors = ["EKR <ekr@rtfm.com>", "Andy Grover <agrover@mozilla.com>"]
|
||||
edition = "2018"
|
||||
license = "MIT/Apache-2.0"
|
||||
|
|
|
@ -2698,7 +2698,7 @@ impl Connection {
|
|||
/// # Errors
|
||||
/// `ConnectionState` if the connecton stat does not allow to create streams.
|
||||
/// `StreamLimitError` if we are limiied by server's stream concurence.
|
||||
pub fn stream_create(&mut self, st: StreamType) -> Res<u64> {
|
||||
pub fn stream_create(&mut self, st: StreamType) -> Res<StreamId> {
|
||||
// Can't make streams while closing, otherwise rely on the stream limits.
|
||||
match self.state {
|
||||
State::Closing { .. } | State::Draining { .. } | State::Closed { .. } => {
|
||||
|
@ -2721,12 +2721,12 @@ impl Connection {
|
|||
/// `InvalidStreamId` the stream does not exist.
|
||||
pub fn stream_priority(
|
||||
&mut self,
|
||||
stream_id: u64,
|
||||
stream_id: StreamId,
|
||||
transmission: TransmissionPriority,
|
||||
retransmission: RetransmissionPriority,
|
||||
) -> Res<()> {
|
||||
self.streams
|
||||
.get_send_stream_mut(stream_id.into())?
|
||||
.get_send_stream_mut(stream_id)?
|
||||
.set_priority(transmission, retransmission);
|
||||
Ok(())
|
||||
}
|
||||
|
@ -2738,10 +2738,8 @@ impl Connection {
|
|||
/// `InvalidStreamId` the stream does not exist,
|
||||
/// `InvalidInput` if length of `data` is zero,
|
||||
/// `FinalSizeError` if the stream has already been closed.
|
||||
pub fn stream_send(&mut self, stream_id: u64, data: &[u8]) -> Res<usize> {
|
||||
self.streams
|
||||
.get_send_stream_mut(stream_id.into())?
|
||||
.send(data)
|
||||
pub fn stream_send(&mut self, stream_id: StreamId, data: &[u8]) -> Res<usize> {
|
||||
self.streams.get_send_stream_mut(stream_id)?.send(data)
|
||||
}
|
||||
|
||||
/// Send all data or nothing on a stream. May cause DATA_BLOCKED or
|
||||
|
@ -2751,10 +2749,10 @@ impl Connection {
|
|||
/// `InvalidStreamId` the stream does not exist,
|
||||
/// `InvalidInput` if length of `data` is zero,
|
||||
/// `FinalSizeError` if the stream has already been closed.
|
||||
pub fn stream_send_atomic(&mut self, stream_id: u64, data: &[u8]) -> Res<bool> {
|
||||
pub fn stream_send_atomic(&mut self, stream_id: StreamId, data: &[u8]) -> Res<bool> {
|
||||
let val = self
|
||||
.streams
|
||||
.get_send_stream_mut(stream_id.into())?
|
||||
.get_send_stream_mut(stream_id)?
|
||||
.send_atomic(data);
|
||||
if let Ok(val) = val {
|
||||
debug_assert!(
|
||||
|
@ -2770,21 +2768,19 @@ impl Connection {
|
|||
/// Bytes that stream_send() is guaranteed to accept for sending.
|
||||
/// i.e. that will not be blocked by flow credits or send buffer max
|
||||
/// capacity.
|
||||
pub fn stream_avail_send_space(&self, stream_id: u64) -> Res<usize> {
|
||||
Ok(self.streams.get_send_stream(stream_id.into())?.avail())
|
||||
pub fn stream_avail_send_space(&self, stream_id: StreamId) -> Res<usize> {
|
||||
Ok(self.streams.get_send_stream(stream_id)?.avail())
|
||||
}
|
||||
|
||||
/// Close the stream. Enqueued data will be sent.
|
||||
pub fn stream_close_send(&mut self, stream_id: u64) -> Res<()> {
|
||||
self.streams.get_send_stream_mut(stream_id.into())?.close();
|
||||
pub fn stream_close_send(&mut self, stream_id: StreamId) -> Res<()> {
|
||||
self.streams.get_send_stream_mut(stream_id)?.close();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Abandon transmission of in-flight and future stream data.
|
||||
pub fn stream_reset_send(&mut self, stream_id: u64, err: AppError) -> Res<()> {
|
||||
self.streams
|
||||
.get_send_stream_mut(stream_id.into())?
|
||||
.reset(err);
|
||||
pub fn stream_reset_send(&mut self, stream_id: StreamId, err: AppError) -> Res<()> {
|
||||
self.streams.get_send_stream_mut(stream_id)?.reset(err);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -2793,16 +2789,16 @@ impl Connection {
|
|||
/// # Errors
|
||||
/// `InvalidStreamId` if the stream does not exist.
|
||||
/// `NoMoreData` if data and fin bit were previously read by the application.
|
||||
pub fn stream_recv(&mut self, stream_id: u64, data: &mut [u8]) -> Res<(usize, bool)> {
|
||||
let stream = self.streams.get_recv_stream_mut(stream_id.into())?;
|
||||
pub fn stream_recv(&mut self, stream_id: StreamId, data: &mut [u8]) -> Res<(usize, bool)> {
|
||||
let stream = self.streams.get_recv_stream_mut(stream_id)?;
|
||||
|
||||
let rb = stream.read(data)?;
|
||||
Ok((rb.0 as usize, rb.1))
|
||||
}
|
||||
|
||||
/// Application is no longer interested in this stream.
|
||||
pub fn stream_stop_sending(&mut self, stream_id: u64, err: AppError) -> Res<()> {
|
||||
let stream = self.streams.get_recv_stream_mut(stream_id.into())?;
|
||||
pub fn stream_stop_sending(&mut self, stream_id: StreamId, err: AppError) -> Res<()> {
|
||||
let stream = self.streams.get_recv_stream_mut(stream_id)?;
|
||||
|
||||
stream.stop_sending(err);
|
||||
Ok(())
|
||||
|
@ -2812,8 +2808,8 @@ impl Connection {
|
|||
/// # Errors
|
||||
/// Returns `InvalidStreamId` if a stream does not exist or the receiving
|
||||
/// side is closed.
|
||||
pub fn set_stream_max_data(&mut self, stream_id: u64, max_data: u64) -> Res<()> {
|
||||
let stream = self.streams.get_recv_stream_mut(stream_id.into())?;
|
||||
pub fn set_stream_max_data(&mut self, stream_id: StreamId, max_data: u64) -> Res<()> {
|
||||
let stream = self.streams.get_recv_stream_mut(stream_id)?;
|
||||
|
||||
stream.set_stream_max_data(max_data);
|
||||
Ok(())
|
||||
|
@ -2826,8 +2822,8 @@ impl Connection {
|
|||
/// # Errors
|
||||
/// Returns `InvalidStreamId` if a stream does not exist or the receiving
|
||||
/// side is closed.
|
||||
pub fn stream_keep_alive(&mut self, stream_id: u64, keep: bool) -> Res<()> {
|
||||
self.streams.keep_alive(stream_id.into(), keep)
|
||||
pub fn stream_keep_alive(&mut self, stream_id: StreamId, keep: bool) -> Res<()> {
|
||||
self.streams.keep_alive(stream_id, keep)
|
||||
}
|
||||
|
||||
pub fn remote_datagram_size(&self) -> u64 {
|
||||
|
|
|
@ -28,7 +28,7 @@ const DEFAULT_IDLE_TIMEOUT: Duration = Duration::from_secs(30);
|
|||
const MAX_QUEUED_DATAGRAMS_DEFAULT: usize = 10;
|
||||
|
||||
/// What to do with preferred addresses.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum PreferredAddressConfig {
|
||||
/// Disabled, whether for client or server.
|
||||
Disabled,
|
||||
|
@ -41,7 +41,7 @@ pub enum PreferredAddressConfig {
|
|||
/// ConnectionParameters use for setting intitial value for QUIC parameters.
|
||||
/// This collects configuration like initial limits, protocol version, and
|
||||
/// congestion control algorithm.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct ConnectionParameters {
|
||||
quic_version: QuicVersion,
|
||||
cc_algorithm: CongestionControlAlgorithm,
|
||||
|
|
|
@ -44,10 +44,11 @@ fn cc_slow_start_to_cong_avoidance_recovery_period() {
|
|||
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_id = client.stream_create(StreamType::BiDi).unwrap();
|
||||
assert_eq!(stream_id, 0);
|
||||
|
||||
// Buffer up lot of data and generate packets
|
||||
let (c_tx_dgrams, mut now) = fill_cwnd(&mut client, 0, now);
|
||||
let (c_tx_dgrams, mut now) = fill_cwnd(&mut client, stream_id, now);
|
||||
assert_full_cwnd(&c_tx_dgrams, POST_HANDSHAKE_CWND);
|
||||
// Predict the packet number of the last packet sent.
|
||||
// We have already sent packets in `connect_rtt_idle`,
|
||||
|
@ -57,7 +58,7 @@ fn cc_slow_start_to_cong_avoidance_recovery_period() {
|
|||
|
||||
// Server: Receive and generate ack
|
||||
now += DEFAULT_RTT / 2;
|
||||
let s_ack = ack_bytes(&mut server, 0, c_tx_dgrams, now);
|
||||
let s_ack = ack_bytes(&mut server, stream_id, c_tx_dgrams, now);
|
||||
assert_eq!(
|
||||
server.stats().frame_tx.largest_acknowledged,
|
||||
flight1_largest
|
||||
|
@ -72,14 +73,14 @@ fn cc_slow_start_to_cong_avoidance_recovery_period() {
|
|||
);
|
||||
|
||||
// Client: send more
|
||||
let (mut c_tx_dgrams, mut now) = fill_cwnd(&mut client, 0, now);
|
||||
let (mut c_tx_dgrams, mut now) = fill_cwnd(&mut client, stream_id, now);
|
||||
assert_full_cwnd(&c_tx_dgrams, POST_HANDSHAKE_CWND * 2);
|
||||
let flight2_largest = flight1_largest + u64::try_from(c_tx_dgrams.len()).unwrap();
|
||||
|
||||
// Server: Receive and generate ack again, but drop first packet
|
||||
now += DEFAULT_RTT / 2;
|
||||
c_tx_dgrams.remove(0);
|
||||
let s_ack = ack_bytes(&mut server, 0, c_tx_dgrams, now);
|
||||
let s_ack = ack_bytes(&mut server, stream_id, c_tx_dgrams, now);
|
||||
assert_eq!(
|
||||
server.stats().frame_tx.largest_acknowledged,
|
||||
flight2_largest
|
||||
|
@ -103,10 +104,11 @@ fn cc_cong_avoidance_recovery_period_unchanged() {
|
|||
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_id = client.stream_create(StreamType::BiDi).unwrap();
|
||||
assert_eq!(stream_id, 0);
|
||||
|
||||
// Buffer up lot of data and generate packets
|
||||
let (mut c_tx_dgrams, now) = fill_cwnd(&mut client, 0, now);
|
||||
let (mut c_tx_dgrams, now) = fill_cwnd(&mut client, stream_id, now);
|
||||
assert_full_cwnd(&c_tx_dgrams, POST_HANDSHAKE_CWND);
|
||||
|
||||
// Drop 0th packet. When acked, this should put client into CARP.
|
||||
|
@ -115,13 +117,13 @@ fn cc_cong_avoidance_recovery_period_unchanged() {
|
|||
let c_tx_dgrams2 = c_tx_dgrams.split_off(5);
|
||||
|
||||
// Server: Receive and generate ack
|
||||
let s_ack = ack_bytes(&mut server, 0, c_tx_dgrams, now);
|
||||
let s_ack = ack_bytes(&mut server, stream_id, c_tx_dgrams, now);
|
||||
client.process_input(s_ack, now);
|
||||
|
||||
let cwnd1 = cwnd(&client);
|
||||
|
||||
// Generate ACK for more received packets
|
||||
let s_ack = ack_bytes(&mut server, 0, c_tx_dgrams2, now);
|
||||
let s_ack = ack_bytes(&mut server, stream_id, c_tx_dgrams2, now);
|
||||
|
||||
// ACK more packets but they were sent before end of recovery period
|
||||
client.process_input(s_ack, now);
|
||||
|
@ -147,8 +149,9 @@ fn single_packet_on_recovery() {
|
|||
let delivered = send_something(&mut client, now);
|
||||
|
||||
// Now fill the congestion window.
|
||||
assert_eq!(client.stream_create(StreamType::BiDi).unwrap(), 0);
|
||||
let (_, now) = fill_cwnd(&mut client, 0, now);
|
||||
let stream_id = client.stream_create(StreamType::BiDi).unwrap();
|
||||
assert_eq!(stream_id, 0);
|
||||
let (_, now) = fill_cwnd(&mut client, stream_id, now);
|
||||
assert!(cwnd_avail(&client) < ACK_ONLY_SIZE_LIMIT);
|
||||
|
||||
// Acknowledge just one packet and cause one packet to be declared lost.
|
||||
|
@ -175,17 +178,18 @@ fn cc_cong_avoidance_recovery_period_to_cong_avoidance() {
|
|||
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_id = client.stream_create(StreamType::BiDi).unwrap();
|
||||
assert_eq!(stream_id, 0);
|
||||
|
||||
// Buffer up lot of data and generate packets
|
||||
let (mut c_tx_dgrams, mut now) = fill_cwnd(&mut client, 0, now);
|
||||
let (mut c_tx_dgrams, mut now) = fill_cwnd(&mut client, stream_id, now);
|
||||
|
||||
// Drop 0th packet. When acked, this should put client into CARP.
|
||||
c_tx_dgrams.remove(0);
|
||||
|
||||
// Server: Receive and generate ack
|
||||
now += DEFAULT_RTT / 2;
|
||||
let s_ack = ack_bytes(&mut server, 0, c_tx_dgrams, now);
|
||||
let s_ack = ack_bytes(&mut server, stream_id, c_tx_dgrams, now);
|
||||
|
||||
// Client: Process ack
|
||||
now += DEFAULT_RTT / 2;
|
||||
|
@ -200,7 +204,7 @@ fn cc_cong_avoidance_recovery_period_to_cong_avoidance() {
|
|||
// Check over several increases to be sure.
|
||||
let mut expected_cwnd = cwnd(&client);
|
||||
// Fill cwnd.
|
||||
let (mut c_tx_dgrams, next_now) = fill_cwnd(&mut client, 0, now);
|
||||
let (mut c_tx_dgrams, next_now) = fill_cwnd(&mut client, stream_id, now);
|
||||
now = next_now;
|
||||
for i in 0..5 {
|
||||
qinfo!("iteration {}", i);
|
||||
|
@ -221,19 +225,19 @@ fn cc_cong_avoidance_recovery_period_to_cong_avoidance() {
|
|||
// 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() - usize::try_from(DEFAULT_ACK_PACKET_TOLERANCE).unwrap() - 1;
|
||||
let s_ack = ack_bytes(&mut server, 0, c_tx_dgrams.drain(..most), now);
|
||||
let s_ack = ack_bytes(&mut server, stream_id, 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);
|
||||
let (mut new_pkts, next_now) = fill_cwnd(&mut client, stream_id, now);
|
||||
now = next_now;
|
||||
next_c_tx_dgrams.append(&mut new_pkts);
|
||||
|
||||
let s_ack = ack_bytes(&mut server, 0, c_tx_dgrams, now);
|
||||
let s_ack = ack_bytes(&mut server, stream_id, 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);
|
||||
let (mut new_pkts, next_now) = fill_cwnd(&mut client, stream_id, now);
|
||||
now = next_now;
|
||||
next_c_tx_dgrams.append(&mut new_pkts);
|
||||
|
||||
|
@ -250,16 +254,15 @@ fn cc_slow_start_to_persistent_congestion_no_acks() {
|
|||
let mut server = default_server();
|
||||
let now = connect_rtt_idle(&mut client, &mut server, DEFAULT_RTT);
|
||||
|
||||
// Create stream 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);
|
||||
let (c_tx_dgrams, mut now) = fill_cwnd(&mut client, stream, now);
|
||||
assert_full_cwnd(&c_tx_dgrams, POST_HANDSHAKE_CWND);
|
||||
|
||||
// Server: Receive and generate ack
|
||||
now += DEFAULT_RTT / 2;
|
||||
mem::drop(ack_bytes(&mut server, 0, c_tx_dgrams, now));
|
||||
mem::drop(ack_bytes(&mut server, stream, c_tx_dgrams, now));
|
||||
|
||||
// ACK lost.
|
||||
induce_persistent_congestion(&mut client, &mut server, stream, now);
|
||||
|
@ -276,18 +279,18 @@ fn cc_slow_start_to_persistent_congestion_some_acks() {
|
|||
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);
|
||||
let (c_tx_dgrams, mut now) = fill_cwnd(&mut client, stream, now);
|
||||
assert_full_cwnd(&c_tx_dgrams, POST_HANDSHAKE_CWND);
|
||||
|
||||
// Server: Receive and generate ack
|
||||
now += Duration::from_millis(100);
|
||||
let s_ack = ack_bytes(&mut server, 0, c_tx_dgrams, now);
|
||||
let s_ack = ack_bytes(&mut server, stream, c_tx_dgrams, now);
|
||||
|
||||
now += Duration::from_millis(100);
|
||||
client.process_input(s_ack, now);
|
||||
|
||||
// send bytes that will be lost
|
||||
let (_, next_now) = fill_cwnd(&mut client, 0, now);
|
||||
let (_, next_now) = fill_cwnd(&mut client, stream, now);
|
||||
now = next_now + Duration::from_millis(100);
|
||||
|
||||
induce_persistent_congestion(&mut client, &mut server, stream, now);
|
||||
|
@ -305,12 +308,12 @@ fn cc_persistent_congestion_to_slow_start() {
|
|||
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);
|
||||
let (c_tx_dgrams, mut now) = fill_cwnd(&mut client, stream, now);
|
||||
assert_full_cwnd(&c_tx_dgrams, POST_HANDSHAKE_CWND);
|
||||
|
||||
// Server: Receive and generate ack
|
||||
now += Duration::from_millis(10);
|
||||
mem::drop(ack_bytes(&mut server, 0, c_tx_dgrams, now));
|
||||
mem::drop(ack_bytes(&mut server, stream, c_tx_dgrams, now));
|
||||
|
||||
// ACK lost.
|
||||
|
||||
|
@ -321,19 +324,19 @@ fn cc_persistent_congestion_to_slow_start() {
|
|||
now += Duration::from_millis(10);
|
||||
|
||||
// Send packets from after start of CARP
|
||||
let (c_tx_dgrams, next_now) = fill_cwnd(&mut client, 0, now);
|
||||
let (c_tx_dgrams, next_now) = fill_cwnd(&mut client, stream, now);
|
||||
assert_eq!(c_tx_dgrams.len(), 2);
|
||||
|
||||
// Server: Receive and generate ack
|
||||
now = next_now + Duration::from_millis(100);
|
||||
let s_ack = ack_bytes(&mut server, 0, c_tx_dgrams, now);
|
||||
let s_ack = ack_bytes(&mut server, stream, c_tx_dgrams, now);
|
||||
|
||||
// No longer in CARP. (pkts acked from after start of CARP)
|
||||
// Should be in slow start 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);
|
||||
let (c_tx_dgrams, _) = fill_cwnd(&mut client, stream, now);
|
||||
assert_eq!(c_tx_dgrams.len(), 4);
|
||||
}
|
||||
|
||||
|
@ -344,23 +347,25 @@ fn ack_are_not_cc() {
|
|||
let now = connect_rtt_idle(&mut client, &mut server, DEFAULT_RTT);
|
||||
|
||||
// Create a stream
|
||||
assert_eq!(client.stream_create(StreamType::BiDi).unwrap(), 0);
|
||||
let stream = client.stream_create(StreamType::BiDi).unwrap();
|
||||
assert_eq!(stream, 0);
|
||||
|
||||
// Buffer up lot of data and generate packets, so that cc window is filled.
|
||||
let (c_tx_dgrams, now) = fill_cwnd(&mut client, 0, now);
|
||||
let (c_tx_dgrams, now) = fill_cwnd(&mut client, stream, now);
|
||||
assert_full_cwnd(&c_tx_dgrams, POST_HANDSHAKE_CWND);
|
||||
|
||||
// The server hasn't received any of these packets yet, the server
|
||||
// won't ACK, but if it sends an ack-eliciting packet instead.
|
||||
qdebug!([server], "Sending ack-eliciting");
|
||||
assert_eq!(server.stream_create(StreamType::BiDi).unwrap(), 1);
|
||||
server.stream_send(1, b"dropped").unwrap();
|
||||
let other_stream = server.stream_create(StreamType::BiDi).unwrap();
|
||||
assert_eq!(other_stream, 1);
|
||||
server.stream_send(other_stream, b"dropped").unwrap();
|
||||
let dropped_packet = server.process(None, now).dgram();
|
||||
assert!(dropped_packet.is_some()); // Now drop this one.
|
||||
|
||||
// Now the server sends a packet that will force an ACK,
|
||||
// because the client will detect a gap.
|
||||
server.stream_send(1, b"sent").unwrap();
|
||||
server.stream_send(other_stream, b"sent").unwrap();
|
||||
let ack_eliciting_packet = server.process(None, now).dgram();
|
||||
assert!(ack_eliciting_packet.is_some());
|
||||
|
||||
|
|
|
@ -12,9 +12,9 @@ use super::{
|
|||
};
|
||||
use crate::packet::PacketBuilder;
|
||||
use crate::stats::FrameStats;
|
||||
use crate::stream_id::{StreamId, StreamType};
|
||||
use crate::tparams::{self, TransportParameter};
|
||||
use crate::tracking::PacketNumberSpace;
|
||||
use crate::StreamType;
|
||||
|
||||
use neqo_common::{qtrace, Encoder};
|
||||
use std::mem;
|
||||
|
@ -239,13 +239,14 @@ fn idle_recv_packet() {
|
|||
let res = client.process(None, now);
|
||||
assert_eq!(res, Output::Callback(default_timeout()));
|
||||
|
||||
assert_eq!(client.stream_create(StreamType::BiDi).unwrap(), 0);
|
||||
assert_eq!(client.stream_send(0, b"hello").unwrap(), 5);
|
||||
let stream = client.stream_create(StreamType::BiDi).unwrap();
|
||||
assert_eq!(stream, 0);
|
||||
assert_eq!(client.stream_send(stream, b"hello").unwrap(), 5);
|
||||
|
||||
// Respond with another packet
|
||||
let out = client.process(None, now + Duration::from_secs(10));
|
||||
server.process_input(out.dgram().unwrap(), now + Duration::from_secs(10));
|
||||
assert_eq!(server.stream_send(0, b"world").unwrap(), 5);
|
||||
assert_eq!(server.stream_send(stream, b"world").unwrap(), 5);
|
||||
let out = server.process_output(now + Duration::from_secs(10));
|
||||
assert_ne!(out.as_dgram_ref(), None);
|
||||
|
||||
|
@ -350,7 +351,7 @@ fn create_stream_idle_rtt(
|
|||
responder: &mut Connection,
|
||||
mut now: Instant,
|
||||
rtt: Duration,
|
||||
) -> (Instant, u64) {
|
||||
) -> (Instant, StreamId) {
|
||||
let check_idle = |endpoint: &mut Connection, now: Instant| {
|
||||
let delay = endpoint.process_output(now).callback();
|
||||
qtrace!([endpoint], "idle timeout {:?}", delay);
|
||||
|
@ -389,7 +390,7 @@ fn create_stream_idle_rtt(
|
|||
(now, stream)
|
||||
}
|
||||
|
||||
fn create_stream_idle(initiator: &mut Connection, responder: &mut Connection) -> u64 {
|
||||
fn create_stream_idle(initiator: &mut Connection, responder: &mut Connection) -> StreamId {
|
||||
let (_, stream) = create_stream_idle_rtt(initiator, responder, now(), Duration::new(0, 0));
|
||||
stream
|
||||
}
|
||||
|
|
|
@ -14,7 +14,9 @@ use crate::events::ConnectionEvent;
|
|||
use crate::path::PATH_MTU_V6;
|
||||
use crate::recovery::ACK_ONLY_SIZE_LIMIT;
|
||||
use crate::stats::{FrameStats, Stats, MAX_PTO_COUNTS};
|
||||
use crate::{ConnectionIdDecoder, ConnectionIdGenerator, ConnectionParameters, Error, StreamType};
|
||||
use crate::{
|
||||
ConnectionIdDecoder, ConnectionIdGenerator, ConnectionParameters, Error, StreamId, StreamType,
|
||||
};
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::cmp::min;
|
||||
|
@ -287,7 +289,7 @@ fn connect_force_idle(client: &mut Connection, server: &mut Connection) {
|
|||
connect_rtt_idle(client, server, Duration::new(0, 0));
|
||||
}
|
||||
|
||||
fn fill_stream(c: &mut Connection, stream: u64) {
|
||||
fn fill_stream(c: &mut Connection, stream: StreamId) {
|
||||
const BLOCK_SIZE: usize = 4_096;
|
||||
loop {
|
||||
let bytes_sent = c.stream_send(stream, &[0x42; BLOCK_SIZE]).unwrap();
|
||||
|
@ -304,7 +306,7 @@ fn fill_stream(c: &mut Connection, stream: u64) {
|
|||
/// 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_cwnd(c: &mut Connection, stream: StreamId, 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()
|
||||
|
@ -343,7 +345,7 @@ fn fill_cwnd(c: &mut Connection, stream: u64, mut now: Instant) -> (Vec<Datagram
|
|||
fn increase_cwnd(
|
||||
sender: &mut Connection,
|
||||
receiver: &mut Connection,
|
||||
stream: u64,
|
||||
stream: StreamId,
|
||||
mut now: Instant,
|
||||
) -> Instant {
|
||||
fill_stream(sender, stream);
|
||||
|
@ -377,7 +379,7 @@ fn increase_cwnd(
|
|||
/// 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
|
||||
fn ack_bytes<D>(dest: &mut Connection, stream: StreamId, in_dgrams: D, now: Instant) -> Datagram
|
||||
where
|
||||
D: IntoIterator<Item = Datagram>,
|
||||
D::IntoIter: ExactSizeIterator,
|
||||
|
@ -412,7 +414,7 @@ fn cwnd_avail(c: &Connection) -> usize {
|
|||
fn induce_persistent_congestion(
|
||||
client: &mut Connection,
|
||||
server: &mut Connection,
|
||||
stream: u64,
|
||||
stream: StreamId,
|
||||
mut now: Instant,
|
||||
) -> Instant {
|
||||
// Note: wait some arbitrary time that should be longer than pto
|
||||
|
|
|
@ -8,7 +8,7 @@ use super::super::{Connection, Error, Output};
|
|||
use super::{connect, default_client, default_server, fill_cwnd, maybe_authenticate};
|
||||
use crate::addr_valid::{AddressValidation, ValidateAddress};
|
||||
use crate::send_stream::{RetransmissionPriority, TransmissionPriority};
|
||||
use crate::{ConnectionEvent, StreamType};
|
||||
use crate::{ConnectionEvent, StreamId, StreamType};
|
||||
|
||||
use neqo_common::event::Provider;
|
||||
use std::cell::RefCell;
|
||||
|
@ -18,7 +18,7 @@ use test_fixture::{self, now};
|
|||
|
||||
const BLOCK_SIZE: usize = 4_096;
|
||||
|
||||
fn fill_stream(c: &mut Connection, id: u64) {
|
||||
fn fill_stream(c: &mut Connection, id: StreamId) {
|
||||
loop {
|
||||
if c.stream_send(id, &[0x42; BLOCK_SIZE]).unwrap() < BLOCK_SIZE {
|
||||
return;
|
||||
|
|
|
@ -344,8 +344,9 @@ fn pto_handshake_frames() {
|
|||
now += Duration::from_millis(10);
|
||||
client.authenticated(AuthenticationStatus::Ok, now);
|
||||
|
||||
assert_eq!(client.stream_create(StreamType::UniDi).unwrap(), 2);
|
||||
assert_eq!(client.stream_send(2, b"zero").unwrap(), 4);
|
||||
let stream = client.stream_create(StreamType::UniDi).unwrap();
|
||||
assert_eq!(stream, 2);
|
||||
assert_eq!(client.stream_send(stream, b"zero").unwrap(), 4);
|
||||
qdebug!("---- client: SH..FIN -> FIN and 1RTT packet");
|
||||
let pkt1 = client.process(None, now).dgram();
|
||||
assert!(pkt1.is_some());
|
||||
|
|
|
@ -15,7 +15,7 @@ use crate::send_stream::{SendStreamState, SEND_BUFFER_SIZE};
|
|||
use crate::tparams::{self, TransportParameter};
|
||||
use crate::tracking::DEFAULT_ACK_PACKET_TOLERANCE;
|
||||
use crate::{Connection, ConnectionError, ConnectionParameters};
|
||||
use crate::{Error, StreamId, StreamType};
|
||||
use crate::{Error, StreamType};
|
||||
|
||||
use neqo_common::{event::Provider, qdebug};
|
||||
use std::cmp::max;
|
||||
|
@ -101,16 +101,14 @@ fn transfer() {
|
|||
.next()
|
||||
.expect("should have a second new stream event");
|
||||
assert!(stream_ids.next().is_none());
|
||||
let (received1, fin1) = server.stream_recv(first_stream.as_u64(), &mut buf).unwrap();
|
||||
let (received1, fin1) = server.stream_recv(first_stream, &mut buf).unwrap();
|
||||
assert_eq!(received1, 4000);
|
||||
assert!(!fin1);
|
||||
let (received2, fin2) = server.stream_recv(first_stream.as_u64(), &mut buf).unwrap();
|
||||
let (received2, fin2) = server.stream_recv(first_stream, &mut buf).unwrap();
|
||||
assert_eq!(received2, 140);
|
||||
assert!(!fin2);
|
||||
|
||||
let (received3, fin3) = server
|
||||
.stream_recv(second_stream.as_u64(), &mut buf)
|
||||
.unwrap();
|
||||
let (received3, fin3) = server.stream_recv(second_stream, &mut buf).unwrap();
|
||||
assert_eq!(received3, 60);
|
||||
assert!(fin3);
|
||||
}
|
||||
|
@ -228,13 +226,13 @@ fn max_data() {
|
|||
assert_eq!(client.stream_send(stream_id, b"hello").unwrap(), 0);
|
||||
client
|
||||
.streams
|
||||
.get_send_stream_mut(stream_id.into())
|
||||
.get_send_stream_mut(stream_id)
|
||||
.unwrap()
|
||||
.mark_as_sent(0, 4096, false);
|
||||
assert_eq!(client.events().count(), 0);
|
||||
client
|
||||
.streams
|
||||
.get_send_stream_mut(stream_id.into())
|
||||
.get_send_stream_mut(stream_id)
|
||||
.unwrap()
|
||||
.mark_as_acked(0, 4096, false);
|
||||
assert_eq!(client.events().count(), 0);
|
||||
|
@ -253,7 +251,7 @@ fn max_data() {
|
|||
// Increase max stream data. Avail space now limited by tx buffer
|
||||
client
|
||||
.streams
|
||||
.get_send_stream_mut(stream_id.into())
|
||||
.get_send_stream_mut(stream_id)
|
||||
.unwrap()
|
||||
.set_max_stream_data(100_000_000);
|
||||
assert_eq!(
|
||||
|
@ -506,10 +504,7 @@ fn stream_data_blocked_generates_max_stream_data() {
|
|||
assert!(!end);
|
||||
|
||||
// Now send `STREAM_DATA_BLOCKED`.
|
||||
let internal_stream = server
|
||||
.streams
|
||||
.get_send_stream_mut(StreamId::from(stream_id))
|
||||
.unwrap();
|
||||
let internal_stream = server.streams.get_send_stream_mut(stream_id).unwrap();
|
||||
if let SendStreamState::Send { fc, .. } = internal_stream.state() {
|
||||
fc.blocked();
|
||||
} else {
|
||||
|
|
|
@ -22,6 +22,10 @@ pub fn dump_packet(
|
|||
pn: PacketNumber,
|
||||
payload: &[u8],
|
||||
) {
|
||||
if ::log::Level::Debug > ::log::max_level() {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut s = String::from("");
|
||||
let mut d = Decoder::from(payload);
|
||||
while d.remaining() > 0 {
|
||||
|
|
|
@ -44,21 +44,21 @@ pub enum ConnectionEvent {
|
|||
},
|
||||
/// New bytes available for reading.
|
||||
RecvStreamReadable {
|
||||
stream_id: u64,
|
||||
stream_id: StreamId,
|
||||
},
|
||||
/// Peer reset the stream.
|
||||
RecvStreamReset {
|
||||
stream_id: u64,
|
||||
stream_id: StreamId,
|
||||
app_error: AppError,
|
||||
},
|
||||
/// Peer has sent STOP_SENDING
|
||||
SendStreamStopSending {
|
||||
stream_id: u64,
|
||||
stream_id: StreamId,
|
||||
app_error: AppError,
|
||||
},
|
||||
/// Peer has acked everything sent on the stream.
|
||||
SendStreamComplete {
|
||||
stream_id: u64,
|
||||
stream_id: StreamId,
|
||||
},
|
||||
/// Peer increased MAX_STREAMS
|
||||
SendStreamCreatable {
|
||||
|
@ -99,9 +99,7 @@ impl ConnectionEvents {
|
|||
}
|
||||
|
||||
pub fn recv_stream_readable(&self, stream_id: StreamId) {
|
||||
self.insert(ConnectionEvent::RecvStreamReadable {
|
||||
stream_id: stream_id.as_u64(),
|
||||
});
|
||||
self.insert(ConnectionEvent::RecvStreamReadable { stream_id });
|
||||
}
|
||||
|
||||
pub fn recv_stream_reset(&self, stream_id: StreamId, app_error: AppError) {
|
||||
|
@ -109,7 +107,7 @@ impl ConnectionEvents {
|
|||
self.remove(|evt| matches!(evt, ConnectionEvent::RecvStreamReadable { stream_id: x } if *x == stream_id.as_u64()));
|
||||
|
||||
self.insert(ConnectionEvent::RecvStreamReset {
|
||||
stream_id: stream_id.as_u64(),
|
||||
stream_id,
|
||||
app_error,
|
||||
});
|
||||
}
|
||||
|
@ -120,10 +118,10 @@ impl ConnectionEvents {
|
|||
|
||||
pub fn send_stream_stop_sending(&self, stream_id: StreamId, app_error: AppError) {
|
||||
// If stopped, no longer writable.
|
||||
self.remove(|evt| matches!(evt, ConnectionEvent::SendStreamWritable { stream_id: x } if *x == stream_id.as_u64()));
|
||||
self.remove(|evt| matches!(evt, ConnectionEvent::SendStreamWritable { stream_id: x } if *x == stream_id));
|
||||
|
||||
self.insert(ConnectionEvent::SendStreamStopSending {
|
||||
stream_id: stream_id.as_u64(),
|
||||
stream_id,
|
||||
app_error,
|
||||
});
|
||||
}
|
||||
|
@ -133,9 +131,7 @@ impl ConnectionEvents {
|
|||
|
||||
self.remove(|evt| matches!(evt, ConnectionEvent::SendStreamStopSending { stream_id: x, .. } if *x == stream_id.as_u64()));
|
||||
|
||||
self.insert(ConnectionEvent::SendStreamComplete {
|
||||
stream_id: stream_id.as_u64(),
|
||||
});
|
||||
self.insert(ConnectionEvent::SendStreamComplete { stream_id });
|
||||
}
|
||||
|
||||
pub fn send_stream_creatable(&self, stream_type: StreamType) {
|
||||
|
@ -287,7 +283,7 @@ mod tests {
|
|||
assert_eq!(
|
||||
events[0],
|
||||
ConnectionEvent::SendStreamStopSending {
|
||||
stream_id: 8,
|
||||
stream_id: StreamId::new(8),
|
||||
app_error: 55
|
||||
}
|
||||
);
|
||||
|
|
|
@ -491,7 +491,7 @@ impl Server {
|
|||
&self.certs,
|
||||
&self.protocols,
|
||||
Rc::clone(&cid_mgr) as _,
|
||||
self.conn_params.clone().quic_version(initial.quic_version),
|
||||
self.conn_params.quic_version(initial.quic_version),
|
||||
);
|
||||
|
||||
if let Ok(mut c) = sconn {
|
||||
|
|
|
@ -113,6 +113,12 @@ impl PartialEq<u64> for StreamId {
|
|||
}
|
||||
}
|
||||
|
||||
impl AsRef<u64> for StreamId {
|
||||
fn as_ref(&self) -> &u64 {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for StreamId {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
write!(f, "{}", self.as_u64())
|
||||
|
|
|
@ -377,7 +377,7 @@ impl Streams {
|
|||
))
|
||||
}
|
||||
|
||||
pub fn stream_create(&mut self, st: StreamType) -> Res<u64> {
|
||||
pub fn stream_create(&mut self, st: StreamType) -> Res<StreamId> {
|
||||
match self.local_stream_limits.take_stream_id(st) {
|
||||
None => Err(Error::StreamLimitError),
|
||||
Some(new_id) => {
|
||||
|
@ -416,7 +416,7 @@ impl Streams {
|
|||
),
|
||||
);
|
||||
}
|
||||
Ok(new_id.as_u64())
|
||||
Ok(new_id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ tpids! {
|
|||
MAX_DATAGRAM_FRAME_SIZE = 0x0020,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, Copy)]
|
||||
pub struct PreferredAddress {
|
||||
v4: Option<SocketAddr>,
|
||||
v6: Option<SocketAddr>,
|
||||
|
@ -761,6 +761,7 @@ mod tests {
|
|||
#[test]
|
||||
#[should_panic]
|
||||
fn preferred_address_neither() {
|
||||
#[allow(clippy::drop_copy)]
|
||||
mem::drop(PreferredAddress::new(None, None));
|
||||
}
|
||||
|
||||
|
|
|
@ -184,14 +184,13 @@ impl SendData {
|
|||
if self.stream_id.is_none() {
|
||||
if let Ok(stream_id) = c.stream_create(StreamType::UniDi) {
|
||||
qdebug!([c], "made stream {} for sending", stream_id);
|
||||
self.stream_id = Some(StreamId::new(stream_id));
|
||||
self.stream_id = Some(stream_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn send(&mut self, c: &mut Connection, stream_id: StreamId) -> GoalStatus {
|
||||
const DATA: &[u8] = &[0; 4096];
|
||||
let stream_id = stream_id.as_u64();
|
||||
let mut status = GoalStatus::Waiting;
|
||||
loop {
|
||||
let end = min(self.remaining, DATA.len());
|
||||
|
@ -266,7 +265,7 @@ impl ReceiveData {
|
|||
let mut status = GoalStatus::Waiting;
|
||||
loop {
|
||||
let end = min(self.remaining, buf.len());
|
||||
let (recvd, _) = c.stream_recv(stream_id.as_u64(), &mut buf[..end]).unwrap();
|
||||
let (recvd, _) = c.stream_recv(stream_id, &mut buf[..end]).unwrap();
|
||||
qtrace!("received {} remaining {}", recvd, self.remaining);
|
||||
if recvd == 0 {
|
||||
return status;
|
||||
|
@ -288,7 +287,7 @@ impl ConnectionGoal for ReceiveData {
|
|||
_now: Instant,
|
||||
) -> GoalStatus {
|
||||
if let ConnectionEvent::RecvStreamReadable { stream_id } = e {
|
||||
self.recv(c, StreamId::new(*stream_id))
|
||||
self.recv(c, *stream_id)
|
||||
} else {
|
||||
GoalStatus::Waiting
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче