Bug 1805652 - neqo v0.6.3, r=necko-reviewers,jesup

Differential Revision: https://phabricator.services.mozilla.com/D164657
This commit is contained in:
Kershaw Chang 2022-12-23 10:16:53 +00:00
parent 00ea9bd0f5
commit 1a5d5eb1c9
68 changed files with 2272 additions and 539 deletions

View file

@ -97,7 +97,7 @@ replace-with = "vendored-sources"
[source."https://github.com/mozilla/neqo"]
git = "https://github.com/mozilla/neqo"
tag = "v0.6.1"
tag = "v0.6.3"
replace-with = "vendored-sources"
[source."https://github.com/mozilla/uniffi-rs.git"]

22
Cargo.lock generated
View file

@ -3594,8 +3594,8 @@ dependencies = [
[[package]]
name = "neqo-common"
version = "0.6.1"
source = "git+https://github.com/mozilla/neqo?tag=v0.6.1#e71e860489447fed25b64bd8ec31a804dccb0dad"
version = "0.6.3"
source = "git+https://github.com/mozilla/neqo?tag=v0.6.3#4fe628bb911e4437169d974baa628c159e96f879"
dependencies = [
"chrono",
"env_logger 0.9.0",
@ -3607,10 +3607,10 @@ dependencies = [
[[package]]
name = "neqo-crypto"
version = "0.6.1"
source = "git+https://github.com/mozilla/neqo?tag=v0.6.1#e71e860489447fed25b64bd8ec31a804dccb0dad"
version = "0.6.3"
source = "git+https://github.com/mozilla/neqo?tag=v0.6.3#4fe628bb911e4437169d974baa628c159e96f879"
dependencies = [
"bindgen 0.59.999",
"bindgen 0.63.0",
"log",
"mozbuild",
"neqo-common",
@ -3621,8 +3621,8 @@ dependencies = [
[[package]]
name = "neqo-http3"
version = "0.6.1"
source = "git+https://github.com/mozilla/neqo?tag=v0.6.1#e71e860489447fed25b64bd8ec31a804dccb0dad"
version = "0.6.3"
source = "git+https://github.com/mozilla/neqo?tag=v0.6.3#4fe628bb911e4437169d974baa628c159e96f879"
dependencies = [
"enumset",
"lazy_static",
@ -3639,8 +3639,8 @@ dependencies = [
[[package]]
name = "neqo-qpack"
version = "0.6.1"
source = "git+https://github.com/mozilla/neqo?tag=v0.6.1#e71e860489447fed25b64bd8ec31a804dccb0dad"
version = "0.6.3"
source = "git+https://github.com/mozilla/neqo?tag=v0.6.3#4fe628bb911e4437169d974baa628c159e96f879"
dependencies = [
"lazy_static",
"log",
@ -3653,8 +3653,8 @@ dependencies = [
[[package]]
name = "neqo-transport"
version = "0.6.1"
source = "git+https://github.com/mozilla/neqo?tag=v0.6.1#e71e860489447fed25b64bd8ec31a804dccb0dad"
version = "0.6.3"
source = "git+https://github.com/mozilla/neqo?tag=v0.6.3#4fe628bb911e4437169d974baa628c159e96f879"
dependencies = [
"indexmap",
"lazy_static",

View file

@ -729,6 +729,12 @@ nsresult Http3Session::ProcessEvents() {
mStreamIdHash.InsertOrUpdate(wtStream->StreamId(),
std::move(wtStream));
} break;
case WebTransportEventExternal::Tag::Datagram:
LOG(
("Http3Session::ProcessEvents - "
"WebTransportEventExternal::Tag::Datagram [this=%p]",
this));
break;
}
} break;
default:

View file

@ -9,10 +9,10 @@ license = "MPL-2.0"
name = "neqo_glue"
[dependencies]
neqo-http3 = { tag = "v0.6.1", git = "https://github.com/mozilla/neqo" }
neqo-transport = { tag = "v0.6.1", git = "https://github.com/mozilla/neqo" }
neqo-common = { tag = "v0.6.1", git = "https://github.com/mozilla/neqo" }
neqo-qpack = { tag = "v0.6.1", git = "https://github.com/mozilla/neqo" }
neqo-http3 = { tag = "v0.6.3", git = "https://github.com/mozilla/neqo" }
neqo-transport = { tag = "v0.6.3", git = "https://github.com/mozilla/neqo" }
neqo-common = { tag = "v0.6.3", git = "https://github.com/mozilla/neqo" }
neqo-qpack = { tag = "v0.6.3", git = "https://github.com/mozilla/neqo" }
nserror = { path = "../../../xpcom/rust/nserror" }
nsstring = { path = "../../../xpcom/rust/nsstring" }
xpcom = { path = "../../../xpcom/rust/xpcom" }
@ -26,7 +26,7 @@ static_prefs = { path = "../../../modules/libpref/init/static_prefs", optional =
winapi = {version = "0.3", features = ["ws2def"] }
[dependencies.neqo-crypto]
tag = "v0.6.1"
tag = "v0.6.3"
git = "https://github.com/mozilla/neqo"
default-features = false
features = ["gecko"]

View file

@ -696,6 +696,9 @@ pub enum WebTransportEventExternal {
stream_type: WebTransportStreamType,
session_id: u64,
},
Datagram {
session_id: u64,
},
}
impl WebTransportEventExternal {
@ -728,6 +731,14 @@ impl WebTransportEventExternal {
stream_type: stream_id.stream_type().into(),
session_id: session_id.as_u64(),
},
WebTransportEvent::Datagram {
session_id,
datagram: _
} => {
WebTransportEventExternal::Datagram {
session_id: session_id.as_u64(),
}
}
}
}
}

View file

@ -6,17 +6,17 @@ edition = "2018"
license = "MPL-2.0"
[dependencies]
neqo-transport = { tag = "v0.6.1", git = "https://github.com/mozilla/neqo" }
neqo-common = { tag = "v0.6.1", git = "https://github.com/mozilla/neqo" }
neqo-http3 = { tag = "v0.6.1", git = "https://github.com/mozilla/neqo" }
neqo-qpack = { tag = "v0.6.1", git = "https://github.com/mozilla/neqo" }
neqo-transport = { tag = "v0.6.3", git = "https://github.com/mozilla/neqo" }
neqo-common = { tag = "v0.6.3", git = "https://github.com/mozilla/neqo" }
neqo-http3 = { tag = "v0.6.3", git = "https://github.com/mozilla/neqo" }
neqo-qpack = { tag = "v0.6.3", git = "https://github.com/mozilla/neqo" }
mio = "0.6.17"
mio-extras = "2.0.5"
log = "0.4.0"
base64 = "0.13"
[dependencies.neqo-crypto]
tag = "v0.6.1"
tag = "v0.6.3"
git = "https://github.com/mozilla/neqo"
default-features = false
features = ["gecko"]

View file

@ -11,6 +11,7 @@ use neqo_crypto::{generate_ech_keys, init_db, AllowZeroRtt, AntiReplay};
use neqo_http3::{
Error, Http3OrWebTransportStream, Http3Parameters, Http3Server, Http3ServerEvent,
WebTransportRequest, WebTransportServerEvent,
WebTransportSessionAcceptAction,
};
use neqo_transport::server::Server;
use neqo_transport::{
@ -431,13 +432,13 @@ impl HttpServer for Http3TestServer {
let path = ph.value();
qtrace!("Serve request {}", path);
if path == "/success" {
session.response(true).unwrap();
session.response(&WebTransportSessionAcceptAction::Accept).unwrap();
} else if path == "/reject" {
session.response(false).unwrap();
session.response(&WebTransportSessionAcceptAction::Reject([Header::new(":status", "404")].to_vec())).unwrap();
} else if path == "/closeafter0ms" {
session.response(true).unwrap();
session.response(&WebTransportSessionAcceptAction::Accept).unwrap();
} else if path == "/closeafter100ms" {
session.response(true).unwrap();
session.response(&WebTransportSessionAcceptAction::Accept).unwrap();
let expires = Instant::now() + Duration::from_millis(100);
if !self.sessions_to_close.contains_key(&expires) {
self.sessions_to_close.insert(expires, Vec::new());
@ -447,17 +448,17 @@ impl HttpServer for Http3TestServer {
.unwrap()
.push(session);
} else if path == "/create_unidi_stream" {
session.response(true).unwrap();
session.response(&WebTransportSessionAcceptAction::Accept).unwrap();
self.sessions_to_create_stream.push((session, StreamType::UniDi));
} else if path == "/create_bidi_stream" {
session.response(true).unwrap();
session.response(&WebTransportSessionAcceptAction::Accept).unwrap();
self.sessions_to_create_stream.push((session, StreamType::BiDi));
} else {
session.response(true).unwrap();
session.response(&WebTransportSessionAcceptAction::Accept).unwrap();
}
}
_ => {
session.response(false).unwrap();
session.response(&WebTransportSessionAcceptAction::Reject([Header::new(":status", "404")].to_vec())).unwrap();
}
}
}
@ -476,6 +477,16 @@ impl HttpServer for Http3TestServer {
self.webtransport_bidi_stream.insert(stream);
}
}
Http3ServerEvent::WebTransport(WebTransportServerEvent::Datagram {
session,
datagram,
}) => {
qdebug!(
"WebTransportServerEvent::Datagram {:?} {:?}",
session,
datagram
);
}
}
}
}

View file

@ -1 +1 @@
{"files":{"Cargo.toml":"1e35362eb965155414173a29d9f0fbe1af3c6a66aa158af6701f235688dacd5c","build.rs":"a17b1bb1bd3de3fc958f72d4d1357f7bc4432faa26640c95b5fbfccf40579d67","src/codec.rs":"5aeb997fd0eca77241d1cc173f0d37aa8d2ec1725d5e3739c28f1999bf7c4bed","src/datagram.rs":"742aa0f39ac24d63431b58e23ebf925e27ec42340e5911020475de5f7f457a6d","src/event.rs":"f60fee9f4b09ef47ff5e4bfa21c07e45ffd5873c292f2605f24d834070127d62","src/header.rs":"b7d4eeb40952b36f71ae1f37ce82c9617af8b84c171576de4eca9d50a3071103","src/hrtime.rs":"c5f9f65a642b5782589a4caaf1e758845daa7ebf56c58dc9ef9f836e48026d29","src/incrdecoder.rs":"458f0228e41018d58f5a83c7895c9ea283f310fcb91c969e22026eb8ca3c84c9","src/lib.rs":"bf3e9922196f09554ef55d24c3331bb4c249d583fbbd9f5815670d9d23160838","src/log.rs":"f1ba46a9c2ef10b27211561f4851f933db941ec85ccd3425376d060034aea051","src/qlog.rs":"ca323c91d61810ebef2ebeb967836dda384a60a9fb492c2b8d1b235a98f2e4bf","src/timer.rs":"e63af7e7df968bf702583f263cfb63e6dca4e599bacffa2de0a6383d85333636","tests/log.rs":"480b165b7907ec642c508b303d63005eee1427115d6973a349eaf6b2242ed18d"},"package":null}
{"files":{"Cargo.toml":"b27c96cc0da9ca1bc8731ee35100064dcf11df63613d08d02a2e07a66a6ffef0","build.rs":"a17b1bb1bd3de3fc958f72d4d1357f7bc4432faa26640c95b5fbfccf40579d67","src/codec.rs":"5aeb997fd0eca77241d1cc173f0d37aa8d2ec1725d5e3739c28f1999bf7c4bed","src/datagram.rs":"742aa0f39ac24d63431b58e23ebf925e27ec42340e5911020475de5f7f457a6d","src/event.rs":"f60fee9f4b09ef47ff5e4bfa21c07e45ffd5873c292f2605f24d834070127d62","src/header.rs":"b7d4eeb40952b36f71ae1f37ce82c9617af8b84c171576de4eca9d50a3071103","src/hrtime.rs":"c5f9f65a642b5782589a4caaf1e758845daa7ebf56c58dc9ef9f836e48026d29","src/incrdecoder.rs":"458f0228e41018d58f5a83c7895c9ea283f310fcb91c969e22026eb8ca3c84c9","src/lib.rs":"bf3e9922196f09554ef55d24c3331bb4c249d583fbbd9f5815670d9d23160838","src/log.rs":"f1ba46a9c2ef10b27211561f4851f933db941ec85ccd3425376d060034aea051","src/qlog.rs":"ca323c91d61810ebef2ebeb967836dda384a60a9fb492c2b8d1b235a98f2e4bf","src/timer.rs":"e63af7e7df968bf702583f263cfb63e6dca4e599bacffa2de0a6383d85333636","tests/log.rs":"480b165b7907ec642c508b303d63005eee1427115d6973a349eaf6b2242ed18d"},"package":null}

View file

@ -1,6 +1,6 @@
[package]
name = "neqo-common"
version = "0.6.1"
version = "0.6.3"
authors = ["Bobby Holley <bobbyholley@gmail.com>"]
edition = "2018"
rust-version = "1.57.0"
@ -12,7 +12,7 @@ log = {version = "0.4.0", default-features = false}
env_logger = {version = "0.9", default-features = false}
lazy_static = "1.3.0"
qlog = "0.4.0"
chrono = "0.4.10"
chrono = {version = "0.4.10", default-features = false, features = ["std"]}
[features]
default = ["deny-warnings"]

View file

@ -1 +1 @@
{"files":{"Cargo.toml":"372b8108f43f8df1f6f26e72e434a2ae0ba00b8b4dba193451eb427738734069","TODO":"ac0f1c2ebcca03f5b3c0cc56c5aedbb030a4b511e438bc07a57361c789f91e9f","bindings/bindings.toml":"cb3e29ac6d1fd7083ffab81494afe1b9a2d9e41c60774438fd9681974c87c252","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":"99df86d0c11da5a8cbcc3be2560e6d764234a21b5d9b5f7ab1b70696e4f8670e","src/aead.rs":"140f77ffb5016836c970c39c6c3a42db9581a14b797b9cd05386d0dd0831fe63","src/aead_fuzzing.rs":"e11ea62b4605b84400edbd5fc394f008fc26d3b67467ba7fe1597b90db4c6a12","src/agent.rs":"1dfab420ca00789f745d440476b272e672ae756536f743d1faa9aa214bc0bcda","src/agentio.rs":"bce4c3dfcfa433209a409ac0c0752f8c95ab37bb6239a42f99b83858e8747bd1","src/auth.rs":"ced1a18f691894984244088020ea25dc1ee678603317f0c7dfc8b8842fa750b4","src/cert.rs":"628fc2384cdeb2a32bfbecb83c99af393df97fd6a76c6e8827e8240c0d47328b","src/constants.rs":"998e77bee88197a240032c1bfbddcff417a25ba82e576a0d2fe18ee9b63cefc7","src/ech.rs":"447f6297f50914249d0c92ec61d74df6c65ce4affceecb8cafe40927e855fe1d","src/err.rs":"f2cc71de2b40d7bba8119eeaee200337db9a0126176ba06e4902d7312facdb58","src/exp.rs":"61586662407359c1ecb8ed4987bc3c702f26ba2e203a091a51b6d6363cbd510f","src/ext.rs":"361277879194dc32f741b8d1894afe5fd3fcc8eb244f7dd5914eeb959b85717d","src/hkdf.rs":"3ff432cc9d40e1dc56e9f983b54b593647c4063a5ae0f16de0a64d033ac9bd94","src/hp.rs":"722a798a7d280b66bd0f8adc6f9135f7d8130f7f0ef45ea12b85d47a88adefda","src/lib.rs":"8d1bfe33999b20eeb5f7eef70d4c634de560de5376029f81b2412bca60176c39","src/once.rs":"b9850384899a1a016e839743d3489c0d4d916e1973746ef8c89872105d7d9736","src/p11.rs":"2ff35a2512bcf894684f24b94640e7fe9d4df6a20264aa3714f2f02fdb99e73d","src/prio.rs":"d9bd43a4d84db70fd552239d0852ea60b960957912f614b929d27dadccca803a","src/replay.rs":"c9bc0261fe1ae22e7212774c315a2669784e57762ca975a15250d4a33dbf3ea3","src/result.rs":"cef34dfcb907723e195b56501132e4560e250b327783cb5e41201da5b63e9b5c","src/secrets.rs":"d5d98e34b100568fecade932832379c52530c70a51ad2d37ee6b2dd008865f01","src/selfencrypt.rs":"f11ae7f6f1e6b602b6e729d71597116fb172190b57102af4f76b22fbe78c8b6a","src/ssl.rs":"ace1a162c3b189d52b5e5f9827c4300b7d47d3b2ae29a976d3bbc46c52328253","src/time.rs":"9bacbed11b0e40402731a9803fd1531272e00e199217c1560130ede50d7020f6","tests/aead.rs":"31b5b4374cc5ca2deee6267c4d5b4858defc74e694ec85af196339a76548a17c","tests/agent.rs":"94819f9eeba2afa0c25adc821755900f1488fd47af6d84d9507a112c29d1752a","tests/ext.rs":"eba9f03accdd598e38292ac88263a81b367d60d5a736a43117a3663de105ec48","tests/handshake.rs":"0fcfa8958686aacb42c56c51c6b234842fe990470d2069a67509869baaa18452","tests/hkdf.rs":"47830c1ea58a02d100522bdde6fabc02bb447ccb85affa0cdc44bc25da1be32a","tests/hp.rs":"316973740210fc1f8166920582795a41347a0aec9024fdc480e2ee83a7a5332d","tests/init.rs":"fc9e392b1efa0d8efb28952f73ffc05e5348e7b2b69207b60e375c3888a252a2","tests/selfencrypt.rs":"1125c858ec4e0a6994f34d162aa066cb003c61b324f268529ea04bcb641347cb"},"package":null}
{"files":{"Cargo.toml":"fc03d4b7219e6615de21d3a5a2fa14e4ccd32014e081848b3fd917fcddf48d42","TODO":"ac0f1c2ebcca03f5b3c0cc56c5aedbb030a4b511e438bc07a57361c789f91e9f","bindings/bindings.toml":"cb3e29ac6d1fd7083ffab81494afe1b9a2d9e41c60774438fd9681974c87c252","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":"536e0436f19152aa5a23c340e93ebc24c3640153a8c885b723a9379432a9c6d4","src/aead.rs":"140f77ffb5016836c970c39c6c3a42db9581a14b797b9cd05386d0dd0831fe63","src/aead_fuzzing.rs":"e11ea62b4605b84400edbd5fc394f008fc26d3b67467ba7fe1597b90db4c6a12","src/agent.rs":"a0029413c37c71599971e08df8352fbfd7b7d1970a889ef85a1cabaff256ab11","src/agentio.rs":"bce4c3dfcfa433209a409ac0c0752f8c95ab37bb6239a42f99b83858e8747bd1","src/auth.rs":"ced1a18f691894984244088020ea25dc1ee678603317f0c7dfc8b8842fa750b4","src/cert.rs":"628fc2384cdeb2a32bfbecb83c99af393df97fd6a76c6e8827e8240c0d47328b","src/constants.rs":"998e77bee88197a240032c1bfbddcff417a25ba82e576a0d2fe18ee9b63cefc7","src/ech.rs":"447f6297f50914249d0c92ec61d74df6c65ce4affceecb8cafe40927e855fe1d","src/err.rs":"f2cc71de2b40d7bba8119eeaee200337db9a0126176ba06e4902d7312facdb58","src/exp.rs":"61586662407359c1ecb8ed4987bc3c702f26ba2e203a091a51b6d6363cbd510f","src/ext.rs":"361277879194dc32f741b8d1894afe5fd3fcc8eb244f7dd5914eeb959b85717d","src/hkdf.rs":"3ff432cc9d40e1dc56e9f983b54b593647c4063a5ae0f16de0a64d033ac9bd94","src/hp.rs":"606cae31d5cbbd5fd8f02e74e8e372ce6fabc7da3d0c625e905ff74e6cdde0e6","src/lib.rs":"8d1bfe33999b20eeb5f7eef70d4c634de560de5376029f81b2412bca60176c39","src/once.rs":"b9850384899a1a016e839743d3489c0d4d916e1973746ef8c89872105d7d9736","src/p11.rs":"d76a3d365fad3d7fbc8f7c0aacefc65ef737b9286672fa800d14711f50736a0e","src/prio.rs":"e5e169296c0ac69919c59fb6c1f8bd6bf079452eaa13d75da0edd41d435d3f6f","src/replay.rs":"c9bc0261fe1ae22e7212774c315a2669784e57762ca975a15250d4a33dbf3ea3","src/result.rs":"cef34dfcb907723e195b56501132e4560e250b327783cb5e41201da5b63e9b5c","src/secrets.rs":"d5d98e34b100568fecade932832379c52530c70a51ad2d37ee6b2dd008865f01","src/selfencrypt.rs":"f11ae7f6f1e6b602b6e729d71597116fb172190b57102af4f76b22fbe78c8b6a","src/ssl.rs":"90dfa84ebad961c5ecf3c2e85f9de7acf678dd3aed7e2b9f068036512f70a97d","src/time.rs":"9bacbed11b0e40402731a9803fd1531272e00e199217c1560130ede50d7020f6","tests/aead.rs":"31b5b4374cc5ca2deee6267c4d5b4858defc74e694ec85af196339a76548a17c","tests/agent.rs":"94819f9eeba2afa0c25adc821755900f1488fd47af6d84d9507a112c29d1752a","tests/ext.rs":"eba9f03accdd598e38292ac88263a81b367d60d5a736a43117a3663de105ec48","tests/handshake.rs":"0fcfa8958686aacb42c56c51c6b234842fe990470d2069a67509869baaa18452","tests/hkdf.rs":"47830c1ea58a02d100522bdde6fabc02bb447ccb85affa0cdc44bc25da1be32a","tests/hp.rs":"316973740210fc1f8166920582795a41347a0aec9024fdc480e2ee83a7a5332d","tests/init.rs":"fc9e392b1efa0d8efb28952f73ffc05e5348e7b2b69207b60e375c3888a252a2","tests/selfencrypt.rs":"1125c858ec4e0a6994f34d162aa066cb003c61b324f268529ea04bcb641347cb"},"package":null}

View file

@ -1,6 +1,6 @@
[package]
name = "neqo-crypto"
version = "0.6.1"
version = "0.6.3"
authors = ["Martin Thomson <mt@lowentropy.net>"]
edition = "2018"
rust-version = "1.57.0"
@ -12,7 +12,7 @@ neqo-common = { path = "../neqo-common" }
log = {version = "0.4.0", default-features = false}
[build-dependencies]
bindgen = {version = "0.59", default-features = false, features= ["runtime"]}
bindgen = {version = "0.63", default-features = false, features= ["runtime"]}
serde = "1.0"
serde_derive = "1.0"
toml = "0.5"

View file

@ -102,7 +102,7 @@ fn nss_dir() -> PathBuf {
let dir = Path::new(&out_dir).join("nss");
if !dir.exists() {
Command::new("hg")
.args(&[
.args([
"clone",
"https://hg.mozilla.org/projects/nss",
dir.to_str().unwrap(),
@ -113,7 +113,7 @@ fn nss_dir() -> PathBuf {
let nspr_dir = Path::new(&out_dir).join("nspr");
if !nspr_dir.exists() {
Command::new("hg")
.args(&[
.args([
"clone",
"https://hg.mozilla.org/projects/nspr",
nspr_dir.to_str().unwrap(),

View file

@ -596,7 +596,7 @@ impl SecretAgent {
/// Return any fatal alert that the TLS stack might have sent.
#[must_use]
pub fn alert(&self) -> Option<&Alert> {
(&*self.alert).as_ref()
(*self.alert).as_ref()
}
/// Call this function to mark the peer as authenticated.
@ -930,7 +930,7 @@ impl Client {
/// Error returned when the configuration is invalid.
pub fn enable_ech(&mut self, ech_config_list: impl AsRef<[u8]>) -> Res<()> {
let config = ech_config_list.as_ref();
qdebug!([self], "Enable ECH for a server: {}", hex_with_len(&config));
qdebug!([self], "Enable ECH for a server: {}", hex_with_len(config));
self.ech_config = Vec::from(config);
if config.is_empty() {
unsafe { ech::SSL_EnableTls13GreaseEch(self.agent.fd, PRBool::from(true)) }
@ -1189,8 +1189,8 @@ impl Deref for Agent {
#[must_use]
fn deref(&self) -> &SecretAgent {
match self {
Self::Client(c) => &**c,
Self::Server(s) => &**s,
Self::Client(c) => c,
Self::Server(s) => s,
}
}
}
@ -1198,8 +1198,8 @@ impl Deref for Agent {
impl DerefMut for Agent {
fn deref_mut(&mut self) -> &mut SecretAgent {
match self {
Self::Client(c) => &mut **c,
Self::Server(s) => &mut **s,
Self::Client(c) => c,
Self::Server(s) => s,
}
}
}

View file

@ -150,7 +150,7 @@ impl HpKey {
output.as_mut_ptr(),
&mut output_len,
c_int::try_from(output.len())?,
(&sample[..Self::SAMPLE_SIZE]).as_ptr().cast(),
sample[..Self::SAMPLE_SIZE].as_ptr().cast(),
c_int::try_from(Self::SAMPLE_SIZE).unwrap(),
)
})?;
@ -162,7 +162,7 @@ impl HpKey {
let params: CK_CHACHA20_PARAMS = CK_CHACHA20_PARAMS {
pBlockCounter: sample.as_ptr() as *mut u8,
blockCounterBits: 32,
pNonce: (&sample[4..Self::SAMPLE_SIZE]).as_ptr() as *mut _,
pNonce: sample[4..Self::SAMPLE_SIZE].as_ptr() as *mut _,
ulNonceBits: 96,
};
let mut output_len: c_uint = 0;
@ -172,10 +172,10 @@ impl HpKey {
**key,
CK_MECHANISM_TYPE::from(CKM_CHACHA20),
addr_of_mut!(param_item),
(&mut output[..]).as_mut_ptr(),
output[..].as_mut_ptr(),
&mut output_len,
c_uint::try_from(output.len())?,
(&output[..]).as_ptr(),
output[..].as_ptr(),
c_uint::try_from(output.len())?,
)
})?;

View file

@ -20,7 +20,6 @@ use std::os::raw::{c_int, c_uint};
use std::ptr::null_mut;
#[allow(clippy::upper_case_acronyms)]
#[allow(unknown_lints, deref_nullptr)] // Until bindgen#1651 is fixed.
#[allow(clippy::unreadable_literal)]
#[allow(unknown_lints, clippy::borrow_as_ptr)]
mod nss_p11 {

View file

@ -5,7 +5,6 @@
// except according to those terms.
#![allow(clippy::upper_case_acronyms)]
#![allow(unknown_lints, deref_nullptr)] // Until bindgen#1651 is fixed.
#![allow(
dead_code,
non_upper_case_globals,

View file

@ -14,7 +14,6 @@
unknown_lints,
clippy::borrow_as_ptr
)]
#![allow(unknown_lints, deref_nullptr)] // Until bindgen#1651 is fixed.
use crate::constants::Epoch;
use crate::err::{secstatus_to_res, Res};

View file

@ -1 +1 @@
{"files":{"Cargo.toml":"7dd3f73b8b65f903607977de05ae02595340dd5e7afe6ac5010e400fc440c506","src/buffered_send_stream.rs":"4bc45ca03252dc34ab421a2af3499191b182a619143c61d5609a46377c9a0f3d","src/client_events.rs":"9d86145febad2f3fb05007eae3f5ad4834c78dd709fe388f05590405e34a614b","src/conn_params.rs":"7e33526de9c83c163049a2caf2bff0f997351cc61fad76fb6e8c6ec4b9f09938","src/connection.rs":"72c3c2a3c19481d519f4c1928c51dc7c1d4ab6e6cb2bd9ecfdfc5d5c09f485bb","src/connection_client.rs":"e4914a8e44eb6045615382eacf2499b0195574f19ce36e161ad3d3e186f69ebb","src/connection_server.rs":"de1e0359b902b1e98c923a8d5488302a68a3312b466590fdddaee6ec8327813b","src/control_stream_local.rs":"b86e1f869ad59bf2663501942a1a65d94c1dbc3e8770982459e0b620be4b6cf0","src/control_stream_remote.rs":"7a261ac7df77e90a428ab0f92457a934a92a8c581462fc1818efd3de0c0ebd69","src/features/extended_connect/mod.rs":"2bc2f0570b11318f3225173001dad1a5f05e4bf60dee49a2bf9d40e3a411e138","src/features/extended_connect/webtransport_session.rs":"abf84892c429c2ee79efd8e215bfd9da182163ba859cd24b6ee4ba6becceb6bd","src/features/extended_connect/webtransport_streams.rs":"784c5e317bb6af33f653ba82c1a5666b657c2a210263a415e913494f61613464","src/features/mod.rs":"a981ebbd03e7bb7ea2313e883452e44f052c48f28edb7fd53a0825911b490230","src/frames/hframe.rs":"8206e1a27ad805899f7e722c05dffa92649704bbaf98ff2a70a7ca1d6a55395e","src/frames/mod.rs":"258dd4bdf2daca19a62cd697d2c7f4709a35668b2b4dce3203675e814c9b40b8","src/frames/reader.rs":"0802cd8b41204bcec424fc6ed704a3bdbed0e5d38444f7a9b0550ad877b076a6","src/frames/tests/hframe.rs":"33a30bb98bb512606a06ae1752e1ed9e4588b7d3f5e9439ec83bb2e779d4ac80","src/frames/tests/mod.rs":"4933c519069ee4dac23587588f2b792c12d1363e92d0105e1eb169082e213559","src/frames/tests/reader.rs":"312a3deda7b3a4bbd7afed879c94d0644fce8e34435365ef9cae1fbaa62496af","src/frames/tests/wtframe.rs":"589ebe1e62ce4da63b37b7d22cde7ba572ddbf29336fdcdbbcd0a745f79dacd8","src/frames/wtframe.rs":"0eebdf9a275cd53ee6525f7387941601d119d933203d9b2425377adf8348d425","src/headers_checks.rs":"b80c1da2d9f336fa88f7b7f2a834d8e90e826260811771c7729785fdc92b20d4","src/lib.rs":"58d23d794cf5c68d6f2b68e93e3e7d1c5546d64d5d2357ca7cb7858aeb434124","src/priority.rs":"ae0fa461031893b4f7e0d12666072e7a4da80b1e8a1c0663ab9f9e27b3242754","src/push_controller.rs":"aa2a64180d8cb1b87682d0d8bbc42167188e8e1890261cb4cabb76de1fcc708b","src/qlog.rs":"44b6cdbb1d9d6ca47b793e9dbe531b8fdbd40147375f7e4c89aeab536c5d286b","src/qpack_decoder_receiver.rs":"75008d8ea5d538ee34aca4df72e58489417604ccafb61b064280782d6754dd0d","src/qpack_encoder_receiver.rs":"f95cc7d49e4d442b93d522f14ddfc581629664d84d6e13d03a520e855bbe442d","src/recv_message.rs":"5f70fb474e387653d7982374131b3b0c08417509469f273ccebf842bfcee836f","src/request_target.rs":"9182b641f7a7b55272e0e2e872d24a35b1207f35399221b08b899857c3e873ab","src/send_message.rs":"9e1b22ede2a105a79d7c02178801e1a46b06a80dc1c0d2a7d69b0eea7e89f319","src/server.rs":"ab00f395f7767d733091af3e3317527e5c302b2e5062b33943211ede75f10109","src/server_connection_events.rs":"3d89c2d9a30ee719acfbaae4b7720cb354eb73b11bc6ceb44571d68b05192b8b","src/server_events.rs":"3081fdd1e1950aeecae031452cd683335fb0a9dcec51722e614c5939f747b9d9","src/settings.rs":"e7babcce34c49d897c7d5ed93ef8e9ad02524cebff96a249c2ce84f1b968be21","src/stream_type_reader.rs":"f790b2aaa6758ad85487d98376895b5ee2c3098ffd4586825e1bb0b3c2375c75","tests/httpconn.rs":"f8d6e6a693d17cf2eb192a730e6fc929bd2814552356ce8d4423a0e3eac8c59d","tests/mod.rs":"fd6aee37243713e80fc526552f21f0222338cec9890409b6575a2a637b17ec1f","tests/priority.rs":"a606e5fa03451e09e28c7d5f1820ee85a4567e3969a1690c979761e62123bf54","tests/send_message.rs":"673ae1d0bf2dce46c21ee8353f45f189d2cb64a2f6e137ae38da6b2262ad066e","tests/webtransport/mod.rs":"635c0b0fe682a844f4366335a40b8b3a6539abe30843ee1bcfaf87a34b1d476c","tests/webtransport/negotiation.rs":"2da85dfd45e3dfdbab7608768d734e1f150e1b0ba14e982cbb6de16ba62789c2","tests/webtransport/sessions.rs":"5b4d8483ac018ad5a28adad5e778e2ed48db9c441d1354f6cf21d8e5c6f1a8b3","tests/webtransport/streams.rs":"fd5f075d93f0241290566f59f747d95530d2df579890fd0f6b9e79a557c89a67"},"package":null}
{"files":{"Cargo.toml":"bf794c0aeb6200aafc695374c2218afd0cf6f7bab7e6b1c370eb0171fb785169","src/buffered_send_stream.rs":"4bc45ca03252dc34ab421a2af3499191b182a619143c61d5609a46377c9a0f3d","src/client_events.rs":"978ab01cf82c2390db442c6d8440209f9498f52a00ede7c8ea9813e649e15ae3","src/conn_params.rs":"7e33526de9c83c163049a2caf2bff0f997351cc61fad76fb6e8c6ec4b9f09938","src/connection.rs":"fe38c174b54b65c696e8fdc89da337cb19783fee78d6d6407223a8d13bb36c6a","src/connection_client.rs":"131b20981697002386ef2dff63345dfa4227547dd028293e16f91f72c1b9cd26","src/connection_server.rs":"365f246a0caa5d310da21587525b4793cd3046d973cce8f3aa62b8a1eac493a7","src/control_stream_local.rs":"b86e1f869ad59bf2663501942a1a65d94c1dbc3e8770982459e0b620be4b6cf0","src/control_stream_remote.rs":"7a261ac7df77e90a428ab0f92457a934a92a8c581462fc1818efd3de0c0ebd69","src/features/extended_connect/mod.rs":"94fc7b98ac335ffad4eb41d162b87f0ae9a86205cd3ef9c79d21081a99d6e610","src/features/extended_connect/tests/mod.rs":"fd6aee37243713e80fc526552f21f0222338cec9890409b6575a2a637b17ec1f","src/features/extended_connect/tests/webtransport/datagrams.rs":"0d8021b94491100d56f4c66ad852bea9d3f1286135e523e217db0e0487284172","src/features/extended_connect/tests/webtransport/mod.rs":"db7b31e6826e2fb6ab8051434d649d78a70d3b7e92e3760f79f7995c7b651558","src/features/extended_connect/tests/webtransport/negotiation.rs":"2d8b0bfa015eb7b57a844aea8abf4ae3c0ab478d1bf84bb53ef8b1837ca7b7ec","src/features/extended_connect/tests/webtransport/sessions.rs":"8471db54293452aa5ae2a0aab565b51e2907238679d5f4074ab3cc05f24036ff","src/features/extended_connect/tests/webtransport/streams.rs":"0ef81aef536f01413b13738cf59f69c7f819a75d655de3d6b905f2f3d84f8509","src/features/extended_connect/webtransport_session.rs":"3dea89800b4b364d140a443d12b25393fa3f50f9bc3f8d6f52e8f9b88cd74049","src/features/extended_connect/webtransport_streams.rs":"9562e5bf321f1338514a70ee89ccd88a15cd4ccf727ea3533b55bb1d5be27f04","src/features/mod.rs":"a981ebbd03e7bb7ea2313e883452e44f052c48f28edb7fd53a0825911b490230","src/frames/hframe.rs":"8206e1a27ad805899f7e722c05dffa92649704bbaf98ff2a70a7ca1d6a55395e","src/frames/mod.rs":"7d0a46ca147336d14781edb8dbee8b03c2e4bcd6646f5473a9d93d31fe73fecb","src/frames/reader.rs":"3d7af10a21833049aa0277caec4abe13d677d2d62526f1c83126f2defe19ee5e","src/frames/tests/hframe.rs":"33a30bb98bb512606a06ae1752e1ed9e4588b7d3f5e9439ec83bb2e779d4ac80","src/frames/tests/mod.rs":"81cac9702e9016dacd60085c2e3968093b356fe682ced33d7c9c1f3151b9201c","src/frames/tests/reader.rs":"312a3deda7b3a4bbd7afed879c94d0644fce8e34435365ef9cae1fbaa62496af","src/frames/tests/wtframe.rs":"589ebe1e62ce4da63b37b7d22cde7ba572ddbf29336fdcdbbcd0a745f79dacd8","src/frames/wtframe.rs":"0eebdf9a275cd53ee6525f7387941601d119d933203d9b2425377adf8348d425","src/headers_checks.rs":"b80c1da2d9f336fa88f7b7f2a834d8e90e826260811771c7729785fdc92b20d4","src/lib.rs":"50ac4975d3b5b5680a1f8d144da16b40f080bbe90e05679fe50d59ad5937d10b","src/priority.rs":"ae0fa461031893b4f7e0d12666072e7a4da80b1e8a1c0663ab9f9e27b3242754","src/push_controller.rs":"aa2a64180d8cb1b87682d0d8bbc42167188e8e1890261cb4cabb76de1fcc708b","src/qlog.rs":"44b6cdbb1d9d6ca47b793e9dbe531b8fdbd40147375f7e4c89aeab536c5d286b","src/qpack_decoder_receiver.rs":"50c5e7679304b227a5f86ab681396d59c5885a8d7a4b72985cce82f3f8eaa129","src/qpack_encoder_receiver.rs":"3deca0555826167bbaf8099eb1b394883e5e5c8c4ee68261f8c4816bdf686eda","src/recv_message.rs":"1f740c59676f5913108de68f41db1dfc1b8d6feeb0467c61ae652d6d26e75682","src/request_target.rs":"9182b641f7a7b55272e0e2e872d24a35b1207f35399221b08b899857c3e873ab","src/send_message.rs":"9e1b22ede2a105a79d7c02178801e1a46b06a80dc1c0d2a7d69b0eea7e89f319","src/server.rs":"cfcb1e9e44f3e4de220fe71cee9f4633bed911f49c1bc40a44b6ff95ec6f5a76","src/server_connection_events.rs":"df18dac1ca9048567a6aac4db34aa031cb80135b1ef6cd3ae67e3f621015e081","src/server_events.rs":"be39b5c07c6f1981b8d4c4afd8dfc704b0b2b440831f364079aeffeec4dedd8f","src/settings.rs":"e7babcce34c49d897c7d5ed93ef8e9ad02524cebff96a249c2ce84f1b968be21","src/stream_type_reader.rs":"983362180f19e47c938b3b81650e71c5bc5a55500ff425b1617d74e2ec9357f5","tests/httpconn.rs":"8b62aa9a24ccc45f436aa57ff7d5b37394d844eced6b204085fc5086b1a643c7","tests/priority.rs":"a606e5fa03451e09e28c7d5f1820ee85a4567e3969a1690c979761e62123bf54","tests/send_message.rs":"673ae1d0bf2dce46c21ee8353f45f189d2cb64a2f6e137ae38da6b2262ad066e","tests/webtransport.rs":"73b1b5a0297452e92c81793b986cbe666f6efe114b9ab5f719be5d3e92f1819c"},"package":null}

View file

@ -1,6 +1,6 @@
[package]
name = "neqo-http3"
version = "0.6.1"
version = "0.6.3"
authors = ["Dragana Damjanovic <dragana.damjano@gmail.com>"]
edition = "2018"
rust-version = "1.57.0"

View file

@ -35,6 +35,10 @@ pub enum WebTransportEvent {
stream_id: StreamId,
session_id: StreamId,
},
Datagram {
session_id: StreamId,
datagram: Vec<u8>,
},
}
#[derive(Debug, PartialEq, Eq, Clone)]
@ -211,6 +215,15 @@ impl ExtendedConnectEvents for Http3ClientEvents {
},
));
}
fn new_datagram(&self, session_id: StreamId, datagram: Vec<u8>) {
self.insert(Http3ClientEvent::WebTransport(
WebTransportEvent::Datagram {
session_id,
datagram,
},
));
}
}
impl Http3ClientEvents {

View file

@ -27,11 +27,12 @@ use crate::{
HttpRecvStreamEvents, NewStreamType, Priority, PriorityHandler, ReceiveOutput, RecvStream,
RecvStreamEvents, SendStream, SendStreamEvents,
};
use neqo_common::{qdebug, qerror, qinfo, qtrace, qwarn, Header, MessageType, Role};
use neqo_common::{qdebug, qerror, qinfo, qtrace, qwarn, Decoder, Header, MessageType, Role};
use neqo_qpack::decoder::QPackDecoder;
use neqo_qpack::encoder::QPackEncoder;
use neqo_transport::{
AppError, Connection, ConnectionError, State, StreamId, StreamType, ZeroRttState,
AppError, Connection, ConnectionError, DatagramTracking, State, StreamId, StreamType,
ZeroRttState,
};
use std::cell::RefCell;
use std::collections::{BTreeSet, HashMap};
@ -41,7 +42,7 @@ use std::rc::Rc;
use crate::{Error, Res};
pub struct RequestDescription<'b, 't, T>
pub(crate) struct RequestDescription<'b, 't, T>
where
T: AsRequestTarget<'t> + ?Sized + Debug,
{
@ -52,6 +53,20 @@ where
pub priority: Priority,
}
pub enum WebTransportSessionAcceptAction {
Accept,
Reject(Vec<Header>),
}
impl ::std::fmt::Display for WebTransportSessionAcceptAction {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
match self {
WebTransportSessionAcceptAction::Accept => f.write_str("Accept"),
WebTransportSessionAcceptAction::Reject(_) => f.write_str("Reject"),
}
}
}
#[derive(Debug)]
enum Http3RemoteSettingsState {
NotReceived,
@ -59,6 +74,19 @@ enum Http3RemoteSettingsState {
ZeroRtt(HSettings),
}
/// States:
/// - `Initializing`: this is the state during the QUIC handshake,
/// - `ZeroRtt`: 0-RTT has been enabled and is active
/// - Connected
/// - GoingAway(StreamId): The connection has received a `GOAWAY` frame
/// - Closing(ConnectionError): The connection is closed. The closing has been initiated by this
/// end of the connection, e.g., the `CONNECTION_CLOSE` frame has been sent. In this state, the
/// connection waits a certain amount of time to retransmit the `CONNECTION_CLOSE` frame if
/// needed.
/// - Closed(ConnectionError): This is the final close state: closing has been initialized by the
/// peer and an ack for the `CONNECTION_CLOSE` frame has been sent or the closing has been
/// initiated by this end of the connection and the ack for the `CONNECTION_CLOSE` has been
/// received or the waiting time has passed.
#[derive(Debug, PartialEq, PartialOrd, Ord, Eq, Clone)]
pub enum Http3State {
Initializing,
@ -79,6 +107,194 @@ impl Http3State {
}
}
/**
# HTTP/3 core implementation
This is the core implementation of HTTP/3 protocol. It implements most of the features of the
protocol. `Http3Client` and `Http3ServerHandler` implement only client and server side behavior.
The API consists of:
- functions that correspond to the `Http3Client` and `Http3ServerHandler` API:
- `new`
- `close`
- `fetch` - only used by the client-side implementation
- `read_data`
- `stream_reset_send`
- `stream_stop_sending`
- `cancel_fetch`
- `stream_close_send`
- functions that correspond to [`WebTransport`](https://w3c.github.io/webtransport/) functions:
- `webtransport_create_session` - only used by the client-side implementation
- `webtransport_session_accept` - only used by the server-side implementation
- `webtransport_close_session`
- `webtransport_create_stream_local` - this function is called when an application wants to open
a new `WebTransport` stream. For example `Http3Client::webtransport_create_stream` will call
this function.
- `webtransport_create_stream_remote` - this is called when a `WebTransport` stream has been
opened by the peer and this function sets up the appropriate handler for the stream.
- functions that are called by `process_http3`
- `process_sending` - some send-streams are buffered streams(see the Streams section) and this
function is called to trigger sending of the buffer data.
- functions that are called to handle `ConnectionEvent`s:
- `add_new_stream`
- `handle_stream_readable`
- `handle_stream_reset`
- `handle_stream_stop_sending`
- `handle_state_change`
- `handle_zero_rtt_rejected`
- Additional functions:
- `set_features_listener`
- `stream_has_pending_data`
- `has_data_to_send`
- `add_streams`
- `add_recv_stream`
- `queue_control_frame`
- `queue_update_priority`
- `set_0rtt_settings`
- `get_settings`
- `state`
- `webtransport_enabled`
## Streams
Each `Http3Connection` holds a list of stream handlers. Each send and receive-handler is registered in
`send_streams` and `recv_streams`. Unidirectional streams are registered only on one of the lists
and bidirectional streams are registered in both lists and the 2 handlers are independent, e.g. one
can be closed and removed ane second may still be active.
The only streams that are not registered are the local control stream, local QPACK decoder stream,
and local QPACK encoder stream. These streams are send-streams and sending data on this stream is
handled a bit differently. This is done in the `process_sending` function, i.e. the control data is
sent first and QPACK data is sent after regular stream data is sent because this stream may have
new data only after regular streams are handled (TODO we may improve this a bit to send QPACK
commands before headers.)
There are the following types of streams:
- `Control`: there is only a receiver stream of this type and the handler is `ControlStreamRemote`.
- `Decoder`: there is only a receiver stream of this type and the handler is `DecoderRecvStream`.
- `Encoder`: there is only a receiver stream of this type and the handler is `EncoderRecvStream`.
- `NewStream`: there is only a receiver stream of this type and the handler is
`NewStreamHeadReader`.
- `Http`: `SendMessage` and `RecvMessage` handlers are responsible for this type of streams.
- `Push`: `RecvMessage` is responsible for this type of streams.
- `ExtendedConnect`: `WebTransportSession` is responsible sender and receiver handler.
- `WebTransport(StreamId)`: `WebTransportSendStream` and `WebTransportRecvStream` are responsible
sender and receiver handler.
- `Unknown`: These are all other stream types that are not unknown to the current implementation
and should be handled properly by the spec, e.g., in our implementation the streams are
reset.
The streams are registered in `send_streams` and `recv_streams` in following ways depending if they
are local or remote:
- local streams:
- all local stream will be registered with the appropriate handler.
- remote streams:
- all new incoming streams are registered with `NewStreamHeadReader`. This is triggered by
`ConnectionEvent::NewStream` and `add_new_stream` is called.
- reading from a `NewStreamHeadReader` stream, via the `receive` function, will decode a stream
type. `NewStreamHeadReader::receive` will return `ReceiveOutput::NewStream(_)` when a stream
type has been decoded. After this point the stream:
- will be regegistered with the appropriate handler,
- will be canceled if is an unknown stream type or
- the connection will fail if it is unallowed stream type (receiveing HTTP request on the
client-side).
The output is handled in `handle_new_stream`, for control, qpack streams and partially
`WebTransport` streams, otherwise the output is handled by `Http3Client` and `Http3ServerHandler`.
### Receiving data
Reading from a stream is triggered by `ConnectionEvent::RecvStreamReadable` events for the stream.
The receive handler is retrieved from `recv_streams` and its `RecvStream::receive` function is
called.
Receiving data on `Http` streams is also triggered by the `read_data` function.
`ConnectionEvent::RecvStreamReadable` events will trigger reading `HEADERS` frame and frame headers
for `DATA` frames which will produce `Http3ClientEvent` or `Http3ServerEvent` events. The content of
`DATA` frames is read by the application using the `read_data` function. The `read_data` function
may read frame headers for consecutive `DATA` frames.
On a `WebTransport(_)` stream data will be read only by the `read_data` function. The
`RecvStream::receive` function only produces an `Http3ClientEvent` or `Http3ServerEvent` event.
The `receive` and `read_data` functions may detect that the stream is done, e.g. FIN received. In
this case, the stream will be removed from the `recv_stream` register, see `remove_recv_stream`.
### Sending data
All sender stream handlers have buffers. Data is first written into a buffer before being supplied
to the QUIC layer. All data except the `DATA` frame and `WebTransport(_)`s payload are written
into the buffer. This includes stream type byte, e.g. `WEBTRANSPORT_STREAM` as well. In the case of
`Http` and `WebTransport(_)` applications can write directly to the QUIC layer using the
`send_data` function to avoid copying data. Sending data via the `send_data` function is only
possible if there is no buffered data.
If a stream has buffered data it will be registered in the `streams_with_pending_data` queue and
actual sending will be performed in the `process_sending` function call. (This is done in this way,
i.e. data is buffered first and then sent, for 2 reasons: in this way, sending will happen in a
single function, therefore error handling and clean up is easier and the QUIIC layer may not be
able to accept all data and being able to buffer data is required in any case.)
The `send` and `send_data` functions may detect that the stream is closed and all outstanding data
has been transferred to the QUIC layer. In this case, the stream will be removed from the
`send_stream` register.
### `ControlStreamRemote`
The `ControlStreamRemote` handler uses `FrameReader` to read and decode frames received on the
control frame. The `receive` returns `ReceiveOutput::ControlFrames(_)` with a list of control
frames read (the list may be empty). The control frames are handled by `Http3Connection` and/or by
`Http3Client` and `Http3ServerHandler`.
### `DecoderRecvStream` and `EncoderRecvStream`
The `receive` functions of these handlers call corresponding `receive` functions of `QPackDecoder`
and `QPackDecoder`.
`DecoderRecvStream` returns `ReceiveOutput::UnblockedStreams(_)` that may contain a list of stream
ids that are unblocked by receiving qpack decoder commands. `Http3Connection` will handle this
output by calling `receive` for the listed stream ids.
`EncoderRecvStream` only returns `ReceiveOutput::NoOutput`.
Both handlers may return an error that will close the connection.
### `NewStreamHeadReader`
A new incoming receiver stream registers a `NewStreamHeadReader` handler. This handler reads the
first bytes of a stream to detect a stream type. The `receive` function returns
`ReceiveOutput::NoOutput` if a stream type is still not known by reading the available stream data
or `ReceiveOutput::NewStream(_)`. The handling of the output is explained above.
### `SendMessage` and `RecvMessage`
`RecvMessage::receive` only returns `ReceiveOutput::NoOutput`. It also have an event listener of
type `HttpRecvStreamEvents`. The listener is called when headers are ready, or data is ready, etc.
For example for `Http` stream the listener will produce `HeaderReady` and `DataReadable` events.
### `WebTransportSession`
A `WebTransport` session is connected to a control stream that is in essence an HTTP transaction.
Therefore, `WebTransportSession` will internally use a `SendMessage` and `RecvMessage` handler to
handle parsing and sending of HTTP part of the control stream. When HTTP headers are exchenged,
`WebTransportSession` will take over handling of stream data. `WebTransportSession` sets
`WebTransportSessionListener` as the `RecvMessage` event listener.
`WebTransportSendStream` and `WebTransportRecvStream` are associated with a `WebTransportSession`
and they will be canceled if the session is closed. To be avle to do this `WebTransportSession`
holds a list of its active streams and clean up is done in `remove_extended_connect`.
### `WebTransportSendStream` and `WebTransportRecvStream`
`WebTransport` streams are associated with a session. `WebTransportSendStream` and
`WebTransportRecvStream` hold a reference to the session and are registered in the session upon
creation by `Http3Connection`. The `WebTransportSendStream` and `WebTransportRecvStream`
handlers will be unregistered from the session if they are closed, reset, or canceled.
The call to function `receive` may produce `Http3ClientEvent::DataReadable`. Actual reading of
data is done in the `read_data` function.
*/
#[derive(Debug)]
pub(crate) struct Http3Connection {
role: Role,
@ -126,10 +342,15 @@ impl Http3Connection {
}
}
/// This function is called when a not default feature needs to be negotiated. This is currently
/// only used for the `WebTransport` feature. The negotiation is done via the `SETTINGS` frame
/// and when the peer's `SETTINGS` frame has been received the listener will be called.
pub fn set_features_listener(&mut self, feature_listener: Http3ClientEvents) {
self.webtransport.set_listener(feature_listener);
}
/// This function creates and initializes, i.e. send stream type, the control and qpack
/// streams.
fn initialize_http3_connection(&mut self, conn: &mut Connection) -> Res<()> {
qinfo!([self], "Initialize the http3 connection.");
self.control_stream_local.create(conn)?;
@ -173,6 +394,10 @@ impl Http3Connection {
!self.streams_with_pending_data.is_empty()
}
/// This function calls the `send` function for all streams that have data to send. If a stream
/// has data to send it will be added to the `streams_with_pending_data` list.
///
/// Control and QPACK streams are handled differently and are never added to the list.
fn send_non_control_streams(&mut self, conn: &mut Connection) -> Res<()> {
let to_send = mem::take(&mut self.streams_with_pending_data);
for stream_id in to_send {
@ -192,7 +417,8 @@ impl Http3Connection {
Ok(())
}
/// Call `send` for all streams that need to send data.
/// Call `send` for all streams that need to send data. See explanation for the main structure
/// for more details.
pub fn process_sending(&mut self, conn: &mut Connection) -> Res<()> {
// check if control stream has data to send.
self.control_stream_local
@ -228,6 +454,8 @@ impl Http3Connection {
}
}
/// This is called when a `ConnectionEvent::NewStream` event is received. This register the
/// stream with a `NewStreamHeadReader` handler.
pub fn add_new_stream(&mut self, stream_id: StreamId) {
qtrace!([self], "A new stream: {}.", stream_id);
self.recv_streams.insert(
@ -236,6 +464,8 @@ impl Http3Connection {
);
}
/// The function calls `receive` for a stream. It also deals with the outcome of a read by
/// calling `handle_stream_manipulation_output`.
#[allow(clippy::option_if_let_else)] // False positive as borrow scope isn't lexical here.
fn stream_receive(&mut self, conn: &mut Connection, stream_id: StreamId) -> Res<ReceiveOutput> {
qtrace!([self], "Readable stream {}.", stream_id);
@ -271,8 +501,9 @@ impl Http3Connection {
/// This function handles reading from all streams, i.e. control, qpack, request/response
/// stream and unidi stream that are still do not have a type.
/// The function cannot handle:
/// 1) a Push stream (if an unknown unidi stream is decoded to be a push stream)
/// 2) frames `MaxPushId` or `Goaway` must be handled by `Http3Client`/`Server`.
/// 1) a `Push(_)`, `Htttp` or `WebTransportStream(_)` stream
/// 2) frames `MaxPushId`, `PriorityUpdateRequest`, `PriorityUpdateRequestPush` or `Goaway`
/// must be handled by `Http3Client`/`Server`.
/// The function returns `ReceiveOutput`.
pub fn handle_stream_readable(
&mut self,
@ -306,7 +537,7 @@ impl Http3Connection {
ReceiveOutput::NewStream(_) => {
unreachable!("NewStream should have been handled already")
}
_ => Ok(output),
ReceiveOutput::NoOutput => Ok(output),
}
}
@ -419,6 +650,17 @@ impl Http3Connection {
}
}
pub fn handle_datagram(&mut self, datagram: &[u8]) {
let mut decoder = Decoder::new(datagram);
let session = decoder
.decode_varint()
.and_then(|id| self.recv_streams.get_mut(&StreamId::from(id)))
.and_then(|stream| stream.webtransport());
if let Some(s) = session {
s.borrow_mut().datagram(decoder.decode_remainder().to_vec());
}
}
fn check_stream_exists(&self, stream_type: Http3StreamType) -> Res<()> {
if self
.recv_streams
@ -431,10 +673,10 @@ impl Http3Connection {
}
}
/// If the new stream is a control stream, this function creates a proper handler
/// If the new stream is a control or QPACK stream, this function creates a proper handler
/// and perform a read.
/// if the new stream is a push stream, the function returns `ReceiveOutput::PushStream`
/// and the caller will handle it.
/// if the new stream is a `Push(_)`, `Http` or `WebTransportStream(_)` stream, the function
/// returns `ReceiveOutput::NewStream(_)` and the caller will handle it.
/// If the stream is of a unknown type the stream will be closed.
fn handle_new_stream(
&mut self,
@ -872,9 +1114,12 @@ impl Http3Connection {
conn: &mut Connection,
stream_id: StreamId,
events: Box<dyn ExtendedConnectEvents>,
accept: bool,
accept_res: &WebTransportSessionAcceptAction,
) -> Res<()> {
qtrace!("Respond to WebTransport session with accept={}.", accept);
qtrace!(
"Respond to WebTransport session with accept={}.",
accept_res
);
if !self.webtransport_enabled() {
return Err(Error::Unavailable);
}
@ -891,17 +1136,17 @@ impl Http3Connection {
let send_stream = self.send_streams.get_mut(&stream_id);
match (send_stream, recv_stream, accept) {
match (send_stream, recv_stream, accept_res) {
(None, None, _) => Err(Error::InvalidStreamId),
(None, Some(_), _) | (Some(_), None, _) => {
// TODO this needs a better error
self.cancel_fetch(stream_id, Error::HttpRequestRejected.code(), conn)?;
Err(Error::InvalidStreamId)
}
(Some(s), Some(_r), false) => {
(Some(s), Some(_r), WebTransportSessionAcceptAction::Reject(headers)) => {
if s.http_stream()
.ok_or(Error::InvalidStreamId)?
.send_headers(&[Header::new(":status", "404")], conn)
.send_headers(headers, conn)
.is_ok()
{
mem::drop(self.stream_close_send(conn, stream_id));
@ -912,7 +1157,7 @@ impl Http3Connection {
}
Ok(())
}
(Some(s), Some(_r), true) => {
(Some(s), Some(_r), WebTransportSessionAcceptAction::Accept) => {
if s.http_stream()
.ok_or(Error::InvalidStreamId)?
.send_headers(&[Header::new(":status", "200")], conn)
@ -1090,8 +1335,25 @@ impl Http3Connection {
}
}
// If the control stream has received frames MaxPushId or Goaway which handling is specific to
// the client and server, we must give them to the specific client/server handler.
pub fn webtransport_send_datagram(
&mut self,
session_id: StreamId,
conn: &mut Connection,
buf: &[u8],
id: impl Into<DatagramTracking>,
) -> Res<()> {
self.recv_streams
.get_mut(&session_id)
.ok_or(Error::InvalidStreamId)?
.webtransport()
.ok_or(Error::InvalidStreamId)?
.borrow_mut()
.send_datagram(conn, buf, id)
}
/// If the control stream has received frames `MaxPushId`, `Goaway`, `PriorityUpdateRequest` or
/// `PriorityUpdateRequestPush` which handling is specific to the client and server, we must
/// give them to the specific client/server handler.
fn handle_control_frame(&mut self, f: HFrame) -> Res<Option<HFrame>> {
qinfo!([self], "Handle a control frame {:?}", f);
if !matches!(f, HFrame::Settings { .. })
@ -1266,11 +1528,7 @@ impl Http3Connection {
wt: &Rc<RefCell<WebTransportSession>>,
conn: &mut Connection,
) {
let out = wt.borrow_mut().take_sub_streams();
if out.is_none() {
return;
}
let (recv, send) = out.unwrap();
let (recv, send) = wt.borrow_mut().take_sub_streams();
for id in recv {
qtrace!("Remove the extended connect sub receiver stream {}", id);

View file

@ -21,10 +21,11 @@ use neqo_common::{
use neqo_crypto::{agent::CertificateInfo, AuthenticationStatus, ResumptionToken, SecretAgentInfo};
use neqo_qpack::Stats as QpackStats;
use neqo_transport::{
AppError, Connection, ConnectionEvent, ConnectionId, ConnectionIdGenerator, Output,
Stats as TransportStats, StreamId, StreamType, Version, ZeroRttState,
AppError, Connection, ConnectionEvent, ConnectionId, ConnectionIdGenerator, DatagramTracking,
Output, Stats as TransportStats, StreamId, StreamType, Version, ZeroRttState,
};
use std::cell::RefCell;
use std::convert::TryFrom;
use std::fmt::Debug;
use std::fmt::Display;
use std::mem;
@ -59,6 +60,231 @@ fn alpn_from_quic_version(version: Version) -> &'static str {
}
}
/// # The HTTP/3 client API
///
/// This module implements the HTTP/3 client API. The main implementation of the protocol is in
/// [connection.rs](https://github.com/mozilla/neqo/blob/main/neqo-http3/src/connection.rs) which
/// implements common behavior for the client-side and the server-side. `Http3Client` structure
/// implements the public API and set of functions that differ between the client and the server.
/// The API is used for:
/// - create and close an endpoint:
/// - [`new`](struct.Http3Client.html#method.new)
/// - [`new_with_conn`](struct.Http3Client.html#method.new_with_conn)
/// - [`close`](struct.Http3Client.html#method.close)
/// - configuring an endpoint:
/// - [`authenticated`](struct.Http3Client.html#method.authenticated)
/// - [`enable_ech`](struct.Http3Client.html#method.enable_ech)
/// - [`enable_resumption`](struct.Http3Client.html#method.enable_resumption)
/// - [`initiate_key_update`](struct.Http3Client.html#method.initiate_key_update)
/// - [`set_qlog`](struct.Http3Client.html#method.set_qlog)
/// - retrieving information about a connection:
/// - [`peer_certificate`](struct.Http3Client.html#method.peer_certificate)
/// - [`qpack_decoder_stats`](struct.Http3Client.html#method.qpack_decoder_stats)
/// - [`qpack_encoder_stats`](struct.Http3Client.html#method.qpack_encoder_stats)
/// - [`transport_stats`](struct.Http3Client.html#method.transport_stats)
/// - [`state`](struct.Http3Client.html#method.state)
/// - [`take_resumption_token`](struct.Http3Client.html#method.take_resumption_token)
/// - [`tls_inf`](struct.Http3Client.html#method.tls_info)
/// - driving HTTP/3 session:
/// - [`process_output`](struct.Http3Client.html#method.process_output)
/// - [`process_input`](struct.Http3Client.html#method.process_input)
/// - [`process`](struct.Http3Client.html#method.process)
/// - create requests, send/receive data, and cancel requests:
/// - [`fetch`](struct.Http3Client.html#method.fetch)
/// - [`send_data`](struct.Http3Client.html#method.send_data)
/// - [`read_dara`](struct.Http3Client.html#method.read_data)
/// - [`stream_close_send`](struct.Http3Client.html#method.stream_close_send)
/// - [`cancel_fetch`](struct.Http3Client.html#method.cancel_fetch)
/// - [`stream_reset_send`](struct.Http3Client.html#method.stream_reset_send)
/// - [`stream_stop_sending`](struct.Http3Client.html#method.stream_stop_sending)
/// - [`set_stream_max_data`](struct.Http3Client.html#method.set_stream_max_data)
/// - priority feature:
/// - [`priority_update`](struct.Http3Client.html#method.priority_update)
/// - `WebTransport` feature:
/// - [`webtransport_create_session`](struct.Http3Client.html#method.webtransport_create_session)
/// - [`webtransport_close_session`](struct.Http3Client.html#method.webtransport_close_session)
/// - [`webtransport_create_stream`](struct.Http3Client.html#method.webtransport_create_sstream)
/// - [`webtransport_enabled`](struct.Http3Client.html#method.webtransport_enabled)
///
/// ## Examples
///
/// ### Fetching a resource
///
/// ```ignore
/// let mut client = Http3Client::new(...);
///
/// // Perform a handshake
/// ...
///
/// let req = client
/// .fetch(
/// Instant::now(),
/// "GET",
/// &("https", "something.com", "/"),
/// &[Header::new("example1", "value1"), Header::new("example1", "value2")],
/// Priority::default(),
/// )
/// .unwrap();
///
/// client.stream_close_send(req).unwrap();
///
/// loop {
/// // exchange packets
/// ...
///
/// while let Some(event) = client.next_event() {
/// match event {
/// Http3ClientEvent::HeaderReady { stream_id, headers, interim, fin } => {
/// println!("New response headers received for stream {:?} [fin={?}, interim={:?}]: {:?}",
/// stream_id,
/// fin,
/// interim,
/// headers,
/// );
/// }
/// Http3ClientEvent::DataReadable { stream_id } => {
/// println!("New data available on stream {}", stream_id);
/// let mut buf = [0; 100];
/// let (amount, fin) = client.read_data(now(), stream_id, &mut buf).unwrap();
/// println!("Read {:?} bytes from stream {:?} [fin={?}]",
/// amount,
/// stream_id,
/// fin,
/// );
/// }
/// _ => {
/// println!("Unhandled event {:?}", event);
/// }
/// }
/// }
/// }
///```
///
/// ### Creating a `WebTransport` session
///
/// ```ignore
/// let mut client = Http3Client::new(...);
///
/// // Perform a handshake
/// ...
///
/// // Create a session
/// let wt_session_id = client
/// .webtransport_create_session(now(), &("https", "something.com", "/"), &[])
/// .unwrap();
///
/// loop {
/// // exchange packets
/// ...
///
/// while let Some(event) = client.next_event() {
/// match event {
/// Http3ClientEvent::WebTransport(WebTransportEvent::Session{
/// stream_id,
/// status
/// }) => {
/// println!("The response from the server: WebTransport session ID {:?} status={:?}",
/// stream_id,
/// status,
/// );
/// }
/// _ => {
/// println!("Unhandled event {:?}", event);
/// }
/// }
/// }
/// }
///
///```
///
/// ### `WebTransport`: create a stream, send and receive data on the stream
///
/// ```ignore
/// const BUF_CLIENT: &[u8] = &[0; 10];
/// // wt_session_id is the session ID of a newly created WebTransport session, see the example above.
///
/// // create a stream
/// let wt_stream_id = client
/// .webtransport_create_stream(wt_session_id, StreamType::BiDi)
/// .unwrap();
///
/// // send data
/// let data_sent = client.send_data(wt_stream_id, BUF_CLIENT).unwrap();
/// assert_eq!(data_sent, BUF_CLIENT.len());
///
/// // close stream for sending
/// client.stream_close_send(wt_stream_id).unwrap();
///
/// // wait for data from the server
/// loop {
/// // exchange packets
/// ...
///
/// while let Some(event) = client.next_event() {
/// match event {
/// Http3ClientEvent::DataReadable{ stream_id } => {
/// println!("Data receivedd form the server on WebTransport stream ID {:?}",
/// stream_id,
/// );
/// let mut buf = [0; 100];
/// let (amount, fin) = client.read_data(now(), stream_id, &mut buf).unwrap();
/// println!("Read {:?} bytes from stream {:?} [fin={?}]",
/// amount,
/// stream_id,
/// fin,
/// );
/// }
/// _ => {
/// println!("Unhandled event {:?}", event);
/// }
/// }
/// }
/// }
/// ```
///
/// ### `WebTransport`: receive a new stream form the server
///
/// ```ignore
/// // wt_session_id is the session ID of a newly created WebTransport session, see the example above.
///
/// // wait for a new stream from the server
/// loop {
/// // exchange packets
/// ...
///
/// while let Some(event) = client.next_event() {
/// match event {
/// Http3ClientEvent::WebTransport(WebTransportEvent::NewStream {
/// stream_id,
/// session_id,
/// }) => {
/// println!("New stream received on session{:?}, stream id={:?} stream type={:?}",
/// sesson_id.stream_id(),
/// stream_id.stream_id(),
/// stream_id.stream_type()
/// );
/// }
/// Http3ClientEvent::DataReadable{ stream_id } => {
/// println!("Data receivedd form the server on WebTransport stream ID {:?}",
/// stream_id,
/// );
/// let mut buf = [0; 100];
/// let (amount, fin) = client.read_data(now(), stream_id, &mut buf).unwrap();
/// println!("Read {:?} bytes from stream {:?} [fin={:?}]",
/// amount,
/// stream_id,
/// fin,
/// );
/// }
/// _ => {
/// println!("Unhandled event {:?}", event);
/// }
/// }
/// }
/// }
/// ```
///
pub struct Http3Client {
conn: Connection,
base_handler: Http3Connection,
@ -75,7 +301,7 @@ impl Display for Http3Client {
impl Http3Client {
/// # Errors
/// Making a `neqo-transport::connection` may produce an error. This can only be a crypto error if
/// the socket can't be created or configured.
/// the crypto context can't be created or configured.
pub fn new(
server_name: impl Into<String>,
cid_manager: Rc<RefCell<dyn ConnectionIdGenerator>>,
@ -103,6 +329,10 @@ impl Http3Client {
))
}
/// This is a similar function to `new`. In this case, `neqo-transport::connection` has been
/// already created.
///
/// It is recommended to use `new` instead.
#[must_use]
pub fn new_with_conn(c: Connection, http3_parameters: Http3Parameters) -> Self {
let events = Http3ClientEvents::default();
@ -125,6 +355,7 @@ impl Http3Client {
self.conn.role()
}
/// The function returns the current state of the connection.
#[must_use]
pub fn state(&self) -> Http3State {
self.base_handler.state()
@ -142,6 +373,10 @@ impl Http3Client {
}
/// This called when peer certificates have been verified.
///
/// `Http3ClientEvent::AuthenticationNeeded` event is emitted when peers certificates are
/// available and need to be verified. When the verification is completed this function is
/// called. To inform HTTP/3 session of the verification results.
pub fn authenticated(&mut self, status: AuthenticationStatus, now: Instant) {
self.conn.authenticated(status, now);
}
@ -175,12 +410,18 @@ impl Http3Client {
})
}
/// Get a resumption token. The correct way to obtain a resumption token is
/// waiting for the `Http3ClientEvent::ResumptionToken` event. However, some
/// servers don't send `NEW_TOKEN` frames and so that event might be slow in
/// arriving. This is especially a problem for short-lived connections, where
/// the connection is closed before any events are released. This retrieves
/// the token, without waiting for the `NEW_TOKEN` frame to arrive.
/// The correct way to obtain a resumption token is to wait for the
/// `Http3ClientEvent::ResumptionToken` event. To emit the event we are waiting for a
/// resumtion token and a `NEW_TOKEN` frame to arrive. Some servers don't send `NEW_TOKEN`
/// frames and in this case, we wait for 3xPTO before emitting an event. This is especially a
/// problem for short-lived connections, where the connection is closed before any events are
/// released. This function retrieves the token, without waiting for a `NEW_TOKEN` frame to
/// arrive.
///
/// In addition to the token, HTTP/3 settings are encoded into the token before giving it to
/// the application(`encode_resumption_token`). When the resumption token is supplied to a new
/// connection the HTTP/3 setting will be decoded and used until the setting are received from
/// the server.
pub fn take_resumption_token(&mut self, now: Instant) -> Option<ResumptionToken> {
self.conn
.take_resumption_token(now)
@ -188,6 +429,10 @@ impl Http3Client {
}
/// This may be call if an application has a resumption token. This must be called before connection starts.
///
/// The resumption token also contains encoded HTTP/3 settings. The settings will be decoded
/// and used until the setting are received from the server.
///
/// # Errors
/// An error is return if token cannot be decoded or a connection is is a wrong state.
/// # Panics
@ -201,7 +446,7 @@ impl Http3Client {
Some(v) => v,
None => return Err(Error::InvalidResumptionToken),
};
qtrace!([self], " settings {}", hex_with_len(&settings_slice));
qtrace!([self], " settings {}", hex_with_len(settings_slice));
let mut dec_settings = Decoder::from(settings_slice);
let mut settings = HSettings::default();
Error::map_error(
@ -209,7 +454,7 @@ impl Http3Client {
Error::InvalidResumptionToken,
)?;
let tok = dec.decode_remainder();
qtrace!([self], " Transport token {}", hex(&tok));
qtrace!([self], " Transport token {}", hex(tok));
self.conn.enable_resumption(now, tok)?;
if self.conn.state().closed() {
let state = self.conn.state().clone();
@ -260,8 +505,9 @@ impl Http3Client {
// API: Request/response
/// This is call to make a new http request. Each request can have headers and they are added when request
/// is created. A response body may be added by calling `send_data`.
/// The function fetches a resource using `method`, `target` and `headers`. A response body
/// may be added by calling `send_data`. `stream_close_send` must be sent to finish the request
/// even if request data are not sent.
/// # Errors
/// If a new stream cannot be created an error will be return.
/// # Panics
@ -308,8 +554,8 @@ impl Http3Client {
self.base_handler.queue_update_priority(stream_id, priority)
}
/// An application may reset a stream(request).
/// Both sides, sending and receiving side, will be closed.
/// An application may cancel a stream(request).
/// Both sides, the receiviing and sending side, 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) -> Res<()> {
@ -343,7 +589,10 @@ impl Http3Client {
.stream_stop_sending(&mut self.conn, stream_id, error)
}
/// To supply a request body this function is called (headers are supplied through the `fetch` function.)
/// This function is used for regular HTTP requests and `WebTransport` streams.
/// In the case of regular HTTP requests, the request body is supplied using this function, and
/// headers are supplied through the `fetch` function.
///
/// # Errors
/// `InvalidStreamId` if the stream does not exist,
/// `AlreadyClosed` if the stream has already been closed.
@ -478,6 +727,35 @@ impl Http3Client {
)
}
/// Send `WebTransport` datagram.
/// # Errors
/// It may return `InvalidStreamId` if a stream does not exist anymore.
/// The function returns `TooMuchData` if the supply buffer is bigger than
/// the allowed remote datagram size.
pub fn webtransport_send_datagram(
&mut self,
session_id: StreamId,
buf: &[u8],
id: impl Into<DatagramTracking>,
) -> Res<()> {
qtrace!("webtransport_send_datagram session:{:?}", session_id);
self.base_handler
.webtransport_send_datagram(session_id, &mut self.conn, buf, id)
}
/// Returns the current max size of a datagram that can fit into a packet.
/// The value will change over time depending on the encoded size of the
/// packet number, ack frames, etc.
/// # Errors
/// The function returns `NotAvailable` if datagrams are not enabled.
/// # Panics
/// This cannot panic. The max varint length is 8.
pub fn webtransport_max_datagram_size(&self, session_id: StreamId) -> Res<u64> {
Ok(self.conn.max_datagram_size()?
- u64::try_from(Encoder::varint_len(session_id.as_u64())).unwrap())
}
/// This function combines `process_input` and `process_output` function.
pub fn process(&mut self, dgram: Option<Datagram>, now: Instant) -> Output {
qtrace!([self], "Process.");
if let Some(d) = dgram {
@ -486,19 +764,34 @@ impl Http3Client {
self.process_output(now)
}
/// Supply an incoming QUIC packet.
/// The function should be called when there is a new UDP packet available. The function will
/// handle the packet payload.
///
/// First, the payload will be handled by the QUIC layer. Afterward, `process_http3` will be
/// called to handle new [`ConnectionEvent`][1]s.
///
/// After this function is called `process_output` should be called to check whether new
/// packets need to be sent or if a timer needs to be updated.
///
/// [1]: ../neqo_transport/enum.ConnectionEvent.html
pub fn process_input(&mut self, dgram: Datagram, now: Instant) {
qtrace!([self], "Process input.");
self.conn.process_input(dgram, now);
self.process_http3(now);
}
// Only used by neqo-interop
/// This should not be used because it gives access to functionalities that may disrupt the
/// proper functioning of the HTTP/3 session.
/// Only used by `neqo-interop`.
pub fn conn(&mut self) -> &mut Connection {
&mut self.conn
}
/// Process HTTP3 layer.
/// When `process_output`, `process_input`, or `process` is called we must call this function
/// as well. The functions calls `Http3Client::check_connection_events` to handle events from
/// the QUC layer and calls `Http3Connection::process_sending` to ensure that HTTP/3 layer
/// data, e.g. control frames, are sent.
fn process_http3(&mut self, now: Instant) {
qtrace!([self], "Process http3 internal.");
match self.base_handler.state() {
@ -521,8 +814,32 @@ impl Http3Client {
}
}
/// Get packet that need to be written into a UDP socket or a timer value if there is no data to send.
/// This function should be called repeatedly until timer value is returned.
/// The function should be called to check if there is a new UDP packet to be sent. It should
/// be called after a new packet is received and processed and after a timer expires (QUIC
/// needs timers to handle events like PTO detection and timers are not implemented by the neqo
/// library, but instead must be driven by the application).
///
/// `process_output` can return:
/// - a [`Output::Datagram(Datagram)`][1]: data that should be sent as a UDP payload,
/// - a [`Output::Callback(Duration)`][1]: the duration of a timer. `process_output` should be called at least after the time expires,
/// - [`Output::None`][1]: this is returned when `Nttp3Client` is done and can be destroyed.
///
/// The application should call this function repeatedly until a timer value or None is
/// returned. After that, the application should call the function again if a new UDP packet is
/// received and processed or the timer value expires.
///
/// The HTTP/3 neqo implementation drives the HTTP/3 and QUC layers, therefore this function
/// will call both layers:
/// - First it calls HTTP/3 layer processing (`process_http3`) to make sure the layer writes
/// data to QUIC layer or cancels streams if needed.
/// - Then QUIC layer processing is called - [`Connection::process_output`][3]. This produces a
/// packet or a timer value. It may also produce ned [`ConnectionEvent`][2]s, e.g. connection
/// state-change event.
/// - Therefore the HTTP/3 layer processing (`process_http3`) is called again.
///
/// [1]: ../neqo_transport/enum.Output.html
/// [2]: ../neqo_transport/struct.ConnectionEvents.html
/// [3]: ../neqo_transport/struct.Connection.html#method.process_output
pub fn process_output(&mut self, now: Instant) -> Output {
qtrace!([self], "Process output.");
@ -537,8 +854,8 @@ impl Http3Client {
out
}
// This function takes the provided result and check for an error.
// An error results in closing the connection.
/// This function takes the provided result and check for an error.
/// An error results in closing the connection.
fn check_result<ERR>(&mut self, now: Instant, res: &Res<ERR>) -> bool {
match &res {
Err(Error::HttpGoaway) => {
@ -559,13 +876,31 @@ impl Http3Client {
}
}
// If this return an error the connection must be closed.
/// This function checks [`ConnectionEvent`][2]s emitted by the QUIC layer, e.g. connection change
/// state events, new incoming stream data is available, a stream is was reset, etc. The HTTP/3
/// layer needs to handle these events. Most of the events are handled by
/// [`Http3Connection`][1] by calling appropriate functions, e.g. `handle_state_change`,
/// `handle_stream_reset`, etc. [`Http3Connection`][1] handle functionalities that are common
/// for the client and server side. Some of the functionalities are specific to the client and
/// they are handled by `Http3Client`. For example, [`ConnectionEvent::RecvStreamReadable`][3] event
/// is handled by `Http3Client::handle_stream_readable`. The function calls
/// `Http3Connection::handle_stream_readable` and then hands the return value as appropriate
/// for the client-side.
///
/// [1]: https://github.com/mozilla/neqo/blob/main/neqo-http3/src/connection.rs
/// [2]: ../neqo_transport/enum.ConnectionEvent.html
/// [3]: ../neqo_transport/enum.ConnectionEvent.html#variant.RecvStreamReadable
fn check_connection_events(&mut self) -> Res<()> {
qtrace!([self], "Check connection events.");
while let Some(e) = self.conn.next_event() {
qdebug!([self], "check_connection_events - event {:?}.", e);
match e {
ConnectionEvent::NewStream { stream_id } => {
// During this event we only add a new stream to the Http3Connection stream list,
// with NewStreamHeadReader stream handler.
// This function will not read from the stream and try to decode the stream.
// RecvStreamReadable will be emitted after this event and reading, i.e. decoding
// of a stream will happen during that event.
self.base_handler.add_new_stream(stream_id);
}
ConnectionEvent::SendStreamWritable { stream_id } => {
@ -617,8 +952,10 @@ impl Http3Client {
self.events.resumption_token(t);
}
}
ConnectionEvent::Datagram(dgram) => {
self.base_handler.handle_datagram(&dgram);
}
ConnectionEvent::SendStreamComplete { .. }
| ConnectionEvent::Datagram { .. }
| ConnectionEvent::OutgoingDatagramOutcome { .. }
| ConnectionEvent::IncomingDatagramDropped => {}
}
@ -626,6 +963,25 @@ impl Http3Client {
Ok(())
}
/// This function handled new data available on a stream. It calls
/// `Http3Client::handle_stream_readable` and handles its response. Reading streams are mostly
/// handled by [`Http3Connection`][1] because most part of it is common for the client and
/// server. The following actions need to be handled by the client-specific code:
/// - `ReceiveOutput::NewStream(NewStreamType::Push(_))` - the server cannot receive a push
/// stream,
/// - `ReceiveOutput::NewStream(NewStreamType::Http)` - client cannot receive a
/// server-initiated HTTP request,
/// - `ReceiveOutput::NewStream(NewStreamType::WebTransportStream(_))` - because
/// `Http3ClientEvents`is needed and events handler is specific to the client.
/// - `ReceiveOutput::ControlFrames(control_frames)` - some control frame handling differs
/// between the client and the server:
/// - `HFrame::CancelPush` - only the client-side may receive it,
/// - `HFrame::MaxPushId { .. }`, `HFrame::PriorityUpdateRequest { .. } ` and
/// `HFrame::PriorityUpdatePush` can only be receive on the server side,
/// - `HFrame::Goaway { stream_id }` needs specific handling by the client by the protocol
/// specification.
///
/// [1]: https://github.com/mozilla/neqo/blob/main/neqo-http3/src/connection.rs
fn handle_stream_readable(&mut self, stream_id: StreamId) -> Res<()> {
match self
.base_handler
@ -4326,7 +4682,7 @@ mod tests {
&mut client,
&mut server,
request_stream_id,
&[0x0, 0x3, 0x61, 0x62, 0x63], // a data frame
[0x0, 0x3, 0x61, 0x62, 0x63], // a data frame
false,
);
@ -4351,7 +4707,7 @@ mod tests {
&mut client,
&mut server,
request_stream_id,
&[0x0, 0x0],
[0x0, 0x0],
true,
);

View file

@ -4,7 +4,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use crate::connection::{Http3Connection, Http3State};
use crate::connection::{Http3Connection, Http3State, WebTransportSessionAcceptAction};
use crate::frames::HFrame;
use crate::recv_message::{RecvMessage, RecvMessageInfo};
use crate::send_message::SendMessage;
@ -14,7 +14,9 @@ use crate::{
ReceiveOutput, Res,
};
use neqo_common::{event::Provider, qdebug, qinfo, qtrace, Header, MessageType, Role};
use neqo_transport::{AppError, Connection, ConnectionEvent, StreamId, StreamType};
use neqo_transport::{
AppError, Connection, ConnectionEvent, DatagramTracking, StreamId, StreamType,
};
use std::rc::Rc;
use std::time::Instant;
@ -140,7 +142,7 @@ impl Http3ServerHandler {
&mut self,
conn: &mut Connection,
stream_id: StreamId,
accept: bool,
accept: &WebTransportSessionAcceptAction,
) -> Res<()> {
self.needs_processing = true;
self.base_handler.webtransport_session_accept(
@ -185,6 +187,18 @@ impl Http3ServerHandler {
)
}
pub fn webtransport_send_datagram(
&mut self,
conn: &mut Connection,
session_id: StreamId,
buf: &[u8],
id: impl Into<DatagramTracking>,
) -> Res<()> {
self.needs_processing = true;
self.base_handler
.webtransport_send_datagram(session_id, conn, buf, id)
}
/// Process HTTTP3 layer.
pub fn process_http3(&mut self, conn: &mut Connection, now: Instant) {
qtrace!([self], "Process http3 internal.");
@ -273,13 +287,13 @@ impl Http3ServerHandler {
s.stream_writable();
}
}
ConnectionEvent::Datagram(dgram) => self.base_handler.handle_datagram(&dgram),
ConnectionEvent::AuthenticationNeeded
| ConnectionEvent::EchFallbackAuthenticationNeeded { .. }
| ConnectionEvent::ZeroRttRejected
| ConnectionEvent::ResumptionToken(..) => return Err(Error::HttpInternal(4)),
ConnectionEvent::SendStreamComplete { .. }
| ConnectionEvent::SendStreamCreatable { .. }
| ConnectionEvent::Datagram { .. }
| ConnectionEvent::OutgoingDatagramOutcome { .. }
| ConnectionEvent::IncomingDatagramDropped => {}
}

View file

@ -6,8 +6,8 @@
#![allow(clippy::module_name_repetitions)]
pub mod webtransport_session;
pub mod webtransport_streams;
pub(crate) mod webtransport_session;
pub(crate) mod webtransport_streams;
use crate::client_events::Http3ClientEvents;
use crate::features::NegotiationState;
@ -15,7 +15,7 @@ use crate::settings::{HSettingType, HSettings};
use crate::{CloseType, Http3StreamInfo, Http3StreamType};
use neqo_transport::{AppError, StreamId};
use std::fmt::Debug;
pub use webtransport_session::WebTransportSession;
pub(crate) use webtransport_session::WebTransportSession;
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum SessionCloseReason {
@ -32,13 +32,13 @@ impl From<CloseType> for SessionCloseReason {
}
CloseType::Done => SessionCloseReason::Clean {
error: 0,
message: "".to_string(),
message: String::new(),
},
}
}
}
pub trait ExtendedConnectEvents: Debug {
pub(crate) trait ExtendedConnectEvents: Debug {
fn session_start(&self, connect_type: ExtendedConnectType, stream_id: StreamId, status: u16);
fn session_end(
&self,
@ -47,10 +47,11 @@ pub trait ExtendedConnectEvents: Debug {
reason: SessionCloseReason,
);
fn extended_connect_new_stream(&self, stream_info: Http3StreamInfo);
fn new_datagram(&self, session_id: StreamId, datagram: Vec<u8>);
}
#[derive(Debug, PartialEq, Copy, Clone, Eq)]
pub enum ExtendedConnectType {
pub(crate) enum ExtendedConnectType {
WebTransport,
}
@ -61,12 +62,6 @@ impl ExtendedConnectType {
"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 {
@ -82,7 +77,7 @@ impl From<ExtendedConnectType> for HSettingType {
}
#[derive(Debug)]
pub struct ExtendedConnectFeature {
pub(crate) struct ExtendedConnectFeature {
feature_negotiation: NegotiationState,
}
@ -107,3 +102,5 @@ impl ExtendedConnectFeature {
self.feature_negotiation.enabled()
}
}
#[cfg(test)]
mod tests;

View file

@ -0,0 +1,128 @@
// 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::features::extended_connect::tests::webtransport::{
wt_default_parameters, WtTest, DATAGRAM_SIZE,
};
use crate::{Error, Http3Parameters};
use neqo_common::Encoder;
use neqo_transport::Error as TransportError;
use std::convert::TryFrom;
const DGRAM: &[u8] = &[0, 100];
#[test]
fn no_datagrams() {
let mut wt = WtTest::new_with_params(
Http3Parameters::default().webtransport(true),
Http3Parameters::default().webtransport(true),
);
let mut wt_session = wt.create_wt_session();
assert_eq!(
wt_session.max_datagram_size(),
Err(Error::TransportError(TransportError::NotAvailable))
);
assert_eq!(
wt.max_datagram_size(wt_session.stream_id()),
Err(Error::TransportError(TransportError::NotAvailable))
);
assert_eq!(
wt_session.send_datagram(DGRAM, None),
Err(Error::TransportError(TransportError::TooMuchData))
);
assert_eq!(
wt.send_datagram(wt_session.stream_id(), DGRAM),
Err(Error::TransportError(TransportError::TooMuchData))
);
wt.exchange_packets();
wt.check_no_datagram_received_client();
wt.check_no_datagram_received_server();
}
#[test]
fn datagrams() {
let mut wt = WtTest::new();
let mut wt_session = wt.create_wt_session();
assert_eq!(
wt_session.max_datagram_size(),
Ok(DATAGRAM_SIZE
- u64::try_from(Encoder::varint_len(wt_session.stream_id().as_u64())).unwrap())
);
assert_eq!(
wt.max_datagram_size(wt_session.stream_id()),
Ok(DATAGRAM_SIZE
- u64::try_from(Encoder::varint_len(wt_session.stream_id().as_u64())).unwrap())
);
assert_eq!(wt_session.send_datagram(DGRAM, None), Ok(()));
assert_eq!(wt.send_datagram(wt_session.stream_id(), DGRAM), Ok(()));
wt.exchange_packets();
wt.check_datagram_received_client(wt_session.stream_id(), DGRAM);
wt.check_datagram_received_server(&wt_session, DGRAM);
}
#[test]
fn datagrams_server_only() {
let mut wt = WtTest::new_with_params(
Http3Parameters::default().webtransport(true),
wt_default_parameters(),
);
let mut wt_session = wt.create_wt_session();
assert_eq!(
wt_session.max_datagram_size(),
Err(Error::TransportError(TransportError::NotAvailable))
);
assert_eq!(
wt.max_datagram_size(wt_session.stream_id()),
Ok(DATAGRAM_SIZE
- u64::try_from(Encoder::varint_len(wt_session.stream_id().as_u64())).unwrap())
);
assert_eq!(
wt_session.send_datagram(DGRAM, None),
Err(Error::TransportError(TransportError::TooMuchData))
);
assert_eq!(wt.send_datagram(wt_session.stream_id(), DGRAM), Ok(()));
wt.exchange_packets();
wt.check_datagram_received_server(&wt_session, DGRAM);
wt.check_no_datagram_received_client();
}
#[test]
fn datagrams_client_only() {
let mut wt = WtTest::new_with_params(
wt_default_parameters(),
Http3Parameters::default().webtransport(true),
);
let mut wt_session = wt.create_wt_session();
assert_eq!(
wt_session.max_datagram_size(),
Ok(DATAGRAM_SIZE
- u64::try_from(Encoder::varint_len(wt_session.stream_id().as_u64())).unwrap())
);
assert_eq!(
wt.max_datagram_size(wt_session.stream_id()),
Err(Error::TransportError(TransportError::NotAvailable))
);
assert_eq!(wt_session.send_datagram(DGRAM, None), Ok(()));
assert_eq!(
wt.send_datagram(wt_session.stream_id(), DGRAM),
Err(Error::TransportError(TransportError::TooMuchData))
);
wt.exchange_packets();
wt.check_datagram_received_client(wt_session.stream_id(), DGRAM);
wt.check_no_datagram_received_server();
}

View file

@ -4,46 +4,57 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
mod datagrams;
mod negotiation;
mod sessions;
mod streams;
use neqo_common::event::Provider;
use neqo_crypto::AuthenticationStatus;
use neqo_http3::{
use crate::{
features::extended_connect::SessionCloseReason, Error, Http3Client, Http3ClientEvent,
Http3OrWebTransportStream, Http3Parameters, Http3Server, Http3ServerEvent, Http3State,
WebTransportEvent, WebTransportRequest, WebTransportServerEvent,
WebTransportSessionAcceptAction,
};
use neqo_transport::{StreamId, StreamType};
use neqo_crypto::AuthenticationStatus;
use neqo_transport::{ConnectionParameters, 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 {
const DATAGRAM_SIZE: u64 = 1200;
pub fn wt_default_parameters() -> Http3Parameters {
Http3Parameters::default()
.webtransport(true)
.connection_parameters(ConnectionParameters::default().datagram_size(DATAGRAM_SIZE))
}
pub fn default_http3_client(client_params: Http3Parameters) -> Http3Client {
fixture_init();
Http3Client::new(
DEFAULT_SERVER_NAME,
Rc::new(RefCell::new(CountingConnectionIdGenerator::default())),
addr(),
addr(),
Http3Parameters::default().webtransport(webtransport),
client_params,
now(),
)
.expect("create a default client")
}
pub fn default_http3_server(webtransport: bool) -> Http3Server {
pub fn default_http3_server(server_params: Http3Parameters) -> Http3Server {
Http3Server::new(
now(),
DEFAULT_KEYS,
DEFAULT_ALPN_H3,
anti_replay(),
Rc::new(RefCell::new(CountingConnectionIdGenerator::default())),
Http3Parameters::default().webtransport(webtransport),
server_params,
None,
)
.expect("create a server")
@ -86,12 +97,15 @@ fn connect_with(client: &mut Http3Client, server: &mut Http3Server) {
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());
std::mem::drop(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);
fn connect(
client_params: Http3Parameters,
server_params: Http3Parameters,
) -> (Http3Client, Http3Server) {
let mut client = default_http3_client(client_params);
let mut server = default_http3_server(server_params);
connect_with(&mut client, &mut server);
(client, server)
}
@ -103,7 +117,12 @@ struct WtTest {
impl WtTest {
pub fn new() -> Self {
let (client, server) = connect(true, true);
let (client, server) = connect(wt_default_parameters(), wt_default_parameters());
Self { client, server }
}
pub fn new_with_params(client_params: Http3Parameters, server_params: Http3Parameters) -> Self {
let (client, server) = connect(client_params, server_params);
Self { client, server }
}
@ -111,7 +130,10 @@ impl WtTest {
connect_with(&mut client, &mut server);
Self { client, server }
}
fn negotiate_wt_session(&mut self, accept: bool) -> (StreamId, Option<WebTransportRequest>) {
fn negotiate_wt_session(
&mut self,
accept: &WebTransportSessionAcceptAction,
) -> (StreamId, Option<WebTransportRequest>) {
let wt_session_id = self
.client
.webtransport_create_session(now(), &("https", "something.com", "/"), &[])
@ -148,7 +170,8 @@ impl WtTest {
}
fn create_wt_session(&mut self) -> WebTransportRequest {
let (wt_session_id, wt_server_session) = self.negotiate_wt_session(true);
let (wt_session_id, wt_server_session) =
self.negotiate_wt_session(&WebTransportSessionAcceptAction::Accept);
let wt_session_negotiated_event = |e| {
matches!(
e,
@ -202,12 +225,12 @@ impl WtTest {
pub fn check_session_closed_event_client(
&mut self,
wt_session_id: StreamId,
expected_reason: SessionCloseReason,
expected_reason: &SessionCloseReason,
) {
let mut event_found = false;
while let Some(event) = self.client.next_event() {
event_found = WtTest::session_closed_client(&event, wt_session_id, &expected_reason);
event_found = WtTest::session_closed_client(&event, wt_session_id, expected_reason);
if event_found {
break;
}
@ -239,13 +262,13 @@ impl WtTest {
pub fn check_session_closed_event_server(
&mut self,
wt_session: &mut WebTransportRequest,
expected_reeason: SessionCloseReason,
expected_reeason: &SessionCloseReason,
) {
let event = self.server.next_event().unwrap();
assert!(WtTest::session_closed_server(
&event,
wt_session.stream_id(),
&expected_reeason
expected_reeason
));
}
@ -353,7 +376,7 @@ impl WtTest {
expected_stop_sending_ids: &[StreamId],
expected_error_stream_stop_sending: Option<u64>,
expected_local: bool,
expected_session_close: Option<(StreamId, SessionCloseReason)>,
expected_session_close: &Option<(StreamId, SessionCloseReason)>,
) {
let mut reset_ids_count = 0;
let mut stop_sending_ids_count = 0;
@ -392,7 +415,6 @@ impl WtTest {
}
fn create_wt_stream_server(
&mut self,
wt_server_session: &mut WebTransportRequest,
stream_type: StreamType,
) -> Http3OrWebTransportStream {
@ -494,7 +516,7 @@ impl WtTest {
expected_error_stream_reset: Option<u64>,
expected_stop_sending_ids: &[StreamId],
expected_error_stream_stop_sending: Option<u64>,
expected_session_close: Option<(StreamId, SessionCloseReason)>,
expected_session_close: &Option<(StreamId, SessionCloseReason)>,
) {
let mut reset_ids_count = 0;
let mut stop_sending_ids_count = 0;
@ -537,11 +559,72 @@ impl WtTest {
}
pub fn session_close_frame_server(
&mut self,
wt_session: &mut WebTransportRequest,
error: u32,
message: &str,
) {
wt_session.close_session(error, message).unwrap();
}
fn max_datagram_size(&self, stream_id: StreamId) -> Result<u64, Error> {
self.client.webtransport_max_datagram_size(stream_id)
}
fn send_datagram(&mut self, stream_id: StreamId, buf: &[u8]) -> Result<(), Error> {
self.client.webtransport_send_datagram(stream_id, buf, None)
}
fn check_datagram_received_client(
&mut self,
expected_stream_id: StreamId,
expected_dgram: &[u8],
) {
let wt_datagram_event = |e| {
matches!(
e,
Http3ClientEvent::WebTransport(WebTransportEvent::Datagram {
session_id,
datagram
}) if session_id == expected_stream_id && datagram == expected_dgram
)
};
assert!(self.client.events().any(wt_datagram_event));
}
fn check_datagram_received_server(
&mut self,
expected_session: &WebTransportRequest,
expected_dgram: &[u8],
) {
let wt_datagram_event = |e| {
matches!(
e,
Http3ServerEvent::WebTransport(WebTransportServerEvent::Datagram {
session,
datagram
}) if session.stream_id() == expected_session.stream_id() && datagram == expected_dgram
)
};
assert!(self.server.events().any(wt_datagram_event));
}
fn check_no_datagram_received_client(&mut self) {
let wt_datagram_event = |e| {
matches!(
e,
Http3ClientEvent::WebTransport(WebTransportEvent::Datagram { .. })
)
};
assert!(!self.client.events().any(wt_datagram_event));
}
fn check_no_datagram_received_server(&mut self) {
let wt_datagram_event = |e| {
matches!(
e,
Http3ServerEvent::WebTransport(WebTransportServerEvent::Datagram { .. })
)
};
assert!(!self.server.events().any(wt_datagram_event));
}
}

View file

@ -0,0 +1,290 @@
// 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 crate::{
settings::{HSetting, HSettingType, HSettings},
Error, HFrame, Http3Client, Http3ClientEvent, Http3Parameters, Http3Server, Http3State,
WebTransportEvent,
};
use neqo_common::{event::Provider, Encoder};
use neqo_crypto::AuthenticationStatus;
use neqo_transport::{Connection, ConnectionError, StreamType};
use std::time::Duration;
use test_fixture::{default_server_h3, now};
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);
}
}
fn connect_wt(wt_enabled_client: bool, wt_enabled_server: bool) -> (Http3Client, Http3Server) {
connect(
Http3Parameters::default().webtransport(wt_enabled_client),
Http3Parameters::default().webtransport(wt_enabled_server),
)
}
#[test]
fn negotiate_wt() {
let (mut client, _server) = connect_wt(true, true);
assert!(client.webtransport_enabled());
check_wt_event(&mut client, true, true);
let (mut client, _server) = connect_wt(true, false);
assert!(!client.webtransport_enabled());
check_wt_event(&mut client, true, false);
let (mut client, _server) = connect_wt(false, true);
assert!(!client.webtransport_enabled());
check_wt_event(&mut client, false, true);
let (mut client, _server) = connect_wt(false, false);
assert!(!client.webtransport_enabled());
check_wt_event(&mut client, false, false);
}
#[derive(PartialEq, Eq)]
enum ClientState {
ClientEnabled,
ClientDisabled,
}
#[derive(PartialEq, Eq)]
enum ClientResumedState {
ClientResumed,
ClientSuspended,
}
#[derive(PartialEq, Eq)]
enum ServerState {
ServerEnabled,
ServerDisabled,
}
#[derive(PartialEq, Eq)]
enum ServerResumedState {
ServerResumed,
ServerSuspended,
}
fn zero_rtt(
client_state: &ClientState,
server_state: &ServerState,
client_resumed: &ClientResumedState,
server_resumed: &ServerResumedState,
) {
let (mut client, mut server) = connect_wt(
ClientState::ClientEnabled.eq(client_state),
ServerState::ServerEnabled.eq(server_state),
);
assert_eq!(
client.webtransport_enabled(),
ClientState::ClientEnabled.eq(client_state) && ServerState::ServerEnabled.eq(server_state)
);
// 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.
std::mem::drop(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(
Http3Parameters::default()
.webtransport(ClientResumedState::ClientResumed.eq(client_resumed)),
);
let mut server = default_http3_server(
Http3Parameters::default()
.webtransport(ServerResumedState::ServerResumed.eq(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(),
ClientResumedState::ClientResumed.eq(client_resumed)
&& ServerResumedState::ServerResumed.eq(server_resumed)
);
check_wt_event(
&mut client,
ClientResumedState::ClientResumed.eq(client_resumed),
ServerResumedState::ServerResumed.eq(server_resumed),
);
}
#[test]
fn zero_rtt_wt_settings() {
zero_rtt(
&ClientState::ClientEnabled,
&ServerState::ServerEnabled,
&ClientResumedState::ClientResumed,
&ServerResumedState::ServerResumed,
);
zero_rtt(
&ClientState::ClientEnabled,
&ServerState::ServerEnabled,
&ClientResumedState::ClientResumed,
&ServerResumedState::ServerSuspended,
);
zero_rtt(
&ClientState::ClientEnabled,
&ServerState::ServerEnabled,
&ClientResumedState::ClientSuspended,
&ServerResumedState::ServerResumed,
);
zero_rtt(
&ClientState::ClientEnabled,
&ServerState::ServerEnabled,
&ClientResumedState::ClientSuspended,
&ServerResumedState::ServerSuspended,
);
zero_rtt(
&ClientState::ClientEnabled,
&ServerState::ServerDisabled,
&ClientResumedState::ClientResumed,
&ServerResumedState::ServerSuspended,
);
zero_rtt(
&ClientState::ClientEnabled,
&ServerState::ServerDisabled,
&ClientResumedState::ClientResumed,
&ServerResumedState::ServerResumed,
);
zero_rtt(
&ClientState::ClientEnabled,
&ServerState::ServerDisabled,
&ClientResumedState::ClientSuspended,
&ServerResumedState::ServerSuspended,
);
zero_rtt(
&ClientState::ClientEnabled,
&ServerState::ServerDisabled,
&ClientResumedState::ClientSuspended,
&ServerResumedState::ServerResumed,
);
zero_rtt(
&ClientState::ClientDisabled,
&ServerState::ServerDisabled,
&ClientResumedState::ClientSuspended,
&ServerResumedState::ServerSuspended,
);
zero_rtt(
&ClientState::ClientDisabled,
&ServerState::ServerDisabled,
&ClientResumedState::ClientSuspended,
&ServerResumedState::ServerResumed,
);
zero_rtt(
&ClientState::ClientDisabled,
&ServerState::ServerDisabled,
&ClientResumedState::ClientResumed,
&ServerResumedState::ServerSuspended,
);
zero_rtt(
&ClientState::ClientDisabled,
&ServerState::ServerDisabled,
&ClientResumedState::ClientResumed,
&ServerResumedState::ServerResumed,
);
zero_rtt(
&ClientState::ClientDisabled,
&ServerState::ServerEnabled,
&ClientResumedState::ClientSuspended,
&ServerResumedState::ServerResumed,
);
zero_rtt(
&ClientState::ClientDisabled,
&ServerState::ServerEnabled,
&ClientResumedState::ClientSuspended,
&ServerResumedState::ServerSuspended,
);
zero_rtt(
&ClientState::ClientDisabled,
&ServerState::ServerEnabled,
&ClientResumedState::ClientResumed,
&ServerResumedState::ServerSuspended,
);
zero_rtt(
&ClientState::ClientDisabled,
&ServerState::ServerEnabled,
&ClientResumedState::ClientResumed,
&ServerResumedState::ServerResumed,
);
}
fn exchange_packets2(client: &mut Http3Client, server: &mut Connection) {
let mut out = None;
loop {
out = client.process(out, now()).dgram();
out = server.process(out, now()).dgram();
if out.is_none() {
break;
}
}
}
#[test]
fn wrong_setting_value() {
const CONTROL_STREAM_TYPE: &[u8] = &[0x0];
let mut client = default_http3_client(Http3Parameters::default());
let mut server = default_server_h3();
exchange_packets2(&mut client, &mut server);
client.authenticated(AuthenticationStatus::Ok, now());
exchange_packets2(&mut client, &mut server);
let control = server.stream_create(StreamType::UniDi).unwrap();
server.stream_send(control, CONTROL_STREAM_TYPE).unwrap();
// Encode a settings frame and send it.
let mut enc = Encoder::default();
let settings = HFrame::Settings {
settings: HSettings::new(&[HSetting::new(HSettingType::EnableWebTransport, 2)]),
};
settings.encode(&mut enc);
assert_eq!(
server.stream_send(control, enc.as_ref()).unwrap(),
enc.as_ref().len()
);
exchange_packets2(&mut client, &mut server);
match client.state() {
Http3State::Closing(err) | Http3State::Closed(err) => {
assert_eq!(
err,
ConnectionError::Application(Error::HttpSettings.code())
);
}
_ => panic!("Wrong state {:?}", client.state()),
};
}

View file

@ -4,16 +4,18 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use crate::webtransport::{default_http3_server, WtTest};
use neqo_common::{event::Provider, Encoder};
use neqo_http3::{
features::extended_connect::SessionCloseReason, frames::WebTransportFrame, Error, Header,
Http3ClientEvent, Http3OrWebTransportStream, Http3Parameters, Http3Server, Http3ServerEvent,
Http3State, Priority, WebTransportEvent, WebTransportServerEvent,
use crate::features::extended_connect::tests::webtransport::{
default_http3_client, default_http3_server, wt_default_parameters, WtTest,
};
use neqo_transport::{ConnectionParameters, StreamType};
use crate::{
features::extended_connect::SessionCloseReason, frames::WebTransportFrame, Error, Header,
Http3ClientEvent, Http3OrWebTransportStream, Http3Server, Http3ServerEvent, Http3State,
Priority, WebTransportEvent, WebTransportServerEvent, WebTransportSessionAcceptAction,
};
use neqo_common::{event::Provider, Encoder};
use neqo_transport::StreamType;
use std::mem;
use test_fixture::{http3_client_with_params, now};
use test_fixture::now;
#[test]
fn wt_session() {
@ -24,9 +26,11 @@ fn wt_session() {
#[test]
fn wt_session_reject() {
let mut wt = WtTest::new();
let (wt_session_id, _wt_session) = wt.negotiate_wt_session(false);
let accept_res =
WebTransportSessionAcceptAction::Reject([Header::new(":status", "404")].to_vec());
let (wt_session_id, _wt_session) = wt.negotiate_wt_session(&accept_res);
wt.check_session_closed_event_client(wt_session_id, SessionCloseReason::Status(404));
wt.check_session_closed_event_client(wt_session_id, &SessionCloseReason::Status(404));
}
#[test]
@ -37,7 +41,7 @@ fn wt_session_close_client() {
wt.cancel_session_client(wt_session.stream_id());
wt.check_session_closed_event_server(
&mut wt_session,
SessionCloseReason::Error(Error::HttpNoError.code()),
&SessionCloseReason::Error(Error::HttpNoError.code()),
);
}
@ -49,7 +53,7 @@ fn wt_session_close_server() {
wt.cancel_session_server(&mut wt_session);
wt.check_session_closed_event_client(
wt_session.stream_id(),
SessionCloseReason::Error(Error::HttpNoError.code()),
&SessionCloseReason::Error(Error::HttpNoError.code()),
);
}
@ -62,9 +66,9 @@ fn wt_session_close_server_close_send() {
wt.exchange_packets();
wt.check_session_closed_event_client(
wt_session.stream_id(),
SessionCloseReason::Clean {
&SessionCloseReason::Clean {
error: 0,
message: "".to_string(),
message: String::new(),
},
);
}
@ -80,7 +84,7 @@ fn wt_session_close_server_stop_sending() {
wt.exchange_packets();
wt.check_session_closed_event_client(
wt_session.stream_id(),
SessionCloseReason::Error(Error::HttpNoError.code()),
&SessionCloseReason::Error(Error::HttpNoError.code()),
);
}
@ -95,12 +99,12 @@ fn wt_session_close_server_reset() {
wt.exchange_packets();
wt.check_session_closed_event_client(
wt_session.stream_id(),
SessionCloseReason::Error(Error::HttpNoError.code()),
&SessionCloseReason::Error(Error::HttpNoError.code()),
);
}
#[test]
fn wt_session_respone_with_1xx() {
fn wt_session_response_with_1xx() {
let mut wt = WtTest::new();
let wt_session_id = wt
@ -134,7 +138,9 @@ fn wt_session_respone_with_1xx() {
wt_server_session
.send_headers(&[Header::new(":status", "111")])
.unwrap();
wt_server_session.response(true).unwrap();
wt_server_session
.response(&WebTransportSessionAcceptAction::Accept)
.unwrap();
wt.exchange_packets();
@ -152,6 +158,19 @@ fn wt_session_respone_with_1xx() {
assert_eq!(wt_session_id, wt_server_session.stream_id());
}
#[test]
fn wt_session_response_with_redirect() {
let mut wt = WtTest::new();
let accept_res = WebTransportSessionAcceptAction::Reject(
[Header::new(":status", "302"), Header::new("location", "/")].to_vec(),
);
let (wt_session_id, _wt_session) = wt.negotiate_wt_session(&accept_res);
wt.check_session_closed_event_client(wt_session_id, &SessionCloseReason::Status(302));
}
#[test]
fn wt_session_respone_200_with_fin() {
let mut wt = WtTest::new();
@ -181,7 +200,9 @@ fn wt_session_respone_200_with_fin() {
}
let mut wt_server_session = wt_server_session.unwrap();
wt_server_session.response(true).unwrap();
wt_server_session
.response(&WebTransportSessionAcceptAction::Accept)
.unwrap();
wt_server_session.stream_close_send().unwrap();
wt.exchange_packets();
@ -192,7 +213,7 @@ fn wt_session_respone_200_with_fin() {
Http3ClientEvent::WebTransport(WebTransportEvent::SessionClosed{
stream_id,
reason
}) if stream_id == wt_session_id && reason == SessionCloseReason::Clean{ error: 0, message: "".to_string()}
}) if stream_id == wt_session_id && reason == SessionCloseReason::Clean{ error: 0, message: String::new()}
)
};
assert!(wt.client.events().any(wt_session_close_event));
@ -212,7 +233,7 @@ fn wt_session_close_frame_client() {
wt.check_session_closed_event_server(
&mut wt_session,
SessionCloseReason::Clean {
&SessionCloseReason::Clean {
error: ERROR_NUM,
message: ERROR_MESSAGE.to_string(),
},
@ -226,12 +247,12 @@ fn wt_session_close_frame_server() {
let mut wt = WtTest::new();
let mut wt_session = wt.create_wt_session();
wt.session_close_frame_server(&mut wt_session, ERROR_NUM, ERROR_MESSAGE);
WtTest::session_close_frame_server(&mut wt_session, ERROR_NUM, ERROR_MESSAGE);
wt.exchange_packets();
wt.check_session_closed_event_client(
wt_session.stream_id(),
SessionCloseReason::Clean {
&SessionCloseReason::Clean {
error: ERROR_NUM,
message: ERROR_MESSAGE.to_string(),
},
@ -257,7 +278,7 @@ fn wt_unknown_session_frame_client() {
wt.exchange_packets();
// The session is still active
let mut unidi_server = wt.create_wt_stream_server(&mut wt_session, StreamType::UniDi);
let mut unidi_server = WtTest::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);
@ -271,14 +292,14 @@ fn wt_unknown_session_frame_client() {
&[],
None,
false,
None,
&None,
);
wt.check_events_after_closing_session_server(
&[],
None,
&[unidi_server.stream_id()],
Some(Error::HttpRequestCancelled.code()),
Some((
&Some((
wt_session.stream_id(),
SessionCloseReason::Clean {
error: ERROR_NUM,
@ -309,11 +330,11 @@ fn wt_close_session_frame_broken_client() {
// check that the webtransport session is closed.
wt.check_session_closed_event_client(
wt_session.stream_id(),
SessionCloseReason::Error(Error::HttpGeneralProtocolStream.code()),
&SessionCloseReason::Error(Error::HttpGeneralProtocolStream.code()),
);
wt.check_session_closed_event_server(
&mut wt_session,
SessionCloseReason::Error(Error::HttpGeneralProtocolStream.code()),
&SessionCloseReason::Error(Error::HttpGeneralProtocolStream.code()),
);
// The Http3 session is still working.
@ -331,18 +352,16 @@ fn receive_request(server: &mut Http3Server) -> Option<Http3OrWebTransportStream
}
#[test]
// Ignoring this test as it is panicking at wt.create_wt_stream_client
// Issue # 1386 is created to track this
#[ignore]
fn wt_close_session_cannot_be_sent_at_once() {
const LIMIT: u64 = 500;
const BUF: &[u8] = &[0; 443];
const ERROR_NUM: u32 = 23;
const ERROR_MESSAGE: &str = "Something went wrong";
let client = http3_client_with_params(
Http3Parameters::default()
.webtransport(true)
.connection_parameters(ConnectionParameters::default().max_data(LIMIT)),
);
let server = default_http3_server(true);
let client = default_http3_client(wt_default_parameters());
let server = default_http3_server(wt_default_parameters());
let mut wt = WtTest::new_with(client, server);
let mut wt_session = wt.create_wt_session();
@ -369,7 +388,7 @@ fn wt_close_session_cannot_be_sent_at_once() {
req.send_data(BUF).unwrap();
// Now close the session.
wt.session_close_frame_server(&mut wt_session, ERROR_NUM, ERROR_MESSAGE);
WtTest::session_close_frame_server(&mut wt_session, ERROR_NUM, ERROR_MESSAGE);
// server cannot create new streams.
assert_eq!(
wt_session.create_stream(StreamType::UniDi),
@ -395,7 +414,7 @@ fn wt_close_session_cannot_be_sent_at_once() {
&[unidi_client],
Some(Error::HttpRequestCancelled.code()),
false,
Some((
&Some((
wt_session.stream_id(),
SessionCloseReason::Clean {
error: ERROR_NUM,
@ -403,5 +422,5 @@ fn wt_close_session_cannot_be_sent_at_once() {
},
)),
);
wt.check_events_after_closing_session_server(&[], None, &[], None, None);
wt.check_events_after_closing_session_server(&[], None, &[], None, &None);
}

View file

@ -4,8 +4,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use crate::webtransport::WtTest;
use neqo_http3::{features::extended_connect::SessionCloseReason, Error};
use crate::features::extended_connect::tests::webtransport::WtTest;
use crate::{features::extended_connect::SessionCloseReason, Error};
use neqo_transport::StreamType;
use std::mem;
@ -40,7 +40,7 @@ fn wt_server_stream_uni() {
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);
let mut wt_server_stream = WtTest::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);
}
@ -52,7 +52,7 @@ fn wt_server_stream_bidi() {
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);
let mut wt_server_stream = WtTest::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);
@ -96,7 +96,7 @@ fn wt_server_stream_uni_closed() {
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);
let mut wt_server_stream = WtTest::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);
@ -109,7 +109,7 @@ fn wt_server_stream_bidi_close() {
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);
let mut wt_server_stream = WtTest::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);
@ -137,7 +137,7 @@ fn wt_server_stream_uni_reset() {
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);
let mut wt_server_stream = WtTest::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);
@ -168,7 +168,7 @@ fn wt_server_stream_bidi_reset() {
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);
let mut wt_server_stream = WtTest::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);
@ -198,7 +198,7 @@ fn wt_server_stream_uni_stop_sending() {
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);
let mut wt_server_stream = WtTest::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());
@ -231,7 +231,7 @@ fn wt_server_stream_bidi_stop_sending() {
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);
let mut wt_server_stream = WtTest::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);
@ -265,7 +265,7 @@ fn wt_client_session_close_1() {
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);
std::mem::drop(wt.receive_data_server(bidi_from_client, true, BUF, false));
wt.cancel_session_client(wt_session.stream_id());
@ -274,7 +274,7 @@ fn wt_client_session_close_1() {
Some(Error::HttpRequestCancelled.code()),
&[bidi_from_client],
Some(Error::HttpRequestCancelled.code()),
Some((
&Some((
wt_session.stream_id(),
SessionCloseReason::Error(Error::HttpNoError.code()),
)),
@ -286,7 +286,7 @@ fn wt_client_session_close_1() {
&[bidi_from_client],
Some(Error::HttpRequestCancelled.code()),
false,
None,
&None,
);
}
@ -300,7 +300,7 @@ fn wt_client_session_close_2() {
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);
std::mem::drop(wt.receive_data_server(unidi_from_client, true, BUF, false));
wt.cancel_session_client(wt_session.stream_id());
@ -309,7 +309,7 @@ fn wt_client_session_close_2() {
Some(Error::HttpRequestCancelled.code()),
&[],
None,
Some((
&Some((
wt_session.stream_id(),
SessionCloseReason::Error(Error::HttpNoError.code()),
)),
@ -321,7 +321,7 @@ fn wt_client_session_close_2() {
&[unidi_from_client],
Some(Error::HttpRequestCancelled.code()),
false,
None,
&None,
);
}
@ -335,7 +335,7 @@ fn wt_client_session_close_3() {
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);
std::mem::drop(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());
@ -345,13 +345,13 @@ fn wt_client_session_close_3() {
None,
&[],
None,
Some((
&Some((
wt_session.stream_id(),
SessionCloseReason::Error(Error::HttpNoError.code()),
)),
);
wt.check_events_after_closing_session_client(&[], None, &[], None, false, None);
wt.check_events_after_closing_session_client(&[], None, &[], None, false, &None);
}
#[test]
@ -374,7 +374,7 @@ fn wt_client_session_close_4() {
None,
&[],
None,
Some((
&Some((
wt_session.stream_id(),
SessionCloseReason::Error(Error::HttpNoError.code()),
)),
@ -386,7 +386,7 @@ fn wt_client_session_close_4() {
&[unidi_from_client],
Some(Error::HttpNoError.code()),
false,
None,
&None,
);
}
@ -410,13 +410,13 @@ fn wt_client_session_close_5() {
Some(Error::HttpNoError.code()),
&[],
None,
Some((
&Some((
wt_session.stream_id(),
SessionCloseReason::Error(Error::HttpNoError.code()),
)),
);
wt.check_events_after_closing_session_client(&[], None, &[], None, false, None);
wt.check_events_after_closing_session_client(&[], None, &[], None, false, &None);
}
#[test]
@ -426,7 +426,7 @@ fn wt_client_session_close_6() {
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);
let mut bidi_from_server = WtTest::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);
@ -437,7 +437,7 @@ fn wt_client_session_close_6() {
Some(Error::HttpRequestCancelled.code()),
&[bidi_from_server.stream_id()],
Some(Error::HttpRequestCancelled.code()),
Some((
&Some((
wt_session.stream_id(),
SessionCloseReason::Error(Error::HttpNoError.code()),
)),
@ -449,7 +449,7 @@ fn wt_client_session_close_6() {
&[bidi_from_server.stream_id()],
Some(Error::HttpRequestCancelled.code()),
false,
None,
&None,
);
}
@ -460,7 +460,7 @@ fn wt_client_session_close_7() {
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);
let mut unidi_from_server = WtTest::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);
@ -471,7 +471,7 @@ fn wt_client_session_close_7() {
None,
&[unidi_from_server.stream_id()],
Some(Error::HttpRequestCancelled.code()),
Some((
&Some((
wt_session.stream_id(),
SessionCloseReason::Error(Error::HttpNoError.code()),
)),
@ -483,7 +483,7 @@ fn wt_client_session_close_7() {
&[],
None,
false,
None,
&None,
);
}
@ -494,7 +494,7 @@ fn wt_client_session_close_8() {
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);
let mut unidi_server = WtTest::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);
@ -506,13 +506,13 @@ fn wt_client_session_close_8() {
None,
&[],
None,
Some((
&Some((
wt_session.stream_id(),
SessionCloseReason::Error(Error::HttpNoError.code()),
)),
);
wt.check_events_after_closing_session_client(&[], None, &[], None, false, None);
wt.check_events_after_closing_session_client(&[], None, &[], None, false, &None);
}
#[test]
@ -522,7 +522,7 @@ fn wt_client_session_close_9() {
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);
let mut unidi_server = WtTest::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());
@ -533,13 +533,13 @@ fn wt_client_session_close_9() {
None,
&[unidi_server.stream_id()],
Some(Error::HttpNoError.code()),
Some((
&Some((
wt_session.stream_id(),
SessionCloseReason::Error(Error::HttpNoError.code()),
)),
);
wt.check_events_after_closing_session_client(&[], None, &[], None, false, None);
wt.check_events_after_closing_session_client(&[], None, &[], None, false, &None);
}
#[test]
@ -549,7 +549,7 @@ fn wt_client_session_close_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);
let mut unidi_server = WtTest::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);
@ -560,7 +560,7 @@ fn wt_client_session_close_10() {
None,
&[],
None,
Some((
&Some((
wt_session.stream_id(),
SessionCloseReason::Error(Error::HttpNoError.code()),
)),
@ -572,7 +572,7 @@ fn wt_client_session_close_10() {
&[],
None,
false,
None,
&None,
);
}
@ -583,7 +583,7 @@ fn wt_client_session_close_11() {
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);
let mut bidi_server = WtTest::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);
@ -597,13 +597,13 @@ fn wt_client_session_close_11() {
None,
&[],
None,
Some((
&Some((
wt_session.stream_id(),
SessionCloseReason::Error(Error::HttpNoError.code()),
)),
);
wt.check_events_after_closing_session_client(&[], None, &[], None, false, None);
wt.check_events_after_closing_session_client(&[], None, &[], None, false, &None);
}
#[test]
@ -613,7 +613,7 @@ fn wt_client_session_close_12() {
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);
let mut bidi_server = WtTest::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);
@ -625,7 +625,7 @@ fn wt_client_session_close_12() {
None,
&[],
None,
Some((
&Some((
wt_session.stream_id(),
SessionCloseReason::Error(Error::HttpNoError.code()),
)),
@ -637,7 +637,7 @@ fn wt_client_session_close_12() {
&[bidi_server.stream_id()],
Some(Error::HttpNoError.code()),
false,
None,
&None,
);
}
@ -650,10 +650,10 @@ fn wt_client_session_close_13() {
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);
std::mem::drop(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);
std::mem::drop(wt.receive_data_server(bidi_client_2, true, BUF, false));
wt.cancel_session_client(wt_session.stream_id());
@ -662,7 +662,7 @@ fn wt_client_session_close_13() {
Some(Error::HttpRequestCancelled.code()),
&[bidi_client_1, bidi_client_2],
Some(Error::HttpRequestCancelled.code()),
Some((
&Some((
wt_session.stream_id(),
SessionCloseReason::Error(Error::HttpNoError.code()),
)),
@ -674,7 +674,7 @@ fn wt_client_session_close_13() {
&[bidi_client_1, bidi_client_2],
Some(Error::HttpRequestCancelled.code()),
false,
None,
&None,
);
}
@ -700,7 +700,7 @@ fn wt_client_session_server_close_1() {
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);
std::mem::drop(wt.receive_data_server(bidi_client, true, BUF, false));
wt.cancel_session_server(&mut wt_session);
@ -710,7 +710,7 @@ fn wt_client_session_server_close_1() {
&[bidi_client],
Some(Error::HttpRequestCancelled.code()),
false,
Some((
&Some((
wt_session.stream_id(),
SessionCloseReason::Error(Error::HttpNoError.code()),
)),
@ -721,7 +721,7 @@ fn wt_client_session_server_close_1() {
Some(Error::HttpRequestCancelled.code()),
&[bidi_client],
Some(Error::HttpRequestCancelled.code()),
None,
&None,
);
}
@ -734,7 +734,7 @@ fn wt_client_session_server_close_2() {
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);
std::mem::drop(wt.receive_data_server(unidi_client, true, BUF, false));
wt.cancel_session_server(&mut wt_session);
@ -744,7 +744,7 @@ fn wt_client_session_server_close_2() {
&[unidi_client],
Some(Error::HttpRequestCancelled.code()),
false,
Some((
&Some((
wt_session.stream_id(),
SessionCloseReason::Error(Error::HttpNoError.code()),
)),
@ -755,7 +755,7 @@ fn wt_client_session_server_close_2() {
Some(Error::HttpRequestCancelled.code()),
&[],
None,
None,
&None,
);
}
@ -780,13 +780,13 @@ fn wt_client_session_server_close_3() {
&[],
None,
false,
Some((
&Some((
wt_session.stream_id(),
SessionCloseReason::Error(Error::HttpNoError.code()),
)),
);
wt.check_events_after_closing_session_server(&[], None, &[], None, None);
wt.check_events_after_closing_session_server(&[], None, &[], None, &None);
}
#[test]
@ -809,13 +809,13 @@ fn wt_client_session_server_close_4() {
&[unidi_client],
Some(Error::HttpNoError.code()),
false,
Some((
&Some((
wt_session.stream_id(),
SessionCloseReason::Error(Error::HttpNoError.code()),
)),
);
wt.check_events_after_closing_session_server(&[], None, &[], None, None);
wt.check_events_after_closing_session_server(&[], None, &[], None, &None);
}
#[test]
@ -825,7 +825,7 @@ fn wt_client_session_server_close_5() {
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);
let mut bidi_server = WtTest::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);
@ -837,7 +837,7 @@ fn wt_client_session_server_close_5() {
&[bidi_server.stream_id()],
Some(Error::HttpRequestCancelled.code()),
false,
Some((
&Some((
wt_session.stream_id(),
SessionCloseReason::Error(Error::HttpNoError.code()),
)),
@ -848,7 +848,7 @@ fn wt_client_session_server_close_5() {
Some(Error::HttpRequestCancelled.code()),
&[bidi_server.stream_id()],
Some(Error::HttpRequestCancelled.code()),
None,
&None,
);
}
@ -859,7 +859,7 @@ fn wt_client_session_server_close_6() {
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);
let mut unidi_server = WtTest::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);
@ -871,7 +871,7 @@ fn wt_client_session_server_close_6() {
&[],
None,
false,
Some((
&Some((
wt_session.stream_id(),
SessionCloseReason::Error(Error::HttpNoError.code()),
)),
@ -881,7 +881,7 @@ fn wt_client_session_server_close_6() {
None,
&[unidi_server.stream_id()],
Some(Error::HttpRequestCancelled.code()),
None,
&None,
);
}
@ -892,7 +892,7 @@ fn wt_client_session_server_close_7() {
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);
let mut unidi_server = WtTest::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);
@ -906,13 +906,13 @@ fn wt_client_session_server_close_7() {
&[],
None,
false,
Some((
&Some((
wt_session.stream_id(),
SessionCloseReason::Error(Error::HttpNoError.code()),
)),
);
wt.check_events_after_closing_session_server(&[], None, &[], None, None);
wt.check_events_after_closing_session_server(&[], None, &[], None, &None);
}
#[test]
@ -922,7 +922,7 @@ fn wt_client_session_server_close_8() {
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);
let mut unidi_server = WtTest::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);
@ -935,13 +935,13 @@ fn wt_client_session_server_close_8() {
&[],
None,
false,
Some((
&Some((
wt_session.stream_id(),
SessionCloseReason::Error(Error::HttpNoError.code()),
)),
);
wt.check_events_after_closing_session_server(&[], None, &[], None, None);
wt.check_events_after_closing_session_server(&[], None, &[], None, &None);
}
#[test]
@ -951,7 +951,7 @@ fn wt_client_session_server_close_9() {
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);
let mut bidi_server = WtTest::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);
@ -967,13 +967,13 @@ fn wt_client_session_server_close_9() {
&[],
None,
false,
Some((
&Some((
wt_session.stream_id(),
SessionCloseReason::Error(Error::HttpNoError.code()),
)),
);
wt.check_events_after_closing_session_server(&[], None, &[], None, None);
wt.check_events_after_closing_session_server(&[], None, &[], None, &None);
}
#[test]
@ -983,7 +983,7 @@ fn wt_client_session_server_close_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);
let mut bidi_server = WtTest::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);
@ -996,13 +996,13 @@ fn wt_client_session_server_close_10() {
&[bidi_server.stream_id()],
Some(Error::HttpNoError.code()),
false,
Some((
&Some((
wt_session.stream_id(),
SessionCloseReason::Error(Error::HttpNoError.code()),
)),
);
wt.check_events_after_closing_session_server(&[], None, &[], None, None);
wt.check_events_after_closing_session_server(&[], None, &[], None, &None);
}
#[test]
@ -1014,10 +1014,10 @@ fn wt_client_session_server_close_11() {
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);
std::mem::drop(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);
std::mem::drop(wt.receive_data_server(bidi_client_2, true, BUF, false));
wt.cancel_session_server(&mut wt_session);
@ -1027,7 +1027,7 @@ fn wt_client_session_server_close_11() {
&[bidi_client_1, bidi_client_2],
Some(Error::HttpRequestCancelled.code()),
false,
Some((
&Some((
wt_session.stream_id(),
SessionCloseReason::Error(Error::HttpNoError.code()),
)),
@ -1038,7 +1038,7 @@ fn wt_client_session_server_close_11() {
Some(Error::HttpRequestCancelled.code()),
&[bidi_client_1, bidi_client_2],
Some(Error::HttpRequestCancelled.code()),
None,
&None,
);
}
@ -1050,7 +1050,7 @@ fn wt_session_close_frame_and_streams_client() {
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);
let mut unidi_server = WtTest::create_wt_stream_server(&mut wt_session, StreamType::UniDi);
wt.send_data_server(&mut unidi_server, BUF);
wt.exchange_packets();
@ -1061,7 +1061,7 @@ fn wt_session_close_frame_and_streams_client() {
&[],
None,
false,
None,
&None,
);
wt.exchange_packets();
@ -1070,7 +1070,7 @@ fn wt_session_close_frame_and_streams_client() {
None,
&[unidi_server.stream_id()],
Some(Error::HttpRequestCancelled.code()),
Some((
&Some((
wt_session.stream_id(),
SessionCloseReason::Clean {
error: ERROR_NUM,

View file

@ -17,7 +17,7 @@ use crate::{
};
use neqo_common::{qtrace, Encoder, Header, MessageType, Role};
use neqo_qpack::{QPackDecoder, QPackEncoder};
use neqo_transport::{Connection, StreamId};
use neqo_transport::{Connection, DatagramTracking, StreamId};
use std::any::Any;
use std::cell::RefCell;
use std::collections::BTreeSet;
@ -39,7 +39,7 @@ impl SessionState {
}
#[derive(Debug)]
pub struct WebTransportSession {
pub(crate) struct WebTransportSession {
control_stream_recv: Box<dyn RecvStream>,
control_stream_send: Box<dyn SendStream>,
stream_event_listener: Rc<RefCell<WebTransportSessionListener>>,
@ -209,15 +209,14 @@ impl WebTransportSession {
}
qtrace!("ExtendedConnect close the session");
self.state = SessionState::Done;
if let CloseType::ResetApp(_) = close_type {
return;
}
if !close_type.locally_initiated() {
self.events.session_end(
ExtendedConnectType::WebTransport,
self.session_id,
SessionCloseReason::from(close_type),
);
}
}
/// # Panics
/// This cannot panic because headers are checked before this function called.
@ -241,7 +240,7 @@ impl WebTransportSession {
self.session_id,
SessionCloseReason::Clean {
error: 0,
message: "".to_string(),
message: String::new(),
},
);
self.state = SessionState::Done;
@ -265,7 +264,7 @@ impl WebTransportSession {
self.session_id,
SessionCloseReason::Clean {
error: 0,
message: "".to_string(),
message: String::new(),
},
);
SessionState::Done
@ -323,11 +322,11 @@ impl WebTransportSession {
matches!(self.state, SessionState::Active)
}
pub fn take_sub_streams(&mut self) -> Option<(BTreeSet<StreamId>, BTreeSet<StreamId>)> {
Some((
pub fn take_sub_streams(&mut self) -> (BTreeSet<StreamId>, BTreeSet<StreamId>) {
(
mem::take(&mut self.recv_streams),
mem::take(&mut self.send_streams),
))
)
}
/// # Errors
@ -358,7 +357,7 @@ impl WebTransportSession {
self.session_id,
SessionCloseReason::Clean {
error: 0,
message: "".to_string(),
message: String::new(),
},
);
self.state = SessionState::Done;
@ -391,6 +390,33 @@ impl WebTransportSession {
fn send_data(&mut self, conn: &mut Connection, buf: &[u8]) -> Res<usize> {
self.control_stream_send.send_data(conn, buf)
}
/// # Errors
/// Returns an error if the datagram exceeds the remote datagram size limit.
pub fn send_datagram(
&self,
conn: &mut Connection,
buf: &[u8],
id: impl Into<DatagramTracking>,
) -> Res<()> {
qtrace!([self], "send_datagram state={:?}", self.state);
if let SessionState::Active = self.state {
let mut dgram_data = Encoder::default();
dgram_data.encode_varint(self.session_id.as_u64() / 4);
dgram_data.encode(buf);
conn.send_datagram(dgram_data.as_ref(), id)?;
} else {
debug_assert!(false);
return Err(Error::Unavailable);
}
Ok(())
}
pub fn datagram(&mut self, datagram: Vec<u8>) {
if let SessionState::Active = self.state {
self.events.new_datagram(self.session_id, datagram);
}
}
}
impl Stream for Rc<RefCell<WebTransportSession>> {

View file

@ -18,7 +18,7 @@ pub const WEBTRANSPORT_UNI_STREAM: u64 = 0x54;
pub const WEBTRANSPORT_STREAM: u64 = 0x41;
#[derive(Debug)]
pub struct WebTransportRecvStream {
pub(crate) struct WebTransportRecvStream {
stream_id: StreamId,
events: Box<dyn RecvStreamEvents>,
session: Rc<RefCell<WebTransportSession>>,
@ -85,7 +85,7 @@ enum WebTransportSenderStreamState {
}
#[derive(Debug)]
pub struct WebTransportSendStream {
pub(crate) struct WebTransportSendStream {
stream_id: StreamId,
state: WebTransportSenderStreamState,
events: Box<dyn SendStreamEvents>,

View file

@ -4,13 +4,18 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
pub mod hframe;
pub mod reader;
pub mod wtframe;
pub(crate) mod hframe;
pub(crate) mod reader;
pub(crate) mod wtframe;
pub use hframe::{HFrame, H3_FRAME_TYPE_HEADERS, H3_FRAME_TYPE_SETTINGS, H3_RESERVED_FRAME_TYPES};
pub use reader::{FrameReader, StreamReaderConnectionWrapper, StreamReaderRecvStreamWrapper};
pub use wtframe::WebTransportFrame;
#[allow(unused_imports)]
pub(crate) use hframe::{
HFrame, H3_FRAME_TYPE_HEADERS, H3_FRAME_TYPE_SETTINGS, H3_RESERVED_FRAME_TYPES,
};
pub(crate) use reader::{
FrameReader, StreamReaderConnectionWrapper, StreamReaderRecvStreamWrapper,
};
pub(crate) use wtframe::WebTransportFrame;
#[cfg(test)]
mod tests;

View file

@ -17,7 +17,7 @@ use std::fmt::Debug;
const MAX_READ_SIZE: usize = 4096;
pub trait FrameDecoder<T> {
pub(crate) trait FrameDecoder<T> {
fn is_known_type(frame_type: u64) -> bool;
/// # Errors
/// Returns `HttpFrameUnexpected` if frames is not alowed, i.e. is a `H3_RESERVED_FRAME_TYPES`.
@ -29,7 +29,7 @@ pub trait FrameDecoder<T> {
fn decode(frame_type: u64, frame_len: u64, data: Option<&[u8]>) -> Res<Option<T>>;
}
pub trait StreamReader {
pub(crate) trait StreamReader {
/// # Errors
/// An error may happen while reading a stream, e.g. early close, protocol error, etc.
/// Return an error if the stream was closed on the transport layer, but that information is not yet
@ -37,7 +37,7 @@ pub trait StreamReader {
fn read_data(&mut self, buf: &mut [u8]) -> Res<(usize, bool)>;
}
pub struct StreamReaderConnectionWrapper<'a> {
pub(crate) struct StreamReaderConnectionWrapper<'a> {
conn: &'a mut Connection,
stream_id: StreamId,
}
@ -57,7 +57,7 @@ impl<'a> StreamReader for StreamReaderConnectionWrapper<'a> {
}
}
pub struct StreamReaderRecvStreamWrapper<'a> {
pub(crate) struct StreamReaderRecvStreamWrapper<'a> {
recv_stream: &'a mut Box<dyn RecvStream>,
conn: &'a mut Connection,
}
@ -86,7 +86,7 @@ enum FrameReaderState {
#[allow(clippy::module_name_repetitions)]
#[derive(Debug)]
pub struct FrameReader {
pub(crate) struct FrameReader {
state: FrameReaderState,
frame_type: u64,
frame_len: u64,

View file

@ -14,7 +14,7 @@ use std::mem;
use test_fixture::{default_client, default_server, now};
#[allow(clippy::many_single_char_names)]
pub fn enc_dec<T: FrameDecoder<T>>(d: &Encoder, st: &str, remaining: usize) -> T {
pub(crate) fn enc_dec<T: FrameDecoder<T>>(d: &Encoder, st: &str, remaining: usize) -> T {
// For data, headers and push_promise we do not read all bytes from the buffer
let d2 = Encoder::from_hex(st);
assert_eq!(d.as_ref(), &d2.as_ref()[..d.as_ref().len()]);

View file

@ -7,16 +7,144 @@
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
#![warn(clippy::pedantic)]
/*!
# The HTTP/3 protocol
This crate implements [RFC9114](https://datatracker.ietf.org/doc/html/rfc9114).
The implementation depends on:
- [neqo-transport](../neqo_transport/index.html) --- implements the QUIC protocol
([RFC9000](https://www.rfc-editor.org/info/rfc9000)) and
- [neqo-qpack](../neqo_qpack/index.html) --- implements QPACK
([RFC9204](https://www.rfc-editor.org/info/rfc9204));
## Features
Both client and server-side HTTP/3 protocols are implemented, although the server-side
implementation is not meant to be used in production and its only purpose is to facilitate testing
of the client-side code.
__`WebTransport`__
([draft version 2](https://datatracker.ietf.org/doc/html/draft-vvv-webtransport-http3-02)) is
supported and can be enabled using [`Http3Parameters`](struct.Http3Parameters.html).
## Interaction with an application
### Driving HTTP/3 session
The crate does not create an OS level UDP socket, it produces, i.e. encodes, data that should be
sent as a payload in a UDP packet and consumes data received on the UDP socket. For example,
[`std::net::UdpSocket`](std::net::UdpSocket) or [`mio::net::UdpSocket`](https://crates.io/crates/mio)
could be used for creating UDP sockets.
The application is responsible for creating a socket, polling the socket, and sending and receiving
data from the socket.
In addition to receiving data HTTP/3 sessions actions may be triggered when a certain amount of
time passes, e.g. after a certain amount of time data may be considered lost and should be
retransmitted, packet pacing requires a timer, etc. The implementation does not use timers, but
instead informs the application when processing needs to be triggered.
The core functions for driving HTTP/3 sessions are:
- __On the client-side__ :
- [`process_output`](struct.Http3Client.html#method.process_output) used for producing UDP
payload. If a payload is not produced this function returns a callback time, e.g. the time when
[`process_output`](struct.Http3Client.html#method.process_output) should be called again.
- [`process_input`](struct.Http3Client.html#method.process_input) used consuming UDP payload.
- [`process`](struct.Http3Client.html#method.process) combines the 2 functions into one, i.e. it
consumes UDP payload if available and produces some UDP payload to be sent or returns a
callback time.
- __On the server-side__ only [`process`](struct.Http3Server.html#method.process) is
available.
An example interaction with a socket:
```ignore
let socket = match UdpSocket::bind(local_addr) {
Err(e) => {
eprintln!("Unable to bind UDP socket: {}", e);
}
Ok(s) => s,
};
let mut client = Http3Client::new(...);
...
// process_output can return 3 values, data to be sent, time duration when process_output should
// be called, and None when Http3Client is done.
match client.process_output(Instant::now()) {
Output::Datagram(dgram) => {
// Send dgram on a socket.
socket.send_to(&dgram[..], dgram.destination())
}
Output::Callback(duration) => {
// the client is idle for “duration”, set read timeout on the socket to this value and
// poll the socket for reading in the meantime.
socket.set_read_timeout(Some(duration)).unwrap();
}
Output::None => {
// client is done.
}
};
...
// Reading new data coming for the network.
match socket.recv_from(&mut buf[..]) {
Ok((sz, remote)) => {
let d = Datagram::new(remote, *local_addr, &buf[..sz]);
client.process_input(d, Instant::now());
}
Err(err) => {
eprintln!("UDP error: {}", err);
}
}
```
### HTTP/3 session events
[`Http3Client`](struct.Http3Client.html) and [`Http3Server`](struct.Http3Server.html) produce
events that can be obtain by calling
[`next_event`](neqo_common/event/trait.Provider.html#tymethod.next_event). The events are of type
[`Http3ClientEvent`](enum.Http3ClientEvent.html) and
[`Http3ServerEvent`](enum.Http3ServerEvent.html) respectively. They are informing the application
when the connection changes state, when new data is received on a stream, etc.
```ignore
...
while let Some(event) = client.next_event() {
match event {
Http3ClientEvent::DataReadable { stream_id } => {
println!("New data available on stream {}", stream_id);
}
Http3ClientEvent::StateChange(Http3State::Connected) => {
println!("Http3 session is in state Connected now");
}
_ => {
println!("Unhandled event {:?}", event);
}
}
}
```
*/
mod buffered_send_stream;
mod client_events;
mod conn_params;
mod connection;
pub mod connection_client;
mod connection_client;
mod connection_server;
mod control_stream_local;
mod control_stream_remote;
pub mod features;
pub mod frames;
mod frames;
mod headers_checks;
mod priority;
mod push_controller;
@ -24,12 +152,12 @@ mod qlog;
mod qpack_decoder_receiver;
mod qpack_encoder_receiver;
mod recv_message;
pub mod request_target;
mod request_target;
mod send_message;
pub mod server;
mod server;
mod server_connection_events;
mod server_events;
pub mod settings;
mod settings;
mod stream_type_reader;
use neqo_qpack::Error as QpackError;
@ -38,24 +166,24 @@ pub use neqo_transport::{Output, StreamId};
use std::fmt::Debug;
use crate::priority::PriorityHandler;
pub use buffered_send_stream::BufferedStream;
use buffered_send_stream::BufferedStream;
pub use client_events::{Http3ClientEvent, WebTransportEvent};
pub use conn_params::Http3Parameters;
pub use connection::Http3State;
pub use connection::{Http3State, WebTransportSessionAcceptAction};
pub use connection_client::Http3Client;
use features::extended_connect::WebTransportSession;
pub use frames::HFrame;
pub use neqo_common::{Header, MessageType};
use frames::HFrame;
pub use neqo_common::Header;
use neqo_common::MessageType;
pub use priority::Priority;
pub use server::Http3Server;
pub use server_events::{
Http3OrWebTransportStream, Http3ServerEvent, WebTransportRequest, WebTransportServerEvent,
};
pub use settings::HttpZeroRttChecker;
use std::any::Any;
use std::cell::RefCell;
use std::rc::Rc;
pub use stream_type_reader::NewStreamType;
use stream_type_reader::NewStreamType;
type Res<T> = Result<T, Error>;
@ -299,9 +427,8 @@ pub enum Http3StreamType {
#[must_use]
#[derive(PartialEq, Eq, Debug)]
pub enum ReceiveOutput {
enum ReceiveOutput {
NoOutput,
PushStream,
ControlFrames(Vec<HFrame>),
UnblockedStreams(Vec<StreamId>),
NewStream(NewStreamType),
@ -313,11 +440,11 @@ impl Default for ReceiveOutput {
}
}
pub trait Stream: Debug {
trait Stream: Debug {
fn stream_type(&self) -> Http3StreamType;
}
pub trait RecvStream: Stream {
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.
@ -345,7 +472,7 @@ pub trait RecvStream: Stream {
}
}
pub trait HttpRecvStream: RecvStream {
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.
@ -400,12 +527,12 @@ impl Http3StreamInfo {
}
}
pub trait RecvStreamEvents: Debug {
trait RecvStreamEvents: Debug {
fn data_readable(&self, _stream_info: Http3StreamInfo) {}
fn recv_closed(&self, _stream_info: Http3StreamInfo, _close_type: CloseType) {}
}
pub trait HttpRecvStreamEvents: RecvStreamEvents {
trait HttpRecvStreamEvents: RecvStreamEvents {
fn header_ready(
&self,
stream_info: Http3StreamInfo,
@ -416,7 +543,7 @@ pub trait HttpRecvStreamEvents: RecvStreamEvents {
fn extended_connect_new_session(&self, _stream_id: StreamId, _headers: Vec<Header>) {}
}
pub trait SendStream: Stream {
trait SendStream: Stream {
/// # Errors
/// Error my occure during sending data, e.g. protocol error, etc.
fn send(&mut self, conn: &mut Connection) -> Res<()>;
@ -454,7 +581,7 @@ pub trait SendStream: Stream {
}
}
pub trait HttpSendStream: SendStream {
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.
@ -465,7 +592,7 @@ pub trait HttpSendStream: SendStream {
fn any(&self) -> &dyn Any;
}
pub trait SendStreamEvents: Debug {
trait SendStreamEvents: Debug {
fn send_closed(&self, _stream_info: Http3StreamInfo, _close_type: CloseType) {}
fn data_writable(&self, _stream_info: Http3StreamInfo) {}
}
@ -477,7 +604,7 @@ pub trait SendStreamEvents: Debug {
/// 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 CloseType {
enum CloseType {
ResetApp(AppError),
ResetRemote(AppError),
LocalError(AppError),
@ -494,4 +621,9 @@ impl CloseType {
Self::Done => None,
}
}
#[must_use]
pub fn locally_initiated(&self) -> bool {
matches!(self, CloseType::ResetApp(_))
}
}

View file

@ -11,7 +11,7 @@ use std::cell::RefCell;
use std::rc::Rc;
#[derive(Debug)]
pub struct DecoderRecvStream {
pub(crate) struct DecoderRecvStream {
stream_id: StreamId,
decoder: Rc<RefCell<QPackDecoder>>,
}

View file

@ -11,7 +11,7 @@ use std::cell::RefCell;
use std::rc::Rc;
#[derive(Debug)]
pub struct EncoderRecvStream {
pub(crate) struct EncoderRecvStream {
stream_id: StreamId,
encoder: Rc<RefCell<QPackEncoder>>,
}

View file

@ -24,7 +24,7 @@ use std::fmt::Debug;
use std::rc::Rc;
#[allow(clippy::module_name_repetitions)]
pub struct RecvMessageInfo {
pub(crate) struct RecvMessageInfo {
pub message_type: MessageType,
pub stream_type: Http3StreamType,
pub stream_id: StreamId,

View file

@ -229,6 +229,13 @@ impl Http3Server {
handler.clone(),
stream_info,
)),
Http3ServerConnEvent::ExtendedConnectDatagram {
session_id,
datagram,
} => self.events.webtransport_datagram(
WebTransportRequest::new(conn.clone(), handler.clone(), session_id),
datagram,
),
}
}
}

View file

@ -55,6 +55,10 @@ pub(crate) enum Http3ServerConnEvent {
reason: SessionCloseReason,
},
ExtendedConnectNewStream(Http3StreamInfo),
ExtendedConnectDatagram {
session_id: StreamId,
datagram: Vec<u8>,
},
}
#[derive(Debug, Default, Clone)]
@ -140,6 +144,13 @@ impl ExtendedConnectEvents for Http3ServerConnEvents {
fn extended_connect_new_stream(&self, stream_info: Http3StreamInfo) {
self.insert(Http3ServerConnEvent::ExtendedConnectNewStream(stream_info));
}
fn new_datagram(&self, session_id: StreamId, datagram: Vec<u8>) {
self.insert(Http3ServerConnEvent::ExtendedConnectDatagram {
session_id,
datagram,
});
}
}
impl Http3ServerConnEvents {

View file

@ -6,17 +6,18 @@
#![allow(clippy::module_name_repetitions)]
use crate::connection::Http3State;
use crate::connection::{Http3State, WebTransportSessionAcceptAction};
use crate::connection_server::Http3ServerHandler;
use crate::{
features::extended_connect::SessionCloseReason, Http3StreamInfo, Http3StreamType, Priority, Res,
};
use neqo_common::{qdebug, qinfo, Header};
use neqo_common::{qdebug, qinfo, Encoder, Header};
use neqo_transport::server::ActiveConnectionRef;
use neqo_transport::{AppError, Connection, StreamId, StreamType};
use neqo_transport::{AppError, Connection, DatagramTracking, StreamId, StreamType};
use std::cell::RefCell;
use std::collections::VecDeque;
use std::convert::TryFrom;
use std::ops::{Deref, DerefMut};
use std::rc::Rc;
@ -244,7 +245,7 @@ impl WebTransportRequest {
/// 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<()> {
pub fn response(&mut self, accept: &WebTransportSessionAcceptAction) -> Res<()> {
qinfo!([self], "Set a response for a WebTransport session.");
self.stream_handler
.handler
@ -298,6 +299,45 @@ impl WebTransportRequest {
Http3StreamInfo::new(id, Http3StreamType::WebTransport(session_id)),
))
}
/// Send `WebTransport` datagram.
/// # Errors
/// It may return `InvalidStreamId` if a stream does not exist anymore.
/// The function returns `TooMuchData` if the supply buffer is bigger than
/// the allowed remote datagram size.
pub fn send_datagram(&mut self, buf: &[u8], id: impl Into<DatagramTracking>) -> Res<()> {
let session_id = self.stream_handler.stream_id();
self.stream_handler
.handler
.borrow_mut()
.webtransport_send_datagram(
&mut self.stream_handler.conn.borrow_mut(),
session_id,
buf,
id,
)
}
#[must_use]
pub fn remote_datagram_size(&self) -> u64 {
self.stream_handler.conn.borrow().remote_datagram_size()
}
/// Returns the current max size of a datagram that can fit into a packet.
/// The value will change over time depending on the encoded size of the
/// packet number, ack frames, etc.
/// # Errors
/// The function returns `NotAvailable` if datagrams are not enabled.
/// # Panics
/// This cannot panic. The max varint length is 8.
pub fn max_datagram_size(&self) -> Res<u64> {
let max_size = self.stream_handler.conn.borrow().max_datagram_size()?;
Ok(max_size
- u64::try_from(Encoder::varint_len(
self.stream_handler.stream_id().as_u64(),
))
.unwrap())
}
}
impl Deref for WebTransportRequest {
@ -340,6 +380,10 @@ pub enum WebTransportServerEvent {
reason: SessionCloseReason,
},
NewStream(Http3OrWebTransportStream),
Datagram {
session: WebTransportRequest,
datagram: Vec<u8>,
},
}
#[derive(Debug, Clone)]
@ -508,4 +552,10 @@ impl Http3ServerEvents {
WebTransportServerEvent::NewStream(stream),
));
}
pub(crate) fn webtransport_datagram(&self, session: WebTransportRequest, datagram: Vec<u8>) {
self.insert(Http3ServerEvent::WebTransport(
WebTransportServerEvent::Datagram { session, datagram },
));
}
}

View file

@ -14,12 +14,12 @@ use neqo_qpack::decoder::QPACK_UNI_STREAM_TYPE_DECODER;
use neqo_qpack::encoder::QPACK_UNI_STREAM_TYPE_ENCODER;
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;
pub(crate) const HTTP3_UNI_STREAM_TYPE_PUSH: u64 = 0x1;
pub(crate) const WEBTRANSPORT_UNI_STREAM: u64 = 0x54;
pub(crate) const WEBTRANSPORT_STREAM: u64 = 0x41;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum NewStreamType {
pub(crate) enum NewStreamType {
Control,
Decoder,
Encoder,
@ -74,7 +74,7 @@ impl NewStreamType {
/// the `ReadType` state, `NewStreamHeadReader` changes to `ReadId` state and from there
/// to `Done` state
#[derive(Debug)]
pub enum NewStreamHeadReader {
pub(crate) enum NewStreamHeadReader {
ReadType {
role: Role,
reader: IncrementalDecoderUint,

View file

@ -6,14 +6,15 @@
#![allow(unused_assignments)]
use neqo_common::{event::Provider, Datagram};
use neqo_common::{event::Provider, qtrace, Datagram};
use neqo_crypto::{AuthenticationStatus, ResumptionToken};
use neqo_http3::{
Header, Http3Client, Http3ClientEvent, Http3OrWebTransportStream, Http3Parameters, Http3Server,
Http3ServerEvent, Http3State, Priority,
};
use neqo_transport::{ConnectionParameters, StreamType};
use neqo_transport::{ConnectionError, ConnectionParameters, Error, Output, StreamType};
use std::mem;
use std::time::{Duration, Instant};
use test_fixture::*;
const RESPONSE_DATA: &[u8] = &[0x61, 0x62, 0x63];
@ -111,6 +112,39 @@ fn connect_peers(hconn_c: &mut Http3Client, hconn_s: &mut Http3Server) -> Option
out.dgram()
}
fn connect_peers_with_network_propagation_delay(
hconn_c: &mut Http3Client,
hconn_s: &mut Http3Server,
net_delay: u64,
) -> (Option<Datagram>, Instant) {
let net_delay = Duration::from_millis(net_delay);
assert_eq!(hconn_c.state(), Http3State::Initializing);
let mut now = now();
let out = hconn_c.process(None, now); // Initial
now += net_delay;
let out = hconn_s.process(out.dgram(), now); // Initial + Handshake
now += net_delay;
let out = hconn_c.process(out.dgram(), now); // ACK
now += net_delay;
let out = hconn_s.process(out.dgram(), now); //consume ACK
assert!(out.dgram().is_none());
let authentication_needed = |e| matches!(e, Http3ClientEvent::AuthenticationNeeded);
assert!(hconn_c.events().any(authentication_needed));
now += net_delay;
hconn_c.authenticated(AuthenticationStatus::Ok, now);
let out = hconn_c.process(None, now); // Handshake
assert_eq!(hconn_c.state(), Http3State::Connected);
now += net_delay;
let out = hconn_s.process(out.dgram(), now); // HANDSHAKE_DONE
now += net_delay;
let out = hconn_c.process(out.dgram(), now); // Consume HANDSHAKE_DONE, send control streams.
now += net_delay;
let out = hconn_s.process(out.dgram(), now); // consume and send control streams.
now += net_delay;
let out = hconn_c.process(out.dgram(), now); // consume control streams.
(out.dgram(), now)
}
fn connect() -> (Http3Client, Http3Server, Option<Datagram>) {
let mut hconn_c = default_http3_client();
let mut hconn_s = default_http3_server();
@ -139,7 +173,7 @@ fn test_connect() {
fn test_fetch() {
let (mut hconn_c, mut hconn_s, dgram) = connect();
eprintln!("-----client");
qtrace!("-----client");
let req = hconn_c
.fetch(
now(),
@ -152,13 +186,13 @@ fn test_fetch() {
assert_eq!(req, 0);
hconn_c.stream_close_send(req).unwrap();
let out = hconn_c.process(dgram, now());
eprintln!("-----server");
qtrace!("-----server");
let out = hconn_s.process(out.dgram(), now());
mem::drop(hconn_c.process(out.dgram(), now()));
process_server_events(&mut hconn_s);
let out = hconn_s.process(None, now());
eprintln!("-----client");
qtrace!("-----client");
mem::drop(hconn_c.process(out.dgram(), now()));
let out = hconn_s.process(None, now());
mem::drop(hconn_c.process(out.dgram(), now()));
@ -379,3 +413,48 @@ fn zerortt() {
exchange_packets(&mut hconn_c, &mut hconn_s, out.dgram());
process_client_events(&mut hconn_c);
}
#[test]
/// When a client has an outstanding fetch, it will send keepalives.
/// Test that it will successfully run until the connection times out.
fn fetch_noresponse_will_idletimeout() {
let mut hconn_c = default_http3_client();
let mut hconn_s = default_http3_server();
let (dgram, mut now) =
connect_peers_with_network_propagation_delay(&mut hconn_c, &mut hconn_s, 10);
qtrace!("-----client");
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);
qtrace!("-----server");
let mut done = false;
while !done {
while let Some(event) = hconn_c.next_event() {
if let Http3ClientEvent::StateChange(state) = event {
match state {
Http3State::Closing(error_code) | Http3State::Closed(error_code) => {
assert_eq!(error_code, ConnectionError::Transport(Error::IdleTimeout));
done = true;
}
_ => {}
}
}
}
if let Output::Callback(t) = hconn_c.process_output(now) {
now += t;
}
}
}

View file

@ -0,0 +1,315 @@
// 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_common::event::Provider;
use neqo_crypto::AuthenticationStatus;
use neqo_http3::{
Http3Client, Http3ClientEvent, Http3OrWebTransportStream, Http3Parameters, Http3Server,
Http3ServerEvent, Http3State, WebTransportEvent, WebTransportRequest, WebTransportServerEvent,
WebTransportSessionAcceptAction,
};
use neqo_transport::{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,
};
fn connect() -> (Http3Client, Http3Server) {
fixture_init();
let mut client = Http3Client::new(
DEFAULT_SERVER_NAME,
Rc::new(RefCell::new(CountingConnectionIdGenerator::default())),
addr(),
addr(),
Http3Parameters::default().webtransport(true),
now(),
)
.expect("create a default client");
let mut server = Http3Server::new(
now(),
DEFAULT_KEYS,
DEFAULT_ALPN_H3,
anti_replay(),
Rc::new(RefCell::new(CountingConnectionIdGenerator::default())),
Http3Parameters::default().webtransport(true),
None,
)
.expect("create a server");
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 mut out = client.process(out.dgram(), now()).dgram();
let connected = |e| matches!(e, Http3ClientEvent::StateChange(Http3State::Connected));
assert!(client.events().any(connected));
assert_eq!(client.state(), Http3State::Connected);
// Exchange H3 setttings
loop {
out = server.process(out, now()).dgram();
let dgram_present = out.is_some();
out = client.process(out, now()).dgram();
if out.is_none() && !dgram_present {
break;
}
}
(client, 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;
}
}
}
fn create_wt_session(client: &mut Http3Client, server: &mut Http3Server) -> WebTransportRequest {
let wt_session_id = client
.webtransport_create_session(now(), &("https", "something.com", "/"), &[])
.unwrap();
exchange_packets(client, server);
let mut wt_server_session = None;
while let Some(event) = 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(&WebTransportSessionAcceptAction::Accept)
.unwrap();
wt_server_session = Some(session);
}
Http3ServerEvent::Data { .. } => {
panic!("There should not be any data events!");
}
_ => {}
}
}
exchange_packets(client, server);
let wt_session_negotiated_event = |e| {
matches!(
e,
Http3ClientEvent::WebTransport(WebTransportEvent::Session{
stream_id,
status
}) if stream_id == wt_session_id && status == 200
)
};
assert!(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 send_data_client(
client: &mut Http3Client,
server: &mut Http3Server,
wt_stream_id: StreamId,
data: &[u8],
) {
assert_eq!(client.send_data(wt_stream_id, data).unwrap(), data.len());
exchange_packets(client, server);
}
fn send_data_server(
client: &mut Http3Client,
server: &mut Http3Server,
wt_stream: &mut Http3OrWebTransportStream,
data: &[u8],
) {
assert_eq!(wt_stream.send_data(data).unwrap(), data.len());
exchange_packets(client, server);
}
fn receive_data_client(
client: &mut Http3Client,
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) = 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) = 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 receive_data_server(
client: &mut Http3Client,
server: &mut Http3Server,
stream_id: StreamId,
new_stream: bool,
expected_data: &[u8],
expected_fin: bool,
) -> Http3OrWebTransportStream {
exchange_packets(client, server);
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) = 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()
}
#[test]
fn wt_client_stream_uni() {
const BUF_CLIENT: &[u8] = &[0; 10];
let (mut client, mut server) = connect();
let wt_session = create_wt_session(&mut client, &mut server);
let wt_stream = client
.webtransport_create_stream(wt_session.stream_id(), StreamType::UniDi)
.unwrap();
send_data_client(&mut client, &mut server, wt_stream, BUF_CLIENT);
exchange_packets(&mut client, &mut server);
receive_data_server(&mut client, &mut 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 client, mut server) = connect();
let wt_session = create_wt_session(&mut client, &mut server);
let wt_client_stream = client
.webtransport_create_stream(wt_session.stream_id(), StreamType::BiDi)
.unwrap();
send_data_client(&mut client, &mut server, wt_client_stream, BUF_CLIENT);
let mut wt_server_stream = receive_data_server(
&mut client,
&mut server,
wt_client_stream,
true,
BUF_CLIENT,
false,
);
send_data_server(&mut client, &mut server, &mut wt_server_stream, BUF_SERVER);
receive_data_client(&mut client, wt_client_stream, false, BUF_SERVER, false);
}
#[test]
fn wt_server_stream_uni() {
const BUF_SERVER: &[u8] = &[2; 30];
let (mut client, mut server) = connect();
let mut wt_session = create_wt_session(&mut client, &mut server);
let mut wt_server_stream = wt_session.create_stream(StreamType::UniDi).unwrap();
send_data_server(&mut client, &mut server, &mut wt_server_stream, BUF_SERVER);
receive_data_client(
&mut 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 client, mut server) = connect();
let mut wt_session = create_wt_session(&mut client, &mut server);
let mut wt_server_stream = wt_session.create_stream(StreamType::BiDi).unwrap();
send_data_server(&mut client, &mut server, &mut wt_server_stream, BUF_SERVER);
receive_data_client(
&mut client,
wt_server_stream.stream_id(),
true,
BUF_SERVER,
false,
);
send_data_client(
&mut client,
&mut server,
wt_server_stream.stream_id(),
BUF_CLIENT,
);
assert_eq!(
receive_data_server(
&mut client,
&mut server,
wt_server_stream.stream_id(),
false,
BUF_CLIENT,
false
)
.stream_id(),
wt_server_stream.stream_id()
);
}

View file

@ -1,156 +0,0 @@
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use super::{connect, default_http3_client, default_http3_server, exchange_packets};
use neqo_common::{event::Provider, Encoder};
use neqo_crypto::AuthenticationStatus;
use neqo_http3::{
settings::{HSetting, HSettingType, HSettings},
Error, HFrame, Http3Client, Http3ClientEvent, Http3State, WebTransportEvent,
};
use neqo_transport::{Connection, ConnectionError, StreamType};
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);
}
fn exchange_packets2(client: &mut Http3Client, server: &mut Connection) {
let mut out = None;
loop {
out = client.process(out, now()).dgram();
out = server.process(out, now()).dgram();
if out.is_none() {
break;
}
}
}
#[test]
fn wrong_setting_value() {
const CONTROL_STREAM_TYPE: &[u8] = &[0x0];
let mut client = default_http3_client(false);
let mut server = default_server_h3();
exchange_packets2(&mut client, &mut server);
client.authenticated(AuthenticationStatus::Ok, now());
exchange_packets2(&mut client, &mut server);
let control = server.stream_create(StreamType::UniDi).unwrap();
server.stream_send(control, CONTROL_STREAM_TYPE).unwrap();
// Encode a settings frame and send it.
let mut enc = Encoder::default();
let settings = HFrame::Settings {
settings: HSettings::new(&[HSetting::new(HSettingType::EnableWebTransport, 2)]),
};
settings.encode(&mut enc);
assert_eq!(
server.stream_send(control, enc.as_ref()).unwrap(),
enc.as_ref().len()
);
exchange_packets2(&mut client, &mut server);
match client.state() {
Http3State::Closing(err) | Http3State::Closed(err) => {
assert_eq!(
err,
ConnectionError::Application(Error::HttpSettings.code())
);
}
_ => panic!("Wrong state {:?}", client.state()),
};
}

View file

@ -1 +1 @@
{"files":{"Cargo.toml":"9c7bc0f5de79c42f0957ab114f8a4ca38c276cc9f90e3823dc3b5e3a85226b66","src/decoder.rs":"8bd336c91cca989883106a9d0bf26b117d224e0e7643960c3e97d0168d1853c4","src/decoder_instructions.rs":"2205c7635b8f0c568f6fe9a63c17028eaf8d29a9b5ac7136b6554cc7fbf35038","src/encoder.rs":"b888a819595fec47037d508b943f5ff04ed52ea376bef1f90e08edc6576e773c","src/encoder_instructions.rs":"1eb4f6eee2d9ff16f96dc5bf80dae9bc04316126f6eca933fb51dbd9218a439c","src/header_block.rs":"76f4c8fad6a13d4d24530cf067d20622cdbd345f7d9779b0be9691a77fa8fb63","src/huffman.rs":"3a9edaf827343ec6e43cfd50fcc0d0077287947160ae630da5c3ddaaefedd010","src/huffman_decode_helper.rs":"2970c57f052878b727c2f764490c54184f5c2608e1d6aa961c3b01509e290122","src/huffman_table.rs":"06fea766a6276ac56c7ee0326faed800a742c15fda1f33bf2513e6cc6a5e6d27","src/lib.rs":"6e25612bb30f0e4361566662da1e5353131ae12f97938c6ac3b2dafbf6a8bc86","src/prefix.rs":"72c587c40aef4ed38cf13b2de91091d671611679be2a9da6f0b24abafaf50dc5","src/qlog.rs":"7618085e27bb3fb1f4d1c73ba501b9a293723293c4020b7cc4129676eb278131","src/qpack_send_buf.rs":"49ded6607ec0859cb3edc5a38ff48f4d2d292f0721673d4e20700d07ac324557","src/reader.rs":"be265cc8c317512f266fafdcc835d0e413caf5280a7cc945bfe6e7e849529d67","src/static_table.rs":"fda9d5c6f38f94b0bf92d3afdf8432dce6e27e189736596e16727090c77b78ec","src/stats.rs":"624dfa3b40858c304097bb0ce5b1be1bb4d7916b1abfc222f1aa705907009730","src/table.rs":"f7091bdd9ad1f8fe3b2298a7dbfd3d285c212d69569cda54f9bcf251cb758a21"},"package":null}
{"files":{"Cargo.toml":"880cdcddc73dceafa69773ae482a1eaaced927504856130b0cc15ec91ac31e59","src/decoder.rs":"8bd336c91cca989883106a9d0bf26b117d224e0e7643960c3e97d0168d1853c4","src/decoder_instructions.rs":"2205c7635b8f0c568f6fe9a63c17028eaf8d29a9b5ac7136b6554cc7fbf35038","src/encoder.rs":"b888a819595fec47037d508b943f5ff04ed52ea376bef1f90e08edc6576e773c","src/encoder_instructions.rs":"1eb4f6eee2d9ff16f96dc5bf80dae9bc04316126f6eca933fb51dbd9218a439c","src/header_block.rs":"9ee3bc4189ae36ae0a926295f6f8b3a1169463a19ccbc65e6d588c8b317020ad","src/huffman.rs":"3a9edaf827343ec6e43cfd50fcc0d0077287947160ae630da5c3ddaaefedd010","src/huffman_decode_helper.rs":"2970c57f052878b727c2f764490c54184f5c2608e1d6aa961c3b01509e290122","src/huffman_table.rs":"06fea766a6276ac56c7ee0326faed800a742c15fda1f33bf2513e6cc6a5e6d27","src/lib.rs":"6e25612bb30f0e4361566662da1e5353131ae12f97938c6ac3b2dafbf6a8bc86","src/prefix.rs":"72c587c40aef4ed38cf13b2de91091d671611679be2a9da6f0b24abafaf50dc5","src/qlog.rs":"7618085e27bb3fb1f4d1c73ba501b9a293723293c4020b7cc4129676eb278131","src/qpack_send_buf.rs":"bc86cce786b6c8a468aed8d436ec4a8a86b3d4a917495fff7931ba4026c36c47","src/reader.rs":"be265cc8c317512f266fafdcc835d0e413caf5280a7cc945bfe6e7e849529d67","src/static_table.rs":"fda9d5c6f38f94b0bf92d3afdf8432dce6e27e189736596e16727090c77b78ec","src/stats.rs":"624dfa3b40858c304097bb0ce5b1be1bb4d7916b1abfc222f1aa705907009730","src/table.rs":"f7091bdd9ad1f8fe3b2298a7dbfd3d285c212d69569cda54f9bcf251cb758a21"},"package":null}

View file

@ -1,6 +1,6 @@
[package]
name = "neqo-qpack"
version = "0.6.1"
version = "0.6.3"
authors = ["Dragana Damjanovic <dragana.damjano@gmail.com>"]
edition = "2018"
rust-version = "1.57.0"

View file

@ -161,7 +161,7 @@ impl HeaderEncoder {
impl Deref for HeaderEncoder {
type Target = [u8];
fn deref(&self) -> &Self::Target {
&*self.buf
&self.buf
}
}

View file

@ -99,7 +99,7 @@ impl QpackData {
impl Deref for QpackData {
type Target = [u8];
fn deref(&self) -> &Self::Target {
&*self.buf
&self.buf
}
}

File diff suppressed because one or more lines are too long

View file

@ -1,6 +1,6 @@
[package]
name = "neqo-transport"
version = "0.6.1"
version = "0.6.3"
authors = ["EKR <ekr@rtfm.com>", "Andy Grover <agrover@mozilla.com>"]
edition = "2018"
rust-version = "1.57.0"

View file

@ -127,13 +127,13 @@ pub struct ConnectionIdRef<'a> {
impl<'a> ::std::fmt::Debug for ConnectionIdRef<'a> {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(f, "CID {}", hex_with_len(&self.cid))
write!(f, "CID {}", hex_with_len(self.cid))
}
}
impl<'a> ::std::fmt::Display for ConnectionIdRef<'a> {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(f, "{}", hex(&self.cid))
write!(f, "{}", hex(self.cid))
}
}

View file

@ -46,7 +46,7 @@ impl IdleTimeout {
IdleTimeoutState::Init => now,
IdleTimeoutState::PacketReceived(t) | IdleTimeoutState::AckElicitingPacketSent(t) => t,
};
let delay = if keep_alive {
let delay = if keep_alive && !self.keep_alive_outstanding {
// For a keep-alive timer, wait for half the timeout interval, but be sure
// not to wait too little or we will send many unnecessary probes.
max(self.timeout / 2, pto)

View file

@ -251,7 +251,7 @@ pub struct Connection {
received_untracked: bool,
/// This is responsible for the QuicDatagrams' handling:
/// https://datatracker.ietf.org/doc/html/draft-ietf-quic-datagram
/// <https://datatracker.ietf.org/doc/html/draft-ietf-quic-datagram>
quic_datagrams: QuicDatagrams,
pub(crate) crypto: Crypto,
@ -607,13 +607,13 @@ impl Connection {
}
}
/// Get a resumption token. The correct way to obtain a resumption token is
/// waiting for the `ConnectionEvent::ResumptionToken` event. However, some
/// servers don't send `NEW_TOKEN` frames and so that event might be slow in
/// arriving. This is especially a problem for short-lived connections, where
/// the connection is closed before any events are released. This retrieves
/// the token, without waiting for the `NEW_TOKEN` frame to arrive.
///
/// The correct way to obtain a resumption token is to wait for the
/// `ConnectionEvent::ResumptionToken` event. To emit the event we are waiting for a
/// resumption token and a `NEW_TOKEN` frame to arrive. Some servers don't send `NEW_TOKEN`
/// frames and in this case, we wait for 3xPTO before emitting an event. This is especially a
/// problem for short-lived connections, where the connection is closed before any events are
/// released. This function retrieves the token, without waiting for a `NEW_TOKEN` frame to
/// arrive.
/// # Panics
/// If this is called on a server.
pub fn take_resumption_token(&mut self, now: Instant) -> Option<ResumptionToken> {
@ -661,20 +661,20 @@ impl Connection {
qtrace!([self], " RTT {:?}", rtt);
let tp_slice = dec.decode_vvec().ok_or(Error::InvalidResumptionToken)?;
qtrace!([self], " transport parameters {}", hex(&tp_slice));
qtrace!([self], " transport parameters {}", hex(tp_slice));
let mut dec_tp = Decoder::from(tp_slice);
let tp =
TransportParameters::decode(&mut dec_tp).map_err(|_| Error::InvalidResumptionToken)?;
let init_token = dec.decode_vvec().ok_or(Error::InvalidResumptionToken)?;
qtrace!([self], " Initial token {}", hex(&init_token));
qtrace!([self], " Initial token {}", hex(init_token));
let tok = dec.decode_remainder();
qtrace!([self], " TLS token {}", hex(&tok));
qtrace!([self], " TLS token {}", hex(tok));
match self.crypto.tls {
Agent::Client(ref mut c) => {
let res = c.enable_resumption(&tok);
let res = c.enable_resumption(tok);
if let Err(e) = res {
self.absorb_error::<Error>(now, Err(Error::from(e)));
return Ok(());
@ -2101,7 +2101,7 @@ impl Connection {
// Determine how we are sending packets (PTO, etc..).
let mtu = path.borrow().mtu();
let profile = self.loss_recovery.send_profile(&*path.borrow(), now);
let profile = self.loss_recovery.send_profile(&path.borrow(), now);
qdebug!([self], "output_path send_profile {:?}", profile);
// Frames for different epochs must go in different packets, but then these
@ -2222,7 +2222,12 @@ impl Connection {
let mut packets: Vec<u8> = encoder.into();
if let Some(mut initial) = initial_sent.take() {
if needs_padding {
qdebug!([self], "pad Initial to path MTU {}", mtu);
qdebug!(
[self],
"pad Initial from {} to path MTU {}",
packets.len(),
mtu
);
initial.size += mtu - packets.len();
packets.resize(mtu, 0);
}
@ -2357,7 +2362,7 @@ impl Connection {
self.cid_manager.set_limit(max_active_cids);
}
self.set_initial_limits();
qlog::connection_tparams_set(&mut self.qlog, &*self.tps.borrow());
qlog::connection_tparams_set(&mut self.qlog, &self.tps.borrow());
Ok(())
}

View file

@ -71,7 +71,7 @@ fn early_application_close() {
let dgram = server.process(dgram, now()).dgram();
assert!(dgram.is_some());
server.close(now(), 77, String::from(""));
server.close(now(), 77, String::new());
assert!(server.state().closed());
let dgram = server.process(None, now()).dgram();
assert!(dgram.is_some());

View file

@ -6,9 +6,9 @@
use super::super::{Connection, ConnectionParameters, IdleTimeout, Output, State};
use super::{
connect, connect_force_idle, connect_with_rtt, default_client, default_server,
maybe_authenticate, new_client, new_server, send_and_receive, send_something, AT_LEAST_PTO,
DEFAULT_STREAM_DATA,
connect, connect_force_idle, connect_rtt_idle, connect_with_rtt, default_client,
default_server, maybe_authenticate, new_client, new_server, send_and_receive, send_something,
AT_LEAST_PTO, DEFAULT_STREAM_DATA,
};
use crate::packet::PacketBuilder;
use crate::stats::FrameStats;
@ -272,7 +272,7 @@ fn idle_caching() {
let mut client = default_client();
let mut server = default_server();
let start = now();
let mut builder = PacketBuilder::short(Encoder::new(), false, &[]);
let mut builder = PacketBuilder::short(Encoder::new(), false, []);
// Perform the first round trip, but drop the Initial from the server.
// The client then caches the Handshake packet.
@ -657,3 +657,78 @@ fn keep_alive_uni() {
server.process_input(dgram.unwrap(), now());
server.stream_keep_alive(stream, true).unwrap();
}
/// Test a keep-alive ping is send if there are outstading ack-eliciting packets and that
/// the connection is closed after the idle timeout passes.
#[test]
fn keep_alive_with_ack_eliciting_packet_lost() {
const RTT: Duration = Duration::from_millis(500); // PTO will be ~1.1125s
// The idle time out will be set to ~ 5 * PTO. (IDLE_TIMEOUT/2 > pto and IDLE_TIMEOUT/2 < pto + 2pto)
// After handshake all packets will be lost. The following steps will happen after the handshake:
// - data will be sent on a stream that is marked for keep-alive, (at start time)
// - PTO timer will trigger first, and the data will be retransmited toghether with a PING, (at the start time + pto)
// - keep-alive timer will trigger and a keep-alive PING will be sent, (at the start time + IDLE_TIMEOUT / 2)
// - PTO timer will trigger again. (at the start time + pto + 2*pto)
// - Idle time out will trigger (at the timeout + IDLE_TIMEOUT)
const IDLE_TIMEOUT: Duration = Duration::from_millis(6000);
let mut client = new_client(ConnectionParameters::default().idle_timeout(IDLE_TIMEOUT));
let mut server = default_server();
let mut now = connect_rtt_idle(&mut client, &mut server, RTT);
// connect_rtt_idle increase now by RTT / 2;
now -= RTT / 2;
assert_idle(&mut client, now, IDLE_TIMEOUT);
// Create a stream.
let stream = client.stream_create(StreamType::BiDi).unwrap();
// Marking the stream for keep-alive changes the idle timeout.
client.stream_keep_alive(stream, true).unwrap();
assert_idle(&mut client, now, IDLE_TIMEOUT / 2);
// Send data on the stream that will be lost.
let _ = client.stream_send(stream, DEFAULT_STREAM_DATA).unwrap();
let _lost_packet = client.process_output(now).dgram();
let pto = client.process_output(now).callback();
// Wait for packet to be marked lost.
assert!(pto < IDLE_TIMEOUT / 2);
now += pto;
let retransmit = client.process_output(now).dgram();
assert!(retransmit.is_some());
let retransmit = client.process_output(now).dgram();
assert!(retransmit.is_some());
// The next callback should be for an idle PING.
assert_eq!(
client.process_output(now).callback(),
IDLE_TIMEOUT / 2 - pto
);
// Wait that long and the client should send a PING frame.
now += IDLE_TIMEOUT / 2 - pto;
let pings_before = client.stats().frame_tx.ping;
let ping = client.process_output(now).dgram();
assert!(ping.is_some());
assert_eq!(client.stats().frame_tx.ping, pings_before + 1);
// The next callback is for a PTO, the PTO timer is 2 * pto now.
assert_eq!(client.process_output(now).callback(), pto * 2);
now += pto * 2;
// Now we will retransmit stream data.
let retransmit = client.process_output(now).dgram();
assert!(retransmit.is_some());
let retransmit = client.process_output(now).dgram();
assert!(retransmit.is_some());
// The next callback will be an idle timeout.
assert_eq!(
client.process_output(now).callback(),
IDLE_TIMEOUT / 2 - 2 * pto
);
now += IDLE_TIMEOUT / 2 - 2 * pto;
let out = client.process_output(now);
assert!(matches!(out, Output::None));
assert!(matches!(client.state(), State::Closed(_)));
}

View file

@ -674,7 +674,7 @@ fn change_flow_control(stream_type: StreamType, new_fc: u64) {
// server should receive a MAX_SREAM_DATA frame if the flow control window is updated.
let out2 = client.process(None, now());
let out3 = server.process(out2.dgram(), now());
let expected = if RECV_BUFFER_START < new_fc { 1 } else { 0 };
let expected = usize::from(RECV_BUFFER_START < new_fc);
assert_eq!(server.stats().frame_rx.max_stream_data, expected);
// If the flow control window has been increased, server can write more data.

View file

@ -803,7 +803,7 @@ mod test {
fc[StreamType::BiDi].add_retired(1);
fc[StreamType::BiDi].send_flowc_update();
// consume the frame
let mut builder = PacketBuilder::short(Encoder::new(), false, &[]);
let mut builder = PacketBuilder::short(Encoder::new(), false, []);
let mut tokens = Vec::new();
fc[StreamType::BiDi].write_frames(&mut builder, &mut tokens, &mut FrameStats::default());
assert_eq!(tokens.len(), 1);

View file

@ -48,6 +48,7 @@ pub use self::connection::{
};
pub use self::events::{ConnectionEvent, ConnectionEvents};
pub use self::frame::CloseError;
pub use self::quic_datagrams::DatagramTracking;
pub use self::stats::Stats;
pub use self::stream_id::{StreamId, StreamType};
pub use self::version::Version;

View file

@ -767,7 +767,7 @@ impl Path {
// Send PATH_RESPONSE.
let resp_sent = if let Some(challenge) = self.challenge.take() {
qtrace!([self], "Responding to path challenge {}", hex(&challenge));
qtrace!([self], "Responding to path challenge {}", hex(challenge));
builder.encode_varint(FRAME_TYPE_PATH_RESPONSE);
builder.encode(&challenge[..]);
if builder.len() > builder.limit() {

View file

@ -338,7 +338,7 @@ fn frame_to_qlogframe(frame: &Frame) -> QuicFrame {
Frame::Crypto { offset, data } => {
QuicFrame::crypto(offset.to_string(), data.len().to_string())
}
Frame::NewToken { token } => QuicFrame::new_token(token.len().to_string(), hex(&token)),
Frame::NewToken { token } => QuicFrame::new_token(token.len().to_string(), hex(token)),
Frame::Stream {
fin,
stream_id,
@ -397,7 +397,7 @@ fn frame_to_qlogframe(frame: &Frame) -> QuicFrame {
sequence_number.to_string(),
retire_prior.to_string(),
connection_id.len() as u64,
hex(&connection_id),
hex(connection_id),
hex(stateless_reset_token),
),
Frame::RetireConnectionId { sequence_number } => {

View file

@ -679,7 +679,7 @@ impl LossRecovery {
};
let (acked_packets, any_ack_eliciting) =
space.remove_acked(acked_ranges, &mut *self.stats.borrow_mut());
space.remove_acked(acked_ranges, &mut self.stats.borrow_mut());
if acked_packets.is_empty() {
// No new information.
return (Vec::new(), Vec::new());
@ -887,7 +887,7 @@ impl LossRecovery {
self.pto_state
.as_mut()
.unwrap()
.count_pto(&mut *self.stats.borrow_mut());
.count_pto(&mut self.stats.borrow_mut());
qlog::metrics_updated(
&mut self.qlog,

View file

@ -1248,7 +1248,7 @@ mod tests {
assert!(s.has_frames_to_write());
// consume it
let mut builder = PacketBuilder::short(Encoder::new(), false, &[]);
let mut builder = PacketBuilder::short(Encoder::new(), false, []);
let mut token = Vec::new();
s.write_frame(&mut builder, &mut token, &mut FrameStats::default());
@ -1361,7 +1361,7 @@ mod tests {
s.read(&mut buf).unwrap();
assert!(session_fc.borrow().frame_needed());
// consume it
let mut builder = PacketBuilder::short(Encoder::new(), false, &[]);
let mut builder = PacketBuilder::short(Encoder::new(), false, []);
let mut token = Vec::new();
session_fc
.borrow_mut()
@ -1382,7 +1382,7 @@ mod tests {
s.read(&mut buf).unwrap();
assert!(session_fc.borrow().frame_needed());
// consume it
let mut builder = PacketBuilder::short(Encoder::new(), false, &[]);
let mut builder = PacketBuilder::short(Encoder::new(), false, []);
let mut token = Vec::new();
session_fc
.borrow_mut()
@ -1630,7 +1630,7 @@ mod tests {
assert!(s.fc().unwrap().frame_needed());
// Write the fc update frame
let mut builder = PacketBuilder::short(Encoder::new(), false, &[]);
let mut builder = PacketBuilder::short(Encoder::new(), false, []);
let mut token = Vec::new();
let mut stats = FrameStats::default();
fc.borrow_mut()

View file

@ -1586,7 +1586,7 @@ mod tests {
ss.insert(StreamId::from(0), s);
let mut tokens = Vec::new();
let mut builder = PacketBuilder::short(Encoder::new(), false, &[]);
let mut builder = PacketBuilder::short(Encoder::new(), false, []);
// Write a small frame: no fin.
let written = builder.len();
@ -1675,7 +1675,7 @@ mod tests {
ss.insert(StreamId::from(0), s);
let mut tokens = Vec::new();
let mut builder = PacketBuilder::short(Encoder::new(), false, &[]);
let mut builder = PacketBuilder::short(Encoder::new(), false, []);
ss.write_frames(
TransmissionPriority::default(),
&mut builder,
@ -1753,7 +1753,7 @@ mod tests {
assert_eq!(s.next_bytes(false), Some((0, &b"ab"[..])));
// This doesn't report blocking yet.
let mut builder = PacketBuilder::short(Encoder::new(), false, &[]);
let mut builder = PacketBuilder::short(Encoder::new(), false, []);
let mut tokens = Vec::new();
let mut stats = FrameStats::default();
s.write_blocked_frame(
@ -1806,7 +1806,7 @@ mod tests {
assert_eq!(s.send_atomic(b"abc").unwrap(), 0);
// Assert that STREAM_DATA_BLOCKED is sent.
let mut builder = PacketBuilder::short(Encoder::new(), false, &[]);
let mut builder = PacketBuilder::short(Encoder::new(), false, []);
let mut tokens = Vec::new();
let mut stats = FrameStats::default();
s.write_blocked_frame(
@ -1893,7 +1893,7 @@ mod tests {
s.mark_as_lost(len_u64, 0, true);
// No frame should be sent here.
let mut builder = PacketBuilder::short(Encoder::new(), false, &[]);
let mut builder = PacketBuilder::short(Encoder::new(), false, []);
let mut tokens = Vec::new();
let mut stats = FrameStats::default();
s.write_stream_frame(
@ -1954,7 +1954,7 @@ mod tests {
s.close();
}
let mut builder = PacketBuilder::short(Encoder::new(), false, &[]);
let mut builder = PacketBuilder::short(Encoder::new(), false, []);
let header_len = builder.len();
builder.set_limit(header_len + space);
@ -2055,7 +2055,7 @@ mod tests {
s.send(data).unwrap();
s.close();
let mut builder = PacketBuilder::short(Encoder::new(), false, &[]);
let mut builder = PacketBuilder::short(Encoder::new(), false, []);
let header_len = builder.len();
// Add 2 for the frame type and stream ID, then add the extra.
builder.set_limit(header_len + data.len() + 2 + extra);

View file

@ -216,7 +216,7 @@ pub struct StatsCell {
impl Deref for StatsCell {
type Target = RefCell<Stats>;
fn deref(&self) -> &Self::Target {
&*self.stats
&self.stats
}
}

View file

@ -880,7 +880,7 @@ mod tests {
}
fn write_frame(rp: &mut RecvdPackets) {
let mut builder = PacketBuilder::short(Encoder::new(), false, &[]);
let mut builder = PacketBuilder::short(Encoder::new(), false, []);
let mut stats = FrameStats::default();
let mut tokens = Vec::new();
rp.write_frame(*NOW, &mut builder, &mut tokens, &mut stats);
@ -1013,7 +1013,7 @@ mod tests {
#[test]
fn drop_spaces() {
let mut tracker = AckTracker::default();
let mut builder = PacketBuilder::short(Encoder::new(), false, &[]);
let mut builder = PacketBuilder::short(Encoder::new(), false, []);
tracker
.get_mut(PacketNumberSpace::Initial)
.unwrap()
@ -1072,7 +1072,7 @@ mod tests {
.set_received(*NOW, 0, true);
assert!(tracker.ack_time(*NOW - Duration::from_millis(1)).is_some());
let mut builder = PacketBuilder::short(Encoder::new(), false, &[]);
let mut builder = PacketBuilder::short(Encoder::new(), false, []);
builder.set_limit(10);
let mut stats = FrameStats::default();
@ -1102,7 +1102,7 @@ mod tests {
.set_received(*NOW, 2, true);
assert!(tracker.ack_time(*NOW - Duration::from_millis(1)).is_some());
let mut builder = PacketBuilder::short(Encoder::new(), false, &[]);
let mut builder = PacketBuilder::short(Encoder::new(), false, []);
builder.set_limit(32);
let mut stats = FrameStats::default();

View file

@ -205,7 +205,7 @@ impl Simulator {
}
if self.nodes.iter().all(|n| n.node.done()) {
let real_elapsed = Instant::now() - real_start;
let real_elapsed = real_start.elapsed();
println!("{}: real elapsed time: {:?}", self.name, real_elapsed);
let elapsed = now - start;
println!("{}: simulated elapsed time: {:?}", self.name, elapsed);