diff --git a/.cargo/config.in b/.cargo/config.in index 960b9149f90e..839847aa3a93 100644 --- a/.cargo/config.in +++ b/.cargo/config.in @@ -20,6 +20,11 @@ git = "https://github.com/chris-zen/coremidi.git" rev = "fc68464b5445caf111e41f643a2e69ccce0b4f83" replace-with = "vendored-sources" +[source."git+https://github.com/cloudflare/quiche?rev=09ea4b244096a013071cfe2175bbf2945fb7f8d1"] +git = "https://github.com/cloudflare/quiche" +rev = "09ea4b244096a013071cfe2175bbf2945fb7f8d1" +replace-with = "vendored-sources" + [source."git+https://github.com/franziskuskiefer/cose-rust?rev=43c22248d136c8b38fe42ea709d08da6355cf04b"] git = "https://github.com/franziskuskiefer/cose-rust" rev = "43c22248d136c8b38fe42ea709d08da6355cf04b" @@ -85,9 +90,9 @@ git = "https://github.com/mozilla/mp4parse-rust" rev = "a138e40ec1c603615873e524b5b22e11c0ec4820" replace-with = "vendored-sources" -[source."git+https://github.com/mozilla/neqo?tag=v0.6.8"] +[source."git+https://github.com/mozilla/neqo?tag=v0.7.0"] git = "https://github.com/mozilla/neqo" -tag = "v0.6.8" +tag = "v0.7.0" replace-with = "vendored-sources" [source."git+https://github.com/mozilla/uniffi-rs.git?rev=afb29ebdc1d9edf15021b1c5332fc9f285bbe13b"] diff --git a/Cargo.lock b/Cargo.lock index da015aedbed5..0251efdc41bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1519,6 +1519,26 @@ dependencies = [ "packed_simd", ] +[[package]] +name = "enum-map" +version = "2.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6866f3bfdf8207509a033af1a75a7b08abda06bbaaeae6669323fd5a097df2e9" +dependencies = [ + "enum-map-derive", +] + +[[package]] +name = "enum-map-derive" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "enumset" version = "1.1.2" @@ -3811,9 +3831,10 @@ dependencies = [ [[package]] name = "neqo-common" -version = "0.6.8" -source = "git+https://github.com/mozilla/neqo?tag=v0.6.8#83735a88217a6b3a6a9d3cd5d9243040c5e41319" +version = "0.7.0" +source = "git+https://github.com/mozilla/neqo?tag=v0.7.0#9489511f7c82786f55bc9c713cddbff825507ed7" dependencies = [ + "enum-map", "env_logger", "lazy_static", "log", @@ -3824,10 +3845,10 @@ dependencies = [ [[package]] name = "neqo-crypto" -version = "0.6.8" -source = "git+https://github.com/mozilla/neqo?tag=v0.6.8#83735a88217a6b3a6a9d3cd5d9243040c5e41319" +version = "0.7.0" +source = "git+https://github.com/mozilla/neqo?tag=v0.7.0#9489511f7c82786f55bc9c713cddbff825507ed7" dependencies = [ - "bindgen 0.64.999", + "bindgen 0.69.2", "log", "mozbuild", "neqo-common", @@ -3838,8 +3859,8 @@ dependencies = [ [[package]] name = "neqo-http3" -version = "0.6.8" -source = "git+https://github.com/mozilla/neqo?tag=v0.6.8#83735a88217a6b3a6a9d3cd5d9243040c5e41319" +version = "0.7.0" +source = "git+https://github.com/mozilla/neqo?tag=v0.7.0#9489511f7c82786f55bc9c713cddbff825507ed7" dependencies = [ "enumset", "lazy_static", @@ -3856,8 +3877,8 @@ dependencies = [ [[package]] name = "neqo-qpack" -version = "0.6.8" -source = "git+https://github.com/mozilla/neqo?tag=v0.6.8#83735a88217a6b3a6a9d3cd5d9243040c5e41319" +version = "0.7.0" +source = "git+https://github.com/mozilla/neqo?tag=v0.7.0#9489511f7c82786f55bc9c713cddbff825507ed7" dependencies = [ "lazy_static", "log", @@ -3870,8 +3891,8 @@ dependencies = [ [[package]] name = "neqo-transport" -version = "0.6.8" -source = "git+https://github.com/mozilla/neqo?tag=v0.6.8#83735a88217a6b3a6a9d3cd5d9243040c5e41319" +version = "0.7.0" +source = "git+https://github.com/mozilla/neqo?tag=v0.7.0#9489511f7c82786f55bc9c713cddbff825507ed7" dependencies = [ "indexmap 1.9.3", "lazy_static", @@ -4517,14 +4538,13 @@ dependencies = [ [[package]] name = "qlog" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "321df7a3199d152be256a416096136191e88b7716f1e2e4c8c05b9f77ffb648b" +version = "0.11.0" +source = "git+https://github.com/cloudflare/quiche?rev=09ea4b244096a013071cfe2175bbf2945fb7f8d1#09ea4b244096a013071cfe2175bbf2945fb7f8d1" dependencies = [ "serde", "serde_derive", "serde_json", - "serde_with 1.999.999", + "serde_with", "smallvec", ] @@ -5014,13 +5034,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_with" -version = "1.999.999" -dependencies = [ - "serde_with 3.0.0", -] - [[package]] name = "serde_with" version = "3.0.0" diff --git a/Cargo.toml b/Cargo.toml index a256dbd81dd7..0c75567c1686 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -124,9 +124,6 @@ memmap2 = { path = "build/rust/memmap2" } # Patch cfg-if 0.1 to 1.0 cfg-if = { path = "build/rust/cfg-if" } -# Patch serde_with 1.0 to 3.0 -serde_with = { path = "build/rust/serde_with" } - # Patch redox_users to an empty crate redox_users = { path = "build/rust/redox_users" } diff --git a/build/rust/serde_with/Cargo.toml b/build/rust/serde_with/Cargo.toml deleted file mode 100644 index 3b78a446dafa..000000000000 --- a/build/rust/serde_with/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "serde_with" -version = "1.999.999" -edition = "2018" -license = "MPL-2.0" - -[lib] -path = "lib.rs" - -[dependencies.serde_with] -version = "3" -default-features = false -features = ["macros"] diff --git a/build/rust/serde_with/lib.rs b/build/rust/serde_with/lib.rs deleted file mode 100644 index 8e8a3aa1d12c..000000000000 --- a/build/rust/serde_with/lib.rs +++ /dev/null @@ -1,5 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -pub use serde_with::*; diff --git a/netwerk/protocol/http/Http3Session.cpp b/netwerk/protocol/http/Http3Session.cpp index 9c703f07763a..0369da94ac59 100644 --- a/netwerk/protocol/http/Http3Session.cpp +++ b/netwerk/protocol/http/Http3Session.cpp @@ -2200,6 +2200,7 @@ void Http3Session::SetSecInfo() { // 0x00-0xff. (https://tools.ietf.org/html/draft-ietf-quic-tls_34#section-4.8) // Since telemetry does not allow more than 100 bucket, we use three diffrent // keys to map all alert codes. +const uint32_t HTTP3_TELEMETRY_TRANSPORT_INTERNAL_ERROR = 15; const uint32_t HTTP3_TELEMETRY_TRANSPORT_END = 16; const uint32_t HTTP3_TELEMETRY_TRANSPORT_UNKNOWN = 17; const uint32_t HTTP3_TELEMETRY_TRANSPORT_CRYPTO_UNKNOWN = 18; @@ -2281,7 +2282,7 @@ void Http3Session::CloseConnectionTelemetry(CloseError& aError, bool aClosing) { switch (aError.tag) { case CloseError::Tag::TransportInternalError: key = "transport_internal"_ns; - value = aError.transport_internal_error._0; + value = HTTP3_TELEMETRY_TRANSPORT_INTERNAL_ERROR; break; case CloseError::Tag::TransportInternalErrorOther: key = "transport_other"_ns; diff --git a/netwerk/socket/neqo_glue/Cargo.toml b/netwerk/socket/neqo_glue/Cargo.toml index 353aeafffa13..0e506c8c2d4b 100644 --- a/netwerk/socket/neqo_glue/Cargo.toml +++ b/netwerk/socket/neqo_glue/Cargo.toml @@ -9,16 +9,16 @@ license = "MPL-2.0" name = "neqo_glue" [dependencies] -neqo-http3 = { tag = "v0.6.8", git = "https://github.com/mozilla/neqo" } -neqo-transport = { tag = "v0.6.8", git = "https://github.com/mozilla/neqo" } -neqo-common = { tag = "v0.6.8", git = "https://github.com/mozilla/neqo" } -neqo-qpack = { tag = "v0.6.8", git = "https://github.com/mozilla/neqo" } +neqo-http3 = { tag = "v0.7.0", git = "https://github.com/mozilla/neqo" } +neqo-transport = { tag = "v0.7.0", git = "https://github.com/mozilla/neqo" } +neqo-common = { tag = "v0.7.0", git = "https://github.com/mozilla/neqo" } +neqo-qpack = { tag = "v0.7.0", git = "https://github.com/mozilla/neqo" } nserror = { path = "../../../xpcom/rust/nserror" } nsstring = { path = "../../../xpcom/rust/nsstring" } xpcom = { path = "../../../xpcom/rust/xpcom" } thin-vec = { version = "0.2.1", features = ["gecko-ffi"] } log = "0.4.0" -qlog = "0.9.0" +qlog = { git = "https://github.com/cloudflare/quiche", rev = "09ea4b244096a013071cfe2175bbf2945fb7f8d1" } libc = "0.2.0" static_prefs = { path = "../../../modules/libpref/init/static_prefs"} uuid = { version = "1.0", features = ["v4"] } @@ -27,7 +27,7 @@ uuid = { version = "1.0", features = ["v4"] } winapi = {version = "0.3", features = ["ws2def"] } [dependencies.neqo-crypto] -tag = "v0.6.8" +tag = "v0.7.0" git = "https://github.com/mozilla/neqo" default-features = false features = ["gecko"] diff --git a/netwerk/socket/neqo_glue/src/lib.rs b/netwerk/socket/neqo_glue/src/lib.rs index 77806dc2e1d1..6c86211ed505 100644 --- a/netwerk/socket/neqo_glue/src/lib.rs +++ b/netwerk/socket/neqo_glue/src/lib.rs @@ -5,7 +5,7 @@ #[cfg(not(windows))] use libc::{AF_INET, AF_INET6}; use neqo_common::event::Provider; -use neqo_common::{self as common, qlog::NeqoQlog, qwarn, Datagram, Header, Role}; +use neqo_common::{self as common, qlog::NeqoQlog, qwarn, Datagram, Header, IpTos, Role}; use neqo_crypto::{init, PRErrorCode}; use neqo_http3::{ features::extended_connect::SessionCloseReason, Error as Http3Error, Http3Client, @@ -318,10 +318,15 @@ pub unsafe extern "C" fn neqo_http3conn_process_input( Ok(addr) => addr, Err(result) => return result, }; - conn.conn.process_input( - Datagram::new(remote, conn.local_addr, (*packet).to_vec()), - get_current_or_last_output_time(&conn.last_output_time), + let d = Datagram::new( + remote, + conn.local_addr, + IpTos::default(), + None, + (*packet).to_vec(), ); + conn.conn + .process_input(&d, get_current_or_last_output_time(&conn.last_output_time)); return NS_OK; } @@ -596,7 +601,7 @@ fn crypto_error_code(err: neqo_crypto::Error) -> u64 { // number. #[repr(C)] pub enum CloseError { - TransportInternalError(u16), + TransportInternalError, TransportInternalErrorOther(u16), TransportError(u64), CryptoError(u64), @@ -610,7 +615,7 @@ pub enum CloseError { impl From for CloseError { fn from(error: TransportError) -> CloseError { match error { - TransportError::InternalError(c) => CloseError::TransportInternalError(c), + TransportError::InternalError => CloseError::TransportInternalError, TransportError::CryptoError(neqo_crypto::Error::EchRetry(_)) => CloseError::EchRetry, TransportError::CryptoError(c) => CloseError::CryptoError(crypto_error_code(c)), TransportError::CryptoAlert(c) => CloseError::CryptoAlert(c), @@ -629,7 +634,8 @@ impl From for CloseError { | TransportError::InvalidToken | TransportError::KeysExhausted | TransportError::ApplicationError - | TransportError::NoAvailablePath => CloseError::TransportError(error.code()), + | TransportError::NoAvailablePath + | TransportError::CryptoBufferExceeded => CloseError::TransportError(error.code()), TransportError::EchRetry(_) => CloseError::EchRetry, TransportError::AckedUnsentPacket => CloseError::TransportInternalErrorOther(0), TransportError::ConnectionIdLimitExceeded => CloseError::TransportInternalErrorOther(1), diff --git a/netwerk/test/http3server/Cargo.toml b/netwerk/test/http3server/Cargo.toml index fe436ad15c85..24fdd281374a 100644 --- a/netwerk/test/http3server/Cargo.toml +++ b/netwerk/test/http3server/Cargo.toml @@ -6,10 +6,10 @@ edition = "2018" license = "MPL-2.0" [dependencies] -neqo-transport = { tag = "v0.6.8", git = "https://github.com/mozilla/neqo" } -neqo-common = { tag = "v0.6.8", git = "https://github.com/mozilla/neqo" } -neqo-http3 = { tag = "v0.6.8", git = "https://github.com/mozilla/neqo" } -neqo-qpack = { tag = "v0.6.8", git = "https://github.com/mozilla/neqo" } +neqo-transport = { tag = "v0.7.0", git = "https://github.com/mozilla/neqo" } +neqo-common = { tag = "v0.7.0", git = "https://github.com/mozilla/neqo" } +neqo-http3 = { tag = "v0.7.0", git = "https://github.com/mozilla/neqo" } +neqo-qpack = { tag = "v0.7.0", git = "https://github.com/mozilla/neqo" } mio = "0.6.17" mio-extras = "2.0.5" log = "0.4.0" @@ -21,7 +21,7 @@ tokio = { version = "1", features = ["rt-multi-thread"] } mozilla-central-workspace-hack = { version = "0.1", features = ["http3server"], optional = true } [dependencies.neqo-crypto] -tag = "v0.6.8" +tag = "v0.7.0" git = "https://github.com/mozilla/neqo" default-features = false features = ["gecko"] diff --git a/netwerk/test/http3server/src/main.rs b/netwerk/test/http3server/src/main.rs index 1fecbc233251..27e5e280e58d 100644 --- a/netwerk/test/http3server/src/main.rs +++ b/netwerk/test/http3server/src/main.rs @@ -7,7 +7,7 @@ #![deny(warnings)] use base64::prelude::*; -use neqo_common::{event::Provider, qdebug, qinfo, qtrace, Datagram, Header}; +use neqo_common::{event::Provider, qdebug, qinfo, qtrace, Datagram, Header, IpTos}; use neqo_crypto::{generate_ech_keys, init_db, AllowZeroRtt, AntiReplay}; use neqo_http3::{ Error, Http3OrWebTransportStream, Http3Parameters, Http3Server, Http3ServerEvent, @@ -193,7 +193,7 @@ impl Http3TestServer { impl HttpServer for Http3TestServer { fn process(&mut self, dgram: Option) -> Output { - self.server.process(dgram, Instant::now()) + self.server.process(dgram.as_ref(), Instant::now()) } fn process_events(&mut self) { @@ -633,7 +633,7 @@ impl HttpServer for Http3TestServer { impl HttpServer for Server { fn process(&mut self, dgram: Option) -> Output { - self.process(dgram, Instant::now()) + self.process(dgram.as_ref(), Instant::now()) } fn process_events(&mut self) { @@ -874,7 +874,7 @@ impl Http3ProxyServer { impl HttpServer for Http3ProxyServer { fn process(&mut self, dgram: Option) -> Output { - self.server.process(dgram, Instant::now()) + self.server.process(dgram.as_ref(), Instant::now()) } fn process_events(&mut self) { @@ -1063,7 +1063,13 @@ fn read_dgram( eprintln!("zero length datagram received?"); Ok(None) } else { - Ok(Some(Datagram::new(remote_addr, *local_address, &buf[..sz]))) + Ok(Some(Datagram::new( + remote_addr, + *local_address, + IpTos::default(), + None, + &buf[..sz], + ))) } } diff --git a/supply-chain/audits.toml b/supply-chain/audits.toml index 57805c8ff47e..7faf44027c7e 100644 --- a/supply-chain/audits.toml +++ b/supply-chain/audits.toml @@ -1530,6 +1530,16 @@ who = "Mike Hommey " criteria = "safe-to-deploy" delta = "0.8.31 -> 0.8.32" +[[audits.enum-map]] +who = "Kershaw Chang " +criteria = "safe-to-deploy" +version = "2.7.3" + +[[audits.enum-map-derive]] +who = "Kershaw Chang " +criteria = "safe-to-deploy" +version = "0.17.0" + [[audits.enum-primitive-derive]] who = "Gabriele Svelto " criteria = "safe-to-deploy" @@ -3066,6 +3076,17 @@ who = "Kershaw Chang " criteria = "safe-to-deploy" version = "0.9.0" +[[audits.qlog]] +who = "Kershaw Chang " +criteria = "safe-to-deploy" +delta = "0.9.0 -> 0.11.0" + +[[audits.qlog]] +who = "Kershaw Chang " +criteria = "safe-to-deploy" +delta = "0.11.0 -> 0.11.0@git:09ea4b244096a013071cfe2175bbf2945fb7f8d1" +importable = false + [[audits.quote]] who = "Nika Layzell " criteria = "safe-to-deploy" diff --git a/supply-chain/config.toml b/supply-chain/config.toml index a0308f2e177b..d2dfa0fcb5b0 100644 --- a/supply-chain/config.toml +++ b/supply-chain/config.toml @@ -165,6 +165,10 @@ notes = "This is a first-party crate which is entirely unrelated to the crates.i audit-as-crates-io = true notes = "This is a first-party crate which is also published to crates.io, but we should publish audits for it for the benefit of the ecosystem." +[policy.qlog] +audit-as-crates-io = true +notes = "Use this revision (09ea4b244096a013071cfe2175bbf2945fb7f8d1) of qlog temporarily." + [policy.rure] audit-as-crates-io = true notes = "Identical to upstream, but with cdylib and staticlib targets disabled to avoid unnecessary build artifacts and linker errors." diff --git a/third_party/rust/enum-map-derive/.cargo-checksum.json b/third_party/rust/enum-map-derive/.cargo-checksum.json new file mode 100644 index 000000000000..2650ba111c63 --- /dev/null +++ b/third_party/rust/enum-map-derive/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.toml":"ebe51a5658f0e34f3f31cd0f4af2a90affbc5e562b6d4695fab1ae31348c2590","LICENSES/Apache-2.0.txt":"074e6e32c86a4c0ef8b3ed25b721ca23aca83df277cd88106ef7177c354615ff","LICENSES/CC0-1.0.txt":"a2010f343487d3f7618affe54f789f5487602331c0a8d03f49e9a7c547cf0499","LICENSES/MIT.txt":"b85dcd3e453d05982552c52b5fc9e0bdd6d23c6f8e844b984a88af32570b0cc0","README.md":"9ba5f04156a8fb4aeec6af6db17b6716c18dc73e1db59300340591f246f4558e","src/derive_enum.rs":"fc478b32e580dabfa31f71db2958faf05125e58b17bf420a881930d96e76ce11","src/derive_struct.rs":"ec7e4d1f44925713098713c8c77af87142f499999a812687d52306cc9255224f","src/lib.rs":"531dd501eced4ebf2733d525417adc9ef724e1c117ffb20be7453a79200c2988"},"package":"f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb"} \ No newline at end of file diff --git a/third_party/rust/enum-map-derive/Cargo.toml b/third_party/rust/enum-map-derive/Cargo.toml new file mode 100644 index 000000000000..2e17e5d8eeb8 --- /dev/null +++ b/third_party/rust/enum-map-derive/Cargo.toml @@ -0,0 +1,54 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +rust-version = "1.61" +name = "enum-map-derive" +version = "0.17.0" +authors = ["Kamila Borowska "] +description = "Macros 1.1 implementation of #[derive(Enum)]" +readme = "README.md" +keywords = [ + "data-structure", + "no_std", + "enum", +] +categories = [ + "data-structures", + "no-std", +] +license = "MIT OR Apache-2.0" +repository = "https://codeberg.org/xfix/enum-map" + +[lib] +proc-macro = true + +[dependencies.proc-macro2] +version = "1.0.60" + +[dependencies.quote] +version = "1.0.7" + +[dependencies.syn] +version = "2.0.0" +features = [ + "derive", + "parsing", + "printing", + "proc-macro", +] +default-features = false + +[dev-dependencies] + +[badges.maintenance] +status = "passively-maintained" diff --git a/third_party/rust/enum-map-derive/LICENSES/Apache-2.0.txt b/third_party/rust/enum-map-derive/LICENSES/Apache-2.0.txt new file mode 100644 index 000000000000..137069b82387 --- /dev/null +++ b/third_party/rust/enum-map-derive/LICENSES/Apache-2.0.txt @@ -0,0 +1,73 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/third_party/rust/enum-map-derive/LICENSES/CC0-1.0.txt b/third_party/rust/enum-map-derive/LICENSES/CC0-1.0.txt new file mode 100644 index 000000000000..0e259d42c996 --- /dev/null +++ b/third_party/rust/enum-map-derive/LICENSES/CC0-1.0.txt @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/third_party/rust/enum-map-derive/LICENSES/MIT.txt b/third_party/rust/enum-map-derive/LICENSES/MIT.txt new file mode 100644 index 000000000000..2071b23b0e08 --- /dev/null +++ b/third_party/rust/enum-map-derive/LICENSES/MIT.txt @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/third_party/rust/enum-map-derive/README.md b/third_party/rust/enum-map-derive/README.md new file mode 100644 index 000000000000..a5b8481d7dd3 --- /dev/null +++ b/third_party/rust/enum-map-derive/README.md @@ -0,0 +1,10 @@ + + +# enum-map-derive + +This is a derive macro for `enum-map`. You don't need to specify it +in dependencies as `enum-map` crate re-exports it. diff --git a/third_party/rust/enum-map-derive/src/derive_enum.rs b/third_party/rust/enum-map-derive/src/derive_enum.rs new file mode 100644 index 000000000000..8dce3de98403 --- /dev/null +++ b/third_party/rust/enum-map-derive/src/derive_enum.rs @@ -0,0 +1,216 @@ +// SPDX-FileCopyrightText: 2021 - 2023 Kamila Borowska +// SPDX-FileCopyrightText: 2021 Bruno Corrêa Zimmermann +// +// SPDX-License-Identifier: MIT OR Apache-2.0 + +use crate::type_length; +use proc_macro2::TokenStream; +use quote::{format_ident, quote, ToTokens}; +use syn::{DataEnum, Fields, FieldsNamed, FieldsUnnamed, Ident, Variant}; + +pub fn generate(name: Ident, data_enum: DataEnum) -> TokenStream { + let mut generator = EnumGenerator::empty(); + for variant in &data_enum.variants { + generator.handle_variant(variant); + } + generator.finish(&name) +} + +#[derive(Debug)] +struct Length { + units: usize, + opaque: TokenStream, +} + +impl ToTokens for Length { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { units, opaque } = self; + tokens.extend(quote! { (#units + #opaque) }); + } +} + +/// Total length is the sum of each variant's length. To represent a variant, its number is added to +/// the sum of previous variant lengths. +#[derive(Debug)] +struct EnumGenerator { + length: Length, + from_usize_arms: TokenStream, + into_usize_arms: TokenStream, +} + +impl EnumGenerator { + fn empty() -> Self { + Self { + length: Length { + units: 0, + opaque: quote! { 0 }, + }, + from_usize_arms: quote! {}, + into_usize_arms: quote! {}, + } + } + + fn finish(&self, name: &Ident) -> TokenStream { + let Self { + length, + from_usize_arms, + into_usize_arms, + } = self; + + quote! { + #[automatically_derived] + impl ::enum_map::Enum for #name { + const LENGTH: ::enum_map::usize = #length; + + #[inline] + fn from_usize(value: ::enum_map::usize) -> Self { + #from_usize_arms { + ::enum_map::out_of_bounds() + } + } + + #[inline] + fn into_usize(self) -> ::enum_map::usize { + match self { + #into_usize_arms + } + } + } + + #[automatically_derived] + impl ::enum_map::EnumArray for #name { + type Array = [V; #length]; + } + } + } + + fn handle_variant(&mut self, variant: &Variant) { + match &variant.fields { + Fields::Unit => self.handle_unit_variant(&variant.ident), + Fields::Unnamed(fields) => self.handle_unnamed_variant(&variant.ident, fields), + Fields::Named(fields) => self.handle_named_variant(&variant.ident, fields), + } + } + + /// Becomes simply `1` in counting, since this is the size of the unit. + fn handle_unit_variant(&mut self, variant: &Ident) { + let Self { + length, + from_usize_arms, + into_usize_arms, + } = self; + *into_usize_arms = quote! { #into_usize_arms Self::#variant => #length, }; + *from_usize_arms = quote! { + #from_usize_arms if value == #length { + Self::#variant + } else + }; + self.length.units += 1; + } + + /// Its size is the product of the sizes of its members. To represent this variant, one can + /// think of this as representing a little-endian number. First member is simply added, but + /// next members are multiplied before being added. + fn handle_unnamed_variant(&mut self, variant: &Ident, fields: &FieldsUnnamed) { + let Self { + length, + from_usize_arms, + into_usize_arms, + } = self; + let mut expr_into = quote! { #length }; + let mut fields_length = quote! { 1usize }; + let mut params_from = quote! {}; + for (i, field) in fields.unnamed.iter().enumerate() { + let ident = format_ident!("p{}", i); + let ty = &field.ty; + let field_length = type_length(ty); + + expr_into = quote! { + (#expr_into + #fields_length * ::enum_map::Enum::into_usize(#ident)) + }; + + params_from = quote! { + #params_from <#ty as ::enum_map::Enum>::from_usize( + (value - #length) / #fields_length % #field_length + ), + }; + + fields_length = quote! { (#fields_length * #field_length) }; + } + + *length = Length { + units: 0, + opaque: quote! { (#length + #fields_length) }, + }; + + let from_arms = &from_usize_arms; + *from_usize_arms = quote! { + #from_arms if value < #length { + Self::#variant(#params_from) + } else + }; + + let mut params_into = quote! {}; + for i in 0..fields.unnamed.len() { + let ident = format_ident!("p{}", i); + params_into = quote! { #params_into #ident, }; + } + + *into_usize_arms = quote! { + #into_usize_arms Self::#variant(#params_into) => #expr_into, + }; + } + + /// Its size is the product of the sizes of its members. To represent this variant, one can + /// think of this as representing a little-endian number. First member is simply added, but + /// next members are multiplied before being added. + fn handle_named_variant(&mut self, variant: &Ident, fields: &FieldsNamed) { + let Self { + length, + from_usize_arms, + into_usize_arms, + } = self; + let mut expr_into = quote! { #length }; + let mut fields_length = quote! { 1usize }; + let mut params_from = quote! {}; + + for field in fields.named.iter() { + let ident = field.ident.as_ref().unwrap(); + let ty = &field.ty; + let field_length = type_length(ty); + + expr_into = quote! { + (#expr_into + #fields_length * ::enum_map::Enum::into_usize(#ident)) + }; + + params_from = quote! { + #params_from #ident: <#ty as ::enum_map::Enum>::from_usize( + (value - #length) / #fields_length % #field_length + ), + }; + + fields_length = quote! { (#fields_length * #field_length) }; + } + + *length = Length { + units: 0, + opaque: quote! { (#length + #fields_length) }, + }; + + *from_usize_arms = quote! { + #from_usize_arms if value < #length { + Self::#variant { #params_from } + } else + }; + + let mut params_into = quote! {}; + for field in fields.named.iter() { + let ident = field.ident.as_ref().unwrap(); + params_into = quote! { #params_into #ident, }; + } + + *into_usize_arms = quote! { + #into_usize_arms Self::#variant { #params_into } => #expr_into, + }; + } +} diff --git a/third_party/rust/enum-map-derive/src/derive_struct.rs b/third_party/rust/enum-map-derive/src/derive_struct.rs new file mode 100644 index 000000000000..94f3f97250e9 --- /dev/null +++ b/third_party/rust/enum-map-derive/src/derive_struct.rs @@ -0,0 +1,129 @@ +// SPDX-FileCopyrightText: 2021 - 2022 Kamila Borowska +// SPDX-FileCopyrightText: 2021 Bruno Corrêa Zimmermann +// +// SPDX-License-Identifier: MIT OR Apache-2.0 + +use crate::type_length; +use proc_macro2::TokenStream; +use quote::quote; +use syn::{DataStruct, Fields, FieldsNamed, FieldsUnnamed, Ident, Index}; + +pub fn generate(name: Ident, data_struct: DataStruct) -> TokenStream { + StructGenerator::from_fields(&data_struct.fields).finish(&name) +} + +/// Total length is the product of each member's length. To represent a struct, one can +/// think of this as representing a little-endian number. First member is simply added, but +/// next members are multiplied before being added. +#[derive(Debug)] +struct StructGenerator { + length: TokenStream, + from_usize: TokenStream, + into_usize: TokenStream, +} + +impl StructGenerator { + fn from_fields(fields: &Fields) -> Self { + match fields { + Fields::Unit => Self::from_unit_fields(), + Fields::Unnamed(fields_data) => Self::from_unnamed_fields(fields_data), + Fields::Named(fields_data) => Self::from_named_fields(fields_data), + } + } + + fn from_unit_fields() -> Self { + Self { + length: quote! { 1usize }, + from_usize: quote! { Self }, + into_usize: quote! { 0usize }, + } + } + + fn from_unnamed_fields(fields: &FieldsUnnamed) -> Self { + let mut params_from = quote! {}; + let mut into_usize = quote! { 0usize }; + let mut length = quote! { 1usize }; + for (i, field) in fields.unnamed.iter().enumerate() { + let ty = &field.ty; + let index_ident = Index::from(i); + let field_length = type_length(ty); + + into_usize = quote! { + (#into_usize + #length * ::enum_map::Enum::into_usize(self.#index_ident)) + }; + + params_from = quote! { + #params_from <#ty as ::enum_map::Enum>::from_usize( + value / #length % #field_length + ), + }; + + length = quote! { (#length * #field_length) }; + } + + let from_usize = quote! { Self(#params_from) }; + Self { + length, + from_usize, + into_usize, + } + } + + fn from_named_fields(fields: &FieldsNamed) -> Self { + let mut params_from = quote! {}; + let mut into_usize = quote! { 0usize }; + let mut length = quote! { 1usize }; + for field in fields.named.iter() { + let ty = &field.ty; + let ident = field.ident.as_ref().unwrap(); + let field_length = type_length(ty); + + into_usize = quote! { + (#into_usize + #length * ::enum_map::Enum::into_usize(self.#ident)) + }; + + params_from = quote! { + #params_from #ident: <#ty as ::enum_map::Enum>::from_usize( + value / #length % #field_length + ), + }; + + length = quote! { (#field_length * #length) }; + } + + let from_usize = quote! { Self { #params_from } }; + Self { + length, + from_usize, + into_usize, + } + } + + fn finish(&self, name: &Ident) -> TokenStream { + let length = &self.length; + let from_usize = &self.from_usize; + let into_usize = &self.into_usize; + + quote! { + #[automatically_derived] + impl ::enum_map::Enum for #name { + const LENGTH: ::enum_map::usize = #length; + + #[inline] + fn from_usize(value: ::enum_map::usize) -> Self { + #from_usize + } + + #[inline] + fn into_usize(self) -> ::enum_map::usize { + #into_usize + } + } + + #[automatically_derived] + impl ::enum_map::EnumArray for #name { + type Array = [V; #length]; + } + } + } +} diff --git a/third_party/rust/enum-map-derive/src/lib.rs b/third_party/rust/enum-map-derive/src/lib.rs new file mode 100644 index 000000000000..0776559b281a --- /dev/null +++ b/third_party/rust/enum-map-derive/src/lib.rs @@ -0,0 +1,178 @@ +// SPDX-FileCopyrightText: 2017 - 2022 Kamila Borowska +// SPDX-FileCopyrightText: 2018 hcpl +// SPDX-FileCopyrightText: 2019 mara +// SPDX-FileCopyrightText: 2021 Bruno Corrêa Zimmermann +// SPDX-FileCopyrightText: 2021 Dietrich +// +// SPDX-License-Identifier: MIT OR Apache-2.0 + +//! Procedural macro implementing `#[derive(Enum)]` +//! +//! This is supposed to used with `enum-map` crate, which provides the +//! actual usage documentation. + +mod derive_enum; +mod derive_struct; + +use proc_macro2::TokenStream; +use quote::quote; +use syn::{Data, DeriveInput, Type}; + +/// Derive macro generating an implementation of trait `Enum`. +/// +/// When using a derive, enum maps are maintained in the order in which +/// enum variants are declared. This is reflected in the value returned +/// by `Enum::into_usize`, iterators of enum map as well as +/// `EnumMap::as_slice` method. +/// +/// # Examples +/// +/// ## Enums Without Payload +/// ``` +/// use enum_map::Enum; +/// +/// #[derive(Enum, Debug, PartialEq, Eq)] +/// enum A { +/// B, +/// C, +/// D, +/// } +/// +/// assert_eq!(A::B.into_usize(), 0); +/// assert_eq!(A::C.into_usize(), 1); +/// assert_eq!(A::D.into_usize(), 2); +/// +/// assert_eq!(A::from_usize(0), A::B); +/// assert_eq!(A::from_usize(1), A::C); +/// assert_eq!(A::from_usize(2), A::D); +/// ``` +/// +/// ## Enums With Payload +/// +/// ``` +/// use enum_map::Enum; +/// +/// #[derive(Enum, Debug, PartialEq, Eq)] +/// enum A { +/// B, +/// C, +/// D, +/// } +/// +/// #[derive(Enum, Debug, PartialEq, Eq)] +/// enum X { +/// Y, +/// Z, +/// } +/// +/// #[derive(Enum, Debug, PartialEq, Eq)] +/// enum Foo { +/// Bar(bool, A), +/// Empty, +/// Baz { fa: A, fx: X }, +/// } +/// +/// assert_eq!(Foo::Bar(false, A::B).into_usize(), 0); +/// assert_eq!(Foo::Bar(false, A::D).into_usize(), 4); +/// assert_eq!(Foo::Bar(true, A::B).into_usize(), 1); +/// assert_eq!(Foo::Bar(true, A::C).into_usize(), 3); +/// assert_eq!(Foo::Empty.into_usize(), 6); +/// assert_eq!(Foo::Baz { fa: A::B, fx: X::Y }.into_usize(), 7); +/// assert_eq!(Foo::Baz { fa: A::B, fx: X::Z }.into_usize(), 10); +/// assert_eq!(Foo::Baz { fa: A::D, fx: X::Y }.into_usize(), 9); +/// +/// assert_eq!(Foo::from_usize(0), Foo::Bar(false, A::B)); +/// assert_eq!(Foo::from_usize(4), Foo::Bar(false, A::D)); +/// assert_eq!(Foo::from_usize(1), Foo::Bar(true, A::B)); +/// assert_eq!(Foo::from_usize(3), Foo::Bar(true, A::C)); +/// assert_eq!(Foo::from_usize(6), Foo::Empty); +/// assert_eq!(Foo::from_usize(7), Foo::Baz { fa: A::B, fx: X::Y }); +/// assert_eq!(Foo::from_usize(10), Foo::Baz { fa: A::B, fx: X::Z }); +/// assert_eq!(Foo::from_usize(9), Foo::Baz { fa: A::D, fx: X::Y }); +/// +/// ``` +/// +/// ## Structs +/// +/// ``` +/// use enum_map::Enum; +/// +/// #[derive(Enum, Debug, PartialEq, Eq)] +/// enum A { +/// B, +/// C, +/// D, +/// } +/// +/// #[derive(Enum, Debug, PartialEq, Eq)] +/// enum X { +/// Y, +/// Z, +/// } +/// +/// #[derive(Enum, Debug, PartialEq, Eq)] +/// struct Foo { +/// bar: bool, +/// baz: A, +/// end: X, +/// } +/// +/// assert_eq!(Foo { bar: false, baz: A::B, end: X::Y }.into_usize(), 0); +/// assert_eq!(Foo { bar: true, baz: A::B, end: X::Y }.into_usize(), 1); +/// assert_eq!(Foo { bar: false, baz: A::D, end: X::Y }.into_usize(), 4); +/// assert_eq!(Foo { bar: true, baz: A::C, end: X::Z }.into_usize(), 9); +/// +/// assert_eq!(Foo::from_usize(0), Foo { bar: false, baz: A::B, end: X::Y }); +/// assert_eq!(Foo::from_usize(1), Foo { bar: true, baz: A::B, end: X::Y }); +/// assert_eq!(Foo::from_usize(4), Foo { bar: false, baz: A::D, end: X::Y }); +/// assert_eq!(Foo::from_usize(9), Foo { bar: true, baz: A::C, end: X::Z }); +/// ``` +/// +/// ## Tuple Structs +/// +/// ``` +/// use enum_map::Enum; +/// +/// #[derive(Enum, Debug, PartialEq, Eq)] +/// enum A { +/// B, +/// C, +/// D, +/// } +/// +/// #[derive(Enum, Debug, PartialEq, Eq)] +/// enum X { +/// Y, +/// Z, +/// } +/// +/// #[derive(Enum, Debug, PartialEq, Eq)] +/// struct Foo(bool, A, X); +/// +/// assert_eq!(Foo(false, A::B, X::Y ).into_usize(), 0); +/// assert_eq!(Foo(true, A::B, X::Y ).into_usize(), 1); +/// assert_eq!(Foo(false, A::D, X::Y ).into_usize(), 4); +/// assert_eq!(Foo(true, A::C, X::Z ).into_usize(), 9); +/// +/// assert_eq!(Foo::from_usize(0), Foo(false, A::B, X::Y)); +/// assert_eq!(Foo::from_usize(1), Foo(true, A::B, X::Y)); +/// assert_eq!(Foo::from_usize(4), Foo(false, A::D, X::Y)); +/// assert_eq!(Foo::from_usize(9), Foo(true, A::C, X::Z)); +#[proc_macro_derive(Enum)] +pub fn derive_enum_map(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input: DeriveInput = syn::parse(input).unwrap(); + + let result = match input.data { + Data::Enum(data_enum) => derive_enum::generate(input.ident, data_enum), + Data::Struct(data_struct) => derive_struct::generate(input.ident, data_struct), + _ => quote! { compile_error! {"#[derive(Enum)] is only defined for enums and structs"} }, + }; + + result.into() +} + +fn type_length(ty: &Type) -> TokenStream { + quote! { + <#ty as ::enum_map::Enum>::LENGTH + } +} diff --git a/third_party/rust/enum-map/.cargo-checksum.json b/third_party/rust/enum-map/.cargo-checksum.json new file mode 100644 index 000000000000..d96424786568 --- /dev/null +++ b/third_party/rust/enum-map/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"CHANGELOG.md":"d64d088715bcbff31fb85a6694fbbaef2802ab19ff91d5f0d71ac715d1fa4417","Cargo.toml":"94a380dc0296261c3f80d1ef71a7c1355757bb67ca902367432084bde5b87233","LICENSES/Apache-2.0.txt":"074e6e32c86a4c0ef8b3ed25b721ca23aca83df277cd88106ef7177c354615ff","LICENSES/CC0-1.0.txt":"a2010f343487d3f7618affe54f789f5487602331c0a8d03f49e9a7c547cf0499","LICENSES/MIT.txt":"b85dcd3e453d05982552c52b5fc9e0bdd6d23c6f8e844b984a88af32570b0cc0","README.md":"eec9d4461de2e1e0f26251a98fd53feaad8582e6614a5a23be17a5b0c9491637","src/arbitrary.rs":"30e90bc431c4e74f756a619f432b30ccadc9b3288fc85f2d4bd160a374d0dfdb","src/enum_map_impls.rs":"9a03bf2500215e3c2dcb7e93cfc4d466bd767bdb9b83673b013f7ef50ac08d04","src/internal.rs":"ea03c0ca57eb52012e77dc3928ba5407109c5d24d373b7d7359ba037d890716f","src/iter.rs":"cc75658ea4c61ed63b35bf12c8f14534e36b2d3c9b1a29ab2efc028de0181d44","src/lib.rs":"3e34c9461dfb5b102d620b0196b30dab2d8d429e801e193f8e2e78adfef9039e","src/serde.rs":"f3a9919e2f6c0721a7e1fa386eb712875c5fce01d4f01d5d4775bc246d45ba90","tests/serde.rs":"98b788415b99c7b764ce2271260a4c38feeec9e539e8d8b01909b052ed41db85","tests/test.rs":"bfab8d9752b73de9085e0c59cc478ad09f87014a2c84b7b8bc16ecedf128b559"},"package":"6866f3bfdf8207509a033af1a75a7b08abda06bbaaeae6669323fd5a097df2e9"} \ No newline at end of file diff --git a/third_party/rust/enum-map/CHANGELOG.md b/third_party/rust/enum-map/CHANGELOG.md new file mode 100644 index 000000000000..84dacb8bd06e --- /dev/null +++ b/third_party/rust/enum-map/CHANGELOG.md @@ -0,0 +1,285 @@ + + +# Version 2.7.3 + +## Other changes + +- Fixed [a regression introduced in 2.7.2 that caused `#[derive(Enum)]` to + generate incorrect code when dealing with enums containing + fields](https://codeberg.org/xfix/enum-map/issues/112). + +# Version 2.7.2 + +## Other changes + +- Reduced RAM usage and improved compilation times when using `derive(Enum)` + for large enums with `overflow-checks` enabled. + +# Version 2.7.1 + +## Other changes + +- Updated author name. + +# Version 2.7.0 + +## New features + +- Implemented `EnumMap::from_fn`. + +# Version 2.6.3 + +## Other changes + +- Updated the repository URL as the project was migrated from GitHub + to Codeberg. + +- This project is now compliant with the REUSE Specification. + +# Version 2.6.2 + +## Other changes + +- Hide `out_of_bounds` reexport from documentation. + +# Version 2.6.1 + +## Other changes + +- Provide better panic message when providing out-of-bounds index + to `Enum::from_usize``. + +# Version 2.6.0 + +## New features + +- `EnumMap::as_array` is now usable in const contexts. + +## Other changes + +- This crate now follows "N minus two" MSRV policy. This means that + it supports the current Rust release, as well as the two before + that. + +- Upgraded syn to 2.0.0. + +# Version 2.5.0 + +## New features + +- Implemented `EnumMap::as_array` and `EnumMap::as_mut_array` + (implemented by [@Fuuzetsu](https://github.com/Fuuzetsu)). + +- Implemented `PartialOrd` and `Ord` for `EnumMap` (implemented by + [@nicarran](https://github.com/nicarran)). + +# Version 2.4.2 + +## Other changes + +- Added license files to crate tarball. +- Added changelog to crate tarball. + +# Version 2.4.1 + +## Other changes + +- Improved performance of code generated for `from_usize` when + deriving `Enum`. + +# Version 2.4.0 + +## New features + +- Implemented `Enum` for `()` (unit type) and `core::cmp::Ordering` + (implemented by [@phimuemue](https://github.com/phimuemue)). + +- Implemented `EnumMap::into_array`. + +# Version 2.3.0 + +## New features + +- `EnumMap::len` is now usable in const contexts. + +## Other changes + +- `Enum` derive now can deal with re-definitions of `usize` and + `unimplemented`. + +# Version 2.2.0 + +## New features + +- `EnumMap::from_array` is now usable in const contexts. + +# Version 2.1.0 + +## New features + +- Implemented `DoubleEndedIterator` for `IntoIter`. + +- Implemented `EnumMap::into_values`. + +## Other changes + +- Changed behavior of `IntoIter` so that it drops rest of the elements + when one destructor panics. + +# Version 2.0.3 + +## Other changes + +- Optimized performance of `enum_map!` macro. + +# Version 2.0.2 + +## Other changes + +- Fixed safety problem when using `enum_map!` macro with enums that + incorrectly implemented `Enum` trait. + +# Version 2.0.1 + +## Other changes + +- Adjusted crate metadata to avoid lib.rs warnings. + +# Version 2.0.0 + +## New features + +- Implemented `FromIterator` for `EnumMap` (implemented by @bit_network + on GitLab). + +- Implemented `EnumMap::map`. + +- Derives support product types in addition to sum types (implemented + by @bzim on GitLab). + +- It's now possible to access enum length by accessing `LENGTH` in + `Enum` trait. + +## Breaking changes + +- `Enum` trait was split into two traits, `Enum` and `EnumArray`. + +# Version 1.1.1 + +## Other changes + +- Worked around a bug in Clippy that caused false positives when using + `use_self` lint for code that derived `Enum` trait. + +# Version 1.1.0 + +## New features + +- Implemented `Arbitrary` for maps where the value type also implements + `Arbitrary`. (You have to enable the "arbitrary" feature.) + +# Version 1.0.0 + +## New features + +- It's now possible to use `return` and `?` within `macro_rules!` macro. + +- `Enum` trait is much simpler having two methods only. + +## Other changes + +- Removed previously deprecated features. + +- Renamed `to_usize` to `into_usize` matching the naming convention + used in Rust programming language. + +# Version 0.6.5 + +## Other changes + +- Deprecated `EnumMap::is_empty` and `EnumMap::new`. `EnumMap::new` usages + can be replaced with `EnumMap::default`. + +# Version 0.6.4 + +## Other changes + +- Deprecated `EnumMap::as_ptr` and `EnumMap::as_mut_ptr`. + +# Version 0.6.3 + +## New features + +- `Iter` and `Values` now implements `Clone` (added by @amanieu). + +# Version 0.6.2. + +## New features + +- Added `EnumMap#clear` method (added by @Riey, thanks :)). + +# Version 0.6.0 + +## Incompatible changes + +- Now requires Rust 1.36. + +# Version 0.5.0 + +- Fixed the issue where an aliasing `From` trait implementation caused + compilation errors with `enum_map!` macro. + +## Incompatible changes + +- Now requires Rust 1.31. + +# Version 0.4.1 + +## New features + +- Default `serde` features are disabled. This allows enabling serde feature when + compiling without `std`. + +# Version 0.4.0 + +Change of `#[derive(EnumMap)]` to `#[derive(Enum)]` was supposed to appear in 0.3.0, +but it was forgotten about. This release fixes just that. + +## Incompatible changes + +- Changed `#[derive(EnumMap)]` to `#[derive(Enum)]` to match trait name. + +# Version 0.3.1 + +- Updated README use `#[derive(EnumMap)]` instead of `#[derive(Enum)]`. + +# Version 0.3.0 + +## New features + +- Implemented compact serde serialization for binary formats like bincode. + +- Iterator traits with exception now implement `FusedIterator`. + +## Incompatible changes + +- Increased required Rust version to 1.26.0. + +- Renamed `Internal` trait to `Enum`. + +- Added new associated constant `POSSIBLE_VALUES` to `Enum` trait, + representing the number of possible values the type can have. Manual + implementations are required to provide it. + +- Removed `Enum` implementation for `Option`. + +- Implemented compact serialization, for formats like `bincode`. This + makes it impossible to deserialize non-compact representation used by + enum-map 0.2.0. + +- `values` method returns `Values` as opposed to `slice::Iter`. diff --git a/third_party/rust/enum-map/Cargo.toml b/third_party/rust/enum-map/Cargo.toml new file mode 100644 index 000000000000..f3f0523e71e8 --- /dev/null +++ b/third_party/rust/enum-map/Cargo.toml @@ -0,0 +1,65 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +rust-version = "1.61" +name = "enum-map" +version = "2.7.3" +authors = ["Kamila Borowska "] +description = "A map with C-like enum keys represented internally as an array" +documentation = "https://docs.rs/enum-map" +readme = "README.md" +keywords = [ + "data-structure", + "no_std", + "enum", +] +categories = [ + "data-structures", + "no-std", +] +license = "MIT OR Apache-2.0" +repository = "https://codeberg.org/xfix/enum-map" + +[package.metadata.docs.rs] +features = [ + "arbitrary", + "serde", +] + +[dependencies.arbitrary] +version = "1.0.0" +optional = true + +[dependencies.enum-map-derive] +version = "0.17.0" + +[dependencies.serde] +version = "1.0.16" +optional = true +default-features = false + +[dev-dependencies.bincode] +version = "1.0.0" + +[dev-dependencies.serde] +version = "1.0.103" +features = ["derive"] + +[dev-dependencies.serde_json] +version = "1.0.2" + +[dev-dependencies.serde_test] +version = "1.0.19" + +[badges.maintenance] +status = "passively-maintained" diff --git a/third_party/rust/enum-map/LICENSES/Apache-2.0.txt b/third_party/rust/enum-map/LICENSES/Apache-2.0.txt new file mode 100644 index 000000000000..137069b82387 --- /dev/null +++ b/third_party/rust/enum-map/LICENSES/Apache-2.0.txt @@ -0,0 +1,73 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/third_party/rust/enum-map/LICENSES/CC0-1.0.txt b/third_party/rust/enum-map/LICENSES/CC0-1.0.txt new file mode 100644 index 000000000000..0e259d42c996 --- /dev/null +++ b/third_party/rust/enum-map/LICENSES/CC0-1.0.txt @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/third_party/rust/enum-map/LICENSES/MIT.txt b/third_party/rust/enum-map/LICENSES/MIT.txt new file mode 100644 index 000000000000..2071b23b0e08 --- /dev/null +++ b/third_party/rust/enum-map/LICENSES/MIT.txt @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/third_party/rust/enum-map/README.md b/third_party/rust/enum-map/README.md new file mode 100644 index 000000000000..ca83e020cc13 --- /dev/null +++ b/third_party/rust/enum-map/README.md @@ -0,0 +1,45 @@ + + +# enum-map + +A library providing enum map providing type safe enum array. It is +implemented using regular Rust arrays, so using them is as fast +as using regular Rust arrays. + +This crate follows the "N minus two" MSRV policy. This means that it +supports the current Rust release, as well as the two before that. + +## Examples + +```rust +#[macro_use] +extern crate enum_map; + +use enum_map::EnumMap; + +#[derive(Debug, Enum)] +enum Example { + A, + B, + C, +} + +fn main() { + let mut map = enum_map! { + Example::A => 1, + Example::B => 2, + Example::C => 3, + }; + map[Example::C] = 4; + + assert_eq!(map[Example::A], 1); + + for (key, &value) in &map { + println!("{:?} has {} as value.", key, value); + } +} +``` diff --git a/third_party/rust/enum-map/src/arbitrary.rs b/third_party/rust/enum-map/src/arbitrary.rs new file mode 100644 index 000000000000..e8c988d2921d --- /dev/null +++ b/third_party/rust/enum-map/src/arbitrary.rs @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: 2021 Bruno Corrêa Zimmermann +// SPDX-FileCopyrightText: 2021 Kamila Borowska +// +// SPDX-License-Identifier: MIT OR Apache-2.0 + +use crate::{enum_map, EnumArray, EnumMap}; +use arbitrary::{Arbitrary, Result, Unstructured}; + +/// Requires crate feature `"arbitrary"` +impl<'a, K: EnumArray, V: Arbitrary<'a>> Arbitrary<'a> for EnumMap { + fn arbitrary(u: &mut Unstructured<'a>) -> Result> { + Ok(enum_map! { + _ => Arbitrary::arbitrary(u)?, + }) + } + + fn size_hint(depth: usize) -> (usize, Option) { + if K::LENGTH == 0 { + (0, Some(0)) + } else { + let (lo, hi) = V::size_hint(depth); + ( + lo.saturating_mul(K::LENGTH), + hi.and_then(|hi| hi.checked_mul(K::LENGTH)), + ) + } + } +} diff --git a/third_party/rust/enum-map/src/enum_map_impls.rs b/third_party/rust/enum-map/src/enum_map_impls.rs new file mode 100644 index 000000000000..545ff33ddc87 --- /dev/null +++ b/third_party/rust/enum-map/src/enum_map_impls.rs @@ -0,0 +1,115 @@ +// SPDX-FileCopyrightText: 2017 - 2021 Kamila Borowska +// SPDX-FileCopyrightText: 2021 Bruno Corrêa Zimmermann +// SPDX-FileCopyrightText: 2021 micycle +// SPDX-FileCopyrightText: 2023 Nicolas Carranza +// +// SPDX-License-Identifier: MIT OR Apache-2.0 + +use crate::{enum_map, EnumArray, EnumMap}; +use core::fmt::{self, Debug, Formatter}; +use core::hash::{Hash, Hasher}; +use core::iter::{Extend, FromIterator}; +use core::ops::{Index, IndexMut}; + +impl + Debug, V: Debug> Debug for EnumMap { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + f.debug_map().entries(self).finish() + } +} + +impl, V> Extend<(K, V)> for EnumMap { + fn extend>(&mut self, iter: I) { + for (key, value) in iter { + self[key] = value; + } + } +} + +impl<'a, K, V> Extend<(&'a K, &'a V)> for EnumMap +where + K: EnumArray + Copy, + V: Copy, +{ + fn extend>(&mut self, iter: I) { + self.extend(iter.into_iter().map(|(&key, &value)| (key, value))); + } +} + +impl FromIterator<(K, V)> for EnumMap +where + Self: Default, + K: EnumArray, +{ + fn from_iter>(iter: I) -> Self { + let mut map = EnumMap::default(); + map.extend(iter); + map + } +} + +impl, V> Index for EnumMap { + type Output = V; + + #[inline] + fn index(&self, key: K) -> &V { + &self.as_slice()[key.into_usize()] + } +} + +impl, V> IndexMut for EnumMap { + #[inline] + fn index_mut(&mut self, key: K) -> &mut V { + &mut self.as_mut_slice()[key.into_usize()] + } +} + +// Implementations provided by derive attribute are too specific, and put requirements on K. +// This is caused by rust-lang/rust#26925. +impl, V> Clone for EnumMap +where + K::Array: Clone, +{ + #[inline] + fn clone(&self) -> Self { + EnumMap { + array: self.array.clone(), + } + } +} + +impl, V> Copy for EnumMap where K::Array: Copy {} + +impl, V: PartialEq> PartialEq for EnumMap { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.as_slice() == other.as_slice() + } +} + +impl, V: Eq> Eq for EnumMap {} + +impl, V: Hash> Hash for EnumMap { + #[inline] + fn hash(&self, state: &mut H) { + self.as_slice().hash(state); + } +} + +impl, V: Default> Default for EnumMap { + #[inline] + fn default() -> Self { + enum_map! { _ => V::default() } + } +} + +impl, V: PartialOrd> PartialOrd for EnumMap { + fn partial_cmp(&self, other: &Self) -> Option { + self.as_slice().partial_cmp(other.as_slice()) + } +} + +impl, V: Ord> Ord for EnumMap { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + self.as_slice().cmp(other.as_slice()) + } +} diff --git a/third_party/rust/enum-map/src/internal.rs b/third_party/rust/enum-map/src/internal.rs new file mode 100644 index 000000000000..2a6235d09d42 --- /dev/null +++ b/third_party/rust/enum-map/src/internal.rs @@ -0,0 +1,159 @@ +// SPDX-FileCopyrightText: 2017 - 2023 Kamila Borowska +// SPDX-FileCopyrightText: 2021 Bruno Corrêa Zimmermann +// SPDX-FileCopyrightText: 2022 philipp +// +// SPDX-License-Identifier: MIT OR Apache-2.0 + +use core::cmp::Ordering; +use core::convert::Infallible; + +/// Enum mapping type. +/// +/// This trait is implemented by `#[derive(Enum)]`. +/// +/// This trait is also implemented by `bool` and `u8`. While `u8` is +/// strictly speaking not an actual enum, there are good reasons to consider +/// it like one, as array of `u8` keys is a relatively common pattern. +pub trait Enum: Sized { + /// Length of the enum. + const LENGTH: usize; + + /// Takes an usize, and returns an element matching `into_usize` function. + fn from_usize(value: usize) -> Self; + /// Returns an unique identifier for a value within range of `0..Array::LENGTH`. + fn into_usize(self) -> usize; +} + +/// Trait associating enum with an array. +/// +/// This exists due to limitations of Rust compiler that prevent arrays from using +/// associated constants in structures. The array length must match `LENGTH` of an +/// `Enum`. +pub trait EnumArray: Enum { + /// Representation of an enum map for type `V`. + type Array: Array; +} + +/// Array for enum-map storage. +/// +/// This trait is inteded for primitive array types (with fixed length). +/// +/// # Safety +/// +/// The array length needs to match actual storage. +pub unsafe trait Array { + // This is necessary duplication because the length in Enum trait can be + // provided by user and may not be trustworthy for unsafe code. + const LENGTH: usize; +} + +unsafe impl Array for [V; N] { + const LENGTH: usize = N; +} + +#[doc(hidden)] +#[inline] +pub fn out_of_bounds() -> ! { + panic!("index out of range for Enum::from_usize"); +} + +impl Enum for bool { + const LENGTH: usize = 2; + + #[inline] + fn from_usize(value: usize) -> Self { + match value { + 0 => false, + 1 => true, + _ => out_of_bounds(), + } + } + #[inline] + fn into_usize(self) -> usize { + usize::from(self) + } +} + +impl EnumArray for bool { + type Array = [T; Self::LENGTH]; +} + +impl Enum for () { + const LENGTH: usize = 1; + + #[inline] + fn from_usize(value: usize) -> Self { + match value { + 0 => (), + _ => out_of_bounds(), + } + } + #[inline] + fn into_usize(self) -> usize { + 0 + } +} + +impl EnumArray for () { + type Array = [T; Self::LENGTH]; +} + +impl Enum for u8 { + const LENGTH: usize = 256; + + #[inline] + fn from_usize(value: usize) -> Self { + value.try_into().unwrap_or_else(|_| out_of_bounds()) + } + #[inline] + fn into_usize(self) -> usize { + usize::from(self) + } +} + +impl EnumArray for u8 { + type Array = [T; Self::LENGTH]; +} + +impl Enum for Infallible { + const LENGTH: usize = 0; + + #[inline] + fn from_usize(_: usize) -> Self { + out_of_bounds(); + } + #[inline] + fn into_usize(self) -> usize { + match self {} + } +} + +impl EnumArray for Infallible { + type Array = [T; Self::LENGTH]; +} + +impl Enum for Ordering { + const LENGTH: usize = 3; + + #[inline] + fn from_usize(value: usize) -> Self { + match value { + 0 => Ordering::Less, + 1 => Ordering::Equal, + 2 => Ordering::Greater, + _ => out_of_bounds(), + } + } + #[inline] + fn into_usize(self) -> usize { + match self { + Ordering::Less => 0, + Ordering::Equal => 1, + Ordering::Greater => 2, + } + } +} + +impl EnumArray for Ordering { + type Array = [T; Self::LENGTH]; +} diff --git a/third_party/rust/enum-map/src/iter.rs b/third_party/rust/enum-map/src/iter.rs new file mode 100644 index 000000000000..393d8d1027ce --- /dev/null +++ b/third_party/rust/enum-map/src/iter.rs @@ -0,0 +1,415 @@ +#![allow(clippy::module_name_repetitions)] + +// SPDX-FileCopyrightText: 2017 - 2022 Kamila Borowska +// SPDX-FileCopyrightText: 2020 Amanieu d'Antras +// SPDX-FileCopyrightText: 2021 Bruno Corrêa Zimmermann +// +// SPDX-License-Identifier: MIT OR Apache-2.0 + +use crate::{EnumArray, EnumMap}; +use core::iter::{Enumerate, FusedIterator}; +use core::marker::PhantomData; +use core::mem::ManuallyDrop; +use core::ops::Range; +use core::ptr; +use core::slice; + +/// Immutable enum map iterator +/// +/// This struct is created by `iter` method or `into_iter` on a reference +/// to `EnumMap`. +/// +/// # Examples +/// +/// ``` +/// use enum_map::{enum_map, Enum}; +/// +/// #[derive(Enum)] +/// enum Example { +/// A, +/// B, +/// C, +/// } +/// +/// let mut map = enum_map! { Example::A => 3, _ => 0 }; +/// assert_eq!(map[Example::A], 3); +/// for (key, &value) in &map { +/// assert_eq!(value, match key { +/// Example::A => 3, +/// _ => 0, +/// }); +/// } +/// ``` +#[derive(Debug)] +pub struct Iter<'a, K, V: 'a> { + _phantom: PhantomData K>, + iterator: Enumerate>, +} + +impl<'a, K: EnumArray, V> Clone for Iter<'a, K, V> { + fn clone(&self) -> Self { + Iter { + _phantom: PhantomData, + iterator: self.iterator.clone(), + } + } +} + +impl<'a, K: EnumArray, V> Iterator for Iter<'a, K, V> { + type Item = (K, &'a V); + #[inline] + fn next(&mut self) -> Option { + self.iterator + .next() + .map(|(index, item)| (K::from_usize(index), item)) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.iterator.size_hint() + } + + fn fold(self, init: B, f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + self.iterator + .map(|(index, item)| (K::from_usize(index), item)) + .fold(init, f) + } +} + +impl<'a, K: EnumArray, V> DoubleEndedIterator for Iter<'a, K, V> { + #[inline] + fn next_back(&mut self) -> Option { + self.iterator + .next_back() + .map(|(index, item)| (K::from_usize(index), item)) + } +} + +impl<'a, K: EnumArray, V> ExactSizeIterator for Iter<'a, K, V> {} + +impl<'a, K: EnumArray, V> FusedIterator for Iter<'a, K, V> {} + +impl<'a, K: EnumArray, V> IntoIterator for &'a EnumMap { + type Item = (K, &'a V); + type IntoIter = Iter<'a, K, V>; + #[inline] + fn into_iter(self) -> Self::IntoIter { + Iter { + _phantom: PhantomData, + iterator: self.as_slice().iter().enumerate(), + } + } +} + +/// Mutable map iterator +/// +/// This struct is created by `iter_mut` method or `into_iter` on a mutable +/// reference to `EnumMap`. +/// +/// # Examples +/// +/// ``` +/// use enum_map::{enum_map, Enum}; +/// +/// #[derive(Debug, Enum)] +/// enum Example { +/// A, +/// B, +/// C, +/// } +/// +/// let mut map = enum_map! { Example::A => 3, _ => 0 }; +/// for (_, value) in &mut map { +/// *value += 1; +/// } +/// assert_eq!(map, enum_map! { Example::A => 4, _ => 1 }); +/// ``` +#[derive(Debug)] +pub struct IterMut<'a, K, V: 'a> { + _phantom: PhantomData K>, + iterator: Enumerate>, +} + +impl<'a, K: EnumArray, V> Iterator for IterMut<'a, K, V> { + type Item = (K, &'a mut V); + #[inline] + fn next(&mut self) -> Option { + self.iterator + .next() + .map(|(index, item)| (K::from_usize(index), item)) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.iterator.size_hint() + } + + fn fold(self, init: B, f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + self.iterator + .map(|(index, item)| (K::from_usize(index), item)) + .fold(init, f) + } +} + +impl<'a, K: EnumArray, V> DoubleEndedIterator for IterMut<'a, K, V> { + #[inline] + fn next_back(&mut self) -> Option { + self.iterator + .next_back() + .map(|(index, item)| (K::from_usize(index), item)) + } +} + +impl<'a, K: EnumArray, V> ExactSizeIterator for IterMut<'a, K, V> {} + +impl<'a, K: EnumArray, V> FusedIterator for IterMut<'a, K, V> {} + +impl<'a, K: EnumArray, V> IntoIterator for &'a mut EnumMap { + type Item = (K, &'a mut V); + type IntoIter = IterMut<'a, K, V>; + #[inline] + fn into_iter(self) -> Self::IntoIter { + IterMut { + _phantom: PhantomData, + iterator: self.as_mut_slice().iter_mut().enumerate(), + } + } +} + +/// A map iterator that moves out of map. +/// +/// This struct is created by `into_iter` on `EnumMap`. +/// +/// # Examples +/// +/// ``` +/// use enum_map::{enum_map, Enum}; +/// +/// #[derive(Debug, Enum)] +/// enum Example { +/// A, +/// B, +/// } +/// +/// let map = enum_map! { Example::A | Example::B => String::from("123") }; +/// for (_, value) in map { +/// assert_eq!(value + "4", "1234"); +/// } +/// ``` +pub struct IntoIter, V> { + map: ManuallyDrop>, + alive: Range, +} + +impl, V> Iterator for IntoIter { + type Item = (K, V); + fn next(&mut self) -> Option<(K, V)> { + let position = self.alive.next()?; + Some((K::from_usize(position), unsafe { + ptr::read(&self.map.as_slice()[position]) + })) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.alive.size_hint() + } +} + +impl, V> DoubleEndedIterator for IntoIter { + fn next_back(&mut self) -> Option<(K, V)> { + let position = self.alive.next_back()?; + Some((K::from_usize(position), unsafe { + ptr::read(&self.map.as_slice()[position]) + })) + } +} + +impl, V> ExactSizeIterator for IntoIter {} + +impl, V> FusedIterator for IntoIter {} + +impl, V> Drop for IntoIter { + #[inline] + fn drop(&mut self) { + unsafe { + ptr::drop_in_place(&mut self.map.as_mut_slice()[self.alive.clone()]); + } + } +} + +impl, V> IntoIterator for EnumMap { + type Item = (K, V); + type IntoIter = IntoIter; + #[inline] + fn into_iter(self) -> Self::IntoIter { + let len = self.len(); + IntoIter { + map: ManuallyDrop::new(self), + alive: 0..len, + } + } +} + +impl, V> EnumMap { + /// An iterator visiting all values. The iterator type is `&V`. + /// + /// # Examples + /// + /// ``` + /// use enum_map::enum_map; + /// + /// let map = enum_map! { false => 3, true => 4 }; + /// let mut values = map.values(); + /// assert_eq!(values.next(), Some(&3)); + /// assert_eq!(values.next(), Some(&4)); + /// assert_eq!(values.next(), None); + /// ``` + #[inline] + pub fn values(&self) -> Values { + Values(self.as_slice().iter()) + } + + /// An iterator visiting all values mutably. The iterator type is `&mut V`. + /// + /// # Examples + /// + /// ``` + /// use enum_map::enum_map; + /// + /// let mut map = enum_map! { _ => 2 }; + /// for value in map.values_mut() { + /// *value += 2; + /// } + /// assert_eq!(map[false], 4); + /// assert_eq!(map[true], 4); + /// ``` + #[inline] + pub fn values_mut(&mut self) -> ValuesMut { + ValuesMut(self.as_mut_slice().iter_mut()) + } + + /// Creates a consuming iterator visiting all the values. The map + /// cannot be used after calling this. The iterator element type + /// is `V`. + /// + /// # Examples + /// + /// ``` + /// use enum_map::enum_map; + /// + /// let mut map = enum_map! { false => "hello", true => "goodbye" }; + /// assert_eq!(map.into_values().collect::>(), ["hello", "goodbye"]); + /// ``` + #[inline] + pub fn into_values(self) -> IntoValues { + IntoValues { + inner: self.into_iter(), + } + } +} + +/// An iterator over the values of `EnumMap`. +/// +/// This `struct` is created by the `values` method of `EnumMap`. +/// See its documentation for more. +pub struct Values<'a, V: 'a>(slice::Iter<'a, V>); + +impl<'a, V> Clone for Values<'a, V> { + fn clone(&self) -> Self { + Values(self.0.clone()) + } +} + +impl<'a, V: 'a> Iterator for Values<'a, V> { + type Item = &'a V; + #[inline] + fn next(&mut self) -> Option<&'a V> { + self.0.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } +} + +impl<'a, V: 'a> DoubleEndedIterator for Values<'a, V> { + #[inline] + fn next_back(&mut self) -> Option<&'a V> { + self.0.next_back() + } +} + +impl<'a, V: 'a> ExactSizeIterator for Values<'a, V> {} + +impl<'a, V: 'a> FusedIterator for Values<'a, V> {} + +/// A mutable iterator over the values of `EnumMap`. +/// +/// This `struct` is created by the `values_mut` method of `EnumMap`. +/// See its documentation for more. +pub struct ValuesMut<'a, V: 'a>(slice::IterMut<'a, V>); + +impl<'a, V: 'a> Iterator for ValuesMut<'a, V> { + type Item = &'a mut V; + #[inline] + fn next(&mut self) -> Option<&'a mut V> { + self.0.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } +} + +impl<'a, V: 'a> DoubleEndedIterator for ValuesMut<'a, V> { + #[inline] + fn next_back(&mut self) -> Option<&'a mut V> { + self.0.next_back() + } +} + +impl<'a, V: 'a> ExactSizeIterator for ValuesMut<'a, V> {} + +impl<'a, V: 'a> FusedIterator for ValuesMut<'a, V> {} + +/// An owning iterator over the values of an `EnumMap`. +/// +/// This `struct` is created by the `into_values` method of `EnumMap`. +/// See its documentation for more. +pub struct IntoValues, V> { + inner: IntoIter, +} + +impl Iterator for IntoValues +where + K: EnumArray, +{ + type Item = V; + + fn next(&mut self) -> Option { + Some(self.inner.next()?.1) + } + + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +impl, V> DoubleEndedIterator for IntoValues { + fn next_back(&mut self) -> Option { + Some(self.inner.next_back()?.1) + } +} + +impl ExactSizeIterator for IntoValues where K: EnumArray {} + +impl FusedIterator for IntoValues where K: EnumArray {} diff --git a/third_party/rust/enum-map/src/lib.rs b/third_party/rust/enum-map/src/lib.rs new file mode 100644 index 000000000000..cb933a3a5c00 --- /dev/null +++ b/third_party/rust/enum-map/src/lib.rs @@ -0,0 +1,509 @@ +// SPDX-FileCopyrightText: 2017 - 2023 Kamila Borowska +// SPDX-FileCopyrightText: 2019 Riey +// SPDX-FileCopyrightText: 2021 Alex Sayers +// SPDX-FileCopyrightText: 2021 Bruno Corrêa Zimmermann +// SPDX-FileCopyrightText: 2022 Cass Fridkin +// SPDX-FileCopyrightText: 2022 Mateusz Kowalczyk +// +// SPDX-License-Identifier: MIT OR Apache-2.0 + +//! An enum mapping type. +//! +//! It is implemented using an array type, so using it is as fast as using Rust +//! arrays. +//! +//! # Examples +//! +//! ``` +//! use enum_map::{enum_map, Enum, EnumMap}; +//! +//! #[derive(Debug, Enum)] +//! enum Example { +//! A(bool), +//! B, +//! C, +//! } +//! +//! let mut map = enum_map! { +//! Example::A(false) => 0, +//! Example::A(true) => 1, +//! Example::B => 2, +//! Example::C => 3, +//! }; +//! map[Example::C] = 4; +//! +//! assert_eq!(map[Example::A(true)], 1); +//! +//! for (key, &value) in &map { +//! println!("{:?} has {} as value.", key, value); +//! } +//! ``` + +#![no_std] +#![deny(missing_docs)] +#![warn(clippy::pedantic)] + +#[cfg(feature = "arbitrary")] +mod arbitrary; +mod enum_map_impls; +mod internal; +mod iter; +#[cfg(feature = "serde")] +mod serde; + +#[doc(hidden)] +pub use core::mem::{self, ManuallyDrop, MaybeUninit}; +#[doc(hidden)] +pub use core::primitive::usize; +use core::slice; +#[doc(hidden)] +// unreachable needs to be exported for compatibility with older versions of enum-map-derive +pub use core::{panic, ptr, unreachable}; +pub use enum_map_derive::Enum; +#[doc(hidden)] +pub use internal::out_of_bounds; +use internal::Array; +pub use internal::{Enum, EnumArray}; +pub use iter::{IntoIter, IntoValues, Iter, IterMut, Values, ValuesMut}; + +// SAFETY: initialized needs to represent number of initialized elements +#[doc(hidden)] +pub struct Guard<'a, K, V> +where + K: EnumArray, +{ + array_mut: &'a mut MaybeUninit, + initialized: usize, +} + +impl Drop for Guard<'_, K, V> +where + K: EnumArray, +{ + fn drop(&mut self) { + // This is safe as arr[..len] is initialized due to + // Guard's type invariant. + unsafe { + ptr::slice_from_raw_parts_mut(self.as_mut_ptr(), self.initialized).drop_in_place(); + } + } +} + +impl<'a, K, V> Guard<'a, K, V> +where + K: EnumArray, +{ + #[doc(hidden)] + pub fn as_mut_ptr(&mut self) -> *mut V { + self.array_mut.as_mut_ptr().cast::() + } + + #[doc(hidden)] + #[must_use] + pub fn new(array_mut: &'a mut MaybeUninit) -> Self { + Self { + array_mut, + initialized: 0, + } + } + + #[doc(hidden)] + #[must_use] + #[allow(clippy::unused_self)] + pub fn storage_length(&self) -> usize { + // SAFETY: We need to use LENGTH from K::Array, as K::LENGTH is + // untrustworthy. + K::Array::LENGTH + } + + #[doc(hidden)] + #[must_use] + pub fn get_key(&self) -> K { + K::from_usize(self.initialized) + } + + #[doc(hidden)] + // Unsafe as it can write out of bounds. + pub unsafe fn push(&mut self, value: V) { + self.as_mut_ptr().add(self.initialized).write(value); + self.initialized += 1; + } +} + +#[doc(hidden)] +pub struct TypeEqualizer<'a, K, V> +where + K: EnumArray, +{ + pub enum_map: [EnumMap; 0], + pub guard: Guard<'a, K, V>, +} + +/// Enum map constructor. +/// +/// This macro allows to create a new enum map in a type safe way. It takes +/// a list of `,` separated pairs separated by `=>`. Left side is `|` +/// separated list of enum keys, or `_` to match all unmatched enum keys, +/// while right side is a value. +/// +/// The iteration order when using this macro is not guaranteed to be +/// consistent. Future releases of this crate may change it, and this is not +/// considered to be a breaking change. +/// +/// # Examples +/// +/// ``` +/// use enum_map::{enum_map, Enum}; +/// +/// #[derive(Enum)] +/// enum Example { +/// A, +/// B, +/// C, +/// D, +/// } +/// +/// let enum_map = enum_map! { +/// Example::A | Example::B => 1, +/// Example::C => 2, +/// _ => 3, +/// }; +/// assert_eq!(enum_map[Example::A], 1); +/// assert_eq!(enum_map[Example::B], 1); +/// assert_eq!(enum_map[Example::C], 2); +/// assert_eq!(enum_map[Example::D], 3); +/// ``` +#[macro_export] +macro_rules! enum_map { + {$($t:tt)*} => {{ + let mut uninit = $crate::MaybeUninit::uninit(); + let mut eq = $crate::TypeEqualizer { + enum_map: [], + guard: $crate::Guard::new(&mut uninit), + }; + if false { + // Safe because this code is unreachable + unsafe { (&mut eq.enum_map).as_mut_ptr().read() } + } else { + for _ in 0..(&eq.guard).storage_length() { + struct __PleaseDoNotUseBreakWithoutLabel; + let _please_do_not_use_continue_without_label; + let value; + #[allow(unreachable_code)] + loop { + _please_do_not_use_continue_without_label = (); + value = match (&eq.guard).get_key() { $($t)* }; + break __PleaseDoNotUseBreakWithoutLabel; + }; + + unsafe { (&mut eq.guard).push(value); } + } + $crate::mem::forget(eq); + // Safe because the array was fully initialized. + $crate::EnumMap::from_array(unsafe { uninit.assume_init() }) + } + }}; +} + +/// An enum mapping. +/// +/// This internally uses an array which stores a value for each possible +/// enum value. To work, it requires implementation of internal (private, +/// although public due to macro limitations) trait which allows extracting +/// information about an enum, which can be automatically generated using +/// `#[derive(Enum)]` macro. +/// +/// Additionally, `bool` and `u8` automatically derives from `Enum`. While +/// `u8` is not technically an enum, it's convenient to consider it like one. +/// In particular, [reverse-complement in benchmark game] could be using `u8` +/// as an enum. +/// +/// # Examples +/// +/// ``` +/// use enum_map::{enum_map, Enum, EnumMap}; +/// +/// #[derive(Enum)] +/// enum Example { +/// A, +/// B, +/// C, +/// } +/// +/// let mut map = EnumMap::default(); +/// // new initializes map with default values +/// assert_eq!(map[Example::A], 0); +/// map[Example::A] = 3; +/// assert_eq!(map[Example::A], 3); +/// ``` +/// +/// [reverse-complement in benchmark game]: +/// http://benchmarksgame.alioth.debian.org/u64q/program.php?test=revcomp&lang=rust&id=2 +pub struct EnumMap, V> { + array: K::Array, +} + +impl, V: Default> EnumMap { + /// Clear enum map with default values. + /// + /// # Examples + /// + /// ``` + /// use enum_map::{Enum, EnumMap}; + /// + /// #[derive(Enum)] + /// enum Example { + /// A, + /// B, + /// } + /// + /// let mut enum_map = EnumMap::<_, String>::default(); + /// enum_map[Example::B] = "foo".into(); + /// enum_map.clear(); + /// assert_eq!(enum_map[Example::A], ""); + /// assert_eq!(enum_map[Example::B], ""); + /// ``` + #[inline] + pub fn clear(&mut self) { + for v in self.as_mut_slice() { + *v = V::default(); + } + } +} + +#[allow(clippy::len_without_is_empty)] +impl, V> EnumMap { + /// Creates an enum map from array. + #[inline] + pub const fn from_array(array: K::Array) -> EnumMap { + EnumMap { array } + } + + /// Create an enum map, where each value is the returned value from `cb` + /// using provided enum key. + /// + /// ``` + /// # use enum_map_derive::*; + /// use enum_map::{enum_map, Enum, EnumMap}; + /// + /// #[derive(Enum, PartialEq, Debug)] + /// enum Example { + /// A, + /// B, + /// } + /// + /// let map = EnumMap::from_fn(|k| k == Example::A); + /// assert_eq!(map, enum_map! { Example::A => true, Example::B => false }) + /// ``` + pub fn from_fn(mut cb: F) -> Self + where + F: FnMut(K) -> V, + { + enum_map! { k => cb(k) } + } + + /// Returns an iterator over enum map. + /// + /// The iteration order is deterministic, and when using [macro@Enum] derive + /// it will be the order in which enum variants are declared. + /// + /// # Examples + /// + /// ``` + /// use enum_map::{enum_map, Enum}; + /// + /// #[derive(Enum, PartialEq)] + /// enum E { + /// A, + /// B, + /// C, + /// } + /// + /// let map = enum_map! { E::A => 1, E::B => 2, E::C => 3}; + /// assert!(map.iter().eq([(E::A, &1), (E::B, &2), (E::C, &3)])); + /// ``` + #[inline] + pub fn iter(&self) -> Iter { + self.into_iter() + } + + /// Returns a mutable iterator over enum map. + #[inline] + pub fn iter_mut(&mut self) -> IterMut { + self.into_iter() + } + + /// Returns number of elements in enum map. + #[inline] + #[allow(clippy::unused_self)] + pub const fn len(&self) -> usize { + K::Array::LENGTH + } + + /// Swaps two indexes. + /// + /// # Examples + /// + /// ``` + /// use enum_map::enum_map; + /// + /// let mut map = enum_map! { false => 0, true => 1 }; + /// map.swap(false, true); + /// assert_eq!(map[false], 1); + /// assert_eq!(map[true], 0); + /// ``` + #[inline] + pub fn swap(&mut self, a: K, b: K) { + self.as_mut_slice().swap(a.into_usize(), b.into_usize()); + } + + /// Consumes an enum map and returns the underlying array. + /// + /// The order of elements is deterministic, and when using [macro@Enum] + /// derive it will be the order in which enum variants are declared. + /// + /// # Examples + /// + /// ``` + /// use enum_map::{enum_map, Enum}; + /// + /// #[derive(Enum, PartialEq)] + /// enum E { + /// A, + /// B, + /// C, + /// } + /// + /// let map = enum_map! { E::A => 1, E::B => 2, E::C => 3}; + /// assert_eq!(map.into_array(), [1, 2, 3]); + /// ``` + pub fn into_array(self) -> K::Array { + self.array + } + + /// Returns a reference to the underlying array. + /// + /// The order of elements is deterministic, and when using [macro@Enum] + /// derive it will be the order in which enum variants are declared. + /// + /// # Examples + /// + /// ``` + /// use enum_map::{enum_map, Enum}; + /// + /// #[derive(Enum, PartialEq)] + /// enum E { + /// A, + /// B, + /// C, + /// } + /// + /// let map = enum_map! { E::A => 1, E::B => 2, E::C => 3}; + /// assert_eq!(map.as_array(), &[1, 2, 3]); + /// ``` + pub const fn as_array(&self) -> &K::Array { + &self.array + } + + /// Returns a mutable reference to the underlying array. + /// + /// The order of elements is deterministic, and when using [macro@Enum] + /// derive it will be the order in which enum variants are declared. + /// + /// # Examples + /// + /// ``` + /// use enum_map::{enum_map, Enum}; + /// + /// #[derive(Enum, PartialEq)] + /// enum E { + /// A, + /// B, + /// C, + /// } + /// + /// let mut map = enum_map! { E::A => 1, E::B => 2, E::C => 3}; + /// map.as_mut_array()[1] = 42; + /// assert_eq!(map.as_array(), &[1, 42, 3]); + /// ``` + pub fn as_mut_array(&mut self) -> &mut K::Array { + &mut self.array + } + + /// Converts an enum map to a slice representing values. + /// + /// The order of elements is deterministic, and when using [macro@Enum] + /// derive it will be the order in which enum variants are declared. + /// + /// # Examples + /// + /// ``` + /// use enum_map::{enum_map, Enum}; + /// + /// #[derive(Enum, PartialEq)] + /// enum E { + /// A, + /// B, + /// C, + /// } + /// + /// let map = enum_map! { E::A => 1, E::B => 2, E::C => 3}; + /// assert_eq!(map.as_slice(), &[1, 2, 3]); + /// ``` + #[inline] + pub fn as_slice(&self) -> &[V] { + unsafe { slice::from_raw_parts(ptr::addr_of!(self.array).cast(), K::Array::LENGTH) } + } + + /// Converts a mutable enum map to a mutable slice representing values. + #[inline] + pub fn as_mut_slice(&mut self) -> &mut [V] { + unsafe { slice::from_raw_parts_mut(ptr::addr_of_mut!(self.array).cast(), K::Array::LENGTH) } + } + + /// Returns an enum map with function `f` applied to each element in order. + /// + /// # Examples + /// + /// ``` + /// use enum_map::enum_map; + /// + /// let a = enum_map! { false => 0, true => 1 }; + /// let b = a.map(|_, x| f64::from(x) + 0.5); + /// assert_eq!(b, enum_map! { false => 0.5, true => 1.5 }); + /// ``` + pub fn map(self, mut f: F) -> EnumMap + where + F: FnMut(K, V) -> T, + K: EnumArray, + { + struct DropOnPanic + where + K: EnumArray, + { + position: usize, + map: ManuallyDrop>, + } + impl Drop for DropOnPanic + where + K: EnumArray, + { + fn drop(&mut self) { + unsafe { + ptr::drop_in_place(&mut self.map.as_mut_slice()[self.position..]); + } + } + } + let mut drop_protect = DropOnPanic { + position: 0, + map: ManuallyDrop::new(self), + }; + enum_map! { + k => { + let value = unsafe { ptr::read(&drop_protect.map.as_slice()[drop_protect.position]) }; + drop_protect.position += 1; + f(k, value) + } + } + } +} diff --git a/third_party/rust/enum-map/src/serde.rs b/third_party/rust/enum-map/src/serde.rs new file mode 100644 index 000000000000..9ac434d5d0ee --- /dev/null +++ b/third_party/rust/enum-map/src/serde.rs @@ -0,0 +1,98 @@ +// SPDX-FileCopyrightText: 2017 - 2023 Kamila Borowska +// SPDX-FileCopyrightText: 2021 Bruno Corrêa Zimmermann +// +// SPDX-License-Identifier: MIT OR Apache-2.0 + +use crate::{enum_map, EnumArray, EnumMap}; +use core::fmt; +use core::marker::PhantomData; +use serde::de::{self, Deserialize, Deserializer, Error, MapAccess, SeqAccess}; +use serde::ser::{Serialize, SerializeTuple, Serializer}; + +/// Requires crate feature `"serde"` +impl + Serialize, V: Serialize> Serialize for EnumMap { + fn serialize(&self, serializer: S) -> Result { + if serializer.is_human_readable() { + serializer.collect_map(self) + } else { + let mut tup = serializer.serialize_tuple(self.len())?; + for value in self.values() { + tup.serialize_element(value)?; + } + tup.end() + } + } +} + +/// Requires crate feature `"serde"` +impl<'de, K, V> Deserialize<'de> for EnumMap +where + K: EnumArray + EnumArray> + Deserialize<'de>, + V: Deserialize<'de>, +{ + fn deserialize>(deserializer: D) -> Result { + if deserializer.is_human_readable() { + deserializer.deserialize_map(HumanReadableVisitor(PhantomData)) + } else { + deserializer.deserialize_tuple(K::LENGTH, CompactVisitor(PhantomData)) + } + } +} + +struct HumanReadableVisitor(PhantomData<(K, V)>); + +impl<'de, K, V> de::Visitor<'de> for HumanReadableVisitor +where + K: EnumArray + EnumArray> + Deserialize<'de>, + V: Deserialize<'de>, +{ + type Value = EnumMap; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "a map") + } + + fn visit_map>(self, mut access: M) -> Result { + let mut entries = EnumMap::default(); + while let Some((key, value)) = access.next_entry()? { + entries[key] = Some(value); + } + for value in entries.values() { + value + .as_ref() + .ok_or_else(|| M::Error::custom("key not specified"))?; + } + Ok(enum_map! { key => entries[key].take().unwrap() }) + } +} + +struct CompactVisitor(PhantomData<(K, V)>); + +impl<'de, K, V> de::Visitor<'de> for CompactVisitor +where + K: EnumArray + EnumArray> + Deserialize<'de>, + V: Deserialize<'de>, +{ + type Value = EnumMap; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "a sequence") + } + + fn visit_seq>(self, mut access: M) -> Result { + let mut entries = EnumMap::default(); + let len = entries.len(); + { + let mut iter = entries.values_mut(); + while let Some(place) = iter.next() { + *place = Some(access.next_element()?.ok_or_else(|| { + M::Error::invalid_length( + len - iter.len() - 1, + &"a sequence with as many elements as there are variants", + ) + })?); + } + } + Ok(enum_map! { key => entries[key].take().unwrap() }) + } +} diff --git a/third_party/rust/enum-map/tests/serde.rs b/third_party/rust/enum-map/tests/serde.rs new file mode 100644 index 000000000000..062ffe246a3e --- /dev/null +++ b/third_party/rust/enum-map/tests/serde.rs @@ -0,0 +1,115 @@ +#![cfg(feature = "serde")] + +// SPDX-FileCopyrightText: 2017 - 2022 Kamila Borowska +// SPDX-FileCopyrightText: 2022 Cass Fridkin +// +// SPDX-License-Identifier: MIT OR Apache-2.0 + +use enum_map::{enum_map, Enum, EnumMap}; +use serde::{Deserialize, Serialize}; +use serde_test::{assert_de_tokens_error, assert_tokens, Compact, Configure, Token}; + +#[derive(Debug, Enum, Deserialize, Serialize)] +enum Example { + A, + B, +} + +#[test] +fn serialization() { + let map = enum_map! { Example::A => 5, Example::B => 10 }; + assert_tokens( + &map.readable(), + &[ + Token::Map { len: Some(2) }, + Token::UnitVariant { + name: "Example", + variant: "A", + }, + Token::I32(5), + Token::UnitVariant { + name: "Example", + variant: "B", + }, + Token::I32(10), + Token::MapEnd, + ], + ); +} + +#[test] +fn compact_serialization() { + let map = enum_map! { Example::A => 5, Example::B => 10 }; + assert_tokens( + &map.compact(), + &[ + Token::Tuple { len: 2 }, + Token::I32(5), + Token::I32(10), + Token::TupleEnd, + ], + ); +} + +#[test] +fn invalid_compact_deserialization() { + assert_de_tokens_error::>>( + &[Token::I32(4)], + "invalid type: integer `4`, expected a sequence", + ); +} + +#[test] +fn too_short_compact_deserialization() { + assert_de_tokens_error::>>( + &[Token::Seq { len: None }, Token::Bool(true), Token::SeqEnd], + "invalid length 1, expected a sequence with as many elements as there are variants", + ); +} + +const JSON: &str = r#"{"A":5,"B":10}"#; + +#[test] +fn json_serialization() { + let map = enum_map! { Example::A => 5, Example::B => 10 }; + assert_eq!(serde_json::to_string(&map).unwrap(), String::from(JSON)); +} + +#[test] +fn json_deserialization() { + let example: EnumMap = serde_json::from_str(JSON).unwrap(); + assert_eq!(example, enum_map! { Example::A => 5, Example::B => 10 }); +} + +#[test] +fn json_invalid_deserialization() { + let example: Result, _> = serde_json::from_str(r"{}"); + assert!(example.is_err()); +} + +#[test] +fn json_invalid_type() { + let example: Result, _> = serde_json::from_str("4"); + assert!(example.is_err()); +} + +#[test] +fn json_invalid_key() { + let example: Result, _> = + serde_json::from_str(r#"{"a": 5, "b": 10, "c": 6}"#); + assert!(example.is_err()); +} + +#[test] +fn bincode_serialization() { + let example = enum_map! { false => 3u8, true => 4u8 }; + let serialized = bincode::serialize(&example).unwrap(); + assert_eq!(example, bincode::deserialize(&serialized).unwrap()); +} + +#[test] +fn bincode_too_short_deserialization() { + assert!( + bincode::deserialize::>(&bincode::serialize(&()).unwrap()).is_err() + ); +} diff --git a/third_party/rust/enum-map/tests/test.rs b/third_party/rust/enum-map/tests/test.rs new file mode 100644 index 000000000000..66cea1fd1be7 --- /dev/null +++ b/third_party/rust/enum-map/tests/test.rs @@ -0,0 +1,696 @@ +// SPDX-FileCopyrightText: 2018 - 2022 Kamila Borowska +// SPDX-FileCopyrightText: 2019 Riey +// SPDX-FileCopyrightText: 2020 Amanieu d'Antras +// SPDX-FileCopyrightText: 2021 Bruno Corrêa Zimmermann +// SPDX-FileCopyrightText: 2021 micycle +// SPDX-FileCopyrightText: 2022 Cass Fridkin +// +// SPDX-License-Identifier: MIT OR Apache-2.0 + +#[macro_use] +extern crate enum_map; + +use enum_map::{Enum, EnumArray, EnumMap, IntoIter}; + +use std::cell::{Cell, RefCell}; +use std::collections::HashSet; +use std::convert::Infallible; +use std::marker::PhantomData; +use std::num::ParseIntError; +use std::panic::{catch_unwind, UnwindSafe}; + +trait From: Sized { + fn from(_: T) -> Self { + unreachable!(); + } +} + +impl From for U {} + +#[derive(Copy, Clone, Debug, Enum, PartialEq)] +enum Example { + A, + B, + C, +} + +#[test] +fn test_bool() { + let mut map = enum_map! { false => 24, true => 42 }; + assert_eq!(map[false], 24); + assert_eq!(map[true], 42); + map[false] += 1; + assert_eq!(map[false], 25); + for (key, item) in &mut map { + if !key { + *item += 1; + } + } + assert_eq!(map[false], 26); + assert_eq!(map[true], 42); +} + +#[test] +fn test_clone() { + let map = enum_map! { false => 3, true => 5 }; + assert_eq!(map.clone(), map); +} + +#[test] +fn test_debug() { + let map = enum_map! { false => 3, true => 5 }; + assert_eq!(format!("{:?}", map), "{false: 3, true: 5}"); +} + +#[test] +fn test_hash() { + let map = enum_map! { false => 3, true => 5 }; + let mut set = HashSet::new(); + set.insert(map); + assert!(set.contains(&map)); +} + +#[test] +fn test_clear() { + let mut map = enum_map! { false => 1, true => 2 }; + map.clear(); + assert_eq!(map[true], 0); + assert_eq!(map[false], 0); +} + +#[test] +fn struct_of_enum() { + #[derive(Copy, Clone, Debug, Enum, PartialEq)] + struct Product { + example: Example, + is_done: bool, + } + + let mut map = enum_map! { + Product { example: Example::A, is_done: false } => "foo", + Product { example: Example::B, is_done: false } => "bar", + Product { example: Example::C, is_done: false } => "baz", + Product { example: Example::A, is_done: true } => "done foo", + Product { example: Example::B, is_done: true } => "bar done", + Product { example: Example::C, is_done: true } => "doooozne", + }; + + assert_eq!( + map[Product { + example: Example::B, + is_done: false + }], + "bar" + ); + assert_eq!( + map[Product { + example: Example::C, + is_done: false + }], + "baz" + ); + assert_eq!( + map[Product { + example: Example::B, + is_done: true + }], + "bar done" + ); + + map[Product { + example: Example::B, + is_done: true, + }] = "not really done"; + assert_eq!( + map[Product { + example: Example::B, + is_done: false + }], + "bar" + ); + assert_eq!( + map[Product { + example: Example::C, + is_done: false + }], + "baz" + ); + assert_eq!( + map[Product { + example: Example::B, + is_done: true + }], + "not really done" + ); +} + +#[test] +fn tuple_struct_of_enum() { + #[derive(Copy, Clone, Debug, Enum, PartialEq)] + struct Product(Example, bool); + + let mut map = enum_map! { + Product(Example::A, false) => "foo", + Product(Example::B, false) => "bar", + Product(Example::C, false) => "baz", + Product(Example::A, true) => "done foo", + Product(Example::B, true) => "bar done", + Product(Example::C, true) => "doooozne", + }; + + assert_eq!(map[Product(Example::B, false)], "bar"); + assert_eq!(map[Product(Example::C, false)], "baz"); + assert_eq!(map[Product(Example::B, true)], "bar done"); + + map[Product(Example::B, true)] = "not really done"; + assert_eq!(map[Product(Example::B, false)], "bar"); + assert_eq!(map[Product(Example::C, false)], "baz"); + assert_eq!(map[Product(Example::B, true)], "not really done"); +} + +#[test] +fn discriminants() { + #[derive(Debug, Enum, PartialEq)] + enum Discriminants { + A = 2000, + B = 3000, + C = 1000, + } + let mut map = EnumMap::default(); + map[Discriminants::A] = 3; + map[Discriminants::B] = 2; + map[Discriminants::C] = 1; + let mut pairs = map.iter(); + assert_eq!(pairs.next(), Some((Discriminants::A, &3))); + assert_eq!(pairs.next(), Some((Discriminants::B, &2))); + assert_eq!(pairs.next(), Some((Discriminants::C, &1))); + assert_eq!(pairs.next(), None); +} + +#[test] +fn extend() { + let mut map = enum_map! { _ => 0 }; + map.extend(vec![(Example::A, 3)]); + map.extend(vec![(&Example::B, &4)]); + assert_eq!( + map, + enum_map! { Example::A => 3, Example::B => 4, Example::C => 0 } + ); +} + +#[test] +fn collect() { + let iter = vec![(Example::A, 5), (Example::B, 7)] + .into_iter() + .map(|(k, v)| (k, v + 1)); + assert_eq!( + iter.collect::>(), + enum_map! { Example::A => 6, Example::B => 8, Example::C => 0 } + ); +} + +#[test] +fn huge_enum() { + #[derive(Enum)] + enum Example { + A, + B, + C, + D, + E, + F, + G, + H, + I, + J, + K, + L, + M, + N, + O, + P, + Q, + R, + S, + T, + U, + V, + W, + X, + Y, + Z, + Aa, + Bb, + Cc, + Dd, + Ee, + Ff, + Gg, + Hh, + Ii, + Jj, + Kk, + Ll, + Mm, + Nn, + Oo, + Pp, + Qq, + Rr, + Ss, + Tt, + Uu, + Vv, + Ww, + Xx, + Yy, + Zz, + } + + let map = enum_map! { _ => 2 }; + assert_eq!(map[Example::Xx], 2); +} + +#[test] +fn iterator_len() { + assert_eq!( + enum_map! { Example::A | Example::B | Example::C => 0 } + .iter() + .len(), + 3 + ); +} + +#[test] +fn iter_mut_len() { + assert_eq!( + enum_map! { Example::A | Example::B | Example::C => 0 } + .iter_mut() + .len(), + 3 + ); +} + +#[test] +fn into_iter_len() { + assert_eq!(enum_map! { Example::A | _ => 0 }.into_iter().len(), 3); +} + +#[test] +fn iterator_next_back() { + assert_eq!( + enum_map! { Example::A => 1, Example::B => 2, Example::C => 3 } + .iter() + .next_back(), + Some((Example::C, &3)) + ); +} + +#[test] +fn iter_mut_next_back() { + assert_eq!( + enum_map! { Example::A => 1, Example::B => 2, Example::C => 3 } + .iter_mut() + .next_back(), + Some((Example::C, &mut 3)) + ); +} + +#[test] +fn into_iter() { + let mut iter = enum_map! { true => 5, false => 7 }.into_iter(); + assert_eq!(iter.next(), Some((false, 7))); + assert_eq!(iter.next(), Some((true, 5))); + assert_eq!(iter.next(), None); + assert_eq!(iter.next(), None); +} + +#[test] +fn into_iter_u8() { + assert_eq!( + enum_map! { i => i }.into_iter().collect::>(), + (0..256).map(|x| (x as u8, x as u8)).collect::>() + ); +} + +struct DropReporter<'a> { + into: &'a RefCell>, + value: usize, +} + +impl<'a> Drop for DropReporter<'a> { + fn drop(&mut self) { + self.into.borrow_mut().push(self.value); + } +} + +#[test] +fn into_iter_drop() { + let dropped = RefCell::new(Vec::default()); + let mut a: IntoIter = enum_map! { + k => DropReporter { + into: &dropped, + value: k as usize, + }, + } + .into_iter(); + assert_eq!(a.next().unwrap().0, Example::A); + assert_eq!(*dropped.borrow(), &[0]); + drop(a); + assert_eq!(*dropped.borrow(), &[0, 1, 2]); +} + +#[test] +fn into_iter_double_ended_iterator() { + let mut iter = enum_map! { 0 => 5, 255 => 7, _ => 0 }.into_iter(); + assert_eq!(iter.next(), Some((0, 5))); + assert_eq!(iter.next_back(), Some((255, 7))); + assert_eq!(iter.next(), Some((1, 0))); + assert_eq!(iter.next_back(), Some((254, 0))); + assert!(iter.rev().eq((2..254).rev().map(|i| (i, 0)))); +} + +#[test] +fn values_rev_collect() { + assert_eq!( + vec![3, 2, 1], + enum_map! { Example::A => 1, Example::B => 2, Example::C => 3 } + .values() + .rev() + .cloned() + .collect::>() + ); +} + +#[test] +fn values_len() { + assert_eq!(enum_map! { false => 0, true => 1 }.values().len(), 2); +} + +#[test] +fn into_values_rev_collect() { + assert_eq!( + vec![3, 2, 1], + enum_map! { Example::A => 1, Example::B => 2, Example::C => 3 } + .into_values() + .rev() + .collect::>() + ); +} + +#[test] +fn into_values_len() { + assert_eq!(enum_map! { false => 0, true => 1 }.into_values().len(), 2); +} + +#[test] +fn values_mut_next_back() { + let mut map = enum_map! { false => 0, true => 1 }; + assert_eq!(map.values_mut().next_back(), Some(&mut 1)); +} +#[test] +fn test_u8() { + let mut map = enum_map! { b'a' => 4, _ => 0 }; + map[b'c'] = 3; + assert_eq!(map[b'a'], 4); + assert_eq!(map[b'b'], 0); + assert_eq!(map[b'c'], 3); + assert_eq!(map.iter().next(), Some((0, &0))); +} + +#[derive(Enum)] +enum Void {} + +#[test] +fn empty_map() { + let void: EnumMap = enum_map! {}; + assert_eq!(void.len(), 0); +} + +#[test] +#[should_panic] +fn empty_value() { + let _void: EnumMap = enum_map! { _ => unreachable!() }; +} + +#[test] +fn empty_infallible_map() { + let void: EnumMap = enum_map! {}; + assert_eq!(void.len(), 0); +} + +#[derive(Clone, Copy)] +enum X { + A(PhantomData<*const ()>), +} + +impl Enum for X { + const LENGTH: usize = 1; + + fn from_usize(arg: usize) -> X { + assert_eq!(arg, 0); + X::A(PhantomData) + } + + fn into_usize(self) -> usize { + 0 + } +} + +impl EnumArray for X { + type Array = [V; Self::LENGTH]; +} + +fn assert_sync_send(_: T) {} + +#[test] +fn assert_enum_map_does_not_copy_sync_send_dependency_of_keys() { + let mut map = enum_map! { X::A(PhantomData) => true }; + assert_sync_send(map); + assert_sync_send(&map); + assert_sync_send(&mut map); + assert_sync_send(map.iter()); + assert_sync_send(map.iter_mut()); + assert_sync_send(map.into_iter()); + assert!(map[X::A(PhantomData)]); +} + +#[test] +fn test_sum() { + assert_eq!( + enum_map! { i => u8::into(i) } + .iter() + .map(|(_, v)| v) + .sum::(), + 32_640 + ); +} + +#[test] +fn test_sum_mut() { + assert_eq!( + enum_map! { i => u8::into(i) } + .iter_mut() + .map(|(_, &mut v)| -> u32 { v }) + .sum::(), + 32_640 + ); +} + +#[test] +fn test_iter_clone() { + struct S(u8); + let map = enum_map! { + Example::A => S(3), + Example::B => S(4), + Example::C => S(1), + }; + let iter = map.iter(); + assert_eq!(iter.clone().map(|(_, S(v))| v).sum::(), 8); + assert_eq!(iter.map(|(_, S(v))| v).sum::(), 8); + let values = map.values(); + assert_eq!(values.clone().map(|S(v)| v).sum::(), 8); + assert_eq!(values.map(|S(v)| v).sum::(), 8); +} + +#[test] +fn question_mark() -> Result<(), ParseIntError> { + let map = enum_map! { false => "2".parse()?, true => "5".parse()? }; + assert_eq!(map, enum_map! { false => 2, true => 5 }); + Ok(()) +} + +#[test] +fn question_mark_failure() { + struct IncOnDrop<'a>(&'a Cell); + + impl Drop for IncOnDrop<'_> { + fn drop(&mut self) { + self.0.set(self.0.get() + 1); + } + } + + fn failible() -> Result, &'static str> { + Err("ERROR!") + } + + fn try_block(inc: &Cell) -> Result<(), &'static str> { + enum_map! { + 32 => failible()?, + _ => { + IncOnDrop(inc) + } + }; + Ok(()) + } + let value = Cell::new(0); + assert_eq!(try_block(&value), Err("ERROR!")); + assert_eq!(value.get(), 32); +} + +#[test] +#[should_panic = "Intentional panic"] +fn map_panic() { + let map: EnumMap = enum_map! { i => i.to_string() }; + map.map(|k, v| { + if k == 2 { + panic!("Intentional panic"); + } + v + " modified" + }); +} + +macro_rules! make_enum_map_macro_safety_test { + ($a:tt $b:tt) => { + // This is misuse of an API, however we need to test that to ensure safety + // as we use unsafe code. + enum E { + A, + B, + C, + } + + impl Enum for E { + const LENGTH: usize = $a; + + fn from_usize(value: usize) -> E { + match value { + 0 => E::A, + 1 => E::B, + 2 => E::C, + _ => unimplemented!(), + } + } + + fn into_usize(self) -> usize { + self as usize + } + } + + impl EnumArray for E { + type Array = [V; $b]; + } + + let map: EnumMap = enum_map! { _ => "Hello, world!".into() }; + map.into_iter(); + }; +} + +#[test] +fn enum_map_macro_safety_under() { + make_enum_map_macro_safety_test!(2 3); +} + +#[test] +fn enum_map_macro_safety_over() { + make_enum_map_macro_safety_test!(3 2); +} + +#[test] +fn drop_panic_into_iter() { + struct DropHandler<'a>(&'a Cell); + impl Drop for DropHandler<'_> { + fn drop(&mut self) { + self.0.set(self.0.get() + 1); + } + } + impl UnwindSafe for DropHandler<'_> {} + struct Storage<'a> { + should_panic: bool, + _drop_handler: DropHandler<'a>, + } + impl Drop for Storage<'_> { + fn drop(&mut self) { + if self.should_panic { + panic!(); + } + } + } + let cell = Cell::new(0); + let map: EnumMap = enum_map! { + v => Storage { should_panic: v == Example::B, _drop_handler: DropHandler(&cell) }, + }; + assert!(catch_unwind(|| { + map.into_iter(); + }) + .is_err()); + assert_eq!(cell.get(), 3); +} + +#[test] +fn test_const_enum_map_from_array() { + const CONST_ENUM_MAP_FROM_ARRAY: EnumMap = EnumMap::from_array([4, 8]); + assert_eq!( + CONST_ENUM_MAP_FROM_ARRAY, + enum_map! { false => 4, true => 8 }, + ); +} + +#[test] +fn usize_override() { + #[allow(non_camel_case_types, dead_code)] + type usize = (); + #[derive(Enum)] + enum X { + A, + B, + } +} + +// Regression test for https://codeberg.org/xfix/enum-map/issues/112 +#[test] +fn test_issue_112() { + #[derive(Enum, PartialEq, Debug)] + enum Inner { + Inner1, + Inner2, + } + + #[derive(Enum, PartialEq, Debug)] + enum Outer { + A, + B(Inner), + C, + D(Inner, Inner), + E, + } + + assert_eq!(Outer::A.into_usize(), 0); + assert_eq!(Outer::A, Outer::from_usize(0)); + assert_eq!(Outer::B(Inner::Inner1).into_usize(), 1); + assert_eq!(Outer::B(Inner::Inner1), Outer::from_usize(1)); + assert_eq!(Outer::B(Inner::Inner2).into_usize(), 2); + assert_eq!(Outer::B(Inner::Inner2), Outer::from_usize(2)); + assert_eq!(Outer::C.into_usize(), 3); + assert_eq!(Outer::C, Outer::from_usize(3)); + assert_eq!(Outer::D(Inner::Inner1, Inner::Inner1).into_usize(), 4); + assert_eq!(Outer::D(Inner::Inner1, Inner::Inner1), Outer::from_usize(4)); + assert_eq!(Outer::D(Inner::Inner2, Inner::Inner1).into_usize(), 5); + assert_eq!(Outer::D(Inner::Inner2, Inner::Inner1), Outer::from_usize(5)); + assert_eq!(Outer::D(Inner::Inner1, Inner::Inner2).into_usize(), 6); + assert_eq!(Outer::D(Inner::Inner1, Inner::Inner2), Outer::from_usize(6)); + assert_eq!(Outer::D(Inner::Inner2, Inner::Inner2).into_usize(), 7); + assert_eq!(Outer::D(Inner::Inner2, Inner::Inner2), Outer::from_usize(7)); + assert_eq!(Outer::E.into_usize(), 8); + assert_eq!(Outer::E, Outer::from_usize(8)); +} diff --git a/third_party/rust/neqo-common/.cargo-checksum.json b/third_party/rust/neqo-common/.cargo-checksum.json index 5dfc5c90249a..f8b692fdfef9 100644 --- a/third_party/rust/neqo-common/.cargo-checksum.json +++ b/third_party/rust/neqo-common/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"b29d3a82e784eaa91625dd36e843cd34800e6528573eb7684cb34989b9a6cbc3","build.rs":"a17b1bb1bd3de3fc958f72d4d1357f7bc4432faa26640c95b5fbfccf40579d67","src/codec.rs":"a317da96f2a2e70313f1a6ed4741261191cef690db25dc789973fd980aafc77f","src/datagram.rs":"742aa0f39ac24d63431b58e23ebf925e27ec42340e5911020475de5f7f457a6d","src/event.rs":"f60fee9f4b09ef47ff5e4bfa21c07e45ffd5873c292f2605f24d834070127d62","src/header.rs":"467b947f78bfe354d8bb51e8df0c2be69e75a45e2be688d81f0d268aa77c89ef","src/hrtime.rs":"fdb72b347c94eefb2fa0bdb669cca4549853d7f767ad7572941adf265f1621f9","src/incrdecoder.rs":"f3b6e964d02c34e7c8fc5f048b4f99cc3b0f2567cabb2e078f0b7894e1baa50e","src/lib.rs":"b1d5f72196b9e846fdc10b099468e3f437b3121b3b2d72727a1f55b5c77c455c","src/log.rs":"2713e29de2d4718b65ad1b0d922702629845b830b195a5b01b018dc395039a85","src/qlog.rs":"c9f4a32950d405fdfbb317c61a4089fa1b75c5de40698e851a62413ceac46c8a","src/timer.rs":"8da10e8300be0795367e2823d3ecf7ec46bcadbedfc28ed5a013794bcd73cfc7","tests/log.rs":"480b165b7907ec642c508b303d63005eee1427115d6973a349eaf6b2242ed18d"},"package":null} \ No newline at end of file +{"files":{"Cargo.toml":"dbb5500f87df7aee6e680ac210ddb56b833aa82d6be5c407474de0895cee14e9","build.rs":"a17b1bb1bd3de3fc958f72d4d1357f7bc4432faa26640c95b5fbfccf40579d67","src/codec.rs":"8c14f09864b095e28ff52e7d96a12a6591fc9c4b20a9cafca6720d132c80efdc","src/datagram.rs":"1a7028d96a2e7385e94265de53189eb824b7cf12e0e2de5d67c3f3f8751b6043","src/event.rs":"4ef9e6f3f5168f2eacb7be982e062e743c64a64e809765d2139122839aa407e5","src/header.rs":"467b947f78bfe354d8bb51e8df0c2be69e75a45e2be688d81f0d268aa77c89ef","src/hrtime.rs":"d7c8849e9ec7a312878ea2bc28939717fa03969fb9aee259a4a516351ee37643","src/incrdecoder.rs":"577c32b9ace51f2daaf940be6d0c391c4f55cd42ef6848c68c1ffc970d8c57b5","src/lib.rs":"47c14084c6d475ebb855f3ed9302b31fa42780b93a816bf098c96987ffe33572","src/log.rs":"c68099eae0e9014be35173ac802165b128433d973390e1111c08df56e71df063","src/qlog.rs":"3f43dc4e5fdccb9d6ee74d9e7b3ff29da63e4eb9f631e4e35446e452d8ec7af6","src/timer.rs":"50a2de20933b7b5884337aded69e59e2523503481308f25de1bba1a11d505be8","src/tos.rs":"5b5a61c699266716afce2f5bda7c98151db3223ede41ce451c390863198e30a2","tests/log.rs":"480b165b7907ec642c508b303d63005eee1427115d6973a349eaf6b2242ed18d"},"package":null} \ No newline at end of file diff --git a/third_party/rust/neqo-common/Cargo.toml b/third_party/rust/neqo-common/Cargo.toml index b14193487a78..b04537bb0ab2 100644 --- a/third_party/rust/neqo-common/Cargo.toml +++ b/third_party/rust/neqo-common/Cargo.toml @@ -11,30 +11,38 @@ [package] edition = "2018" -rust-version = "1.65.0" +rust-version = "1.70.0" name = "neqo-common" -version = "0.6.8" +version = "0.7.0" authors = ["Bobby Holley "] build = "build.rs" license = "MIT OR Apache-2.0" [dependencies] -lazy_static = "1.3.0" -qlog = "0.9.0" +enum-map = "2.7" +lazy_static = "1.4" [dependencies.env_logger] version = "0.10" default-features = false [dependencies.log] -version = "0.4.0" +version = "0.4" default-features = false +[dependencies.qlog] +git = "https://github.com/cloudflare/quiche" +rev = "09ea4b244096a013071cfe2175bbf2945fb7f8d1" + [dependencies.time] -version = "=0.3.23" +version = "0.3.23" features = ["formatting"] +[dev-dependencies.test-fixture] +path = "../test-fixture" + [features] +ci = [] deny-warnings = [] [target."cfg(windows)".dependencies.winapi] diff --git a/third_party/rust/neqo-common/src/codec.rs b/third_party/rust/neqo-common/src/codec.rs index 99ba9ec52ac4..57ff13f39fa6 100644 --- a/third_party/rust/neqo-common/src/codec.rs +++ b/third_party/rust/neqo-common/src/codec.rs @@ -34,7 +34,9 @@ impl<'a> Decoder<'a> { } /// Skip n bytes. + /// /// # Panics + /// /// If the remaining quantity is less than `n`. pub fn skip(&mut self, n: usize) { assert!(self.remaining() >= n, "insufficient data"); @@ -90,7 +92,9 @@ impl<'a> Decoder<'a> { } /// Decodes an unsigned integer of length 1..=8. + /// /// # Panics + /// /// This panics if `n` is not in the range `1..=8`. pub fn decode_uint(&mut self, n: usize) -> Option { assert!(n > 0 && n <= 8); @@ -198,7 +202,9 @@ pub struct Encoder { impl Encoder { /// Static helper function for previewing the results of encoding without doing it. + /// /// # Panics + /// /// When `v` is too large. #[must_use] pub const fn varint_len(v: u64) -> usize { @@ -212,7 +218,9 @@ impl Encoder { } /// Static helper to determine how long a varint-prefixed array encodes to. + /// /// # Panics + /// /// When `len` doesn't fit in a `u64`. #[must_use] pub fn vvec_len(len: usize) -> usize { @@ -261,7 +269,9 @@ impl Encoder { } /// Don't use this except in testing. + /// /// # Panics + /// /// When `s` contains non-hex values or an odd number of values. #[must_use] pub fn from_hex(s: impl AsRef) -> Self { @@ -291,7 +301,9 @@ impl Encoder { } /// Encode an integer of any size up to u64. + /// /// # Panics + /// /// When `n` is outside the range `1..=8`. #[allow(clippy::cast_possible_truncation)] pub fn encode_uint>(&mut self, n: usize, v: T) -> &mut Self { @@ -304,7 +316,9 @@ impl Encoder { } /// Encode a QUIC varint. + /// /// # Panics + /// /// When `v >= 1<<62`. pub fn encode_varint>(&mut self, v: T) -> &mut Self { let v = v.into(); @@ -319,7 +333,9 @@ impl Encoder { } /// Encode a vector in TLS style. + /// /// # Panics + /// /// When `v` is longer than 2^64. pub fn encode_vec(&mut self, n: usize, v: &[u8]) -> &mut Self { self.encode_uint(n, u64::try_from(v.as_ref().len()).unwrap()) @@ -327,7 +343,9 @@ impl Encoder { } /// Encode a vector in TLS style using a closure for the contents. + /// /// # Panics + /// /// When `f()` returns a length larger than `2^8n`. #[allow(clippy::cast_possible_truncation)] pub fn encode_vec_with(&mut self, n: usize, f: F) -> &mut Self { @@ -343,7 +361,9 @@ impl Encoder { } /// Encode a vector with a varint length. + /// /// # Panics + /// /// When `v` is longer than 2^64. pub fn encode_vvec(&mut self, v: &[u8]) -> &mut Self { self.encode_varint(u64::try_from(v.as_ref().len()).unwrap()) @@ -351,7 +371,9 @@ impl Encoder { } /// Encode a vector with a varint length using a closure. + /// /// # Panics + /// /// When `f()` writes more than 2^62 bytes. #[allow(clippy::cast_possible_truncation)] pub fn encode_vvec_with(&mut self, f: F) -> &mut Self { diff --git a/third_party/rust/neqo-common/src/datagram.rs b/third_party/rust/neqo-common/src/datagram.rs index 0316dd230989..1729c8ed8d12 100644 --- a/third_party/rust/neqo-common/src/datagram.rs +++ b/third_party/rust/neqo-common/src/datagram.rs @@ -4,23 +4,32 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::net::SocketAddr; -use std::ops::Deref; +use std::{net::SocketAddr, ops::Deref}; -use crate::hex_with_len; +use crate::{hex_with_len, IpTos}; -#[derive(PartialEq, Eq, Clone)] +#[derive(Clone, PartialEq, Eq)] pub struct Datagram { src: SocketAddr, dst: SocketAddr, + tos: IpTos, + ttl: Option, d: Vec, } impl Datagram { - pub fn new>>(src: SocketAddr, dst: SocketAddr, d: V) -> Self { + pub fn new>>( + src: SocketAddr, + dst: SocketAddr, + tos: IpTos, + ttl: Option, + d: V, + ) -> Self { Self { src, dst, + tos, + ttl, d: d.into(), } } @@ -34,6 +43,16 @@ impl Datagram { pub fn destination(&self) -> SocketAddr { self.dst } + + #[must_use] + pub fn tos(&self) -> IpTos { + self.tos + } + + #[must_use] + pub fn ttl(&self) -> Option { + self.ttl + } } impl Deref for Datagram { @@ -48,10 +67,25 @@ impl std::fmt::Debug for Datagram { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!( f, - "Datagram {:?}->{:?}: {}", + "Datagram {:?} TTL {:?} {:?}->{:?}: {}", + self.tos, + self.ttl, self.src, self.dst, hex_with_len(&self.d) ) } } + +#[cfg(test)] +use test_fixture::datagram; + +#[test] +fn fmt_datagram() { + let d = datagram([0; 1].to_vec()); + assert_eq!( + format!("{d:?}"), + "Datagram IpTos(Cs0, NotEct) TTL Some(128) [fe80::1]:443->[fe80::1]:443: [1]: 00" + .to_string() + ); +} diff --git a/third_party/rust/neqo-common/src/event.rs b/third_party/rust/neqo-common/src/event.rs index 8598383e763f..26052b7571f6 100644 --- a/third_party/rust/neqo-common/src/event.rs +++ b/third_party/rust/neqo-common/src/event.rs @@ -4,8 +4,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::iter::Iterator; -use std::marker::PhantomData; +use std::{iter::Iterator, marker::PhantomData}; /// An event provider is able to generate a stream of events. pub trait Provider { diff --git a/third_party/rust/neqo-common/src/hrtime.rs b/third_party/rust/neqo-common/src/hrtime.rs index 2ac0a08cddab..62d2567d4235 100644 --- a/third_party/rust/neqo-common/src/hrtime.rs +++ b/third_party/rust/neqo-common/src/hrtime.rs @@ -27,12 +27,12 @@ impl Period { const MIN: Period = Period(1); #[cfg(windows)] - fn as_uint(&self) -> UINT { + fn as_uint(self) -> UINT { UINT::from(self.0) } #[cfg(target_os = "macos")] - fn scaled(&self, scale: f64) -> f64 { + fn scaled(self, scale: f64) -> f64 { scale * f64::from(self.0) } } @@ -126,6 +126,7 @@ mod mac { } const THREAD_TIME_CONSTRAINT_POLICY: thread_policy_flavor_t = 2; + #[allow(clippy::cast_possible_truncation)] const THREAD_TIME_CONSTRAINT_POLICY_COUNT: mach_msg_type_number_t = (size_of::() / size_of::()) as mach_msg_type_number_t; @@ -163,7 +164,7 @@ mod mac { thread_policy_set( pthread_mach_thread_np(pthread_self()), THREAD_TIME_CONSTRAINT_POLICY, - addr_of_mut!(policy) as _, // horror! + addr_of_mut!(policy).cast(), // horror! THREAD_TIME_CONSTRAINT_POLICY_COUNT, ) }; @@ -180,6 +181,7 @@ mod mac { /// Create a realtime policy and set it. pub fn set_realtime(base: f64) { + #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] let policy = thread_time_constraint_policy { period: base as u32, // Base interval computation: (base * 0.5) as u32, @@ -198,7 +200,7 @@ mod mac { thread_policy_get( pthread_mach_thread_np(pthread_self()), THREAD_TIME_CONSTRAINT_POLICY, - addr_of_mut!(policy) as _, // horror! + addr_of_mut!(policy).cast(), // horror! &mut count, &mut get_default, ) @@ -292,14 +294,14 @@ impl Time { if let Some(p) = self.active { mac::set_realtime(p.scaled(self.scale)); } else { - mac::set_thread_policy(self.deflt.clone()); + mac::set_thread_policy(self.deflt); } } #[cfg(windows)] { if let Some(p) = self.active { - assert_eq!(0, unsafe { timeBeginPeriod(p.as_uint()) }); + _ = unsafe { timeBeginPeriod(p.as_uint()) }; } } } @@ -309,7 +311,7 @@ impl Time { #[cfg(windows)] { if let Some(p) = self.active { - assert_eq!(0, unsafe { timeEndPeriod(p.as_uint()) }); + _ = unsafe { timeEndPeriod(p.as_uint()) }; } } } @@ -370,14 +372,20 @@ impl Drop for Time { } } -#[cfg(test)] +// Only run these tests in CI on platforms other than MacOS and Windows, where the timer +// inaccuracies are too high to pass the tests. +#[cfg(all( + test, + not(all(any(target_os = "macos", target_os = "windows"), feature = "ci")) +))] mod test { - use super::Time; use std::{ thread::{sleep, spawn}, time::{Duration, Instant}, }; + use super::Time; + const ONE: Duration = Duration::from_millis(1); const ONE_AND_A_BIT: Duration = Duration::from_micros(1500); /// A limit for when high resolution timers are disabled. diff --git a/third_party/rust/neqo-common/src/incrdecoder.rs b/third_party/rust/neqo-common/src/incrdecoder.rs index e78a90f7868e..8468102cb6b0 100644 --- a/third_party/rust/neqo-common/src/incrdecoder.rs +++ b/third_party/rust/neqo-common/src/incrdecoder.rs @@ -21,7 +21,9 @@ impl IncrementalDecoderUint { } /// Consume some data. + /// /// # Panics + /// /// Never, but this is not something the compiler can tell. pub fn consume(&mut self, dv: &mut Decoder) -> Option { if let Some(r) = &mut self.remaining { @@ -87,7 +89,9 @@ impl IncrementalDecoderBuffer { } /// Consume some bytes from the decoder. + /// /// # Panics + /// /// Never; but rust doesn't know that. pub fn consume(&mut self, dv: &mut Decoder) -> Option> { let amount = min(self.remaining, dv.remaining()); @@ -109,7 +113,9 @@ pub struct IncrementalDecoderIgnore { impl IncrementalDecoderIgnore { /// Make a new ignoring decoder. + /// /// # Panics + /// /// If the amount to ignore is zero. #[must_use] pub fn new(n: usize) -> Self { diff --git a/third_party/rust/neqo-common/src/lib.rs b/third_party/rust/neqo-common/src/lib.rs index 3fb0fd27ece6..853b05705bca 100644 --- a/third_party/rust/neqo-common/src/lib.rs +++ b/third_party/rust/neqo-common/src/lib.rs @@ -16,16 +16,20 @@ mod incrdecoder; pub mod log; pub mod qlog; pub mod timer; - -pub use self::codec::{Decoder, Encoder}; -pub use self::datagram::Datagram; -pub use self::header::Header; -pub use self::incrdecoder::{ - IncrementalDecoderBuffer, IncrementalDecoderIgnore, IncrementalDecoderUint, -}; +pub mod tos; use std::fmt::Write; +use enum_map::Enum; + +pub use self::{ + codec::{Decoder, Encoder}, + datagram::Datagram, + header::Header, + incrdecoder::{IncrementalDecoderBuffer, IncrementalDecoderIgnore, IncrementalDecoderUint}, + tos::{IpTos, IpTosDscp, IpTosEcn}, +}; + #[must_use] pub fn hex(buf: impl AsRef<[u8]>) -> String { let mut ret = String::with_capacity(buf.as_ref().len() * 2); @@ -75,7 +79,7 @@ pub const fn const_min(a: usize, b: usize) -> usize { [a, b][(a >= b) as usize] } -#[derive(Debug, PartialEq, Eq, Copy, Clone)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Enum)] /// Client or Server. pub enum Role { Client, diff --git a/third_party/rust/neqo-common/src/log.rs b/third_party/rust/neqo-common/src/log.rs index e37676552302..d9c30b98b1c1 100644 --- a/third_party/rust/neqo-common/src/log.rs +++ b/third_party/rust/neqo-common/src/log.rs @@ -6,11 +6,10 @@ #![allow(clippy::module_name_repetitions)] +use std::{io::Write, sync::Once, time::Instant}; + use env_logger::Builder; use lazy_static::lazy_static; -use std::io::Write; -use std::sync::Once; -use std::time::Instant; #[macro_export] macro_rules! do_log { diff --git a/third_party/rust/neqo-common/src/qlog.rs b/third_party/rust/neqo-common/src/qlog.rs index ac03ecfcb0da..3da83509903a 100644 --- a/third_party/rust/neqo-common/src/qlog.rs +++ b/third_party/rust/neqo-common/src/qlog.rs @@ -31,6 +31,7 @@ pub struct NeqoQlogShared { impl NeqoQlog { /// Create an enabled `NeqoQlog` configuration. + /// /// # Errors /// /// Will return `qlog::Error` if cannot write to the new log. @@ -48,6 +49,11 @@ impl NeqoQlog { }) } + #[must_use] + pub fn inner(&self) -> Rc>> { + Rc::clone(&self.inner) + } + /// Create a disabled `NeqoQlog` configuration. #[must_use] pub fn disabled() -> Self { @@ -144,3 +150,39 @@ pub fn new_trace(role: Role) -> qlog::TraceSeq { }), } } + +#[cfg(test)] +mod test { + use qlog::events::Event; + use test_fixture::EXPECTED_LOG_HEADER; + + const EV_DATA: qlog::events::EventData = + qlog::events::EventData::SpinBitUpdated(qlog::events::connectivity::SpinBitUpdated { + state: true, + }); + + const EXPECTED_LOG_EVENT: &str = concat!( + "\u{1e}", + r#"{"time":0.0,"name":"connectivity:spin_bit_updated","data":{"state":true}}"#, + "\n" + ); + + #[test] + fn new_neqo_qlog() { + let (_log, contents) = test_fixture::new_neqo_qlog(); + assert_eq!(contents.to_string(), EXPECTED_LOG_HEADER); + } + + #[test] + fn add_event() { + let (mut log, contents) = test_fixture::new_neqo_qlog(); + log.add_event(|| Some(Event::with_time(1.1, EV_DATA))); + assert_eq!( + contents.to_string(), + format!( + "{EXPECTED_LOG_HEADER}{e}", + e = EXPECTED_LOG_EVENT.replace("\"time\":0.0,", "\"time\":1.1,") + ) + ); + } +} diff --git a/third_party/rust/neqo-common/src/timer.rs b/third_party/rust/neqo-common/src/timer.rs index 24cb0abdbc59..e8532af4427b 100644 --- a/third_party/rust/neqo-common/src/timer.rs +++ b/third_party/rust/neqo-common/src/timer.rs @@ -4,9 +4,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::convert::TryFrom; -use std::mem; -use std::time::{Duration, Instant}; +use std::{ + convert::TryFrom, + mem, + time::{Duration, Instant}, +}; /// Internal structure for a timer item. struct TimerItem { @@ -21,10 +23,10 @@ impl TimerItem { } /// A timer queue. -/// This uses a classic timer wheel arrangement, with some characteristics that might be considered peculiar. -/// Each slot in the wheel is sorted (complexity O(N) insertions, but O(logN) to find cut points). -/// Time is relative, the wheel has an origin time and it is unable to represent times that are more than -/// `granularity * capacity` past that time. +/// This uses a classic timer wheel arrangement, with some characteristics that might be considered +/// peculiar. Each slot in the wheel is sorted (complexity O(N) insertions, but O(logN) to find cut +/// points). Time is relative, the wheel has an origin time and it is unable to represent times that +/// are more than `granularity * capacity` past that time. pub struct Timer { items: Vec>>, now: Instant, @@ -34,7 +36,9 @@ pub struct Timer { impl Timer { /// Construct a new wheel at the given granularity, starting at the given time. + /// /// # Panics + /// /// When `capacity` is too large to fit in `u32` or `granularity` is zero. pub fn new(now: Instant, granularity: Duration, capacity: usize) -> Self { assert!(u32::try_from(capacity).is_ok()); @@ -109,7 +113,9 @@ impl Timer { } /// Asserts if the time given is in the past or too far in the future. + /// /// # Panics + /// /// When `time` is in the past relative to previous calls. pub fn add(&mut self, time: Instant, item: T) { assert!(time >= self.now); @@ -241,9 +247,10 @@ impl Timer { #[cfg(test)] mod test { - use super::{Duration, Instant, Timer}; use lazy_static::lazy_static; + use super::{Duration, Instant, Timer}; + lazy_static! { static ref NOW: Instant = Instant::now(); } diff --git a/third_party/rust/neqo-common/src/tos.rs b/third_party/rust/neqo-common/src/tos.rs new file mode 100644 index 000000000000..aa360d1d5319 --- /dev/null +++ b/third_party/rust/neqo-common/src/tos.rs @@ -0,0 +1,290 @@ +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::fmt::Debug; + +use enum_map::Enum; + +/// ECN (Explicit Congestion Notification) codepoints mapped to the +/// lower 2 bits of the TOS field. +/// +#[derive(Copy, Clone, PartialEq, Eq, Enum, Default, Debug)] +#[repr(u8)] +pub enum IpTosEcn { + #[default] + /// Not-ECT, Not ECN-Capable Transport, RFC3168 + NotEct = 0b00, + + /// ECT(1), ECN-Capable Transport(1), RFC8311 and RFC9331 + Ect1 = 0b01, + + /// ECT(0), ECN-Capable Transport(0), RFC3168 + Ect0 = 0b10, + + /// CE, Congestion Experienced, RFC3168 + Ce = 0b11, +} + +impl From for u8 { + fn from(v: IpTosEcn) -> Self { + v as u8 + } +} + +impl From for IpTosEcn { + fn from(v: u8) -> Self { + match v & 0b11 { + 0b00 => IpTosEcn::NotEct, + 0b01 => IpTosEcn::Ect1, + 0b10 => IpTosEcn::Ect0, + 0b11 => IpTosEcn::Ce, + _ => unreachable!(), + } + } +} + +/// Diffserv Codepoints, mapped to the upper six bits of the TOS field. +/// +#[derive(Copy, Clone, PartialEq, Eq, Enum, Default, Debug)] +#[repr(u8)] +pub enum IpTosDscp { + #[default] + /// Class Selector 0, RFC2474 + Cs0 = 0b0000_0000, + + /// Class Selector 1, RFC2474 + Cs1 = 0b0010_0000, + + /// Class Selector 2, RFC2474 + Cs2 = 0b0100_0000, + + /// Class Selector 3, RFC2474 + Cs3 = 0b0110_0000, + + /// Class Selector 4, RFC2474 + Cs4 = 0b1000_0000, + + /// Class Selector 5, RFC2474 + Cs5 = 0b1010_0000, + + /// Class Selector 6, RFC2474 + Cs6 = 0b1100_0000, + + /// Class Selector 7, RFC2474 + Cs7 = 0b1110_0000, + + /// Assured Forwarding 11, RFC2597 + Af11 = 0b0010_1000, + + /// Assured Forwarding 12, RFC2597 + Af12 = 0b0011_0000, + + /// Assured Forwarding 13, RFC2597 + Af13 = 0b0011_1000, + + /// Assured Forwarding 21, RFC2597 + Af21 = 0b0100_1000, + + /// Assured Forwarding 22, RFC2597 + Af22 = 0b0101_0000, + + /// Assured Forwarding 23, RFC2597 + Af23 = 0b0101_1000, + + /// Assured Forwarding 31, RFC2597 + Af31 = 0b0110_1000, + + /// Assured Forwarding 32, RFC2597 + Af32 = 0b0111_0000, + + /// Assured Forwarding 33, RFC2597 + Af33 = 0b0111_1000, + + /// Assured Forwarding 41, RFC2597 + Af41 = 0b1000_1000, + + /// Assured Forwarding 42, RFC2597 + Af42 = 0b1001_0000, + + /// Assured Forwarding 43, RFC2597 + Af43 = 0b1001_1000, + + /// Expedited Forwarding, RFC3246 + Ef = 0b1011_1000, + + /// Capacity-Admitted Traffic, RFC5865 + VoiceAdmit = 0b1011_0000, + + /// Lower-Effort, RFC8622 + Le = 0b0000_0100, +} + +impl From for u8 { + fn from(v: IpTosDscp) -> Self { + v as u8 + } +} + +impl From for IpTosDscp { + fn from(v: u8) -> Self { + match v & 0b1111_1100 { + 0b0000_0000 => IpTosDscp::Cs0, + 0b0010_0000 => IpTosDscp::Cs1, + 0b0100_0000 => IpTosDscp::Cs2, + 0b0110_0000 => IpTosDscp::Cs3, + 0b1000_0000 => IpTosDscp::Cs4, + 0b1010_0000 => IpTosDscp::Cs5, + 0b1100_0000 => IpTosDscp::Cs6, + 0b1110_0000 => IpTosDscp::Cs7, + 0b0010_1000 => IpTosDscp::Af11, + 0b0011_0000 => IpTosDscp::Af12, + 0b0011_1000 => IpTosDscp::Af13, + 0b0100_1000 => IpTosDscp::Af21, + 0b0101_0000 => IpTosDscp::Af22, + 0b0101_1000 => IpTosDscp::Af23, + 0b0110_1000 => IpTosDscp::Af31, + 0b0111_0000 => IpTosDscp::Af32, + 0b0111_1000 => IpTosDscp::Af33, + 0b1000_1000 => IpTosDscp::Af41, + 0b1001_0000 => IpTosDscp::Af42, + 0b1001_1000 => IpTosDscp::Af43, + 0b1011_1000 => IpTosDscp::Ef, + 0b1011_0000 => IpTosDscp::VoiceAdmit, + 0b0000_0100 => IpTosDscp::Le, + _ => unreachable!(), + } + } +} + +/// The type-of-service field in an IP packet. +#[allow(clippy::module_name_repetitions)] +#[derive(Copy, Clone, PartialEq, Eq)] +pub struct IpTos(u8); + +impl From for IpTos { + fn from(v: IpTosEcn) -> Self { + Self(u8::from(v)) + } +} +impl From for IpTos { + fn from(v: IpTosDscp) -> Self { + Self(u8::from(v)) + } +} +impl From<(IpTosDscp, IpTosEcn)> for IpTos { + fn from(v: (IpTosDscp, IpTosEcn)) -> Self { + Self(u8::from(v.0) | u8::from(v.1)) + } +} +impl From for u8 { + fn from(v: IpTos) -> Self { + v.0 + } +} + +impl Debug for IpTos { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("IpTos") + .field(&IpTosDscp::from(self.0 & 0xfc)) + .field(&IpTosEcn::from(self.0 & 0x3)) + .finish() + } +} + +impl Default for IpTos { + fn default() -> Self { + (IpTosDscp::default(), IpTosEcn::default()).into() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn iptosecn_into_u8() { + assert_eq!(u8::from(IpTosEcn::NotEct), 0b00); + assert_eq!(u8::from(IpTosEcn::Ect1), 0b01); + assert_eq!(u8::from(IpTosEcn::Ect0), 0b10); + assert_eq!(u8::from(IpTosEcn::Ce), 0b11); + } + + #[test] + fn u8_into_iptosecn() { + assert_eq!(IpTosEcn::from(0b00), IpTosEcn::NotEct); + assert_eq!(IpTosEcn::from(0b01), IpTosEcn::Ect1); + assert_eq!(IpTosEcn::from(0b10), IpTosEcn::Ect0); + assert_eq!(IpTosEcn::from(0b11), IpTosEcn::Ce); + } + + #[test] + fn iptosdscp_into_u8() { + assert_eq!(u8::from(IpTosDscp::Cs0), 0b0000_0000); + assert_eq!(u8::from(IpTosDscp::Cs1), 0b0010_0000); + assert_eq!(u8::from(IpTosDscp::Cs2), 0b0100_0000); + assert_eq!(u8::from(IpTosDscp::Cs3), 0b0110_0000); + assert_eq!(u8::from(IpTosDscp::Cs4), 0b1000_0000); + assert_eq!(u8::from(IpTosDscp::Cs5), 0b1010_0000); + assert_eq!(u8::from(IpTosDscp::Cs6), 0b1100_0000); + assert_eq!(u8::from(IpTosDscp::Cs7), 0b1110_0000); + assert_eq!(u8::from(IpTosDscp::Af11), 0b0010_1000); + assert_eq!(u8::from(IpTosDscp::Af12), 0b0011_0000); + assert_eq!(u8::from(IpTosDscp::Af13), 0b0011_1000); + assert_eq!(u8::from(IpTosDscp::Af21), 0b0100_1000); + assert_eq!(u8::from(IpTosDscp::Af22), 0b0101_0000); + assert_eq!(u8::from(IpTosDscp::Af23), 0b0101_1000); + assert_eq!(u8::from(IpTosDscp::Af31), 0b0110_1000); + assert_eq!(u8::from(IpTosDscp::Af32), 0b0111_0000); + assert_eq!(u8::from(IpTosDscp::Af33), 0b0111_1000); + assert_eq!(u8::from(IpTosDscp::Af41), 0b1000_1000); + assert_eq!(u8::from(IpTosDscp::Af42), 0b1001_0000); + assert_eq!(u8::from(IpTosDscp::Af43), 0b1001_1000); + assert_eq!(u8::from(IpTosDscp::Ef), 0b1011_1000); + assert_eq!(u8::from(IpTosDscp::VoiceAdmit), 0b1011_0000); + assert_eq!(u8::from(IpTosDscp::Le), 0b0000_0100); + } + + #[test] + fn u8_into_iptosdscp() { + assert_eq!(IpTosDscp::from(0b0000_0000), IpTosDscp::Cs0); + assert_eq!(IpTosDscp::from(0b0010_0000), IpTosDscp::Cs1); + assert_eq!(IpTosDscp::from(0b0100_0000), IpTosDscp::Cs2); + assert_eq!(IpTosDscp::from(0b0110_0000), IpTosDscp::Cs3); + assert_eq!(IpTosDscp::from(0b1000_0000), IpTosDscp::Cs4); + assert_eq!(IpTosDscp::from(0b1010_0000), IpTosDscp::Cs5); + assert_eq!(IpTosDscp::from(0b1100_0000), IpTosDscp::Cs6); + assert_eq!(IpTosDscp::from(0b1110_0000), IpTosDscp::Cs7); + assert_eq!(IpTosDscp::from(0b0010_1000), IpTosDscp::Af11); + assert_eq!(IpTosDscp::from(0b0011_0000), IpTosDscp::Af12); + assert_eq!(IpTosDscp::from(0b0011_1000), IpTosDscp::Af13); + assert_eq!(IpTosDscp::from(0b0100_1000), IpTosDscp::Af21); + assert_eq!(IpTosDscp::from(0b0101_0000), IpTosDscp::Af22); + assert_eq!(IpTosDscp::from(0b0101_1000), IpTosDscp::Af23); + assert_eq!(IpTosDscp::from(0b0110_1000), IpTosDscp::Af31); + assert_eq!(IpTosDscp::from(0b0111_0000), IpTosDscp::Af32); + assert_eq!(IpTosDscp::from(0b0111_1000), IpTosDscp::Af33); + assert_eq!(IpTosDscp::from(0b1000_1000), IpTosDscp::Af41); + assert_eq!(IpTosDscp::from(0b1001_0000), IpTosDscp::Af42); + assert_eq!(IpTosDscp::from(0b1001_1000), IpTosDscp::Af43); + assert_eq!(IpTosDscp::from(0b1011_1000), IpTosDscp::Ef); + assert_eq!(IpTosDscp::from(0b1011_0000), IpTosDscp::VoiceAdmit); + assert_eq!(IpTosDscp::from(0b0000_0100), IpTosDscp::Le); + } + + #[test] + fn iptosecn_into_iptos() { + let ecn = IpTosEcn::default(); + let iptos_ecn: IpTos = ecn.into(); + assert_eq!(u8::from(iptos_ecn), ecn as u8); + } + + #[test] + fn iptosdscp_into_iptos() { + let dscp = IpTosDscp::default(); + let iptos_dscp: IpTos = dscp.into(); + assert_eq!(u8::from(iptos_dscp), dscp as u8); + } +} diff --git a/third_party/rust/neqo-crypto/.cargo-checksum.json b/third_party/rust/neqo-crypto/.cargo-checksum.json index ec0bdb68fac7..ff4ab0fc6617 100644 --- a/third_party/rust/neqo-crypto/.cargo-checksum.json +++ b/third_party/rust/neqo-crypto/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"efe17a5183bd81198a0cee488fde3f73b0aabc79b9275018805e67bb9de50f4a","TODO":"ac0f1c2ebcca03f5b3c0cc56c5aedbb030a4b511e438bc07a57361c789f91e9f","bindings/bindings.toml":"b1a04fda8f317d1cfb7dfdbe87f2887a825c054c61f4858d49f66b0429d8e27c","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":"973971b2e1acd73efdb0166d5a4b07aa6ce1cf74fd02dc66d69fd2a74e521e48","src/aead.rs":"fd4c6669ad978cc4f7027b8cfac1228e84db288b294fdb0558e730bc1515136d","src/aead_fuzzing.rs":"20de62c7b23a048030a4ac7c7ef7ce53e31289f375a24ccb7e5c36c3625a62c1","src/agent.rs":"3b189ab5150f54e0416b60c95904456cdcf379f4eb564feb7c2c7bc2b8729cad","src/agentio.rs":"bce4c3dfcfa433209a409ac0c0752f8c95ab37bb6239a42f99b83858e8747bd1","src/auth.rs":"ced1a18f691894984244088020ea25dc1ee678603317f0c7dfc8b8842fa750b4","src/cert.rs":"2a353b3230e3bd0c9ff91620a2e43c1dcaf493b1ff7997e6b51f8af589b925c1","src/constants.rs":"998e77bee88197a240032c1bfbddcff417a25ba82e576a0d2fe18ee9b63cefc7","src/ech.rs":"f67072f51dc52f7444c1983ba404b3db7366d84e9a798421d978ab244b6b57e9","src/err.rs":"8b2156f280c8823b2f9767bdd98fcf5c20ddc2e650301be005d1b96e15256f4a","src/exp.rs":"cec59d61fc95914f9703d2fb6490a8507af993c9db710dde894f2f8fd38123c7","src/ext.rs":"4b3bc6a9eb8f4d466e627281902f2385549531ea627a76d9b8d223f212c04284","src/hkdf.rs":"a53a414bc53dd080a9cb716e604aa73b81df80a8ef4f7406d54264e078e8a867","src/hp.rs":"7f98ef9b79262a3628f0bfdc6acd33d3c0ef4c20931b62c46ec0adcf2a337497","src/lib.rs":"59fbfc50f27addbdeac0dfd3e34f652eb51e2a3056ce11c587ff0da35b453843","src/once.rs":"b9850384899a1a016e839743d3489c0d4d916e1973746ef8c89872105d7d9736","src/p11.rs":"c2f70d8772c816761fb089000f1249af25754c7faf3c02dfd9e18bbfcbc6cbf6","src/prio.rs":"e5e169296c0ac69919c59fb6c1f8bd6bf079452eaa13d75da0edd41d435d3f6f","src/replay.rs":"ad019f543c36d217f7e4ec23cd46117c17aaca75d56147bfc80c286f208659d2","src/result.rs":"0587cbb6aace71a7f9765ef7c01dcd9f73a49dcc6331e1d8fe4de2aef6ca65b6","src/secrets.rs":"076b72410af797e48c528287159181acf30d5cf88c1213ad539d3446aecf7388","src/selfencrypt.rs":"6f39a62424048790e9a0b4660ed15644f09927b926b1dfa6d5e4695921d2e082","src/ssl.rs":"b16a5f154353a1cac1b5f887f25b8f934d60f9847e1e8e0e87ddaf980b64f530","src/time.rs":"88647eb712825cacf5b551e2d26abcad57b343fe3fbbaa3a94de75955247083c","tests/aead.rs":"5ee882f316c49b0217273583cb7c12396da444d7cdb60e354b40aac38fc70a63","tests/agent.rs":"101a905a1168d139d9b0a5598036eaf3e7b2c110644e01200a6a7ba0a65aa51f","tests/ext.rs":"6ad47ca33c55aa7e775b6dd4bab55144d17ac0aba3b8fdbc203ece6f11851a82","tests/handshake.rs":"d5d542203d9f322e39391fe2748b2de38ee11da1b5577cd81b179a8613e424a7","tests/hkdf.rs":"47830c1ea58a02d100522bdde6fabc02bb447ccb85affa0cdc44bc25da1be32a","tests/hp.rs":"ec8b38b5421e52bee18da3417e0e52afe270dbe2d365ba8c3956f878ea7aa2d2","tests/init.rs":"fc9e392b1efa0d8efb28952f73ffc05e5348e7b2b69207b60e375c3888a252a2","tests/selfencrypt.rs":"1125c858ec4e0a6994f34d162aa066cb003c61b324f268529ea04bcb641347cb"},"package":null} \ No newline at end of file +{"files":{"Cargo.toml":"7f7348b55033e19bbe51b07ee50313c87237fe09b56b338af9ab24e00aab32c6","bindings/bindings.toml":"0660c1661318b8a5094834c2f1bb12266287ef467307f66947eff7762528f70a","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":"e712c16cb830a83eb4ea1f50dd341a4c30e1cce95d8c45af97030bc8ad0ae829","src/aead.rs":"b7cda4b89298cfd122cd2e1e94c462840e966c60f4832eb441106563ac332e00","src/aead_fuzzing.rs":"c3e590572314e0bb3fafa13dac3c831358b8a7b5570fe9cfe592752fce8cbdee","src/agent.rs":"c4fe47f9f5b0af20e3418da2e2ddce0ac2ca9665c0502115904f66a554e486ee","src/agentio.rs":"847ac63f6406e33bf20a861cadbfe6301ffa15bd73a5291298ffa93511b87dd5","src/auth.rs":"ced1a18f691894984244088020ea25dc1ee678603317f0c7dfc8b8842fa750b4","src/cert.rs":"6fc09012f994300ff4a7951bf8981aa266220521f58b8ff0989fee6dc1f27df9","src/constants.rs":"f22bf16bd8cb539862cb1e47138dbba79e93fe738f4b907e465891326f98883c","src/ech.rs":"58b7e0a1d2d52c59889cf8b735902577f7c3df93dfb89c72af2646b7aef29f39","src/err.rs":"fca0222167883231a5e0a569a593f44214501819adf5aadf814be27891c87c24","src/exp.rs":"cec59d61fc95914f9703d2fb6490a8507af993c9db710dde894f2f8fd38123c7","src/ext.rs":"c6ab9aefbbca531466dea938d853b1e42ed51816238afe400b20dbdb0111690b","src/hkdf.rs":"8e6cc5dce0f36efa4e13f5a24e2879bdbf10fb9a2b7dc8f13692e47d8959cdc8","src/hp.rs":"62ec073d99cf8bf3a123838c7d9b51bfdf68887148961f6307288e8dd56ac711","src/lib.rs":"40d9ac97c307c8161c2bf48156cc82377f81ad6e709f99cfd7dc0131dc192f86","src/once.rs":"b9850384899a1a016e839743d3489c0d4d916e1973746ef8c89872105d7d9736","src/p11.rs":"6c0f2f1b18e9bf9088a5ca5bdc99e789bb42234f7d2fe24d0b463bc957cb84a2","src/prio.rs":"e5e169296c0ac69919c59fb6c1f8bd6bf079452eaa13d75da0edd41d435d3f6f","src/replay.rs":"1ff4a12f6135ef2c42aef2b0947e26fd6241cd4b359020245608046452a7fcb0","src/result.rs":"0587cbb6aace71a7f9765ef7c01dcd9f73a49dcc6331e1d8fe4de2aef6ca65b6","src/secrets.rs":"4ffaa66f25df47dadf042063bff5953effa7bf2f4920cafe827757d6a659cb58","src/selfencrypt.rs":"4d2f4a6ea0fc94502130413ab5e2ea82612228f38a96a1865bf7d2b3f440620e","src/ssl.rs":"c83baa5518b81dd06f2e4072ea3c2d666ccdeb8b1ff6e3746eea9f1af47023a6","src/time.rs":"9204f3a384fb9dd2c3816c88666ad61ac3538f9e2f028954e81fd335a1479070","tests/aead.rs":"efdb92a060ca1957d890da1604513369559cb43195ee54149ed3ab47958dad59","tests/agent.rs":"0e55354595ae5f0e1ab83731564da57ba88a296e00692147c47df7067a0f416a","tests/ext.rs":"54657b45bd86d2561bb0f548736bc6f141bb664a5b043506f428422919ab95d4","tests/handshake.rs":"40701bc22f16d1ba9b9bd9683738e52b96faafee4119f7057437dae705f7867a","tests/hkdf.rs":"4160978b96505c1f1b7d6c4b5f43536ff7bd791c8746f9546c9fbc0fce5cf1c7","tests/hp.rs":"8eeee21a439e0f991145dff07b01283ae39ccd4b8dac4d011d43a464f73db670","tests/init.rs":"fc9e392b1efa0d8efb28952f73ffc05e5348e7b2b69207b60e375c3888a252a2","tests/selfencrypt.rs":"6edd0914b8466d79ecfb569c6d86995fd364b0dc71be2a0554e82f736ebd6b7c"},"package":null} \ No newline at end of file diff --git a/third_party/rust/neqo-crypto/Cargo.toml b/third_party/rust/neqo-crypto/Cargo.toml index a5b6f795fbfc..73c1fcb364c0 100644 --- a/third_party/rust/neqo-crypto/Cargo.toml +++ b/third_party/rust/neqo-crypto/Cargo.toml @@ -11,15 +11,15 @@ [package] edition = "2018" -rust-version = "1.65.0" +rust-version = "1.70.0" name = "neqo-crypto" -version = "0.6.8" +version = "0.7.0" authors = ["Martin Thomson "] build = "build.rs" license = "MIT OR Apache-2.0" [dependencies.log] -version = "0.4.0" +version = "~0.4.17" default-features = false [dependencies.neqo-common] @@ -29,12 +29,12 @@ path = "../neqo-common" path = "../test-fixture" [build-dependencies] -serde = "1.0" -serde_derive = "1.0" -toml = "0.5" +serde = "1.0.195" +serde_derive = "1.0.195" +toml = "0.5.11" [build-dependencies.bindgen] -version = "0.64" +version = "0.69.1" features = ["runtime"] default-features = false diff --git a/third_party/rust/neqo-crypto/TODO b/third_party/rust/neqo-crypto/TODO deleted file mode 100644 index b0552ea10f9c..000000000000 --- a/third_party/rust/neqo-crypto/TODO +++ /dev/null @@ -1,4 +0,0 @@ -early data - API in place for inspection, but depends on resumption -handle panics more gracefully for extension handlers -client certificates -read/write - probably never \ No newline at end of file diff --git a/third_party/rust/neqo-crypto/bindings/bindings.toml b/third_party/rust/neqo-crypto/bindings/bindings.toml index 7c35a0a224a7..3e5c1fdf7d2d 100644 --- a/third_party/rust/neqo-crypto/bindings/bindings.toml +++ b/third_party/rust/neqo-crypto/bindings/bindings.toml @@ -49,6 +49,7 @@ functions = [ "SSL_PeerSignedCertTimestamps", "SSL_PeerStapledOCSPResponses", "SSL_ResetHandshake", + "SSL_SendAdditionalKeyShares", "SSL_SetNextProtoNego", "SSL_SetURL", "SSL_VersionRangeSet", diff --git a/third_party/rust/neqo-crypto/build.rs b/third_party/rust/neqo-crypto/build.rs index e19f1977105d..a63c34dedb5f 100644 --- a/third_party/rust/neqo-crypto/build.rs +++ b/third_party/rust/neqo-crypto/build.rs @@ -7,13 +7,15 @@ #![cfg_attr(feature = "deny-warnings", deny(warnings))] #![warn(clippy::pedantic)] +use std::{ + collections::HashMap, + env, fs, + path::{Path, PathBuf}, + process::Command, +}; + use bindgen::Builder; use serde_derive::Deserialize; -use std::collections::HashMap; -use std::env; -use std::fs; -use std::path::{Path, PathBuf}; -use std::process::Command; const BINDINGS_DIR: &str = "bindings"; const BINDINGS_CONFIG: &str = "bindings.toml"; @@ -60,7 +62,8 @@ fn is_debug() -> bool { // On windows, this doesn't just work, you have to set LIBCLANG_PATH. // Rather than download the 400Mb+ files, like gecko does, let's just reuse their work. fn setup_clang() { - if env::consts::OS != "windows" { + // If this isn't Windows, or we're in CI, then we don't need to do anything. + if env::consts::OS != "windows" || env::var("GITHUB_WORKFLOW").unwrap() == "CI" { return; } println!("rerun-if-env-changed=LIBCLANG_PATH"); @@ -130,6 +133,11 @@ fn nss_dir() -> PathBuf { } fn get_bash() -> PathBuf { + // If BASH is set, use that. + if let Ok(bash) = env::var("BASH") { + return PathBuf::from(bash); + } + // When running under MOZILLABUILD, we need to make sure not to invoke // another instance of bash that might be sitting around (like WSL). match env::var("MOZILLABUILD") { @@ -257,7 +265,7 @@ fn build_bindings(base: &str, bindings: &Bindings, flags: &[String], gecko: bool builder = builder.clang_arg("-DANDROID"); } if bindings.cplusplus { - builder = builder.clang_args(&["-x", "c++", "-std=c++11"]); + builder = builder.clang_args(&["-x", "c++", "-std=c++14"]); } } diff --git a/third_party/rust/neqo-crypto/src/aead.rs b/third_party/rust/neqo-crypto/src/aead.rs index 41cdf664691d..a2f009a4037a 100644 --- a/third_party/rust/neqo-crypto/src/aead.rs +++ b/third_party/rust/neqo-crypto/src/aead.rs @@ -4,6 +4,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::{ + convert::{TryFrom, TryInto}, + fmt, + ops::{Deref, DerefMut}, + os::raw::{c_char, c_uint}, + ptr::null_mut, +}; + use crate::{ constants::{Cipher, Version}, err::Res, @@ -13,14 +21,6 @@ use crate::{ ssl::{self, PRUint16, PRUint64, PRUint8, SSLAeadContext}, }; -use std::{ - convert::{TryFrom, TryInto}, - fmt, - ops::{Deref, DerefMut}, - os::raw::{c_char, c_uint}, - ptr::null_mut, -}; - experimental_api!(SSL_MakeAead( version: PRUint16, cipher: PRUint16, @@ -62,6 +62,7 @@ impl RealAead { /// Create a new AEAD based on the indicated TLS version and cipher suite. /// /// # Errors + /// /// Returns `Error` when the supporting NSS functions fail. pub fn new( _fuzzing: bool, @@ -107,6 +108,7 @@ impl RealAead { /// the value provided in `Aead::expansion`. /// /// # Errors + /// /// If the input can't be protected or any input is too large for NSS. pub fn encrypt<'a>( &self, @@ -139,6 +141,7 @@ impl RealAead { /// the final result will be shorter. /// /// # Errors + /// /// If the input isn't authenticated or any input is too large for NSS. pub fn decrypt<'a>( &self, diff --git a/third_party/rust/neqo-crypto/src/aead_fuzzing.rs b/third_party/rust/neqo-crypto/src/aead_fuzzing.rs index 4293d2bc7026..4e5a6de07f23 100644 --- a/third_party/rust/neqo-crypto/src/aead_fuzzing.rs +++ b/third_party/rust/neqo-crypto/src/aead_fuzzing.rs @@ -4,12 +4,15 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use crate::constants::{Cipher, Version}; -use crate::err::{sec::SEC_ERROR_BAD_DATA, Error, Res}; -use crate::p11::SymKey; -use crate::RealAead; use std::fmt; +use crate::{ + constants::{Cipher, Version}, + err::{sec::SEC_ERROR_BAD_DATA, Error, Res}, + p11::SymKey, + RealAead, +}; + pub const FIXED_TAG_FUZZING: &[u8] = &[0x0a; 16]; pub struct FuzzingAead { @@ -76,8 +79,8 @@ impl FuzzingAead { let len_encrypted = input.len() - FIXED_TAG_FUZZING.len(); // Check that: // 1) expansion is all zeros and - // 2) if the encrypted data is also supplied that at least some values - // are no zero (otherwise padding will be interpreted as a valid packet) + // 2) if the encrypted data is also supplied that at least some values are no zero + // (otherwise padding will be interpreted as a valid packet) if &input[len_encrypted..] == FIXED_TAG_FUZZING && (len_encrypted == 0 || input[..len_encrypted].iter().any(|x| *x != 0x0)) { diff --git a/third_party/rust/neqo-crypto/src/agent.rs b/third_party/rust/neqo-crypto/src/agent.rs index 3612fec7e336..cd0bb4cb12bf 100644 --- a/third_party/rust/neqo-crypto/src/agent.rs +++ b/third_party/rust/neqo-crypto/src/agent.rs @@ -4,6 +4,21 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::{ + cell::RefCell, + convert::TryFrom, + ffi::{CStr, CString}, + mem::{self, MaybeUninit}, + ops::{Deref, DerefMut}, + os::raw::{c_uint, c_void}, + pin::Pin, + ptr::{null, null_mut}, + rc::Rc, + time::Instant, +}; + +use neqo_common::{hex_snip_middle, hex_with_len, qdebug, qinfo, qtrace, qwarn}; + pub use crate::{ agentio::{as_c_void, Record, RecordList}, cert::CertificateInfo, @@ -25,19 +40,6 @@ use crate::{ ssl::{self, PRBool}, time::{Time, TimeHolder}, }; -use neqo_common::{hex_snip_middle, hex_with_len, qdebug, qinfo, qtrace, qwarn}; -use std::{ - cell::RefCell, - convert::TryFrom, - ffi::{CStr, CString}, - mem::{self, MaybeUninit}, - ops::{Deref, DerefMut}, - os::raw::{c_uint, c_void}, - pin::Pin, - ptr::{null, null_mut}, - rc::Rc, - time::Instant, -}; /// The maximum number of tickets to remember for a given connection. const MAX_TICKETS: usize = 4; @@ -157,6 +159,7 @@ impl SecretAgentPreInfo { } /// # Panics + /// /// If `usize` is less than 32 bits and the value is too large. #[must_use] pub fn max_early_data(&self) -> usize { @@ -183,6 +186,7 @@ impl SecretAgentPreInfo { /// which contains a valid ECH configuration. /// /// # Errors + /// /// When the public name is not valid UTF-8. (Note: names should be ASCII.) pub fn ech_public_name(&self) -> Res> { if self.info.valuesSet & ssl::ssl_preinfo_ech == 0 || self.info.echPublicName.is_null() { @@ -395,6 +399,7 @@ impl SecretAgent { /// Default configuration. /// /// # Errors + /// /// If `set_version_range` fails. fn configure(&mut self, grease: bool) -> Res<()> { self.set_version_range(TLS_VERSION_1_3, TLS_VERSION_1_3)?; @@ -411,6 +416,7 @@ impl SecretAgent { /// Set the versions that are supported. /// /// # Errors + /// /// If the range of versions isn't supported. pub fn set_version_range(&mut self, min: Version, max: Version) -> Res<()> { let range = ssl::SSLVersionRange { min, max }; @@ -420,6 +426,7 @@ impl SecretAgent { /// Enable a set of ciphers. Note that the order of these is not respected. /// /// # Errors + /// /// If NSS can't enable or disable ciphers. pub fn set_ciphers(&mut self, ciphers: &[Cipher]) -> Res<()> { if self.state != HandshakeState::New { @@ -447,6 +454,7 @@ impl SecretAgent { /// Set key exchange groups. /// /// # Errors + /// /// If the underlying API fails (which shouldn't happen). pub fn set_groups(&mut self, groups: &[Group]) -> Res<()> { // SSLNamedGroup is a different size to Group, so copy one by one. @@ -461,9 +469,21 @@ impl SecretAgent { }) } + /// Set the number of additional key shares that will be sent in the client hello + /// + /// # Errors + /// + /// If the underlying API fails (which shouldn't happen). + pub fn send_additional_key_shares(&mut self, count: usize) -> Res<()> { + secstatus_to_res(unsafe { + ssl::SSL_SendAdditionalKeyShares(self.fd, c_uint::try_from(count)?) + }) + } + /// Set TLS options. /// /// # Errors + /// /// Returns an error if the option or option value is invalid; i.e., never. pub fn set_option(&mut self, opt: ssl::Opt, value: bool) -> Res<()> { opt.set(self.fd, value) @@ -472,6 +492,7 @@ impl SecretAgent { /// Enable 0-RTT. /// /// # Errors + /// /// See `set_option`. pub fn enable_0rtt(&mut self) -> Res<()> { self.set_option(ssl::Opt::EarlyData, true) @@ -480,6 +501,7 @@ impl SecretAgent { /// Disable the `EndOfEarlyData` message. /// /// # Errors + /// /// See `set_option`. pub fn disable_end_of_early_data(&mut self) -> Res<()> { self.set_option(ssl::Opt::SuppressEndOfEarlyData, true) @@ -493,8 +515,11 @@ impl SecretAgent { /// 255 octets in length. /// /// # Errors + /// /// This should always panic rather than return an error. + /// /// # Panics + /// /// If any of the provided `protocols` are more than 255 bytes long. /// /// [RFC7301]: https://datatracker.ietf.org/doc/html/rfc7301 @@ -539,11 +564,12 @@ impl SecretAgent { /// Install an extension handler. /// - /// This can be called multiple times with different values for `ext`. The handler is provided as - /// `Rc>` so that the caller is able to hold a reference to the handler and later - /// access any state that it accumulates. + /// This can be called multiple times with different values for `ext`. The handler is provided + /// as `Rc>` so that the caller is able to hold a reference to the handler + /// and later access any state that it accumulates. /// /// # Errors + /// /// When the extension handler can't be successfully installed. pub fn extension_handler( &mut self, @@ -587,6 +613,7 @@ impl SecretAgent { /// Calling this function collects all the relevant information. /// /// # Errors + /// /// When the underlying socket functions fail. pub fn preinfo(&self) -> Res { SecretAgentPreInfo::new(self.fd) @@ -605,7 +632,9 @@ impl SecretAgent { } /// Call this function to mark the peer as authenticated. + /// /// # Panics + /// /// If the handshake doesn't need to be authenticated. pub fn authenticated(&mut self, status: AuthenticationStatus) { assert!(self.state.authentication_needed()); @@ -654,6 +683,7 @@ impl SecretAgent { /// function if you want to proceed, because this will mark the certificate as OK. /// /// # Errors + /// /// When the handshake fails this returns an error. pub fn handshake(&mut self, now: Instant, input: &[u8]) -> Res> { self.now.set(now)?; @@ -690,6 +720,7 @@ impl SecretAgent { /// If you send data from multiple epochs, you might end up being sad. /// /// # Errors + /// /// When the handshake fails this returns an error. pub fn handshake_raw(&mut self, now: Instant, input: Option) -> Res { self.now.set(now)?; @@ -717,6 +748,7 @@ impl SecretAgent { } /// # Panics + /// /// If setup fails. #[allow(unknown_lints, clippy::branches_sharing_code)] pub fn close(&mut self) { @@ -822,6 +854,7 @@ impl Client { /// Create a new client agent. /// /// # Errors + /// /// Errors returned if the socket can't be created or configured. pub fn new(server_name: impl Into, grease: bool) -> Res { let server_name = server_name.into(); @@ -911,6 +944,7 @@ impl Client { /// Enable resumption, using a token previously provided. /// /// # Errors + /// /// Error returned when the resumption token is invalid or /// the socket is not able to use the value. pub fn enable_resumption(&mut self, token: impl AsRef<[u8]>) -> Res<()> { @@ -934,6 +968,7 @@ impl Client { /// ECH greasing. When that is done, there is no need to look for `EchRetry` /// /// # Errors + /// /// 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(); @@ -986,7 +1021,8 @@ pub enum ZeroRttCheckResult { Fail, } -/// A `ZeroRttChecker` is used by the agent to validate the application token (as provided by `send_ticket`) +/// A `ZeroRttChecker` is used by the agent to validate the application token (as provided by +/// `send_ticket`) pub trait ZeroRttChecker: std::fmt::Debug + std::marker::Unpin { fn check(&self, token: &[u8]) -> ZeroRttCheckResult; } @@ -1027,6 +1063,7 @@ impl Server { /// Create a new server agent. /// /// # Errors + /// /// Errors returned when NSS fails. pub fn new(certificates: &[impl AsRef]) -> Res { let mut agent = SecretAgent::new()?; @@ -1080,7 +1117,8 @@ impl Server { ssl::SSLHelloRetryRequestAction::ssl_hello_retry_reject_0rtt } ZeroRttCheckResult::HelloRetryRequest(tok) => { - // Don't bother propagating errors from this, because it should be caught in testing. + // Don't bother propagating errors from this, because it should be caught in + // testing. assert!(tok.len() <= usize::try_from(retry_token_max).unwrap()); let slc = std::slice::from_raw_parts_mut(retry_token, tok.len()); slc.copy_from_slice(&tok); @@ -1094,6 +1132,7 @@ impl Server { /// via the Deref implementation on Server. /// /// # Errors + /// /// Returns an error if the underlying NSS functions fail. pub fn enable_0rtt( &mut self, @@ -1121,6 +1160,7 @@ impl Server { /// The records that are sent are captured and returned. /// /// # Errors + /// /// If NSS is unable to send a ticket, or if this agent is incorrectly configured. pub fn send_ticket(&mut self, now: Instant, extra: &[u8]) -> Res { self.agent.now.set(now)?; @@ -1136,6 +1176,7 @@ impl Server { /// Enable encrypted client hello (ECH). /// /// # Errors + /// /// Fails when NSS cannot create a key pair. pub fn enable_ech( &mut self, diff --git a/third_party/rust/neqo-crypto/src/agentio.rs b/third_party/rust/neqo-crypto/src/agentio.rs index 1d39b2398a02..2bcc5405300d 100644 --- a/third_party/rust/neqo-crypto/src/agentio.rs +++ b/third_party/rust/neqo-crypto/src/agentio.rs @@ -4,21 +4,24 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use crate::constants::{ContentType, Epoch}; -use crate::err::{nspr, Error, PR_SetError, Res}; -use crate::prio; -use crate::ssl; +use std::{ + cmp::min, + convert::{TryFrom, TryInto}, + fmt, mem, + ops::Deref, + os::raw::{c_uint, c_void}, + pin::Pin, + ptr::{null, null_mut}, + vec::Vec, +}; use neqo_common::{hex, hex_with_len, qtrace}; -use std::cmp::min; -use std::convert::{TryFrom, TryInto}; -use std::fmt; -use std::mem; -use std::ops::Deref; -use std::os::raw::{c_uint, c_void}; -use std::pin::Pin; -use std::ptr::{null, null_mut}; -use std::vec::Vec; + +use crate::{ + constants::{ContentType, Epoch}, + err::{nspr, Error, PR_SetError, Res}, + prio, ssl, +}; // Alias common types. type PrFd = *mut prio::PRFileDesc; diff --git a/third_party/rust/neqo-crypto/src/cert.rs b/third_party/rust/neqo-crypto/src/cert.rs index 14d91843d32f..64e63ec71a03 100644 --- a/third_party/rust/neqo-crypto/src/cert.rs +++ b/third_party/rust/neqo-crypto/src/cert.rs @@ -4,18 +4,22 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use crate::err::secstatus_to_res; -use crate::p11::{CERTCertListNode, CERT_GetCertificateDer, CertList, Item, SECItem, SECItemArray}; -use crate::ssl::{ - PRFileDesc, SSL_PeerCertificateChain, SSL_PeerSignedCertTimestamps, - SSL_PeerStapledOCSPResponses, +use std::{ + convert::TryFrom, + ptr::{addr_of, NonNull}, + slice, }; + use neqo_common::qerror; -use std::convert::TryFrom; -use std::ptr::{addr_of, NonNull}; - -use std::slice; +use crate::{ + err::secstatus_to_res, + p11::{CERTCertListNode, CERT_GetCertificateDer, CertList, Item, SECItem, SECItemArray}, + ssl::{ + PRFileDesc, SSL_PeerCertificateChain, SSL_PeerSignedCertTimestamps, + SSL_PeerStapledOCSPResponses, + }, +}; pub struct CertificateInfo { certs: CertList, diff --git a/third_party/rust/neqo-crypto/src/constants.rs b/third_party/rust/neqo-crypto/src/constants.rs index 21e1a5acebb9..76db972290e1 100644 --- a/third_party/rust/neqo-crypto/src/constants.rs +++ b/third_party/rust/neqo-crypto/src/constants.rs @@ -62,6 +62,7 @@ remap_enum! { TLS_GRP_EC_SECP384R1 = ssl_grp_ec_secp384r1, TLS_GRP_EC_SECP521R1 = ssl_grp_ec_secp521r1, TLS_GRP_EC_X25519 = ssl_grp_ec_curve25519, + TLS_GRP_KEM_XYBER768D00 = ssl_grp_kem_xyber768d00, } } diff --git a/third_party/rust/neqo-crypto/src/ech.rs b/third_party/rust/neqo-crypto/src/ech.rs index 5425e1a64c5a..1f54c4592ec8 100644 --- a/third_party/rust/neqo-crypto/src/ech.rs +++ b/third_party/rust/neqo-crypto/src/ech.rs @@ -4,6 +4,15 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::{ + convert::TryFrom, + ffi::CString, + os::raw::{c_char, c_uint}, + ptr::{addr_of_mut, null_mut}, +}; + +use neqo_common::qtrace; + use crate::{ err::{ssl::SSL_ERROR_ECH_RETRY_WITH_ECH, Error, Res}, experimental_api, @@ -13,14 +22,6 @@ use crate::{ }, ssl::{PRBool, PRFileDesc}, }; -use neqo_common::qtrace; -use std::{ - convert::TryFrom, - ffi::CString, - os::raw::{c_char, c_uint}, - ptr::{addr_of_mut, null_mut}, -}; - pub use crate::{ p11::{HpkeAeadId as AeadId, HpkeKdfId as KdfId, HpkeKemId as KemId}, ssl::HpkeSymmetricSuite as SymmetricSuite, @@ -89,8 +90,11 @@ pub fn convert_ech_error(fd: *mut PRFileDesc, err: Error) -> Error { /// Generate a key pair for encrypted client hello (ECH). /// /// # Errors +/// /// When NSS fails to generate a key pair or when the KEM is not supported. +/// /// # Panics +/// /// When underlying types aren't large enough to hold keys. So never. pub fn generate_keys() -> Res<(PrivateKey, PublicKey)> { let slot = Slot::internal()?; @@ -109,6 +113,7 @@ pub fn generate_keys() -> Res<(PrivateKey, PublicKey)> { // If we have tracing on, try to ensure that key data can be read. let insensitive_secret_ptr = if log::log_enabled!(log::Level::Trace) { + #[allow(clippy::useless_conversion)] // TODO: Remove when we bump the MSRV to 1.74.0. unsafe { p11::PK11_GenerateKeyPairWithOpFlags( *slot, @@ -126,6 +131,7 @@ pub fn generate_keys() -> Res<(PrivateKey, PublicKey)> { }; assert_eq!(insensitive_secret_ptr.is_null(), public_ptr.is_null()); let secret_ptr = if insensitive_secret_ptr.is_null() { + #[allow(clippy::useless_conversion)] // TODO: Remove when we bump the MSRV to 1.74.0. unsafe { p11::PK11_GenerateKeyPairWithOpFlags( *slot, @@ -151,6 +157,7 @@ pub fn generate_keys() -> Res<(PrivateKey, PublicKey)> { /// Encode a configuration for encrypted client hello (ECH). /// /// # Errors +/// /// When NSS fails to generate a valid configuration encoding (i.e., unlikely). pub fn encode_config(config: u8, public_name: &str, pk: &PublicKey) -> Res> { // A sensible fixed value for the maximum length of a name. diff --git a/third_party/rust/neqo-crypto/src/err.rs b/third_party/rust/neqo-crypto/src/err.rs index fae81f9cb9c4..187303d2a960 100644 --- a/third_party/rust/neqo-crypto/src/err.rs +++ b/third_party/rust/neqo-crypto/src/err.rs @@ -7,8 +7,7 @@ #![allow(dead_code)] #![allow(clippy::upper_case_acronyms)] -use std::os::raw::c_char; -use std::str::Utf8Error; +use std::{os::raw::c_char, str::Utf8Error}; use crate::ssl::{SECStatus, SECSuccess}; @@ -19,9 +18,7 @@ mod codes { include!(concat!(env!("OUT_DIR"), "/nss_sslerr.rs")); include!(concat!(env!("OUT_DIR"), "/mozpkix.rs")); } -pub use codes::mozilla_pkix_ErrorCode as mozpkix; -pub use codes::SECErrorCodes as sec; -pub use codes::SSLErrorCodes as ssl; +pub use codes::{mozilla_pkix_ErrorCode as mozpkix, SECErrorCodes as sec, SSLErrorCodes as ssl}; pub mod nspr { include!(concat!(env!("OUT_DIR"), "/nspr_err.rs")); } @@ -137,10 +134,13 @@ pub fn is_blocked(result: &Res<()>) -> bool { #[cfg(test)] mod tests { - use crate::err::{self, is_blocked, secstatus_to_res, Error, PRErrorCode, PR_SetError}; - use crate::ssl::{SECFailure, SECSuccess}; use test_fixture::fixture_init; + use crate::{ + err::{self, is_blocked, secstatus_to_res, Error, PRErrorCode, PR_SetError}, + ssl::{SECFailure, SECSuccess}, + }; + fn set_error_code(code: PRErrorCode) { // This code doesn't work without initializing NSS first. fixture_init(); diff --git a/third_party/rust/neqo-crypto/src/ext.rs b/third_party/rust/neqo-crypto/src/ext.rs index aa89677b9881..310e87a1b7ad 100644 --- a/third_party/rust/neqo-crypto/src/ext.rs +++ b/third_party/rust/neqo-crypto/src/ext.rs @@ -4,6 +4,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::{ + cell::RefCell, + convert::TryFrom, + os::raw::{c_uint, c_void}, + pin::Pin, + rc::Rc, +}; + use crate::{ agentio::as_c_void, constants::{Extension, HandshakeMessage, TLS_HS_CLIENT_HELLO, TLS_HS_ENCRYPTED_EXTENSIONS}, @@ -13,13 +21,6 @@ use crate::{ SSLExtensionHandler, SSLExtensionWriter, SSLHandshakeType, }, }; -use std::{ - cell::RefCell, - convert::TryFrom, - os::raw::{c_uint, c_void}, - pin::Pin, - rc::Rc, -}; experimental_api!(SSL_InstallExtensionHooks( fd: *mut PRFileDesc, @@ -74,7 +75,7 @@ impl ExtensionTracker { f(&mut *rc.borrow_mut()) } - #[allow(clippy::cast_possible_truncation)] + #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] unsafe extern "C" fn extension_writer( _fd: *mut PRFileDesc, message: SSLHandshakeType::Type, @@ -105,7 +106,7 @@ impl ExtensionTracker { arg: *mut c_void, ) -> SECStatus { let d = std::slice::from_raw_parts(data, len as usize); - #[allow(clippy::cast_possible_truncation)] + #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] Self::wrap_handler_call(arg, |handler| { // Cast is safe here because the message type is always part of the enum match handler.handle(message as HandshakeMessage, d) { @@ -121,11 +122,13 @@ impl ExtensionTracker { /// Use the provided handler to manage an extension. This is quite unsafe. /// /// # Safety + /// /// The holder of this `ExtensionTracker` needs to ensure that it lives at /// least as long as the file descriptor, as NSS provides no way to remove /// an extension handler once it is configured. /// /// # Errors + /// /// If the underlying NSS API fails to register a handler. pub unsafe fn new( fd: *mut PRFileDesc, diff --git a/third_party/rust/neqo-crypto/src/hkdf.rs b/third_party/rust/neqo-crypto/src/hkdf.rs index 3745d646d593..e3cf77418cac 100644 --- a/third_party/rust/neqo-crypto/src/hkdf.rs +++ b/third_party/rust/neqo-crypto/src/hkdf.rs @@ -4,6 +4,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::{ + convert::TryFrom, + os::raw::{c_char, c_uint}, + ptr::null_mut, +}; + use crate::{ constants::{ Cipher, Version, TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384, @@ -16,12 +22,6 @@ use crate::{ }, }; -use std::{ - convert::TryFrom, - os::raw::{c_char, c_uint}, - ptr::null_mut, -}; - experimental_api!(SSL_HkdfExtract( version: Version, cipher: Cipher, @@ -54,6 +54,7 @@ fn key_size(version: Version, cipher: Cipher) -> Res { /// Generate a random key of the right size for the given suite. /// /// # Errors +/// /// Only if NSS fails. pub fn generate_key(version: Version, cipher: Cipher) -> Res { import_key(version, &random(key_size(version, cipher)?)) @@ -62,12 +63,14 @@ pub fn generate_key(version: Version, cipher: Cipher) -> Res { /// Import a symmetric key for use with HKDF. /// /// # Errors +/// /// Errors returned if the key buffer is an incompatible size or the NSS functions fail. pub fn import_key(version: Version, buf: &[u8]) -> Res { if version != TLS_VERSION_1_3 { return Err(Error::UnsupportedVersion); } let slot = Slot::internal()?; + #[allow(clippy::useless_conversion)] // TODO: Remove when we bump the MSRV to 1.74.0. let key_ptr = unsafe { PK11_ImportDataKey( *slot, @@ -84,6 +87,7 @@ pub fn import_key(version: Version, buf: &[u8]) -> Res { /// Extract a PRK from the given salt and IKM using the algorithm defined in RFC 5869. /// /// # Errors +/// /// Errors returned if inputs are too large or the NSS functions fail. pub fn extract( version: Version, @@ -103,6 +107,7 @@ pub fn extract( /// Expand a PRK using the HKDF-Expand-Label function defined in RFC 8446. /// /// # Errors +/// /// Errors returned if inputs are too large or the NSS functions fail. pub fn expand_label( version: Version, diff --git a/third_party/rust/neqo-crypto/src/hp.rs b/third_party/rust/neqo-crypto/src/hp.rs index fea67e9953cf..2479eff8f5d7 100644 --- a/third_party/rust/neqo-crypto/src/hp.rs +++ b/third_party/rust/neqo-crypto/src/hp.rs @@ -4,6 +4,15 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::{ + cell::RefCell, + convert::TryFrom, + fmt::{self, Debug}, + os::raw::{c_char, c_int, c_uint}, + ptr::{addr_of_mut, null, null_mut}, + rc::Rc, +}; + use crate::{ constants::{ Cipher, Version, TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384, @@ -16,14 +25,6 @@ use crate::{ CK_CHACHA20_PARAMS, CK_MECHANISM_TYPE, }, }; -use std::{ - cell::RefCell, - convert::TryFrom, - fmt::{self, Debug}, - os::raw::{c_char, c_int, c_uint}, - ptr::{addr_of_mut, null, null_mut}, - rc::Rc, -}; experimental_api!(SSL_HkdfExpandLabelWithMech( version: Version, @@ -62,8 +63,11 @@ impl HpKey { /// QUIC-specific API for extracting a header-protection key. /// /// # Errors + /// /// Errors if HKDF fails or if the label is too long to fit in a `c_uint`. + /// /// # Panics + /// /// When `cipher` is not known to this code. #[allow(clippy::cast_sign_loss)] // Cast for PK11_GetBlockSize is safe. pub fn extract(version: Version, cipher: Cipher, prk: &SymKey, label: &str) -> Res { @@ -72,6 +76,7 @@ impl HpKey { let l = label.as_bytes(); let mut secret: *mut PK11SymKey = null_mut(); + #[allow(clippy::useless_conversion)] // TODO: Remove when we bump the MSRV to 1.74.0. let (mech, key_size) = match cipher { TLS_AES_128_GCM_SHA256 => (CK_MECHANISM_TYPE::from(CKM_AES_ECB), 16), TLS_AES_256_GCM_SHA384 => (CK_MECHANISM_TYPE::from(CKM_AES_ECB), 32), @@ -99,6 +104,8 @@ impl HpKey { let res = match cipher { TLS_AES_128_GCM_SHA256 | TLS_AES_256_GCM_SHA384 => { + // TODO: Remove when we bump the MSRV to 1.74.0. + #[allow(clippy::useless_conversion)] let context_ptr = unsafe { PK11_CreateContextBySymKey( mech, @@ -138,9 +145,12 @@ impl HpKey { /// Generate a header protection mask for QUIC. /// /// # Errors + /// /// An error is returned if the NSS functions fail; a sample of the /// wrong size is the obvious cause. + /// /// # Panics + /// /// When the mechanism for our key is not supported. pub fn mask(&self, sample: &[u8]) -> Res> { let mut output = vec![0_u8; self.block_size()]; @@ -171,6 +181,8 @@ impl HpKey { }; let mut output_len: c_uint = 0; let mut param_item = Item::wrap_struct(¶ms); + // TODO: Remove when we bump the MSRV to 1.74.0. + #[allow(clippy::useless_conversion)] secstatus_to_res(unsafe { PK11_Encrypt( **key, diff --git a/third_party/rust/neqo-crypto/src/lib.rs b/third_party/rust/neqo-crypto/src/lib.rs index 332e58a033f1..05424ee1f3b9 100644 --- a/third_party/rust/neqo-crypto/src/lib.rs +++ b/third_party/rust/neqo-crypto/src/lib.rs @@ -37,15 +37,19 @@ pub mod selfencrypt; mod ssl; mod time; +use std::{ + ffi::CString, + path::{Path, PathBuf}, + ptr::null, +}; + #[cfg(not(feature = "fuzzing"))] pub use self::aead::RealAead as Aead; - -#[cfg(feature = "fuzzing")] -pub use self::aead_fuzzing::FuzzingAead as Aead; - #[cfg(feature = "fuzzing")] pub use self::aead::RealAead; - +#[cfg(feature = "fuzzing")] +pub use self::aead_fuzzing::FuzzingAead as Aead; +use self::once::OnceResult; pub use self::{ agent::{ Agent, AllowZeroRtt, Client, HandshakeState, Record, RecordList, ResumptionToken, @@ -66,15 +70,7 @@ pub use self::{ ssl::Opt, }; -use self::once::OnceResult; - -use std::{ - ffi::CString, - path::{Path, PathBuf}, - ptr::null, -}; - -const MINIMUM_NSS_VERSION: &str = "3.74"; +const MINIMUM_NSS_VERSION: &str = "3.97"; #[allow(non_upper_case_globals, clippy::redundant_static_lifetimes)] #[allow(clippy::upper_case_acronyms)] @@ -119,8 +115,11 @@ fn version_check() { ); } -/// Initialize NSS. This only executes the initialization routines once, so if there is any chance that +/// Initialize NSS. This only executes the initialization routines once, so if there is any chance +/// that +/// /// # Panics +/// /// When NSS initialization fails. pub fn init() { // Set time zero. @@ -153,7 +152,9 @@ fn enable_ssl_trace() { } /// Initialize with a database. +/// /// # Panics +/// /// If NSS cannot be initialized. pub fn init_db>(dir: P) { time::init(); @@ -196,6 +197,7 @@ pub fn init_db>(dir: P) { } /// # Panics +/// /// If NSS isn't initialized. pub fn assert_initialized() { unsafe { diff --git a/third_party/rust/neqo-crypto/src/p11.rs b/third_party/rust/neqo-crypto/src/p11.rs index c7e47cbf152d..508d240062a1 100644 --- a/third_party/rust/neqo-crypto/src/p11.rs +++ b/third_party/rust/neqo-crypto/src/p11.rs @@ -9,8 +9,6 @@ #![allow(non_camel_case_types)] #![allow(non_snake_case)] -use crate::err::{secstatus_to_res, Error, Res}; -use neqo_common::hex_with_len; use std::{ convert::TryFrom, mem, @@ -19,6 +17,10 @@ use std::{ ptr::null_mut, }; +use neqo_common::hex_with_len; + +use crate::err::{secstatus_to_res, Error, Res}; + #[allow(clippy::upper_case_acronyms)] #[allow(clippy::unreadable_literal)] #[allow(unknown_lints, clippy::borrow_as_ptr)] @@ -39,6 +41,7 @@ macro_rules! scoped_ptr { /// Create a new instance of `$scoped` from a pointer. /// /// # Errors + /// /// When passed a null pointer generates an error. pub fn from_ptr(ptr: *mut $target) -> Result { if ptr.is_null() { @@ -80,8 +83,11 @@ impl PublicKey { /// Get the HPKE serialization of the public key. /// /// # Errors + /// /// When the key cannot be exported, which can be because the type is not supported. + /// /// # Panics + /// /// When keys are too large to fit in `c_uint/usize`. So only on programming error. pub fn key_data(&self) -> Res> { let mut buf = vec![0; 100]; @@ -124,12 +130,16 @@ impl PrivateKey { /// Get the bits of the private key. /// /// # Errors + /// /// When the key cannot be exported, which can be because the type is not supported /// or because the key data cannot be extracted from the PKCS#11 module. + /// /// # Panics + /// /// When the values are too large to fit. So never. pub fn key_data(&self) -> Res> { let mut key_item = Item::make_empty(); + #[allow(clippy::useless_conversion)] // TODO: Remove when we bump the MSRV to 1.74.0. secstatus_to_res(unsafe { PK11_ReadRawAttribute( PK11ObjectType::PK11_TypePrivKey, @@ -187,6 +197,7 @@ impl SymKey { /// You really don't want to use this. /// /// # Errors + /// /// Internal errors in case of failures in NSS. pub fn as_bytes(&self) -> Res<&[u8]> { secstatus_to_res(unsafe { PK11_ExtractKeyValue(self.ptr) })?; @@ -268,6 +279,7 @@ impl Item { /// content that is referenced there. /// /// # Safety + /// /// This dereferences two pointers. It doesn't get much less safe. pub unsafe fn into_vec(self) -> Vec { let b = self.ptr.as_ref().unwrap(); @@ -279,7 +291,9 @@ impl Item { } /// Generate a randomized buffer. +/// /// # Panics +/// /// When `size` is too large or NSS fails. #[must_use] pub fn random(size: usize) -> Vec { @@ -293,9 +307,10 @@ pub fn random(size: usize) -> Vec { #[cfg(test)] mod test { - use super::random; use test_fixture::fixture_init; + use super::random; + #[test] fn randomness() { fixture_init(); diff --git a/third_party/rust/neqo-crypto/src/replay.rs b/third_party/rust/neqo-crypto/src/replay.rs index 8f35ed6401b4..d4d3677f5c08 100644 --- a/third_party/rust/neqo-crypto/src/replay.rs +++ b/third_party/rust/neqo-crypto/src/replay.rs @@ -4,11 +4,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use crate::{ - err::Res, - ssl::PRFileDesc, - time::{Interval, PRTime, Time}, -}; use std::{ convert::{TryFrom, TryInto}, ops::{Deref, DerefMut}, @@ -17,6 +12,12 @@ use std::{ time::{Duration, Instant}, }; +use crate::{ + err::Res, + ssl::PRFileDesc, + time::{Interval, PRTime, Time}, +}; + // This is an opaque struct in NSS. #[allow(clippy::upper_case_acronyms)] #[allow(clippy::empty_enum)] @@ -55,6 +56,7 @@ impl AntiReplay { /// See the documentation in NSS for advice on how to set these values. /// /// # Errors + /// /// Returns an error if `now` is in the past relative to our baseline or /// NSS is unable to generate an anti-replay context. pub fn new(now: Instant, window: Duration, k: usize, bits: usize) -> Res { diff --git a/third_party/rust/neqo-crypto/src/secrets.rs b/third_party/rust/neqo-crypto/src/secrets.rs index 7fff5d4f68e2..75677636b6c1 100644 --- a/third_party/rust/neqo-crypto/src/secrets.rs +++ b/third_party/rust/neqo-crypto/src/secrets.rs @@ -4,6 +4,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::{os::raw::c_void, pin::Pin}; + +use neqo_common::qdebug; + use crate::{ agentio::as_c_void, constants::Epoch, @@ -11,8 +15,6 @@ use crate::{ p11::{PK11SymKey, PK11_ReferenceSymKey, SymKey}, ssl::{PRFileDesc, SSLSecretCallback, SSLSecretDirection}, }; -use neqo_common::qdebug; -use std::{os::raw::c_void, pin::Pin}; experimental_api!(SSL_SecretCallback( fd: *mut PRFileDesc, diff --git a/third_party/rust/neqo-crypto/src/selfencrypt.rs b/third_party/rust/neqo-crypto/src/selfencrypt.rs index 62d7057435a5..b8a63153fd26 100644 --- a/third_party/rust/neqo-crypto/src/selfencrypt.rs +++ b/third_party/rust/neqo-crypto/src/selfencrypt.rs @@ -4,14 +4,17 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use crate::constants::{Cipher, Version}; -use crate::err::{Error, Res}; -use crate::p11::{random, SymKey}; -use crate::{hkdf, Aead}; +use std::mem; use neqo_common::{hex, qinfo, qtrace, Encoder}; -use std::mem; +use crate::{ + constants::{Cipher, Version}, + err::{Error, Res}, + hkdf, + p11::{random, SymKey}, + Aead, +}; #[derive(Debug)] pub struct SelfEncrypt { @@ -27,6 +30,7 @@ impl SelfEncrypt { const SALT_LENGTH: usize = 16; /// # Errors + /// /// Failure to generate a new HKDF key using NSS results in an error. pub fn new(version: Version, cipher: Cipher) -> Res { let key = hkdf::generate_key(version, cipher)?; @@ -46,9 +50,11 @@ impl SelfEncrypt { Aead::new(false, self.version, self.cipher, &secret, "neqo self") } - /// Rotate keys. This causes any previous key that is being held to be replaced by the current key. + /// Rotate keys. This causes any previous key that is being held to be replaced by the current + /// key. /// /// # Errors + /// /// Failure to generate a new HKDF key using NSS results in an error. pub fn rotate(&mut self) -> Res<()> { let new_key = hkdf::generate_key(self.version, self.cipher)?; @@ -65,6 +71,7 @@ impl SelfEncrypt { /// caller is responsible for carrying the AAD as appropriate. /// /// # Errors + /// /// Failure to protect using NSS AEAD APIs produces an error. pub fn seal(&self, aad: &[u8], plaintext: &[u8]) -> Res> { // Format is: @@ -117,6 +124,7 @@ impl SelfEncrypt { /// Open the protected `ciphertext`. /// /// # Errors + /// /// Returns an error when the self-encrypted object is invalid; /// when the keys have been rotated; or when NSS fails. #[allow(clippy::similar_names)] // aad is similar to aead diff --git a/third_party/rust/neqo-crypto/src/ssl.rs b/third_party/rust/neqo-crypto/src/ssl.rs index 08776f34ba1c..8aaacffae604 100644 --- a/third_party/rust/neqo-crypto/src/ssl.rs +++ b/third_party/rust/neqo-crypto/src/ssl.rs @@ -15,11 +15,13 @@ clippy::borrow_as_ptr )] -use crate::constants::Epoch; -use crate::err::{secstatus_to_res, Res}; - use std::os::raw::{c_uint, c_void}; +use crate::{ + constants::Epoch, + err::{secstatus_to_res, Res}, +}; + include!(concat!(env!("OUT_DIR"), "/nss_ssl.rs")); mod SSLOption { include!(concat!(env!("OUT_DIR"), "/nss_sslopt.rs")); diff --git a/third_party/rust/neqo-crypto/src/time.rs b/third_party/rust/neqo-crypto/src/time.rs index 981ac6f42011..84dbfdb4a587 100644 --- a/third_party/rust/neqo-crypto/src/time.rs +++ b/third_party/rust/neqo-crypto/src/time.rs @@ -6,13 +6,6 @@ #![allow(clippy::upper_case_acronyms)] -use crate::{ - agentio::as_c_void, - err::{Error, Res}, - once::OnceResult, - ssl::{PRFileDesc, SSLTimeFunc}, -}; - use std::{ boxed::Box, convert::{TryFrom, TryInto}, @@ -22,6 +15,13 @@ use std::{ time::{Duration, Instant}, }; +use crate::{ + agentio::as_c_void, + err::{Error, Res}, + once::OnceResult, + ssl::{PRFileDesc, SSLTimeFunc}, +}; + include!(concat!(env!("OUT_DIR"), "/nspr_time.rs")); experimental_api!(SSL_SetTimeFunc( @@ -207,13 +207,14 @@ impl Default for TimeHolder { #[cfg(test)] mod test { - use super::{get_base, init, Interval, PRTime, Time}; - use crate::err::Res; use std::{ convert::{TryFrom, TryInto}, time::{Duration, Instant}, }; + use super::{get_base, init, Interval, PRTime, Time}; + use crate::err::Res; + #[test] fn convert_stable() { init(); diff --git a/third_party/rust/neqo-crypto/tests/aead.rs b/third_party/rust/neqo-crypto/tests/aead.rs index b9721e3d648b..0ee1e66c384e 100644 --- a/third_party/rust/neqo-crypto/tests/aead.rs +++ b/third_party/rust/neqo-crypto/tests/aead.rs @@ -2,9 +2,10 @@ #![warn(clippy::pedantic)] #![cfg(not(feature = "fuzzing"))] -use neqo_crypto::constants::{Cipher, TLS_AES_128_GCM_SHA256, TLS_VERSION_1_3}; -use neqo_crypto::hkdf; -use neqo_crypto::Aead; +use neqo_crypto::{ + constants::{Cipher, TLS_AES_128_GCM_SHA256, TLS_VERSION_1_3}, + hkdf, Aead, +}; use test_fixture::fixture_init; const AAD: &[u8] = &[ diff --git a/third_party/rust/neqo-crypto/tests/agent.rs b/third_party/rust/neqo-crypto/tests/agent.rs index 82e105fd1a1b..c2c83c467c32 100644 --- a/third_party/rust/neqo-crypto/tests/agent.rs +++ b/third_party/rust/neqo-crypto/tests/agent.rs @@ -1,20 +1,21 @@ #![cfg_attr(feature = "deny-warnings", deny(warnings))] #![warn(clippy::pedantic)] +use std::boxed::Box; + use neqo_crypto::{ generate_ech_keys, AuthenticationStatus, Client, Error, HandshakeState, SecretAgentPreInfo, Server, ZeroRttCheckResult, ZeroRttChecker, TLS_AES_128_GCM_SHA256, - TLS_CHACHA20_POLY1305_SHA256, TLS_GRP_EC_SECP256R1, TLS_VERSION_1_3, + TLS_CHACHA20_POLY1305_SHA256, TLS_GRP_EC_SECP256R1, TLS_GRP_EC_X25519, TLS_VERSION_1_3, }; -use std::boxed::Box; - mod handshake; +use test_fixture::{fixture_init, now}; + use crate::handshake::{ connect, connect_fail, forward_records, resumption_setup, PermissiveZeroRttChecker, Resumption, ZERO_RTT_TOKEN_DATA, }; -use test_fixture::{fixture_init, now}; #[test] fn make_client() { @@ -155,6 +156,48 @@ fn chacha_client() { ); } +#[test] +fn server_prefers_first_client_share() { + fixture_init(); + let mut client = Client::new("server.example", true).expect("should create client"); + let mut server = Server::new(&["key"]).expect("should create server"); + server + .set_groups(&[TLS_GRP_EC_X25519, TLS_GRP_EC_SECP256R1]) + .expect("groups set"); + client + .set_groups(&[TLS_GRP_EC_X25519, TLS_GRP_EC_SECP256R1]) + .expect("groups set"); + client + .send_additional_key_shares(1) + .expect("should set additional key share count"); + + connect(&mut client, &mut server); + + assert_eq!(client.info().unwrap().key_exchange(), TLS_GRP_EC_X25519); + assert_eq!(server.info().unwrap().key_exchange(), TLS_GRP_EC_X25519); +} + +#[test] +fn server_prefers_second_client_share() { + fixture_init(); + let mut client = Client::new("server.example", true).expect("should create client"); + let mut server = Server::new(&["key"]).expect("should create server"); + server + .set_groups(&[TLS_GRP_EC_SECP256R1, TLS_GRP_EC_X25519]) + .expect("groups set"); + client + .set_groups(&[TLS_GRP_EC_X25519, TLS_GRP_EC_SECP256R1]) + .expect("groups set"); + client + .send_additional_key_shares(1) + .expect("should set additional key share count"); + + connect(&mut client, &mut server); + + assert_eq!(client.info().unwrap().key_exchange(), TLS_GRP_EC_SECP256R1); + assert_eq!(server.info().unwrap().key_exchange(), TLS_GRP_EC_SECP256R1); +} + #[test] fn p256_server() { fixture_init(); @@ -170,6 +213,27 @@ fn p256_server() { assert_eq!(server.info().unwrap().key_exchange(), TLS_GRP_EC_SECP256R1); } +#[test] +fn p256_server_hrr() { + fixture_init(); + let mut client = Client::new("server.example", true).expect("should create client"); + let mut server = Server::new(&["key"]).expect("should create server"); + server + .set_groups(&[TLS_GRP_EC_SECP256R1]) + .expect("groups set"); + client + .set_groups(&[TLS_GRP_EC_X25519, TLS_GRP_EC_SECP256R1]) + .expect("groups set"); + client + .send_additional_key_shares(0) + .expect("should set additional key share count"); + + connect(&mut client, &mut server); + + assert_eq!(client.info().unwrap().key_exchange(), TLS_GRP_EC_SECP256R1); + assert_eq!(server.info().unwrap().key_exchange(), TLS_GRP_EC_SECP256R1); +} + #[test] fn alpn() { fixture_init(); diff --git a/third_party/rust/neqo-crypto/tests/ext.rs b/third_party/rust/neqo-crypto/tests/ext.rs index 02d78603b678..9ae81133f578 100644 --- a/third_party/rust/neqo-crypto/tests/ext.rs +++ b/third_party/rust/neqo-crypto/tests/ext.rs @@ -1,11 +1,13 @@ #![cfg_attr(feature = "deny-warnings", deny(warnings))] #![warn(clippy::pedantic)] -use neqo_crypto::constants::{HandshakeMessage, TLS_HS_CLIENT_HELLO, TLS_HS_ENCRYPTED_EXTENSIONS}; -use neqo_crypto::ext::{ExtensionHandler, ExtensionHandlerResult, ExtensionWriterResult}; -use neqo_crypto::{Client, Server}; -use std::cell::RefCell; -use std::rc::Rc; +use std::{cell::RefCell, rc::Rc}; + +use neqo_crypto::{ + constants::{HandshakeMessage, TLS_HS_CLIENT_HELLO, TLS_HS_ENCRYPTED_EXTENSIONS}, + ext::{ExtensionHandler, ExtensionHandlerResult, ExtensionWriterResult}, + Client, Server, +}; use test_fixture::fixture_init; mod handshake; diff --git a/third_party/rust/neqo-crypto/tests/handshake.rs b/third_party/rust/neqo-crypto/tests/handshake.rs index 779ec5ac222a..b2d8b9cc34d6 100644 --- a/third_party/rust/neqo-crypto/tests/handshake.rs +++ b/third_party/rust/neqo-crypto/tests/handshake.rs @@ -1,12 +1,12 @@ #![allow(dead_code)] +use std::{mem, time::Instant}; + use neqo_common::qinfo; use neqo_crypto::{ AntiReplay, AuthenticationStatus, Client, HandshakeState, RecordList, Res, ResumptionToken, SecretAgent, Server, ZeroRttCheckResult, ZeroRttChecker, }; -use std::mem; -use std::time::Instant; use test_fixture::{anti_replay, fixture_init, now}; /// Consume records until the handshake state changes. diff --git a/third_party/rust/neqo-crypto/tests/hkdf.rs b/third_party/rust/neqo-crypto/tests/hkdf.rs index 10a66f10b700..b4dde482f80a 100644 --- a/third_party/rust/neqo-crypto/tests/hkdf.rs +++ b/third_party/rust/neqo-crypto/tests/hkdf.rs @@ -1,11 +1,13 @@ #![cfg_attr(feature = "deny-warnings", deny(warnings))] #![warn(clippy::pedantic)] -use neqo_crypto::constants::{ - Cipher, TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384, TLS_CHACHA20_POLY1305_SHA256, - TLS_VERSION_1_3, +use neqo_crypto::{ + constants::{ + Cipher, TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384, TLS_CHACHA20_POLY1305_SHA256, + TLS_VERSION_1_3, + }, + hkdf, SymKey, }; -use neqo_crypto::{hkdf, SymKey}; use test_fixture::fixture_init; const SALT: &[u8] = &[ diff --git a/third_party/rust/neqo-crypto/tests/hp.rs b/third_party/rust/neqo-crypto/tests/hp.rs index 2e0aea6b8aee..43b96869d8a3 100644 --- a/third_party/rust/neqo-crypto/tests/hp.rs +++ b/third_party/rust/neqo-crypto/tests/hp.rs @@ -1,6 +1,8 @@ #![cfg_attr(feature = "deny-warnings", deny(warnings))] #![warn(clippy::pedantic)] +use std::mem; + use neqo_crypto::{ constants::{ Cipher, TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384, TLS_CHACHA20_POLY1305_SHA256, @@ -9,7 +11,6 @@ use neqo_crypto::{ hkdf, hp::HpKey, }; -use std::mem; use test_fixture::fixture_init; fn make_hp(cipher: Cipher) -> HpKey { diff --git a/third_party/rust/neqo-crypto/tests/selfencrypt.rs b/third_party/rust/neqo-crypto/tests/selfencrypt.rs index 5828f09392d6..fd9d4ea1eab5 100644 --- a/third_party/rust/neqo-crypto/tests/selfencrypt.rs +++ b/third_party/rust/neqo-crypto/tests/selfencrypt.rs @@ -2,8 +2,12 @@ #![warn(clippy::pedantic)] #![cfg(not(feature = "fuzzing"))] -use neqo_crypto::constants::{TLS_AES_128_GCM_SHA256, TLS_VERSION_1_3}; -use neqo_crypto::{init, selfencrypt::SelfEncrypt, Error}; +use neqo_crypto::{ + constants::{TLS_AES_128_GCM_SHA256, TLS_VERSION_1_3}, + init, + selfencrypt::SelfEncrypt, + Error, +}; #[test] fn se_create() { diff --git a/third_party/rust/neqo-http3/.cargo-checksum.json b/third_party/rust/neqo-http3/.cargo-checksum.json index 9ac198c4dd03..2705291744e0 100644 --- a/third_party/rust/neqo-http3/.cargo-checksum.json +++ b/third_party/rust/neqo-http3/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"5f26a2e98550f2d981392bc0d401d3673a2e59e4fceb32561062138f8c6711ea","src/buffered_send_stream.rs":"4bc45ca03252dc34ab421a2af3499191b182a619143c61d5609a46377c9a0f3d","src/client_events.rs":"b22ab079e7a06a3c51fdd3b642c0f503ca25c6d242eb5a20cbe9c86f43a8048d","src/conn_params.rs":"409f2899475fe6dfecacf191f08ea0905b0477aebacd165424542d40bbd62991","src/connection.rs":"49745c98331acd9b9eb3bb877675e0706e150627e305c192fa9e14deb17eb81a","src/connection_client.rs":"04844490ada0f00ea09e667a1f6f03e558a9f1c9ce3f00bd9552a0beef24b952","src/connection_server.rs":"542233f40064a6deba1ed54f81570760047c158cace41a7221a79be1ead462b9","src/control_stream_local.rs":"4b0224ecced01d2fce4192c72d682c01a8486fbdb2c575e5206d3777e646c400","src/control_stream_remote.rs":"7a261ac7df77e90a428ab0f92457a934a92a8c581462fc1818efd3de0c0ebd69","src/features/extended_connect/mod.rs":"98fd04d65ebb71a327a667ff9071f007aad4015c29af43a8c25f216b76c45276","src/features/extended_connect/tests/mod.rs":"fd6aee37243713e80fc526552f21f0222338cec9890409b6575a2a637b17ec1f","src/features/extended_connect/tests/webtransport/datagrams.rs":"8ba487a3bbd75e45b9b091397d8948e98ed8f5f8bcee033d629712833794747d","src/features/extended_connect/tests/webtransport/mod.rs":"5378f8192dafcd65b61318094375d7e5a45468e97dbc25495c6ff1a527734f33","src/features/extended_connect/tests/webtransport/negotiation.rs":"f870162ddb079028900154051624863cdbfbacfbe152b6424c0f67b5d3998881","src/features/extended_connect/tests/webtransport/sessions.rs":"53a052b52006193d5e4e3f0a74144572617737de9ff3ae00be033a246088d265","src/features/extended_connect/tests/webtransport/streams.rs":"4d590c8ee46ab6bd965053572994c252182f361698f598fd29ce51a530e5d15a","src/features/extended_connect/webtransport_session.rs":"6fb5d63005a714d72e8d51093d102bdc7f8b6cb62b1533e88386a50901673559","src/features/extended_connect/webtransport_streams.rs":"747be0d9510b87b9bb301c4299d66311bb433334dbe8e3e85568cea85d4294a2","src/features/mod.rs":"a981ebbd03e7bb7ea2313e883452e44f052c48f28edb7fd53a0825911b490230","src/frames/hframe.rs":"eb7e783c4d4b9b1befc25398be3dce45c1ac253ecd40d14f9d867a1fd8c4002f","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":"e8e46257b7e58ee636965b40499fef3f154523111abbc0341882ace9ba156c1b","src/headers_checks.rs":"ff1b3beca3569e3350bcdb413b8e055eaf0548fdacbaacd66d3147f832444879","src/lib.rs":"dda89089d02c1b0b44563da29b3f402f986812fbc8c78a57a0a20b727fa04407","src/priority.rs":"6bca5851629890b9c7264608d215c71841c7305deeada3d92e8c626b25457f88","src/push_controller.rs":"aa2a64180d8cb1b87682d0d8bbc42167188e8e1890261cb4cabb76de1fcc708b","src/qlog.rs":"877d1b21d2a252b7575254f68530c887cedc71f6d73a3903d9659d90b8206c29","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":"43cac1e6d5a31fe26007035c12b45916c7a7c7d6564153b3694bf1109fd6aa01","src/server.rs":"afe2710fd2f6927a4b69286eef30f472ac8036f5ccf11f06953a8b3673dc5049","src/server_connection_events.rs":"5fc125a13d9da8be5ad2d165206990b55fff890920e723db2314580159afa5d9","src/server_events.rs":"9f685a7bf28018dcdfc773c27f84c8307221a819b2fa27805e5462bbf9aa6c19","src/settings.rs":"a47a7e84dc745ec88d9eccead786ec1a2355b33b30d988d01a2e8a7043168d98","src/stream_type_reader.rs":"cb84654c2d18e1e382d4ea6c05d29026552928eb6a7de9612e7d6d454e357f03","tests/httpconn.rs":"8b62aa9a24ccc45f436aa57ff7d5b37394d844eced6b204085fc5086b1a643c7","tests/priority.rs":"09c3203e92a711cf8d59ba2abcdaef5399c04f0330bc14860216ad707b54c13a","tests/send_message.rs":"673ae1d0bf2dce46c21ee8353f45f189d2cb64a2f6e137ae38da6b2262ad066e","tests/webtransport.rs":"053fbe155cf33c0cce1e5e8f7c7d44e8bf82d69cd419d0c8147e92555bf89575"},"package":null} \ No newline at end of file +{"files":{"Cargo.toml":"fe3c1114cfbb94004bf56740c0d373568cb459efdb12504e67f31923fbd436e1","src/buffered_send_stream.rs":"f45bdf9ad2a04b3828c74ff5440681d3c9d1af39b55470e4f729842dc2412295","src/client_events.rs":"e1392e7bbb62fb0505a4d8bcd27559699bbf38f3c94e7d8cae7291db82e6334c","src/conn_params.rs":"224a8ea6ef632930a7788a1cabf47ce69ad41bd4bc8dcf3053fbd998fdb38e82","src/connection.rs":"09aeb123f8dc6b903dd7d30579e5bb09ed8f70bfae563fb2fcc1871c67d604d4","src/connection_client.rs":"ed1c9ebf443f49dbf12c193953a71ec0e6b95555e1927afce813d2a8324758be","src/connection_server.rs":"ca33b50650bd1ca2a952851b72712d55ec2e48b48f1f06e4184c808b8e1e009a","src/control_stream_local.rs":"d6ecc0adc926e1d5cec9a378317f9dfcfeeb9840a0873a2afb380c2d252d8c54","src/control_stream_remote.rs":"59eb4041e366d92f9f294e8446755caa5e91fd943bba7b79b726698ba13be248","src/features/extended_connect/mod.rs":"3b02f6b18627f3855465a81b1d9b285e6f13839e75a8a6db648ed9082908d7f0","src/features/extended_connect/tests/mod.rs":"fd6aee37243713e80fc526552f21f0222338cec9890409b6575a2a637b17ec1f","src/features/extended_connect/tests/webtransport/datagrams.rs":"7e3bdd591b9c7d02f69954629f889d52bd54f13dbca11d555e614138c2a55107","src/features/extended_connect/tests/webtransport/mod.rs":"fed03f0ded21a9f17be5be99e4572e16dd0c8598e37044df3228990ea7fcc9f4","src/features/extended_connect/tests/webtransport/negotiation.rs":"98254ef8446581ec520026b04ef9549645602181b61602c9936f6660141edf0b","src/features/extended_connect/tests/webtransport/sessions.rs":"de3d836f666c2bec31e70b33bdc2669572cabbe17df2225db7282613a224a364","src/features/extended_connect/tests/webtransport/streams.rs":"8b3c34cac1b2171252a4bb53d420ac2098549a20309c327bf56e2e9ba9e33538","src/features/extended_connect/webtransport_session.rs":"a6472eca50a2d097aa6ba8a76b45ae69fe2edd2696b2953945faa3ce6e7417f9","src/features/extended_connect/webtransport_streams.rs":"a9a106eefc93a9f6e9e1c246df64904353de1c4fbcd394b338e6b117f6c677f5","src/features/mod.rs":"925aae4427ad82e4d019354802b223d53db5e5585d4a940f5417a24a9503d7ee","src/frames/hframe.rs":"726842108261c9af1e7576bc546e7bd7bea86fbef4a5804f4b45a2b4612e2679","src/frames/mod.rs":"7d0a46ca147336d14781edb8dbee8b03c2e4bcd6646f5473a9d93d31fe73fecb","src/frames/reader.rs":"4883e25064da1fb3a6ae46b5d15e6bcfec9c5bbea55a1937ecdb9465b62a93b2","src/frames/tests/hframe.rs":"01ec74eb3eb25d95042aa0263f9267f89535e6b7b8c1161fab4ba9ee5352d4a7","src/frames/tests/mod.rs":"0610609b316767a6a022837d32ee0452e37ea296fde37e51bec87e7c77e923a3","src/frames/tests/reader.rs":"2bfadc7afbc41bff9f5f930b31550259a8a92484d35f6c5d8dd8fd9acfb88f5b","src/frames/tests/wtframe.rs":"589ebe1e62ce4da63b37b7d22cde7ba572ddbf29336fdcdbbcd0a745f79dacd8","src/frames/wtframe.rs":"c80518d1569de277767c7ccb7441898aadbfc5fb2afb968c1d5105f8d175ccff","src/headers_checks.rs":"44891c16dda6b7ef742058ecb0a8d34e219c51cae1216c09c661cf72d9a5e7d5","src/lib.rs":"ed8da14e573cc5a97afb012a78af7f076eb83b5cc20cb4fe432eb7136a3ffe52","src/priority.rs":"10d9dcfcd4585f2ca256daf254c78a428297c41976c6548f19cd3ed2222b7cd2","src/push_controller.rs":"eb27c7c2a52c6108c0e4d040b021775a2b573f32d78b7ac8652ff46fd549f780","src/qlog.rs":"b1e6108b018abb077f218d1806e0a83370afa87709e26b3d51f482ae5d9b9c82","src/qpack_decoder_receiver.rs":"c927dfc3e58c71d282210ba79280f6f03e789733bc3bedc247e68bab516b9e9e","src/qpack_encoder_receiver.rs":"d0ac03cc111b6e1c555a8654d3234116f2b135b5b040edac23cefe2d640beba9","src/recv_message.rs":"06666c22101cda41de14682dc7e2e6721f2821bd45baefc22caceae4ccfcf2e0","src/request_target.rs":"6041a69a0a74969ec08bc164509c055e9bad99f53bbeb16c0aa17d108dd68b8c","src/send_message.rs":"70f8a91d85515f42a64a88bd2a9480175b12596bc082f77587cc5bcff9ce996c","src/server.rs":"ab6d4c80cb5f6c070f74d8df27e7bd62d5c8a8e7756ff9d1a31d3f9ff91327a1","src/server_connection_events.rs":"12d353ca6301467f6d475dde3b789951a5716c89ddd7dbf1383efef8082361f3","src/server_events.rs":"c96cff96d5893a9ab7165d17e3d1afaafc5492418b30003c1c26ca8f489ab7ca","src/settings.rs":"476b154b5eea4c8d69a4a790fee3e527cef4d375df1cfb5eed04ec56406fe15a","src/stream_type_reader.rs":"7a7226b7911d69f7e00ec4987c2a32a5e8a33463203398cbee1e6645d2691478","tests/httpconn.rs":"bb6927801a8c75e4f05eb6cdb1e7f2d57be69b74e68ddad2a1614f2aeed04369","tests/priority.rs":"3418be17fbdfdbcfd80dc4532f9365f405925442fabc916f2b22f90aee89629f","tests/send_message.rs":"1e893216d9252e6fb69a0fb291b4f8b8ea954847c346ff7f9347d7895618cabf","tests/webtransport.rs":"cb30d348c0ce05efb722abac3b1c524216fa4cbde8b62a1d1e3238c3fadecbe7"},"package":null} \ No newline at end of file diff --git a/third_party/rust/neqo-http3/Cargo.toml b/third_party/rust/neqo-http3/Cargo.toml index 0fdc507fc9b0..8eeb2a58bfd9 100644 --- a/third_party/rust/neqo-http3/Cargo.toml +++ b/third_party/rust/neqo-http3/Cargo.toml @@ -11,22 +11,21 @@ [package] edition = "2018" -rust-version = "1.65.0" +rust-version = "1.70.0" name = "neqo-http3" -version = "0.6.8" +version = "0.7.0" authors = ["Dragana Damjanovic "] license = "MIT OR Apache-2.0" [dependencies] enumset = "1.1.2" -lazy_static = "1.3.0" -qlog = "0.9.0" -sfv = "0.9.1" -smallvec = "1.0.0" -url = "2.0" +lazy_static = "1.4" +sfv = "0.9.3" +smallvec = "1.11.1" +url = "2.5" [dependencies.log] -version = "0.4.0" +version = "0.4.17" default-features = false [dependencies.neqo-common] @@ -41,6 +40,10 @@ path = "./../neqo-qpack" [dependencies.neqo-transport] path = "./../neqo-transport" +[dependencies.qlog] +git = "https://github.com/cloudflare/quiche" +rev = "09ea4b244096a013071cfe2175bbf2945fb7f8d1" + [dev-dependencies.test-fixture] path = "../test-fixture" diff --git a/third_party/rust/neqo-http3/src/buffered_send_stream.rs b/third_party/rust/neqo-http3/src/buffered_send_stream.rs index 2a7d01bb740e..4f6761fa803a 100644 --- a/third_party/rust/neqo-http3/src/buffered_send_stream.rs +++ b/third_party/rust/neqo-http3/src/buffered_send_stream.rs @@ -4,10 +4,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use crate::Res; use neqo_common::qtrace; use neqo_transport::{Connection, StreamId}; +use crate::Res; + #[derive(Debug, PartialEq, Eq)] pub enum BufferedStream { Uninitialized, @@ -36,6 +37,7 @@ impl BufferedStream { } /// # Panics + /// /// If the `BufferedStream` is initialized more than one it will panic. pub fn init(&mut self, stream_id: StreamId) { debug_assert!(&Self::Uninitialized == self); @@ -46,6 +48,7 @@ impl BufferedStream { } /// # Panics + /// /// This functon cannot be called before the `BufferedStream` is initialized. pub fn buffer(&mut self, to_buf: &[u8]) { if let Self::Initialized { buf, .. } = self { @@ -56,6 +59,7 @@ impl BufferedStream { } /// # Errors + /// /// Returns `neqo_transport` errors. pub fn send_buffer(&mut self, conn: &mut Connection) -> Res { let label = ::neqo_common::log_subject!(::log::Level::Debug, self); @@ -76,6 +80,7 @@ impl BufferedStream { } /// # Errors + /// /// Returns `neqo_transport` errors. pub fn send_atomic(&mut self, conn: &mut Connection, to_send: &[u8]) -> Res { // First try to send anything that is in the buffer. diff --git a/third_party/rust/neqo-http3/src/client_events.rs b/third_party/rust/neqo-http3/src/client_events.rs index f21ec5929e13..4b2ebc6c300b 100644 --- a/third_party/rust/neqo-http3/src/client_events.rs +++ b/third_party/rust/neqo-http3/src/client_events.rs @@ -6,19 +6,18 @@ #![allow(clippy::module_name_repetitions)] -use crate::connection::Http3State; -use crate::settings::HSettingType; -use crate::{ - features::extended_connect::{ExtendedConnectEvents, ExtendedConnectType, SessionCloseReason}, - CloseType, Http3StreamInfo, HttpRecvStreamEvents, RecvStreamEvents, SendStreamEvents, -}; +use std::{cell::RefCell, collections::VecDeque, rc::Rc}; + use neqo_common::{event::Provider as EventProvider, Header}; use neqo_crypto::ResumptionToken; use neqo_transport::{AppError, StreamId, StreamType}; -use std::cell::RefCell; -use std::collections::VecDeque; -use std::rc::Rc; +use crate::{ + connection::Http3State, + features::extended_connect::{ExtendedConnectEvents, ExtendedConnectType, SessionCloseReason}, + settings::HSettingType, + CloseType, Http3StreamInfo, HttpRecvStreamEvents, RecvStreamEvents, SendStreamEvents, +}; #[derive(Debug, PartialEq, Eq, Clone)] pub enum WebTransportEvent { diff --git a/third_party/rust/neqo-http3/src/conn_params.rs b/third_party/rust/neqo-http3/src/conn_params.rs index 1ba2a601ad7d..23a5d2cc67e1 100644 --- a/third_party/rust/neqo-http3/src/conn_params.rs +++ b/third_party/rust/neqo-http3/src/conn_params.rs @@ -4,9 +4,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::cmp::min; + use neqo_qpack::QpackSettings; use neqo_transport::ConnectionParameters; -use std::cmp::min; const QPACK_MAX_TABLE_SIZE_DEFAULT: u64 = 65536; const QPACK_TABLE_SIZE_LIMIT: u64 = (1 << 30) - 1; @@ -53,6 +54,7 @@ impl Http3Parameters { } /// # Panics + /// /// The table size must be smaller than 1 << 30 by the spec. #[must_use] pub fn max_table_size_encoder(mut self, mut max_table: u64) -> Self { @@ -68,6 +70,7 @@ impl Http3Parameters { } /// # Panics + /// /// The table size must be smaller than 1 << 30 by the spec. #[must_use] pub fn max_table_size_decoder(mut self, mut max_table: u64) -> Self { diff --git a/third_party/rust/neqo-http3/src/connection.rs b/third_party/rust/neqo-http3/src/connection.rs index f2d0f2880655..bb2b5a6ce0bc 100644 --- a/third_party/rust/neqo-http3/src/connection.rs +++ b/third_party/rust/neqo-http3/src/connection.rs @@ -6,41 +6,43 @@ #![allow(clippy::module_name_repetitions)] -use crate::control_stream_local::ControlStreamLocal; -use crate::control_stream_remote::ControlStreamRemote; -use crate::features::extended_connect::{ - webtransport_session::WebTransportSession, - webtransport_streams::{WebTransportRecvStream, WebTransportSendStream}, - ExtendedConnectEvents, ExtendedConnectFeature, ExtendedConnectType, -}; -use crate::frames::HFrame; -use crate::push_controller::PushController; -use crate::qpack_decoder_receiver::DecoderRecvStream; -use crate::qpack_encoder_receiver::EncoderRecvStream; -use crate::recv_message::{RecvMessage, RecvMessageInfo}; -use crate::request_target::{AsRequestTarget, RequestTarget}; -use crate::send_message::SendMessage; -use crate::settings::{HSettingType, HSettings, HttpZeroRttChecker}; -use crate::stream_type_reader::NewStreamHeadReader; -use crate::{ - client_events::Http3ClientEvents, CloseType, Http3Parameters, Http3StreamType, - HttpRecvStreamEvents, NewStreamType, Priority, PriorityHandler, ReceiveOutput, RecvStream, - RecvStreamEvents, SendStream, SendStreamEvents, +use std::{ + cell::RefCell, + collections::{BTreeSet, HashMap}, + fmt::Debug, + mem, + rc::Rc, }; + use neqo_common::{qdebug, qerror, qinfo, qtrace, qwarn, Decoder, Header, MessageType, Role}; -use neqo_qpack::decoder::QPackDecoder; -use neqo_qpack::encoder::QPackEncoder; +use neqo_qpack::{decoder::QPackDecoder, encoder::QPackEncoder}; use neqo_transport::{ streams::SendOrder, AppError, Connection, ConnectionError, DatagramTracking, State, StreamId, StreamType, ZeroRttState, }; -use std::cell::RefCell; -use std::collections::{BTreeSet, HashMap}; -use std::fmt::Debug; -use std::mem; -use std::rc::Rc; -use crate::{Error, Res}; +use crate::{ + client_events::Http3ClientEvents, + control_stream_local::ControlStreamLocal, + control_stream_remote::ControlStreamRemote, + features::extended_connect::{ + webtransport_session::WebTransportSession, + webtransport_streams::{WebTransportRecvStream, WebTransportSendStream}, + ExtendedConnectEvents, ExtendedConnectFeature, ExtendedConnectType, + }, + frames::HFrame, + push_controller::PushController, + qpack_decoder_receiver::DecoderRecvStream, + qpack_encoder_receiver::EncoderRecvStream, + recv_message::{RecvMessage, RecvMessageInfo}, + request_target::{AsRequestTarget, RequestTarget}, + send_message::SendMessage, + settings::{HSettingType, HSettings, HttpZeroRttChecker}, + stream_type_reader::NewStreamHeadReader, + CloseType, Error, Http3Parameters, Http3StreamType, HttpRecvStreamEvents, NewStreamType, + Priority, PriorityHandler, ReceiveOutput, RecvStream, RecvStreamEvents, Res, SendStream, + SendStreamEvents, +}; pub(crate) struct RequestDescription<'b, 't, T> where @@ -79,8 +81,8 @@ enum Http3RemoteSettingsState { /// - `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 +/// - 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 @@ -384,7 +386,8 @@ impl Http3Connection { Ok(()) } - /// Inform a `HttpConnection` that a stream has data to send and that `send` should be called for the stream. + /// Inform a `HttpConnection` that a stream has data to send and that `send` should be called + /// for the stream. pub fn stream_has_pending_data(&mut self, stream_id: StreamId) { self.streams_with_pending_data.insert(stream_id); } @@ -502,8 +505,8 @@ impl Http3Connection { /// stream and unidi stream that are still do not have a type. /// The function cannot handle: /// 1) a `Push(_)`, `Htttp` or `WebTransportStream(_)` stream - /// 2) frames `MaxPushId`, `PriorityUpdateRequest`, `PriorityUpdateRequestPush` or `Goaway` - /// must be handled by `Http3Client`/`Server`. + /// 2) frames `MaxPushId`, `PriorityUpdateRequest`, `PriorityUpdateRequestPush` or `Goaway` must + /// be handled by `Http3Client`/`Server`. /// The function returns `ReceiveOutput`. pub fn handle_stream_readable( &mut self, @@ -579,8 +582,8 @@ impl Http3Connection { Ok(()) } - /// This is called when `neqo_transport::Connection` state has been change to take proper actions in - /// the HTTP3 layer. + /// This is called when `neqo_transport::Connection` state has been change to take proper + /// actions in the HTTP3 layer. pub fn handle_state_change(&mut self, conn: &mut Connection, state: &State) -> Res { qdebug!([self], "Handle state change {:?}", state); match state { @@ -626,7 +629,8 @@ impl Http3Connection { } } - /// This is called when 0RTT has been reset to clear `send_streams`, `recv_streams` and settings. + /// This is called when 0RTT has been reset to clear `send_streams`, `recv_streams` and + /// settings. pub fn handle_zero_rtt_rejected(&mut self) -> Res<()> { if self.state == Http3State::ZeroRtt { self.state = Http3State::Initializing; @@ -774,16 +778,16 @@ impl Http3Connection { /// This function will not handle the output of the function completely, but only /// handle the indication that a stream is closed. There are 2 cases: /// - an error occurred or - /// - the stream is done, i.e. the second value in `output` tuple is true if - /// the stream is done and can be removed from the `recv_streams` + /// - the stream is done, i.e. the second value in `output` tuple is true if the stream is done + /// and can be removed from the `recv_streams` /// How it is handling `output`: /// - if the stream is done, it removes the stream from `recv_streams` /// - if the stream is not done and there is no error, return `output` and the caller will /// handle it. /// - in case of an error: - /// - if it is only a stream error and the stream is not critical, send `STOP_SENDING` - /// frame, remove the stream from `recv_streams` and inform the listener that the stream - /// has been reset. + /// - if it is only a stream error and the stream is not critical, send `STOP_SENDING` frame, + /// remove the stream from `recv_streams` and inform the listener that the stream has been + /// reset. /// - otherwise this is a connection error. In this case, propagate the error to the caller /// that will handle it properly. fn handle_stream_manipulation_output( @@ -861,7 +865,8 @@ impl Http3Connection { } fn create_bidi_transport_stream(&self, conn: &mut Connection) -> Res { - // Requests cannot be created when a connection is in states: Initializing, GoingAway, Closing and Closed. + // Requests cannot be created when a connection is in states: Initializing, GoingAway, + // Closing and Closed. match self.state() { Http3State::GoingAway(..) | Http3State::Closing(..) | Http3State::Closed(..) => { return Err(Error::AlreadyClosed) @@ -927,8 +932,9 @@ impl Http3Connection { )), ); - // Call immediately send so that at least headers get sent. This will make Firefox faster, since - // it can send request body immediately in most cases and does not need to do a complete process loop. + // Call immediately send so that at least headers get sent. This will make Firefox faster, + // since it can send request body immediately in most cases and does not need to do + // a complete process loop. self.send_streams .get_mut(&stream_id) .ok_or(Error::InvalidStreamId)? @@ -936,11 +942,13 @@ impl Http3Connection { Ok(()) } - /// Stream data are read directly into a buffer supplied as a parameter of this function to avoid copying - /// data. + /// Stream data are read directly into a buffer supplied as a parameter of this function to + /// avoid copying data. + /// /// # Errors - /// It returns an error if a stream does not exist or an error happens while reading a stream, e.g. - /// early close, protocol error, etc. + /// + /// It returns an error if a stream does not exist or an error happens while reading a stream, + /// e.g. early close, protocol error, etc. pub fn read_data( &mut self, conn: &mut Connection, @@ -1004,7 +1012,9 @@ impl Http3Connection { } /// Set the stream `SendOrder`. + /// /// # Errors + /// /// Returns `InvalidStreamId` if the stream id doesn't exist pub fn stream_set_sendorder( conn: &mut Connection, @@ -1018,7 +1028,9 @@ impl Http3Connection { /// Set the stream Fairness. Fair streams will share bandwidth with other /// streams of the same sendOrder group (or the unordered group). Unfair streams /// will give bandwidth preferentially to the lowest streamId with data to send. + /// /// # Errors + /// /// Returns `InvalidStreamId` if the stream id doesn't exist pub fn stream_set_fairness( conn: &mut Connection, @@ -1088,8 +1100,8 @@ impl Http3Connection { .send_streams .get_mut(&stream_id) .ok_or(Error::InvalidStreamId)?; - // The following function may return InvalidStreamId from the transport layer if the stream has been closed - // already. It is ok to ignore it here. + // The following function may return InvalidStreamId from the transport layer if the stream + // has been closed already. It is ok to ignore it here. mem::drop(send_stream.close(conn)); if send_stream.done() { self.remove_send_stream(stream_id, conn); @@ -1184,7 +1196,8 @@ impl Http3Connection { .is_ok() { mem::drop(self.stream_close_send(conn, stream_id)); - // TODO issue 1294: add a timer to clean up the recv_stream if the peer does not do that in a short time. + // TODO issue 1294: add a timer to clean up the recv_stream if the peer does not + // do that in a short time. self.streams_with_pending_data.insert(stream_id); } else { self.cancel_fetch(stream_id, Error::HttpRequestRejected.code(), conn)?; @@ -1571,8 +1584,8 @@ impl Http3Connection { for id in recv { qtrace!("Remove the extended connect sub receiver stream {}", id); - // Use CloseType::ResetRemote so that an event will be sent. CloseType::LocalError would have - // the same effect. + // Use CloseType::ResetRemote so that an event will be sent. CloseType::LocalError would + // have the same effect. if let Some(mut s) = self.recv_streams.remove(&id) { mem::drop(s.reset(CloseType::ResetRemote(Error::HttpRequestCancelled.code()))); } diff --git a/third_party/rust/neqo-http3/src/connection_client.rs b/third_party/rust/neqo-http3/src/connection_client.rs index 51cd8e2935b0..5cc0541c0cbc 100644 --- a/third_party/rust/neqo-http3/src/connection_client.rs +++ b/third_party/rust/neqo-http3/src/connection_client.rs @@ -4,16 +4,16 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use crate::{ - client_events::{Http3ClientEvent, Http3ClientEvents}, - connection::{Http3Connection, Http3State, RequestDescription}, - frames::HFrame, - push_controller::{PushController, RecvPushEvents}, - recv_message::{RecvMessage, RecvMessageInfo}, - request_target::AsRequestTarget, - settings::HSettings, - Http3Parameters, Http3StreamType, NewStreamType, Priority, PriorityHandler, ReceiveOutput, +use std::{ + cell::RefCell, + convert::TryFrom, + fmt::{Debug, Display}, + mem, + net::SocketAddr, + rc::Rc, + time::Instant, }; + use neqo_common::{ event::Provider as EventProvider, hex, hex_with_len, qdebug, qinfo, qlog::NeqoQlog, qtrace, Datagram, Decoder, Encoder, Header, MessageType, Role, @@ -25,20 +25,21 @@ use neqo_transport::{ DatagramTracking, Output, RecvStreamStats, SendStreamStats, Stats as TransportStats, StreamId, StreamType, Version, ZeroRttState, }; -use std::{ - cell::RefCell, - convert::TryFrom, - fmt::{Debug, Display}, - mem, - net::SocketAddr, - rc::Rc, - time::Instant, + +use crate::{ + client_events::{Http3ClientEvent, Http3ClientEvents}, + connection::{Http3Connection, Http3State, RequestDescription}, + frames::HFrame, + push_controller::{PushController, RecvPushEvents}, + recv_message::{RecvMessage, RecvMessageInfo}, + request_target::AsRequestTarget, + settings::HSettings, + Error, Http3Parameters, Http3StreamType, NewStreamType, Priority, PriorityHandler, + ReceiveOutput, Res, }; -use crate::{Error, Res}; - -// This is used for filtering send_streams and recv_Streams with a stream_ids greater than or equal a given id. -// Only the same type (bidirectional or unidirectionsl) streams are filtered. +// This is used for filtering send_streams and recv_Streams with a stream_ids greater than or equal +// a given id. Only the same type (bidirectional or unidirectionsl) streams are filtered. fn id_gte(base: StreamId) -> impl FnMut((&StreamId, &U)) -> Option + 'static where U: ?Sized, @@ -71,43 +72,43 @@ fn alpn_from_quic_version(version: Version) -> &'static str { /// 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) +/// - [`Http3Client::new`] +/// - [`Http3Client::new_with_conn`] +/// - [`Http3Client::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) +/// - [`Http3Client::authenticated`] +/// - [`Http3Client::enable_ech`] +/// - [`Http3Client::enable_resumption`] +/// - [`Http3Client::initiate_key_update`] +/// - [`Http3Client::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) +/// - [`Http3Client::peer_certificate`] +/// - [`Http3Client::qpack_decoder_stats`] +/// - [`Http3Client::qpack_encoder_stats`] +/// - [`Http3Client::transport_stats`] +/// - [`Http3Client::state`] +/// - [`Http3Client::take_resumption_token`] +/// - [`Http3Client::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) +/// - [`Http3Client::process_output`] +/// - [`Http3Client::process_input`] +/// - [`Http3Client::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) +/// - [`Http3Client::fetch`] +/// - [`Http3Client::send_data`] +/// - [`Http3Client::read_data`] +/// - [`Http3Client::stream_close_send`] +/// - [`Http3Client::cancel_fetch`] +/// - [`Http3Client::stream_reset_send`] +/// - [`Http3Client::stream_stop_sending`] +/// - [`Http3Client::set_stream_max_data`] /// - priority feature: -/// - [`priority_update`](struct.Http3Client.html#method.priority_update) +/// - [`Http3Client::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) +/// - [`Http3Client::webtransport_create_session`] +/// - [`Http3Client::webtransport_close_session`] +/// - [`Http3Client::webtransport_create_stream`] +/// - [`Http3Client::webtransport_enabled`] /// /// ## Examples /// @@ -161,7 +162,7 @@ fn alpn_from_quic_version(version: Version) -> &'static str { /// } /// } /// } -///``` +/// ``` /// /// ### Creating a `WebTransport` session /// @@ -198,8 +199,7 @@ fn alpn_from_quic_version(version: Version) -> &'static str { /// } /// } /// } -/// -///``` +/// ``` /// /// ### `WebTransport`: create a stream, send and receive data on the stream /// @@ -287,7 +287,6 @@ fn alpn_from_quic_version(version: Version) -> &'static str { /// } /// } /// ``` -/// pub struct Http3Client { conn: Connection, base_handler: Http3Connection, @@ -303,8 +302,9 @@ 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 crypto context can't be created or configured. + /// + /// Making a `neqo-transport::connection` may produce an error. This can only be a crypto error + /// if the crypto context can't be created or configured. pub fn new( server_name: impl Into, cid_manager: Rc>, @@ -391,6 +391,7 @@ impl Http3Client { /// Enable encrypted client hello (ECH). /// /// # Errors + /// /// Fails when the configuration provided is bad. pub fn enable_ech(&mut self, ech_config_list: impl AsRef<[u8]>) -> Res<()> { self.conn.client_enable_ech(ech_config_list)?; @@ -399,7 +400,9 @@ impl Http3Client { /// Get the connection id, which is useful for disambiguating connections to /// the same origin. + /// /// # Panics + /// /// Never, because clients always have this field. #[must_use] pub fn connection_id(&self) -> &ConnectionId { @@ -433,14 +436,18 @@ impl Http3Client { .and_then(|t| self.encode_resumption_token(&t)) } - /// This may be call if an application has a resumption token. This must be called before connection starts. + /// 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 + /// /// On closing if the base handler can't handle it (debug only). pub fn enable_resumption(&mut self, now: Instant, token: impl AsRef<[u8]>) -> Res<()> { if self.base_handler.state != Http3State::Initializing { @@ -499,7 +506,9 @@ impl Http3Client { } /// Attempt to force a key update. + /// /// # Errors + /// /// If the connection isn't confirmed, or there is an outstanding key update, this /// returns `Err(Error::TransportError(neqo_transport::Error::KeyUpdateBlocked))`. pub fn initiate_key_update(&mut self) -> Res<()> { @@ -512,9 +521,13 @@ impl Http3Client { /// 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 + /// /// `SendMessage` implements `http_stream` so it will not panic. pub fn fetch<'x, 't: 'x, T>( &mut self, @@ -550,7 +563,9 @@ impl Http3Client { /// Send an [`PRIORITY_UPDATE`-frame][1] on next `Http3Client::process_output()` call. /// Returns if the priority got changed. + /// /// # Errors + /// /// `InvalidStreamId` if the stream does not exist /// /// [1]: https://datatracker.ietf.org/doc/html/draft-kazuho-httpbis-priority-04#section-5.2 @@ -560,7 +575,9 @@ impl Http3Client { /// 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<()> { qinfo!([self], "reset_stream {} error={}.", stream_id, error); @@ -569,7 +586,9 @@ impl Http3Client { } /// This is call when application is done sending a request. + /// /// # Errors + /// /// An error will be return if stream does not exist. pub fn stream_close_send(&mut self, stream_id: StreamId) -> Res<()> { qinfo!([self], "Close sending side stream={}.", stream_id); @@ -578,6 +597,7 @@ impl Http3Client { } /// # Errors + /// /// An error will be return if a stream does not exist. pub fn stream_reset_send(&mut self, stream_id: StreamId, error: AppError) -> Res<()> { qinfo!([self], "stream_reset_send {} error={}.", stream_id, error); @@ -586,6 +606,7 @@ impl Http3Client { } /// # Errors + /// /// An error will be return if a stream does not exist. pub fn stream_stop_sending(&mut self, stream_id: StreamId, error: AppError) -> Res<()> { qinfo!([self], "stream_stop_sending {} error={}.", stream_id, error); @@ -598,11 +619,13 @@ impl Http3Client { /// headers are supplied through the `fetch` function. /// /// # Errors + /// /// `InvalidStreamId` if the stream does not exist, /// `AlreadyClosed` if the stream has already been closed. - /// `TransportStreamDoesNotExist` if the transport stream does not exist (this may happen if `process_output` - /// has not been called when needed, and HTTP3 layer has not picked up the info that the stream has been closed.) - /// `InvalidInput` if an empty buffer has been supplied. + /// `TransportStreamDoesNotExist` if the transport stream does not exist (this may happen if + /// `process_output` has not been called when needed, and HTTP3 layer has not picked up the + /// info that the stream has been closed.) `InvalidInput` if an empty buffer has been + /// supplied. pub fn send_data(&mut self, stream_id: StreamId, buf: &[u8]) -> Res { qinfo!( [self], @@ -617,11 +640,13 @@ impl Http3Client { .send_data(&mut self.conn, buf) } - /// Response data are read directly into a buffer supplied as a parameter of this function to avoid copying - /// data. + /// Response data are read directly into a buffer supplied as a parameter of this function to + /// avoid copying data. + /// /// # Errors - /// It returns an error if a stream does not exist or an error happen while reading a stream, e.g. - /// early close, protocol error, etc. + /// + /// It returns an error if a stream does not exist or an error happen while reading a stream, + /// e.g. early close, protocol error, etc. pub fn read_data( &mut self, now: Instant, @@ -641,7 +666,9 @@ impl Http3Client { // API: Push streams /// Cancel a push + /// /// # Errors + /// /// `InvalidStreamId` if the stream does not exist. pub fn cancel_push(&mut self, push_id: u64) -> Res<()> { self.push_handler @@ -651,9 +678,11 @@ impl Http3Client { /// Push response data are read directly into a buffer supplied as a parameter of this function /// to avoid copying data. + /// /// # Errors - /// It returns an error if a stream does not exist(`InvalidStreamId`) or an error has happened while - /// reading a stream, e.g. early close, protocol error, etc. + /// + /// It returns an error if a stream does not exist(`InvalidStreamId`) or an error has happened + /// while reading a stream, e.g. early close, protocol error, etc. pub fn push_read_data( &mut self, now: Instant, @@ -670,8 +699,9 @@ impl Http3Client { } // API WebTransport - + // /// # Errors + /// /// If `WebTransport` cannot be created, e.g. the `WebTransport` support is /// not negotiated or the HTTP/3 connection is closed. pub fn webtransport_create_session<'x, 't: 'x, T>( @@ -699,11 +729,14 @@ impl Http3Client { } /// Close `WebTransport` cleanly + /// /// # Errors + /// /// `InvalidStreamId` if the stream does not exist, - /// `TransportStreamDoesNotExist` if the transport stream does not exist (this may happen if `process_output` - /// has not been called when needed, and HTTP3 layer has not picked up the info that the stream has been closed.) - /// `InvalidInput` if an empty buffer has been supplied. + /// `TransportStreamDoesNotExist` if the transport stream does not exist (this may happen if + /// `process_output` has not been called when needed, and HTTP3 layer has not picked up the + /// info that the stream has been closed.) `InvalidInput` if an empty buffer has been + /// supplied. pub fn webtransport_close_session( &mut self, session_id: StreamId, @@ -715,6 +748,7 @@ impl Http3Client { } /// # Errors + /// /// This may return an error if the particular session does not exist /// or the connection is not in the active state. pub fn webtransport_create_stream( @@ -732,7 +766,9 @@ 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. @@ -749,10 +785,14 @@ impl Http3Client { /// 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. + /// 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 { Ok(self.conn.max_datagram_size()? @@ -760,9 +800,13 @@ impl Http3Client { } /// Sets the `SendOrder` for a given stream + /// /// # Errors + /// /// It may return `InvalidStreamId` if a stream does not exist anymore. + /// /// # Panics + /// /// This cannot panic. pub fn webtransport_set_sendorder( &mut self, @@ -773,16 +817,22 @@ impl Http3Client { } /// Sets the `Fairness` for a given stream + /// /// # Errors + /// /// It may return `InvalidStreamId` if a stream does not exist anymore. + /// /// # Panics + /// /// This cannot panic. pub fn webtransport_set_fairness(&mut self, stream_id: StreamId, fairness: bool) -> Res<()> { Http3Connection::stream_set_fairness(&mut self.conn, stream_id, fairness) } /// Returns the current `SendStreamStats` of a `WebTransportSendStream`. + /// /// # Errors + /// /// `InvalidStreamId` if the stream does not exist. pub fn webtransport_send_stream_stats(&mut self, stream_id: StreamId) -> Res { self.base_handler @@ -793,7 +843,9 @@ impl Http3Client { } /// Returns the current `RecvStreamStats` of a `WebTransportRecvStream`. + /// /// # Errors + /// /// `InvalidStreamId` if the stream does not exist. pub fn webtransport_recv_stream_stats(&mut self, stream_id: StreamId) -> Res { self.base_handler @@ -804,7 +856,7 @@ impl Http3Client { } /// This function combines `process_input` and `process_output` function. - pub fn process(&mut self, dgram: Option, now: Instant) -> Output { + pub fn process(&mut self, dgram: Option<&Datagram>, now: Instant) -> Output { qtrace!([self], "Process."); if let Some(d) = dgram { self.process_input(d, now); @@ -822,12 +874,26 @@ impl Http3Client { /// 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) { + pub fn process_input(&mut self, dgram: &Datagram, now: Instant) { qtrace!([self], "Process input."); self.conn.process_input(dgram, now); self.process_http3(now); } + pub fn process_multiple_input<'a, I>(&mut self, dgrams: I, now: Instant) + where + I: IntoIterator, + I::IntoIter: ExactSizeIterator, + { + let dgrams = dgrams.into_iter(); + qtrace!([self], "Process multiple datagrams, len={}", dgrams.len()); + if dgrams.len() == 0 { + return; + } + self.conn.process_multiple_input(dgrams, now); + self.process_http3(now); + } + /// 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`. @@ -869,7 +935,8 @@ impl Http3Client { /// /// `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, + /// - 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 @@ -924,14 +991,14 @@ impl Http3Client { } } - /// 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 + /// 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 + /// 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. /// @@ -944,11 +1011,11 @@ impl Http3Client { 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. + // 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. + // 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 } => { @@ -1022,12 +1089,12 @@ impl Http3Client { /// - `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: + /// 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::PriorityUpdatePush` can only be receive on the server side, /// - `HFrame::Goaway { stream_id }` needs specific handling by the client by the protocol - /// specification. + /// specification. /// /// [1]: https://github.com/mozilla/neqo/blob/main/neqo-http3/src/connection.rs fn handle_stream_readable(&mut self, stream_id: StreamId) -> Res<()> { @@ -1180,7 +1247,9 @@ impl Http3Client { } /// Increases `max_stream_data` for a `stream_id`. + /// /// # Errors + /// /// Returns `InvalidStreamId` if a stream does not exist or the receiving /// side is closed. pub fn set_stream_max_data(&mut self, stream_id: StreamId, max_data: u64) -> Res<()> { @@ -1227,6 +1296,20 @@ impl EventProvider for Http3Client { #[cfg(test)] mod tests { + use std::{convert::TryFrom, mem, time::Duration}; + + use neqo_common::{event::Provider, qtrace, Datagram, Decoder, Encoder}; + use neqo_crypto::{AllowZeroRtt, AntiReplay, ResumptionToken}; + use neqo_qpack::{encoder::QPackEncoder, QpackSettings}; + use neqo_transport::{ + ConnectionError, ConnectionEvent, ConnectionParameters, Output, State, StreamId, + StreamType, Version, RECV_BUFFER_SIZE, SEND_BUFFER_SIZE, + }; + use test_fixture::{ + addr, anti_replay, default_server_h3, fixture_init, new_server, now, + CountingConnectionIdGenerator, DEFAULT_ALPN_H3, DEFAULT_KEYS, DEFAULT_SERVER_NAME, + }; + use super::{ AuthenticationStatus, Connection, Error, HSettings, Header, Http3Client, Http3ClientEvent, Http3Parameters, Http3State, Rc, RefCell, @@ -1237,18 +1320,6 @@ mod tests { settings::{HSetting, HSettingType, H3_RESERVED_SETTINGS}, Http3Server, Priority, RecvStream, }; - use neqo_common::{event::Provider, qtrace, Datagram, Decoder, Encoder}; - use neqo_crypto::{AllowZeroRtt, AntiReplay, ResumptionToken}; - use neqo_qpack::{encoder::QPackEncoder, QpackSettings}; - use neqo_transport::{ - ConnectionError, ConnectionEvent, ConnectionParameters, Output, State, StreamId, - StreamType, Version, RECV_BUFFER_SIZE, SEND_BUFFER_SIZE, - }; - use std::{convert::TryFrom, mem, time::Duration}; - use test_fixture::{ - addr, anti_replay, default_server_h3, fixture_init, new_server, now, - CountingConnectionIdGenerator, DEFAULT_ALPN_H3, DEFAULT_KEYS, DEFAULT_SERVER_NAME, - }; fn assert_closed(client: &Http3Client, expected: &Error) { match client.state() { @@ -1580,11 +1651,11 @@ mod tests { assert_eq!(client.state(), Http3State::Initializing); assert_eq!(*server.conn.state(), State::Init); - let out = server.conn.process(out.dgram(), now()); + let out = server.conn.process(out.as_dgram_ref(), now()); assert_eq!(*server.conn.state(), State::Handshaking); - let out = client.process(out.dgram(), now()); - let out = server.conn.process(out.dgram(), now()); + let out = client.process(out.as_dgram_ref(), now()); + let out = server.conn.process(out.as_dgram_ref(), now()); assert!(out.as_dgram_ref().is_none()); let authentication_needed = |e| matches!(e, Http3ClientEvent::AuthenticationNeeded); @@ -1597,12 +1668,14 @@ mod tests { fn connect_only_transport_with(client: &mut Http3Client, server: &mut TestServer) { let out = handshake_only(client, server); - let out = client.process(out.dgram(), now()); + let out = client.process(out.as_dgram_ref(), now()); let connected = |e| matches!(e, Http3ClientEvent::StateChange(Http3State::Connected)); assert!(client.events().any(connected)); assert_eq!(client.state(), Http3State::Connected); - server.conn.process_input(out.dgram().unwrap(), now()); + server + .conn + .process_input(out.as_dgram_ref().unwrap(), now()); assert!(server.conn.state().connected()); } @@ -1616,8 +1689,10 @@ mod tests { fn send_and_receive_client_settings(client: &mut Http3Client, server: &mut TestServer) { // send and receive client settings - let dgram = client.process(None, now()).dgram(); - server.conn.process_input(dgram.unwrap(), now()); + let out = client.process(None, now()); + server + .conn + .process_input(out.as_dgram_ref().unwrap(), now()); server.check_client_control_qpack_streams_no_resumption(); } @@ -1631,8 +1706,8 @@ mod tests { server.create_qpack_streams(); // Send the server's control and qpack streams data. - let dgram = server.conn.process(None, now()).dgram(); - client.process_input(dgram.unwrap(), now()); + let out = server.conn.process(None, now()); + client.process_input(out.as_dgram_ref().unwrap(), now()); // assert no error occured. assert_eq!(client.state(), Http3State::Connected); @@ -1692,8 +1767,8 @@ mod tests { 0x43, 0xd3, 0xc1, ]; - // For fetch request fetch("GET", "https", "something.com", "/", &[(String::from("myheaders", "myvalue"))]) - // the following request header frame will be sent: + // For fetch request fetch("GET", "https", "something.com", "/", &[(String::from("myheaders", + // "myvalue"))]) the following request header frame will be sent: const EXPECTED_REQUEST_HEADER_FRAME_VERSION2: &[u8] = &[ 0x01, 0x11, 0x02, 0x80, 0xd1, 0xd7, 0x50, 0x89, 0x41, 0xe9, 0x2a, 0x67, 0x35, 0x53, 0x2e, 0x43, 0xd3, 0xc1, 0x10, @@ -1701,8 +1776,8 @@ mod tests { const HTTP_HEADER_FRAME_0: &[u8] = &[0x01, 0x06, 0x00, 0x00, 0xd9, 0x54, 0x01, 0x30]; - // The response header from HTTP_HEADER_FRAME (0x01, 0x06, 0x00, 0x00, 0xd9, 0x54, 0x01, 0x30) are - // decoded into: + // The response header from HTTP_HEADER_FRAME (0x01, 0x06, 0x00, 0x00, 0xd9, 0x54, 0x01, 0x30) + // are decoded into: fn check_response_header_0(header: &[Header]) { let expected_response_header_0 = &[ Header::new(":status", "200"), @@ -1774,8 +1849,10 @@ mod tests { ) -> StreamId { let request_stream_id = make_request(client, close_sending_side, &[]); - let dgram = client.process(None, now()).dgram(); - server.conn.process_input(dgram.unwrap(), now()); + let out = client.process(None, now()); + server + .conn + .process_input(out.as_dgram_ref().unwrap(), now()); // find the new request/response stream and send frame v on it. while let Some(e) = server.conn.next_event() { @@ -1805,7 +1882,7 @@ mod tests { } let dgram = server.conn.process_output(now()).dgram(); if let Some(d) = dgram { - client.process_input(d, now()); + client.process_input(&d, now()); } request_stream_id } @@ -1834,8 +1911,8 @@ mod tests { server.conn.stream_close_send(stream_id).unwrap(); } let out = server.conn.process(None, now()); - let out = client.process(out.dgram(), now()); - mem::drop(server.conn.process(out.dgram(), now())); + let out = client.process(out.as_dgram_ref(), now()); + mem::drop(server.conn.process(out.as_dgram_ref(), now())); } const PUSH_PROMISE_DATA: &[u8] = &[ @@ -1873,8 +1950,8 @@ mod tests { let push_stream_id = send_push_data(&mut server.conn, push_id, close_push_stream); let out = server.conn.process(None, now()); - let out = client.process(out.dgram(), now()); - mem::drop(server.conn.process(out.dgram(), now())); + let out = client.process(out.as_dgram_ref(), now()); + mem::drop(server.conn.process(out.as_dgram_ref(), now())); push_stream_id } @@ -1888,8 +1965,8 @@ mod tests { send_push_promise(&mut server.conn, stream_id, push_id); let out = server.conn.process(None, now()); - let out = client.process(out.dgram(), now()); - mem::drop(server.conn.process(out.dgram(), now())); + let out = client.process(out.as_dgram_ref(), now()); + mem::drop(server.conn.process(out.as_dgram_ref(), now())); } fn send_cancel_push_and_exchange_packets( @@ -1906,8 +1983,8 @@ mod tests { .unwrap(); let out = server.conn.process(None, now()); - let out = client.process(out.dgram(), now()); - mem::drop(server.conn.process(out.dgram(), now())); + let out = client.process(out.as_dgram_ref(), now()); + mem::drop(server.conn.process(out.as_dgram_ref(), now())); } const PUSH_DATA: &[u8] = &[ @@ -2074,7 +2151,7 @@ mod tests { .stream_close_send(server.control_stream_id.unwrap()) .unwrap(); let out = server.conn.process(None, now()); - client.process(out.dgram(), now()); + client.process(out.as_dgram_ref(), now()); assert_closed(&client, &Error::HttpClosedCriticalStream); } @@ -2088,7 +2165,7 @@ mod tests { .stream_reset_send(server.control_stream_id.unwrap(), Error::HttpNoError.code()) .unwrap(); let out = server.conn.process(None, now()); - client.process(out.dgram(), now()); + client.process(out.as_dgram_ref(), now()); assert_closed(&client, &Error::HttpClosedCriticalStream); } @@ -2102,7 +2179,7 @@ mod tests { .stream_reset_send(server.encoder_stream_id.unwrap(), Error::HttpNoError.code()) .unwrap(); let out = server.conn.process(None, now()); - client.process(out.dgram(), now()); + client.process(out.as_dgram_ref(), now()); assert_closed(&client, &Error::HttpClosedCriticalStream); } @@ -2116,7 +2193,7 @@ mod tests { .stream_reset_send(server.decoder_stream_id.unwrap(), Error::HttpNoError.code()) .unwrap(); let out = server.conn.process(None, now()); - client.process(out.dgram(), now()); + client.process(out.as_dgram_ref(), now()); assert_closed(&client, &Error::HttpClosedCriticalStream); } @@ -2130,7 +2207,7 @@ mod tests { .stream_stop_sending(CLIENT_SIDE_CONTROL_STREAM_ID, Error::HttpNoError.code()) .unwrap(); let out = server.conn.process(None, now()); - client.process(out.dgram(), now()); + client.process(out.as_dgram_ref(), now()); assert_closed(&client, &Error::HttpClosedCriticalStream); } @@ -2144,7 +2221,7 @@ mod tests { .stream_stop_sending(CLIENT_SIDE_ENCODER_STREAM_ID, Error::HttpNoError.code()) .unwrap(); let out = server.conn.process(None, now()); - client.process(out.dgram(), now()); + client.process(out.as_dgram_ref(), now()); assert_closed(&client, &Error::HttpClosedCriticalStream); } @@ -2158,7 +2235,7 @@ mod tests { .stream_stop_sending(CLIENT_SIDE_DECODER_STREAM_ID, Error::HttpNoError.code()) .unwrap(); let out = server.conn.process(None, now()); - client.process(out.dgram(), now()); + client.process(out.as_dgram_ref(), now()); assert_closed(&client, &Error::HttpClosedCriticalStream); } @@ -2175,7 +2252,7 @@ mod tests { .stream_send(control_stream, &[0x0, 0x1, 0x3, 0x0, 0x1, 0x2]); assert_eq!(sent, Ok(6)); let out = server.conn.process(None, now()); - client.process(out.dgram(), now()); + client.process(out.as_dgram_ref(), now()); assert_closed(&client, &Error::HttpMissingSettings); } @@ -2191,7 +2268,7 @@ mod tests { ); assert_eq!(sent, Ok(8)); let out = server.conn.process(None, now()); - client.process(out.dgram(), now()); + client.process(out.as_dgram_ref(), now()); assert_closed(&client, &Error::HttpFrameUnexpected); } @@ -2205,7 +2282,7 @@ mod tests { .unwrap(); let out = server.conn.process(None, now()); - client.process(out.dgram(), now()); + client.process(out.as_dgram_ref(), now()); assert_closed(&client, &Error::HttpFrameUnexpected); } @@ -2254,8 +2331,8 @@ mod tests { _ = server.conn.stream_send(push_stream_id, v).unwrap(); let out = server.conn.process(None, now()); - let out = client.process(out.dgram(), now()); - mem::drop(server.conn.process(out.dgram(), now())); + let out = client.process(out.as_dgram_ref(), now()); + mem::drop(server.conn.process(out.as_dgram_ref(), now())); assert_closed(&client, &Error::HttpFrameUnexpected); } @@ -2314,8 +2391,8 @@ mod tests { .stream_send(new_stream_id, &[0x41, 0x19, 0x4, 0x4, 0x6, 0x0, 0x8, 0x0]) .unwrap(); let out = server.conn.process(None, now()); - let out = client.process(out.dgram(), now()); - mem::drop(server.conn.process(out.dgram(), now())); + let out = client.process(out.as_dgram_ref(), now()); + mem::drop(server.conn.process(out.as_dgram_ref(), now())); // check for stop-sending with Error::HttpStreamCreation. let mut stop_sending_event_found = false; @@ -2343,7 +2420,7 @@ mod tests { // Generate packet with the above bad h3 input let out = server.conn.process(None, now()); // Process bad input and close the connection. - mem::drop(client.process(out.dgram(), now())); + mem::drop(client.process(out.as_dgram_ref(), now())); assert_closed(&client, &Error::HttpFrameUnexpected); } @@ -2390,38 +2467,38 @@ mod tests { let mut sent = server.conn.stream_send(control_stream, &[0x0]); assert_eq!(sent, Ok(1)); let out = server.conn.process(None, now()); - client.process(out.dgram(), now()); + client.process(out.as_dgram_ref(), now()); // start sending SETTINGS frame sent = server.conn.stream_send(control_stream, &[0x4]); assert_eq!(sent, Ok(1)); let out = server.conn.process(None, now()); - client.process(out.dgram(), now()); + client.process(out.as_dgram_ref(), now()); sent = server.conn.stream_send(control_stream, &[0x4]); assert_eq!(sent, Ok(1)); let out = server.conn.process(None, now()); - client.process(out.dgram(), now()); + client.process(out.as_dgram_ref(), now()); sent = server.conn.stream_send(control_stream, &[0x6]); assert_eq!(sent, Ok(1)); let out = server.conn.process(None, now()); - client.process(out.dgram(), now()); + client.process(out.as_dgram_ref(), now()); sent = server.conn.stream_send(control_stream, &[0x0]); assert_eq!(sent, Ok(1)); let out = server.conn.process(None, now()); - client.process(out.dgram(), now()); + client.process(out.as_dgram_ref(), now()); sent = server.conn.stream_send(control_stream, &[0x8]); assert_eq!(sent, Ok(1)); let out = server.conn.process(None, now()); - client.process(out.dgram(), now()); + client.process(out.as_dgram_ref(), now()); sent = server.conn.stream_send(control_stream, &[0x0]); assert_eq!(sent, Ok(1)); let out = server.conn.process(None, now()); - client.process(out.dgram(), now()); + client.process(out.as_dgram_ref(), now()); assert_eq!(client.state(), Http3State::Connected); @@ -2429,37 +2506,37 @@ mod tests { sent = server.conn.stream_send(control_stream, &[0x5]); assert_eq!(sent, Ok(1)); let out = server.conn.process(None, now()); - client.process(out.dgram(), now()); + client.process(out.as_dgram_ref(), now()); sent = server.conn.stream_send(control_stream, &[0x5]); assert_eq!(sent, Ok(1)); let out = server.conn.process(None, now()); - client.process(out.dgram(), now()); + client.process(out.as_dgram_ref(), now()); sent = server.conn.stream_send(control_stream, &[0x4]); assert_eq!(sent, Ok(1)); let out = server.conn.process(None, now()); - client.process(out.dgram(), now()); + client.process(out.as_dgram_ref(), now()); sent = server.conn.stream_send(control_stream, &[0x61]); assert_eq!(sent, Ok(1)); let out = server.conn.process(None, now()); - client.process(out.dgram(), now()); + client.process(out.as_dgram_ref(), now()); sent = server.conn.stream_send(control_stream, &[0x62]); assert_eq!(sent, Ok(1)); let out = server.conn.process(None, now()); - client.process(out.dgram(), now()); + client.process(out.as_dgram_ref(), now()); sent = server.conn.stream_send(control_stream, &[0x63]); assert_eq!(sent, Ok(1)); let out = server.conn.process(None, now()); - client.process(out.dgram(), now()); + client.process(out.as_dgram_ref(), now()); sent = server.conn.stream_send(control_stream, &[0x64]); assert_eq!(sent, Ok(1)); let out = server.conn.process(None, now()); - client.process(out.dgram(), now()); + client.process(out.as_dgram_ref(), now()); // PUSH_PROMISE on a control stream will cause an error assert_closed(&client, &Error::HttpFrameUnexpected); @@ -2467,7 +2544,8 @@ mod tests { #[test] fn fetch_basic() { - // Connect exchange headers and send a request. Also check if the correct header frame has been sent. + // Connect exchange headers and send a request. Also check if the correct header frame has + // been sent. let (mut client, mut server, request_stream_id) = connect_and_send_request(true); // send response - 200 Content-Length: 7 @@ -2535,14 +2613,14 @@ mod tests { let d1 = dgram(&mut client.conn); let d2 = dgram(&mut client.conn); - server.conn.process_input(d2, now()); - server.conn.process_input(d1, now()); + server.conn.process_input(&d2, now()); + server.conn.process_input(&d1, now()); let d3 = dgram(&mut server.conn); let d4 = dgram(&mut server.conn); - client.process_input(d4, now()); - client.process_input(d3, now()); + client.process_input(&d4, now()); + client.process_input(&d3, now()); let ack = client.process_output(now()).dgram(); - server.conn.process_input(ack.unwrap(), now()); + server.conn.process_input(&ack.unwrap(), now()); } /// The client should keep a connection alive if it has unanswered requests. @@ -2562,7 +2640,7 @@ mod tests { request_stream_id: StreamId, ) { let out = server.process(None, now()); - client.process(out.dgram(), now()); + client.process(out.as_dgram_ref(), now()); while let Some(e) = client.next_event() { match e { @@ -2607,7 +2685,8 @@ mod tests { // Send a request with the request body. #[test] fn fetch_with_data() { - // Connect exchange headers and send a request. Also check if the correct header frame has been sent. + // Connect exchange headers and send a request. Also check if the correct header frame has + // been sent. let (mut client, mut server, request_stream_id) = connect_and_send_request(false); // Get DataWritable for the request stream so that we can write the request body. @@ -2618,7 +2697,7 @@ mod tests { client.stream_close_send(request_stream_id).unwrap(); let out = client.process(None, now()); - mem::drop(server.conn.process(out.dgram(), now())); + mem::drop(server.conn.process(out.as_dgram_ref(), now())); // find the new request/response stream and send response on it. while let Some(e) = server.conn.next_event() { @@ -2649,9 +2728,11 @@ mod tests { read_response(&mut client, &mut server.conn, request_stream_id); } - // send a request with request body containing request_body. We expect to receive expected_data_frame_header. + // send a request with request body containing request_body. We expect to receive + // expected_data_frame_header. fn fetch_with_data_length_xbytes(request_body: &[u8], expected_data_frame_header: &[u8]) { - // Connect exchange headers and send a request. Also check if the correct header frame has been sent. + // Connect exchange headers and send a request. Also check if the correct header frame has + // been sent. let (mut client, mut server, request_stream_id) = connect_and_send_request(false); // Get DataWritable for the request stream so that we can write the request body. @@ -2666,8 +2747,8 @@ mod tests { // We need to loop a bit until all data has been sent. let mut out = client.process(None, now()); for _i in 0..20 { - out = server.conn.process(out.dgram(), now()); - out = client.process(out.dgram(), now()); + out = server.conn.process(out.as_dgram_ref(), now()); + out = client.process(out.as_dgram_ref(), now()); } // check request body is received. @@ -2737,7 +2818,8 @@ mod tests { expected_second_data_frame_header: &[u8], expected_second_data_frame: &[u8], ) { - // Connect exchange headers and send a request. Also check if the correct header frame has been sent. + // Connect exchange headers and send a request. Also check if the correct header frame has + // been sent. let (mut client, mut server, request_stream_id) = connect_and_send_request(false); // Get DataWritable for the request stream so that we can write the request body. @@ -2759,11 +2841,11 @@ mod tests { // We need to loop a bit until all data has been sent. Once for every 1K // of data. for _i in 0..SEND_BUFFER_SIZE / 1000 { - out = server.conn.process(out.dgram(), now()); - out = client.process(out.dgram(), now()); + out = server.conn.process(out.as_dgram_ref(), now()); + out = client.process(out.as_dgram_ref(), now()); } - // check received frames and send a response. + // Check received frames and send a response. while let Some(e) = server.conn.next_event() { if let ConnectionEvent::RecvStreamReadable { stream_id } = e { if stream_id == request_stream_id { @@ -2852,7 +2934,8 @@ mod tests { } // Send 2 frames. For the second one we can only send 16383 bytes. - // After the first frame there is exactly 16383+4 bytes left in the send buffer, but we can only send 16383 bytes. + // After the first frame there is exactly 16383+4 bytes left in the send buffer, but we can only + // send 16383 bytes. #[test] fn fetch_two_data_frame_second_16383bytes_place_for_16387() { let (buf, hdr) = alloc_buffer(SEND_BUFFER_SIZE - 16410); @@ -2860,7 +2943,8 @@ mod tests { } // Send 2 frames. For the second one we can only send 16383 bytes. - // After the first frame there is exactly 16383+5 bytes left in the send buffer, but we can only send 16383 bytes. + // After the first frame there is exactly 16383+5 bytes left in the send buffer, but we can only + // send 16383 bytes. #[test] fn fetch_two_data_frame_second_16383bytes_place_for_16388() { let (buf, hdr) = alloc_buffer(SEND_BUFFER_SIZE - 16411); @@ -2868,7 +2952,8 @@ mod tests { } // Send 2 frames. For the second one we can send 16384 bytes. - // After the first frame there is exactly 16384+5 bytes left in the send buffer, but we can send 16384 bytes. + // After the first frame there is exactly 16384+5 bytes left in the send buffer, but we can send + // 16384 bytes. #[test] fn fetch_two_data_frame_second_16384bytes_place_for_16389() { let (buf, hdr) = alloc_buffer(SEND_BUFFER_SIZE - 16412); @@ -2878,7 +2963,8 @@ mod tests { // Test receiving STOP_SENDING with the HttpNoError error code. #[test] fn test_stop_sending_early_response() { - // Connect exchange headers and send a request. Also check if the correct header frame has been sent. + // Connect exchange headers and send a request. Also check if the correct header frame has + // been sent. let (mut client, mut server, request_stream_id) = connect_and_send_request(false); // Stop sending with early_response. @@ -2955,7 +3041,8 @@ mod tests { // Server sends stop sending and reset. #[test] fn test_stop_sending_other_error_with_reset() { - // Connect exchange headers and send a request. Also check if the correct header frame has been sent. + // Connect exchange headers and send a request. Also check if the correct header frame has + // been sent. let (mut client, mut server, request_stream_id) = connect_and_send_request(false); // Stop sending with RequestRejected. @@ -2974,7 +3061,7 @@ mod tests { ); let out = server.conn.process(None, now()); - client.process(out.dgram(), now()); + client.process(out.as_dgram_ref(), now()); let mut reset = false; let mut stop_sending = false; @@ -3018,7 +3105,8 @@ mod tests { // Server sends stop sending with RequestRejected, but it does not send reset. #[test] fn test_stop_sending_other_error_wo_reset() { - // Connect exchange headers and send a request. Also check if the correct header frame has been sent. + // Connect exchange headers and send a request. Also check if the correct header frame has + // been sent. let (mut client, mut server, request_stream_id) = connect_and_send_request(false); // Stop sending with RequestRejected. @@ -3030,7 +3118,7 @@ mod tests { ); let out = server.conn.process(None, now()); - client.process(out.dgram(), now()); + client.process(out.as_dgram_ref(), now()); let mut stop_sending = false; @@ -3065,7 +3153,8 @@ mod tests { // in client.events. The events will be removed. #[test] fn test_stop_sending_and_reset_other_error_with_events() { - // Connect exchange headers and send a request. Also check if the correct header frame has been sent. + // Connect exchange headers and send a request. Also check if the correct header frame has + // been sent. let (mut client, mut server, request_stream_id) = connect_and_send_request(false); // send response - 200 Content-Length: 3 @@ -3094,7 +3183,7 @@ mod tests { ); let out = server.conn.process(None, now()); - client.process(out.dgram(), now()); + client.process(out.as_dgram_ref(), now()); let mut reset = false; @@ -3138,7 +3227,8 @@ mod tests { // The events will be removed. #[test] fn test_stop_sending_other_error_with_events() { - // Connect exchange headers and send a request. Also check if the correct header frame has been sent. + // Connect exchange headers and send a request. Also check if the correct header frame has + // been sent. let (mut client, mut server, request_stream_id) = connect_and_send_request(false); // send response - 200 Content-Length: 3 @@ -3161,7 +3251,7 @@ mod tests { ); let out = server.conn.process(None, now()); - client.process(out.dgram(), now()); + client.process(out.as_dgram_ref(), now()); let mut stop_sending = false; let mut header_ready = false; @@ -3201,7 +3291,8 @@ mod tests { // Server sends a reset. We will close sending side as well. #[test] fn test_reset_wo_stop_sending() { - // Connect exchange headers and send a request. Also check if the correct header frame has been sent. + // Connect exchange headers and send a request. Also check if the correct header frame has + // been sent. let (mut client, mut server, request_stream_id) = connect_and_send_request(false); // Send a reset. @@ -3213,7 +3304,7 @@ mod tests { ); let out = server.conn.process(None, now()); - client.process(out.dgram(), now()); + client.process(out.as_dgram_ref(), now()); let mut reset = false; @@ -3303,7 +3394,7 @@ mod tests { assert_eq!(request_stream_id_3, 8); let out = client.process(None, now()); - mem::drop(server.conn.process(out.dgram(), now())); + mem::drop(server.conn.process(out.as_dgram_ref(), now())); _ = server .conn @@ -3325,7 +3416,7 @@ mod tests { } } let out = server.conn.process(None, now()); - client.process(out.dgram(), now()); + client.process(out.as_dgram_ref(), now()); let mut stream_reset = false; while let Some(e) = client.next_event() { @@ -3387,7 +3478,7 @@ mod tests { assert_eq!(request_stream_id_3, 8); let out = client.process(None, now()); - mem::drop(server.conn.process(out.dgram(), now())); + mem::drop(server.conn.process(out.as_dgram_ref(), now())); // First send a Goaway frame with an higher number _ = server @@ -3396,7 +3487,7 @@ mod tests { .unwrap(); let out = server.conn.process(None, now()); - client.process(out.dgram(), now()); + client.process(out.as_dgram_ref(), now()); // Check that there is one reset for stream_id 8 let mut stream_reset_1 = 0; @@ -3482,7 +3573,7 @@ mod tests { .unwrap(); let out = server.conn.process(None, now()); - client.process(out.dgram(), now()); + client.process(out.as_dgram_ref(), now()); assert_eq!(client.state(), Http3State::GoingAway(StreamId::new(4))); @@ -3493,7 +3584,7 @@ mod tests { .unwrap(); let out = server.conn.process(None, now()); - client.process(out.dgram(), now()); + client.process(out.as_dgram_ref(), now()); assert_closed(&client, &Error::HttpGeneralProtocol); } @@ -3508,7 +3599,7 @@ mod tests { .unwrap(); let out = server.conn.process(None, now()); - client.process(out.dgram(), now()); + client.process(out.as_dgram_ref(), now()); assert_closed(&client, &Error::HttpId); } @@ -3521,7 +3612,7 @@ mod tests { server.conn.stream_close_send(request_stream_id).unwrap(); let out = server.conn.process(None, now()); - client.process(out.dgram(), now()); + client.process(out.as_dgram_ref(), now()); // Recv HeaderReady wo headers with fin. let e = client.events().next().unwrap(); @@ -3619,7 +3710,7 @@ mod tests { server.conn.stream_close_send(request_stream_id).unwrap(); let out = server.conn.process(None, now()); - client.process(out.dgram(), now()); + client.process(out.as_dgram_ref(), now()); // Recv DataReadable wo data with fin while let Some(e) = client.next_event() { @@ -3666,7 +3757,7 @@ mod tests { server.conn.stream_close_send(request_stream_id).unwrap(); let out = server.conn.process(None, now()); - client.process(out.dgram(), now()); + client.process(out.as_dgram_ref(), now()); // Recv HeaderReady with fin. while let Some(e) = client.next_event() { @@ -3717,7 +3808,7 @@ mod tests { .unwrap(); let out = server.conn.process(None, now()); - client.process(out.dgram(), now()); + client.process(out.as_dgram_ref(), now()); // Recv headers wo fin while let Some(e) = client.next_event() { @@ -3744,7 +3835,7 @@ mod tests { server.conn.stream_close_send(request_stream_id).unwrap(); let out = server.conn.process(None, now()); - client.process(out.dgram(), now()); + client.process(out.as_dgram_ref(), now()); // Recv no data, but do get fin while let Some(e) = client.next_event() { @@ -3814,7 +3905,7 @@ mod tests { // ok NOW send fin server.conn.stream_close_send(request_stream_id).unwrap(); let out = server.conn.process(None, now()); - client.process(out.dgram(), now()); + client.process(out.as_dgram_ref(), now()); // fin wo data should generate DataReadable let e = client.events().next().unwrap(); @@ -3938,7 +4029,8 @@ mod tests { header_block: encoded_headers.to_vec(), }; - // Send the encoder instructions, but delay them so that the stream is blocked on decoding headers. + // Send the encoder instructions, but delay them so that the stream is blocked on decoding + // headers. let encoder_inst_pkt = server.conn.process(None, now()); // Send response @@ -3959,10 +4051,10 @@ mod tests { assert!(!client.events().any(header_ready_event)); // Let client receive the encoder instructions. - mem::drop(client.process(encoder_inst_pkt.dgram(), now())); + mem::drop(client.process(encoder_inst_pkt.as_dgram_ref(), now())); let out = server.conn.process(None, now()); - mem::drop(client.process(out.dgram(), now())); + mem::drop(client.process(out.as_dgram_ref(), now())); mem::drop(client.process(None, now())); let mut recv_header = false; @@ -4006,7 +4098,8 @@ mod tests { header_block: encoded_headers.to_vec(), }; - // Send the encoder instructions, but delay them so that the stream is blocked on decoding headers. + // Send the encoder instructions, but delay them so that the stream is blocked on decoding + // headers. let encoder_inst_pkt = server.conn.process(None, now()); let mut d = Encoder::default(); @@ -4024,7 +4117,7 @@ mod tests { assert!(!hconn.events().any(header_ready_event)); // Let client receive the encoder instructions. - let _out = hconn.process(encoder_inst_pkt.dgram(), now()); + let _out = hconn.process(encoder_inst_pkt.as_dgram_ref(), now()); let mut recv_header = false; // Now the stream is unblocked. After headers we will receive a fin. @@ -4052,7 +4145,7 @@ mod tests { server.send_ticket(now(), &[]).expect("can send ticket"); let out = server.process_output(now()); assert!(out.as_dgram_ref().is_some()); - client.process_input(out.dgram().unwrap(), now()); + client.process_input(out.as_dgram_ref().unwrap(), now()); // We do not have a token so we need to wait for a resumption token timer to trigger. client.process_output(now() + Duration::from_millis(250)); assert_eq!(client.state(), Http3State::Connected); @@ -4096,7 +4189,7 @@ mod tests { assert_eq!(client.state(), Http3State::ZeroRtt); assert_eq!(*server.conn.state(), State::Init); - let out = server.conn.process(out.dgram(), now()); + let out = server.conn.process(out.as_dgram_ref(), now()); // Check that control and qpack streams are received and a // SETTINGS frame has been received. @@ -4109,10 +4202,10 @@ mod tests { ); assert_eq!(*server.conn.state(), State::Handshaking); - let out = client.process(out.dgram(), now()); + let out = client.process(out.as_dgram_ref(), now()); assert_eq!(client.state(), Http3State::Connected); - mem::drop(server.conn.process(out.dgram(), now())); + mem::drop(server.conn.process(out.as_dgram_ref(), now())); assert!(server.conn.state().connected()); assert!(client.tls_info().unwrap().resumed()); @@ -4131,7 +4224,7 @@ mod tests { assert_eq!(client.state(), Http3State::ZeroRtt); assert_eq!(*server.conn.state(), State::Init); - let out = server.conn.process(out.dgram(), now()); + let out = server.conn.process(out.as_dgram_ref(), now()); // Check that control and qpack streams are received and a // SETTINGS frame has been received. @@ -4144,11 +4237,11 @@ mod tests { ); assert_eq!(*server.conn.state(), State::Handshaking); - let out = client.process(out.dgram(), now()); + let out = client.process(out.as_dgram_ref(), now()); assert_eq!(client.state(), Http3State::Connected); - let out = server.conn.process(out.dgram(), now()); + let out = server.conn.process(out.as_dgram_ref(), now()); assert!(server.conn.state().connected()); - let out = client.process(out.dgram(), now()); + let out = client.process(out.as_dgram_ref(), now()); assert!(out.as_dgram_ref().is_none()); // After the server has been connected, send a response. @@ -4215,9 +4308,9 @@ mod tests { let client_0rtt = client.process(None, now()); assert!(client_0rtt.as_dgram_ref().is_some()); - let server_hs = server.process(client_hs.dgram(), now()); + let server_hs = server.process(client_hs.as_dgram_ref(), now()); assert!(server_hs.as_dgram_ref().is_some()); // Should produce ServerHello etc... - let server_ignored = server.process(client_0rtt.dgram(), now()); + let server_ignored = server.process(client_0rtt.as_dgram_ref(), now()); assert!(server_ignored.as_dgram_ref().is_none()); // The server shouldn't receive that 0-RTT data. @@ -4225,7 +4318,7 @@ mod tests { assert!(!server.events().any(recvd_stream_evt)); // Client should get a rejection. - let client_out = client.process(server_hs.dgram(), now()); + let client_out = client.process(server_hs.as_dgram_ref(), now()); assert!(client_out.as_dgram_ref().is_some()); let recvd_0rtt_reject = |e| e == Http3ClientEvent::ZeroRttRejected; assert!(client.events().any(recvd_0rtt_reject)); @@ -4236,7 +4329,7 @@ mod tests { assert_eq!(res.unwrap_err(), Error::InvalidStreamId); // Client will send Setting frame and open new qpack streams. - mem::drop(server.process(client_out.dgram(), now())); + mem::drop(server.process(client_out.as_dgram_ref(), now())); TestServer::new_with_conn(server).check_client_control_qpack_streams_no_resumption(); // Check that we can send a request and that the stream_id starts again from 0. @@ -4267,7 +4360,7 @@ mod tests { assert_eq!(client.state(), Http3State::ZeroRtt); assert_eq!(*server.conn.state(), State::Init); - let out = server.conn.process(out.dgram(), now()); + let out = server.conn.process(out.as_dgram_ref(), now()); // Check that control and qpack streams anda SETTINGS frame are received. // Also qpack encoder stream will send "change capacity" instruction because it has @@ -4279,10 +4372,10 @@ mod tests { ); assert_eq!(*server.conn.state(), State::Handshaking); - let out = client.process(out.dgram(), now()); + let out = client.process(out.as_dgram_ref(), now()); assert_eq!(client.state(), Http3State::Connected); - mem::drop(server.conn.process(out.dgram(), now())); + mem::drop(server.conn.process(out.as_dgram_ref(), now())); assert!(server.conn.state().connected()); assert!(client.tls_info().unwrap().resumed()); @@ -4298,7 +4391,7 @@ mod tests { assert_eq!(sent.unwrap(), enc.len()); let out = server.conn.process(None, now()); - client.process(out.dgram(), now()); + client.process(out.as_dgram_ref(), now()); assert_eq!(&client.state(), expected_client_state); assert!(server.conn.state().connected()); @@ -4658,7 +4751,7 @@ mod tests { server.conn.stream_close_send(request_stream_id).unwrap(); let out = server.conn.process(None, now()); - client.process(out.dgram(), now()); + client.process(out.as_dgram_ref(), now()); let events: Vec = client.events().collect(); @@ -4770,7 +4863,8 @@ mod tests { #[test] fn no_data_ready_events_after_fin() { - // Connect exchange headers and send a request. Also check if the correct header frame has been sent. + // Connect exchange headers and send a request. Also check if the correct header frame has + // been sent. let (mut client, mut server, request_stream_id) = connect_and_send_request(true); // send response - 200 Content-Length: 7 @@ -4873,7 +4967,7 @@ mod tests { _ = server.conn.stream_send(request_stream_id, &[0, 0]).unwrap(); server.conn.stream_close_send(request_stream_id).unwrap(); let dgram = server.conn.process_output(now()).dgram(); - client.process_input(dgram.unwrap(), now()); + client.process_input(&dgram.unwrap(), now()); let data_readable_event = |e: &_| matches!(e, Http3ClientEvent::DataReadable { stream_id } if *stream_id == request_stream_id); assert_eq!(client.events().filter(data_readable_event).count(), 1); @@ -4897,7 +4991,7 @@ mod tests { server.create_control_stream(); // Send the server's control stream data. let out = server.conn.process(None, now()); - client.process(out.dgram(), now()); + client.process(out.as_dgram_ref(), now()); server.create_qpack_streams(); let qpack_pkt1 = server.conn.process(None, now()); @@ -4905,7 +4999,7 @@ mod tests { let request_stream_id = make_request(&mut client, true, &[]); let out = client.process(None, now()); - mem::drop(server.conn.process(out.dgram(), now())); + mem::drop(server.conn.process(out.as_dgram_ref(), now())); setup_server_side_encoder(&mut client, &mut server); @@ -4925,7 +5019,7 @@ mod tests { // Send the encoder instructions, let out = server.conn.process(None, now()); - client.process(out.dgram(), now()); + client.process(out.as_dgram_ref(), now()); // Send response let mut d = Encoder::default(); @@ -4940,13 +5034,13 @@ mod tests { server.conn.stream_close_send(request_stream_id).unwrap(); let out = server.conn.process(None, now()); - mem::drop(client.process(out.dgram(), now())); + mem::drop(client.process(out.as_dgram_ref(), now())); let header_ready_event = |e| matches!(e, Http3ClientEvent::HeaderReady { .. }); assert!(!client.events().any(header_ready_event)); // Let client receive the encoder instructions. - mem::drop(client.process(qpack_pkt1.dgram(), now())); + mem::drop(client.process(qpack_pkt1.as_dgram_ref(), now())); assert!(client.events().any(header_ready_event)); } @@ -4983,7 +5077,8 @@ mod tests { assert_eq!(client.state(), Http3State::Connected); - // Check that the push has been closed, e.g. calling cancel_push should return InvalidStreamId. + // Check that the push has been closed, e.g. calling cancel_push should return + // InvalidStreamId. assert_eq!(client.cancel_push(0), Err(Error::InvalidStreamId)); } @@ -5021,8 +5116,8 @@ mod tests { // Reading push data will stop the client from being idle. _ = send_push_data(&mut server.conn, 0, false); - let dgram = server.conn.process_output(now()).dgram(); - client.process_input(dgram.unwrap(), now()); + let out = server.conn.process_output(now()); + client.process_input(out.as_dgram_ref().unwrap(), now()); let mut buf = [0; 16]; let (read, fin) = client.push_read_data(now(), 0, &mut buf).unwrap(); @@ -5074,7 +5169,8 @@ mod tests { assert_eq!(client.state(), Http3State::Connected); - // Check that the push has been closed, e.g. calling cancel_push should return InvalidStreamId. + // Check that the push has been closed, e.g. calling cancel_push should return + // InvalidStreamId. assert_eq!(client.cancel_push(0), Err(Error::InvalidStreamId)); assert_eq!(client.cancel_push(1), Err(Error::InvalidStreamId)); } @@ -5331,7 +5427,7 @@ mod tests { assert_eq!(request_stream_id_2, 4); let out = client.process(None, now()); - mem::drop(server.conn.process(out.dgram(), now())); + mem::drop(server.conn.process(out.as_dgram_ref(), now())); send_push_promise_and_exchange_packets(&mut client, &mut server, request_stream_id_2, 5); @@ -5367,7 +5463,7 @@ mod tests { assert_eq!(request_stream_id_2, 4); let out = client.process(None, now()); - mem::drop(server.conn.process(out.dgram(), now())); + mem::drop(server.conn.process(out.as_dgram_ref(), now())); send_push_promise_and_exchange_packets(&mut client, &mut server, request_stream_id_2, 5); @@ -5415,7 +5511,7 @@ mod tests { assert_eq!(request_stream_id_2, 4); let out = client.process(None, now()); - mem::drop(server.conn.process(out.dgram(), now())); + mem::drop(server.conn.process(out.as_dgram_ref(), now())); send_push_promise_and_exchange_packets(&mut client, &mut server, request_stream_id_2, 5); @@ -5424,7 +5520,7 @@ mod tests { assert!(!client.events().any(push_event)); } - // Test that max_push_id is enforced when a push promise frame is received. + // Test that max_push_id is enforced when a push promise frame is received. #[test] fn exceed_max_push_id_promise() { // Connect and send a request @@ -5508,7 +5604,7 @@ mod tests { ); let out = client.process(None, now()); - mem::drop(server.conn.process(out.dgram(), now())); + mem::drop(server.conn.process(out.as_dgram_ref(), now())); // Check max_push_id frame has been received let control_stream_readable = @@ -5526,8 +5622,8 @@ mod tests { send_push_data(&mut server.conn, 8, true); let out = server.conn.process(None, now()); - let out = client.process(out.dgram(), now()); - mem::drop(server.conn.process(out.dgram(), now())); + let out = client.process(out.as_dgram_ref(), now()); + mem::drop(server.conn.process(out.as_dgram_ref(), now())); assert_eq!(client.state(), Http3State::Connected); @@ -5588,7 +5684,8 @@ mod tests { ))); } - // Test CANCEL_PUSH frame: after cancel push any new PUSH_PROMISE or push stream will be ignored. + // Test CANCEL_PUSH frame: after cancel push any new PUSH_PROMISE or push stream will be + // ignored. #[test] fn cancel_push_ignore_promise() { // Connect and send a request @@ -5604,7 +5701,8 @@ mod tests { // Assert that we do not have any push event. assert!(!check_push_events(&mut client)); - // Check that the push has been closed, e.g. calling cancel_push should return InvalidStreamId. + // Check that the push has been closed, e.g. calling cancel_push should return + // InvalidStreamId. assert_eq!(client.cancel_push(0), Err(Error::InvalidStreamId)); // Check that the push has been canceled by the client. @@ -5633,7 +5731,8 @@ mod tests { // Assert that we do not have any push event. assert!(!check_push_events(&mut client)); - // Check that the push has been closed, e.g. calling cancel_push should return InvalidStreamId. + // Check that the push has been closed, e.g. calling cancel_push should return + // InvalidStreamId. assert_eq!(client.cancel_push(0), Err(Error::InvalidStreamId)); // Check that the push has been canceled by the client. @@ -5661,7 +5760,8 @@ mod tests { // Assert that we do not have any push event. assert!(!check_push_events(&mut client)); - // Check that the push has been closed, e.g. calling cancel_push should return InvalidStreamId. + // Check that the push has been closed, e.g. calling cancel_push should return + // InvalidStreamId. assert_eq!(client.cancel_push(0), Err(Error::InvalidStreamId)); // Check that the push has been canceled by the client. @@ -5674,7 +5774,8 @@ mod tests { assert_eq!(client.state(), Http3State::Connected); } - // Test a push stream reset after a new PUSH_PROMISE or/and push stream. The events will be ignored. + // Test a push stream reset after a new PUSH_PROMISE or/and push stream. The events will be + // ignored. #[test] fn cancel_push_stream_after_push_promise_and_push_stream() { // Connect and send a request @@ -5689,13 +5790,14 @@ mod tests { .conn .stream_reset_send(push_stream_id, Error::HttpRequestCancelled.code()) .unwrap(); - let out = server.conn.process(None, now()).dgram(); - client.process(out, now()); + let out = server.conn.process(None, now()); + client.process(out.as_dgram_ref(), now()); // Assert that we do not have any push event. assert!(!check_push_events(&mut client)); - // Check that the push has been closed, e.g. calling cancel_push should return InvalidStreamId. + // Check that the push has been closed, e.g. calling cancel_push should return + // InvalidStreamId. assert_eq!(client.cancel_push(0), Err(Error::InvalidStreamId)); assert_eq!(client.state(), Http3State::Connected); @@ -5715,15 +5817,16 @@ mod tests { .conn .stream_reset_send(push_stream_id, Error::HttpRequestCancelled.code()) .unwrap(); - let out = server.conn.process(None, now()).dgram(); - client.process(out, now()); + let out = server.conn.process(None, now()); + client.process(out.as_dgram_ref(), now()); send_push_promise_and_exchange_packets(&mut client, &mut server, request_stream_id, 0); // Assert that we do not have any push event. assert!(!check_push_events(&mut client)); - // Check that the push has been closed, e.g. calling cancel_push should return InvalidStreamId. + // Check that the push has been closed, e.g. calling cancel_push should return + // InvalidStreamId. assert_eq!(client.cancel_push(0), Err(Error::InvalidStreamId)); assert_eq!(client.state(), Http3State::Connected); @@ -5742,13 +5845,15 @@ mod tests { // Assert that we do not have any push event. assert!(!check_push_events(&mut client)); - // Check that the push has been closed, e.g. calling cancel_push should return InvalidStreamId. + // Check that the push has been closed, e.g. calling cancel_push should return + // InvalidStreamId. assert_eq!(client.cancel_push(0), Err(Error::InvalidStreamId)); assert_eq!(client.state(), Http3State::Connected); } - // Test that push_promise and push data events will be removed after application calls cancel_push. + // Test that push_promise and push data events will be removed after application calls + // cancel_push. #[test] fn app_cancel_push_after_push_promise_and_push_stream() { // Connect and send a request @@ -5759,13 +5864,14 @@ mod tests { send_push_data_and_exchange_packets(&mut client, &mut server, 0, false); assert!(client.cancel_push(0).is_ok()); - let out = client.process(None, now()).dgram(); - mem::drop(server.conn.process(out, now())); + let out = client.process(None, now()); + mem::drop(server.conn.process(out.as_dgram_ref(), now())); // Assert that we do not have any push event. assert!(!check_push_events(&mut client)); - // Check that the push has been closed, e.g. calling cancel_push should return InvalidStreamId. + // Check that the push has been closed, e.g. calling cancel_push should return + // InvalidStreamId. assert_eq!(client.cancel_push(0), Err(Error::InvalidStreamId)); // Check that the push has been canceled by the client. @@ -5789,15 +5895,16 @@ mod tests { send_push_data_and_exchange_packets(&mut client, &mut server, 0, false); assert!(client.cancel_push(0).is_ok()); - let out = client.process(None, now()).dgram(); - mem::drop(server.conn.process(out, now())); + let out = client.process(None, now()); + mem::drop(server.conn.process(out.as_dgram_ref(), now())); send_push_promise_and_exchange_packets(&mut client, &mut server, request_stream_id, 0); // Assert that we do not have any push event. assert!(!check_push_events(&mut client)); - // Check that the push has been closed, e.g. calling cancel_push should return InvalidStreamId. + // Check that the push has been closed, e.g. calling cancel_push should return + // InvalidStreamId. assert_eq!(client.cancel_push(0), Err(Error::InvalidStreamId)); // Check that the push has been canceled by the client. @@ -5831,7 +5938,7 @@ mod tests { .send_encoder_updates(&mut server.conn) .unwrap(); let out = server.conn.process(None, now()); - mem::drop(client.process(out.dgram(), now())); + mem::drop(client.process(out.as_dgram_ref(), now())); } fn setup_server_side_encoder(client: &mut Http3Client, server: &mut TestServer) { @@ -5879,7 +5986,8 @@ mod tests { header_block: encoded_headers.to_vec(), }; - // Send the encoder instructions, but delay them so that the stream is blocked on decoding headers. + // Send the encoder instructions, but delay them so that the stream is blocked on decoding + // headers. let encoder_inst_pkt = server.conn.process(None, now()).dgram(); assert!(encoder_inst_pkt.is_some()); @@ -5903,7 +6011,7 @@ mod tests { assert!(!check_push_events(&mut client)); // Let client receive the encoder instructions. - let _out = client.process(encoder_inst_pkt, now()); + let _out = client.process(encoder_inst_pkt.as_ref(), now()); // PushPromise is blocked wathing for encoder instructions. assert!(check_push_events(&mut client)); @@ -5943,7 +6051,7 @@ mod tests { assert!(check_data_readable(&mut client)); // Let client receive the encoder instructions. - let _out = client.process(encoder_inst_pkt, now()); + let _out = client.process(encoder_inst_pkt.as_ref(), now()); // PushPromise is blocked wathing for encoder instructions. assert!(check_push_events(&mut client)); @@ -5985,7 +6093,7 @@ mod tests { assert!(check_header_ready(&mut client)); // Let client receive the encoder instructions. - let _out = client.process(encoder_inst_pkt, now()); + let _out = client.process(encoder_inst_pkt.as_ref(), now()); // PushPromise is blocked wathing for encoder instructions. assert!(check_push_events(&mut client)); @@ -6006,7 +6114,7 @@ mod tests { .send_and_insert(&mut server.conn, b"content-length", b"1234") .unwrap(); let encoder_inst_pkt1 = server.conn.process(None, now()).dgram(); - let _out = client.process(encoder_inst_pkt1, now()); + let _out = client.process(encoder_inst_pkt1.as_ref(), now()); // Send a PushPromise that is blocked until encoder_inst_pkt2 is process by the client. let encoder_inst_pkt2 = @@ -6041,7 +6149,7 @@ mod tests { assert!(!check_header_ready(&mut client)); // Let client receive the encoder instructions. - let _out = client.process(encoder_inst_pkt2, now()); + let _out = client.process(encoder_inst_pkt2.as_ref(), now()); // The response headers are blocked. assert!(check_header_ready_and_push_promise(&mut client)); @@ -6114,12 +6222,12 @@ mod tests { assert!(!check_header_ready(&mut client)); // Let client receive the encoder instructions. - let _out = client.process(encoder_inst_pkt1, now()); + let _out = client.process(encoder_inst_pkt1.as_ref(), now()); assert!(check_push_events(&mut client)); // Let client receive the encoder instructions. - let _out = client.process(encoder_inst_pkt2, now()); + let _out = client.process(encoder_inst_pkt2.as_ref(), now()); assert!(check_header_ready_and_push_promise(&mut client)); } @@ -6154,7 +6262,7 @@ mod tests { .unwrap(); let out = client.process(None, now()); - mem::drop(server.conn.process(out.dgram(), now())); + mem::drop(server.conn.process(out.as_dgram_ref(), now())); // Check that encoder got stream_canceled instruction. let mut inst = [0_u8; 100]; let (amount, fin) = server @@ -6218,7 +6326,7 @@ mod tests { ); // Now read headers. - mem::drop(client.process(encoder_insts.dgram(), now())); + mem::drop(client.process(encoder_insts.as_dgram_ref(), now())); } #[test] @@ -6229,7 +6337,7 @@ mod tests { mem::drop(client.cancel_fetch(request_stream_id, Error::HttpRequestCancelled.code())); assert_eq!(server.encoder.borrow_mut().stats().stream_cancelled_recv, 0); let out = client.process(None, now()); - mem::drop(server.conn.process(out.dgram(), now())); + mem::drop(server.conn.process(out.as_dgram_ref(), now())); mem::drop(server.encoder_receiver.receive(&mut server.conn)); assert_eq!(server.encoder.borrow_mut().stats().stream_cancelled_recv, 1); } @@ -6277,8 +6385,8 @@ mod tests { .unwrap(); assert_eq!(server.encoder.borrow_mut().stats().stream_cancelled_recv, 0); let out = server.conn.process(None, now()); - let out = client.process(out.dgram(), now()); - mem::drop(server.conn.process(out.dgram(), now())); + let out = client.process(out.as_dgram_ref(), now()); + mem::drop(server.conn.process(out.as_dgram_ref(), now())); mem::drop(server.encoder_receiver.receive(&mut server.conn)); assert_eq!(server.encoder.borrow_mut().stats().stream_cancelled_recv, 1); } @@ -6314,7 +6422,7 @@ mod tests { assert_eq!(server.encoder.borrow_mut().stats().stream_cancelled_recv, 0); let out = client.process(None, now()); - mem::drop(server.conn.process(out.dgram(), now())); + mem::drop(server.conn.process(out.as_dgram_ref(), now())); mem::drop(server.encoder_receiver.receive(&mut server.conn).unwrap()); assert_eq!(server.encoder.borrow_mut().stats().stream_cancelled_recv, 1); } @@ -6338,7 +6446,7 @@ mod tests { ); // Exchange encoder instructions - mem::drop(client.process(encoder_instruct, now())); + mem::drop(client.process(encoder_instruct.as_ref(), now())); let header_ready_event = |e| matches!(e, Http3ClientEvent::HeaderReady { .. }); assert!(client.events().any(header_ready_event)); @@ -6351,7 +6459,7 @@ mod tests { assert_eq!(server.encoder.borrow_mut().stats().stream_cancelled_recv, 0); let out = client.process(None, now()); - mem::drop(server.conn.process(out.dgram(), now())); + mem::drop(server.conn.process(out.as_dgram_ref(), now())); mem::drop(server.encoder_receiver.receive(&mut server.conn).unwrap()); assert_eq!(server.encoder.borrow_mut().stats().stream_cancelled_recv, 0); } @@ -6377,7 +6485,7 @@ mod tests { // Send the encoder instructions. let out = server.conn.process(None, now()); - mem::drop(client.process(out.dgram(), now())); + mem::drop(client.process(out.as_dgram_ref(), now())); // Send PushPromise that will be blocked waiting for decoder instructions. mem::drop( @@ -6407,7 +6515,7 @@ mod tests { .unwrap(); let out = client.process(None, now()); - mem::drop(server.conn.process(out.dgram(), now())); + mem::drop(server.conn.process(out.as_dgram_ref(), now())); mem::drop(server.encoder_receiver.receive(&mut server.conn).unwrap()); assert_eq!(server.encoder.borrow_mut().stats().stream_cancelled_recv, 1); } @@ -6421,7 +6529,7 @@ mod tests { .unwrap(); assert_eq!(server.encoder.borrow_mut().stats().stream_cancelled_recv, 0); let out = client.process(None, now()); - mem::drop(server.conn.process(out.dgram(), now())); + mem::drop(server.conn.process(out.as_dgram_ref(), now())); mem::drop(server.encoder_receiver.receive(&mut server.conn).unwrap()); assert_eq!(server.encoder.borrow_mut().stats().stream_cancelled_recv, 0); } @@ -6474,7 +6582,7 @@ mod tests { assert!(!client.events().any(header_ready_event)); // Now make the encoder instructions available. - mem::drop(client.process(encoder_insts.dgram(), now())); + mem::drop(client.process(encoder_insts.as_dgram_ref(), now())); // Header blocks for both streams should be ready. let mut count_responses = 0; @@ -6518,7 +6626,7 @@ mod tests { let sent = server.conn.stream_send(control_stream, enc.as_ref()); assert_eq!(sent, Ok(4)); let out = server.conn.process(None, now()); - client.process(out.dgram(), now()); + client.process(out.as_dgram_ref(), now()); assert_closed(&client, &Error::HttpSettings); } } @@ -6618,8 +6726,8 @@ mod tests { } ); - let out = client.process(None, now()).dgram(); - mem::drop(server.conn.process(out, now())); + let out = client.process(None, now()); + mem::drop(server.conn.process(out.as_dgram_ref(), now())); // Check that server has received a reset. let stop_sending_event = |e| { @@ -6751,8 +6859,8 @@ mod tests { assert!(client.events().any(push_reset_event)); - let out = client.process(None, now()).dgram(); - mem::drop(server.conn.process(out, now())); + let out = client.process(None, now()); + mem::drop(server.conn.process(out.as_dgram_ref(), now())); // Check that server has received a reset. let stop_sending_event = |e| { @@ -6766,7 +6874,7 @@ mod tests { fn handshake_client_error(client: &mut Http3Client, server: &mut TestServer, error: &Error) { let out = handshake_only(client, server); - client.process(out.dgram(), now()); + client.process(out.as_dgram_ref(), now()); assert_closed(client, error); } @@ -6927,14 +7035,14 @@ mod tests { let is_done = |c: &Http3Client| matches!(c.state(), Http3State::Connected); while !is_done(&mut client) { maybe_authenticate(&mut client); - datagram = client.process(datagram, now()).dgram(); - datagram = server.process(datagram, now()).dgram(); + datagram = client.process(datagram.as_ref(), now()).dgram(); + datagram = server.process(datagram.as_ref(), now()).dgram(); } // exchange qpack settings, server will send a token as well. - datagram = client.process(datagram, now()).dgram(); - datagram = server.process(datagram, now()).dgram(); - mem::drop(client.process(datagram, now()).dgram()); + datagram = client.process(datagram.as_ref(), now()).dgram(); + datagram = server.process(datagram.as_ref(), now()).dgram(); + mem::drop(client.process(datagram.as_ref(), now()).dgram()); client .events() @@ -6988,15 +7096,15 @@ mod tests { // Exchange packets until header-ack is received. // These many packet exchange is needed, to get a header-ack. // TODO this may be optimize at Http3Server. - let out = client.process(None, now()).dgram(); - let out = server.process(out, now()).dgram(); - let out = client.process(out, now()).dgram(); - let out = server.process(out, now()).dgram(); - let out = client.process(out, now()).dgram(); - let out = server.process(out, now()).dgram(); - let out = client.process(out, now()).dgram(); - let out = server.process(out, now()).dgram(); - mem::drop(client.process(out, now())); + let out = client.process(None, now()); + let out = server.process(out.as_dgram_ref(), now()); + let out = client.process(out.as_dgram_ref(), now()); + let out = server.process(out.as_dgram_ref(), now()); + let out = client.process(out.as_dgram_ref(), now()); + let out = server.process(out.as_dgram_ref(), now()); + let out = client.process(out.as_dgram_ref(), now()); + let out = server.process(out.as_dgram_ref(), now()); + mem::drop(client.process(out.as_dgram_ref(), now())); // The header ack for the first request has been received. assert_eq!(client.qpack_encoder_stats().header_acks_recv, 1); @@ -7052,7 +7160,7 @@ mod tests { _ = server.conn.stream_send(push_stream_id, &[0]).unwrap(); server.conn.stream_close_send(push_stream_id).unwrap(); let out = server.conn.process(None, now()); - client.process(out.dgram(), now()); + client.process(out.as_dgram_ref(), now()); assert_closed(&client, &Error::HttpGeneralProtocol); } @@ -7076,21 +7184,22 @@ mod tests { let md_before = server.conn.stats().frame_tx.max_data; // sending the http request and most most of the request data - let out = client.process(None, now()).dgram(); - let out = server.conn.process(out, now()).dgram(); + let out = client.process(None, now()); + let out = server.conn.process(out.as_dgram_ref(), now()); // the server responses with an ack, but the max_data didn't change assert_eq!(md_before, server.conn.stats().frame_tx.max_data); - let out = client.process(out, now()).dgram(); - let out = server.conn.process(out, now()).dgram(); + let out = client.process(out.as_dgram_ref(), now()); + let out = server.conn.process(out.as_dgram_ref(), now()); // the server increased the max_data during the second read if that isn't the case - // in the future and therefore this asserts fails, the request data on stream 0 could be read - // to cause a max_update frame + // in the future and therefore this asserts fails, the request data on stream 0 could be + // read to cause a max_update frame assert_eq!(md_before + 1, server.conn.stats().frame_tx.max_data); - // make sure that the server didn't receive a priority_update on client control stream (stream_id 2) yet + // make sure that the server didn't receive a priority_update on client control stream + // (stream_id 2) yet let mut buf = [0; 32]; assert_eq!( server.conn.stream_recv(StreamId::new(2), &mut buf), @@ -7098,8 +7207,10 @@ mod tests { ); // the client now sends the priority update - let out = client.process(out, now()).dgram(); - server.conn.process_input(out.unwrap(), now()); + let out = client.process(out.as_dgram_ref(), now()); + server + .conn + .process_input(out.as_dgram_ref().unwrap(), now()); // check that the priority_update arrived at the client control stream let num_read = server.conn.stream_recv(StreamId::new(2), &mut buf).unwrap(); @@ -7127,7 +7238,8 @@ mod tests { header_block: encoded_headers.to_vec(), }; - // Send the encoder instructions, but delay them so that the stream is blocked on decoding headers. + // Send the encoder instructions, but delay them so that the stream is blocked on decoding + // headers. let encoder_inst_pkt = server.conn.process(None, now()); // Send response @@ -7145,7 +7257,7 @@ mod tests { ); // Let client receive the encoder instructions. - client.process_input(encoder_inst_pkt.dgram().unwrap(), now()); + client.process_input(encoder_inst_pkt.as_dgram_ref().unwrap(), now()); let reset_event = |e| matches!(e, Http3ClientEvent::Reset { stream_id, .. } if stream_id == request_stream_id); assert!(client.events().any(reset_event)); diff --git a/third_party/rust/neqo-http3/src/connection_server.rs b/third_party/rust/neqo-http3/src/connection_server.rs index c8cab52dd02d..097209a2269a 100644 --- a/third_party/rust/neqo-http3/src/connection_server.rs +++ b/third_party/rust/neqo-http3/src/connection_server.rs @@ -4,21 +4,22 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use crate::connection::{Http3Connection, Http3State, WebTransportSessionAcceptAction}; -use crate::frames::HFrame; -use crate::recv_message::{RecvMessage, RecvMessageInfo}; -use crate::send_message::SendMessage; -use crate::server_connection_events::{Http3ServerConnEvent, Http3ServerConnEvents}; -use crate::{ - Error, Http3Parameters, Http3StreamType, NewStreamType, Priority, PriorityHandler, - ReceiveOutput, Res, -}; +use std::{rc::Rc, time::Instant}; + use neqo_common::{event::Provider, qdebug, qinfo, qtrace, Header, MessageType, Role}; use neqo_transport::{ AppError, Connection, ConnectionEvent, DatagramTracking, StreamId, StreamType, }; -use std::rc::Rc; -use std::time::Instant; + +use crate::{ + connection::{Http3Connection, Http3State, WebTransportSessionAcceptAction}, + frames::HFrame, + recv_message::{RecvMessage, RecvMessageInfo}, + send_message::SendMessage, + server_connection_events::{Http3ServerConnEvent, Http3ServerConnEvents}, + Error, Http3Parameters, Http3StreamType, NewStreamType, Priority, PriorityHandler, + ReceiveOutput, Res, +}; #[derive(Debug)] pub struct Http3ServerHandler { @@ -48,12 +49,15 @@ impl Http3ServerHandler { } /// Supply a response for a request. + /// /// # Errors + /// /// `InvalidStreamId` if the stream does not exist, /// `AlreadyClosed` if the stream has already been closed. - /// `TransportStreamDoesNotExist` if the transport stream does not exist (this may happen if `process_output` - /// has not been called when needed, and HTTP3 layer has not picked up the info that the stream has been closed.) - /// `InvalidInput` if an empty buffer has been supplied. + /// `TransportStreamDoesNotExist` if the transport stream does not exist (this may happen if + /// `process_output` has not been called when needed, and HTTP3 layer has not picked up the + /// info that the stream has been closed.) `InvalidInput` if an empty buffer has been + /// supplied. pub(crate) fn send_data( &mut self, stream_id: StreamId, @@ -89,7 +93,9 @@ impl Http3ServerHandler { } /// This is called when application is done sending a request. + /// /// # Errors + /// /// An error will be returned if stream does not exist. pub fn stream_close_send(&mut self, stream_id: StreamId, conn: &mut Connection) -> Res<()> { qinfo!([self], "Close sending side stream={}.", stream_id); @@ -101,7 +107,9 @@ impl Http3ServerHandler { /// An application may reset a stream(request). /// Both sides, sending and receiving side, will be closed. + /// /// # Errors + /// /// An error will be return if a stream does not exist. pub fn cancel_fetch( &mut self, @@ -154,11 +162,14 @@ impl Http3ServerHandler { } /// Close `WebTransport` cleanly + /// /// # Errors + /// /// `InvalidStreamId` if the stream does not exist, - /// `TransportStreamDoesNotExist` if the transport stream does not exist (this may happen if `process_output` - /// has not been called when needed, and HTTP3 layer has not picked up the info that the stream has been closed.) - /// `InvalidInput` if an empty buffer has been supplied. + /// `TransportStreamDoesNotExist` if the transport stream does not exist (this may happen if + /// `process_output` has not been called when needed, and HTTP3 layer has not picked up the + /// info that the stream has been closed.) `InvalidInput` if an empty buffer has been + /// supplied. pub fn webtransport_close_session( &mut self, conn: &mut Connection, @@ -354,7 +365,7 @@ impl Http3ServerHandler { } HFrame::PriorityUpdatePush { element_id, priority } => { // TODO: check if the element_id references a promised push stream or - // is greater than the maximum Push ID. + // is greater than the maximum Push ID. self.events.priority_update(StreamId::from(element_id), priority); Ok(()) } @@ -383,11 +394,13 @@ impl Http3ServerHandler { } } - /// Response data are read directly into a buffer supplied as a parameter of this function to avoid copying - /// data. + /// Response data are read directly into a buffer supplied as a parameter of this function to + /// avoid copying data. + /// /// # Errors - /// It returns an error if a stream does not exist or an error happen while reading a stream, e.g. - /// early close, protocol error, etc. + /// + /// It returns an error if a stream does not exist or an error happen while reading a stream, + /// e.g. early close, protocol error, etc. pub fn read_data( &mut self, conn: &mut Connection, diff --git a/third_party/rust/neqo-http3/src/control_stream_local.rs b/third_party/rust/neqo-http3/src/control_stream_local.rs index e6d63c3502ee..62676ee39198 100644 --- a/third_party/rust/neqo-http3/src/control_stream_local.rs +++ b/third_party/rust/neqo-http3/src/control_stream_local.rs @@ -4,12 +4,15 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use crate::frames::HFrame; -use crate::{BufferedStream, Http3StreamType, RecvStream, Res}; +use std::{ + collections::{HashMap, VecDeque}, + convert::TryFrom, +}; + use neqo_common::{qtrace, Encoder}; use neqo_transport::{Connection, StreamId, StreamType}; -use std::collections::{HashMap, VecDeque}; -use std::convert::TryFrom; + +use crate::{frames::HFrame, BufferedStream, Http3StreamType, RecvStream, Res}; pub const HTTP3_UNI_STREAM_TYPE_CONTROL: u64 = 0x0; diff --git a/third_party/rust/neqo-http3/src/control_stream_remote.rs b/third_party/rust/neqo-http3/src/control_stream_remote.rs index 7b42ed2b11c7..aef4b4c0a4d6 100644 --- a/third_party/rust/neqo-http3/src/control_stream_remote.rs +++ b/third_party/rust/neqo-http3/src/control_stream_remote.rs @@ -4,12 +4,16 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use crate::frames::{FrameReader, HFrame, StreamReaderConnectionWrapper}; -use crate::{CloseType, Error, Http3StreamType, ReceiveOutput, RecvStream, Res, Stream}; use neqo_common::qdebug; use neqo_transport::{Connection, StreamId}; -/// The remote control stream is responsible only for reading frames. The frames are handled by `Http3Connection`. +use crate::{ + frames::{FrameReader, HFrame, StreamReaderConnectionWrapper}, + CloseType, Error, Http3StreamType, ReceiveOutput, RecvStream, Res, Stream, +}; + +/// The remote control stream is responsible only for reading frames. The frames are handled by +/// `Http3Connection`. #[derive(Debug)] pub(crate) struct ControlStreamRemote { stream_id: StreamId, diff --git a/third_party/rust/neqo-http3/src/features/extended_connect/mod.rs b/third_party/rust/neqo-http3/src/features/extended_connect/mod.rs index 6be92dabbaca..77655833f7b1 100644 --- a/third_party/rust/neqo-http3/src/features/extended_connect/mod.rs +++ b/third_party/rust/neqo-http3/src/features/extended_connect/mod.rs @@ -9,15 +9,19 @@ pub(crate) mod webtransport_session; pub(crate) mod webtransport_streams; -use crate::client_events::Http3ClientEvents; -use crate::features::NegotiationState; -use crate::settings::{HSettingType, HSettings}; -use crate::{CloseType, Http3StreamInfo, Http3StreamType}; +use std::fmt::Debug; + use neqo_common::Header; use neqo_transport::{AppError, StreamId}; -use std::fmt::Debug; pub(crate) use webtransport_session::WebTransportSession; +use crate::{ + client_events::Http3ClientEvents, + features::NegotiationState, + settings::{HSettingType, HSettings}, + CloseType, Http3StreamInfo, Http3StreamType, +}; + #[derive(Debug, PartialEq, Eq, Clone)] pub enum SessionCloseReason { Error(AppError), diff --git a/third_party/rust/neqo-http3/src/features/extended_connect/tests/webtransport/datagrams.rs b/third_party/rust/neqo-http3/src/features/extended_connect/tests/webtransport/datagrams.rs index 1b9511b255dd..1c58596dd3aa 100644 --- a/third_party/rust/neqo-http3/src/features/extended_connect/tests/webtransport/datagrams.rs +++ b/third_party/rust/neqo-http3/src/features/extended_connect/tests/webtransport/datagrams.rs @@ -4,13 +4,17 @@ // 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, WebTransportRequest}; +use std::convert::TryFrom; + use neqo_common::Encoder; use neqo_transport::Error as TransportError; -use std::convert::TryFrom; + +use crate::{ + features::extended_connect::tests::webtransport::{ + wt_default_parameters, WtTest, DATAGRAM_SIZE, + }, + Error, Http3Parameters, WebTransportRequest, +}; const DGRAM: &[u8] = &[0, 100]; diff --git a/third_party/rust/neqo-http3/src/features/extended_connect/tests/webtransport/mod.rs b/third_party/rust/neqo-http3/src/features/extended_connect/tests/webtransport/mod.rs index 4ac5f72b0ffe..51dc47e4c14a 100644 --- a/third_party/rust/neqo-http3/src/features/extended_connect/tests/webtransport/mod.rs +++ b/third_party/rust/neqo-http3/src/features/extended_connect/tests/webtransport/mod.rs @@ -8,7 +8,15 @@ mod datagrams; mod negotiation; mod sessions; mod streams; +use std::{cell::RefCell, rc::Rc, time::Duration}; + use neqo_common::event::Provider; +use neqo_crypto::AuthenticationStatus; +use neqo_transport::{ConnectionParameters, StreamId, StreamType}; +use test_fixture::{ + addr, anti_replay, fixture_init, now, CountingConnectionIdGenerator, DEFAULT_ALPN_H3, + DEFAULT_KEYS, DEFAULT_SERVER_NAME, +}; use crate::{ features::extended_connect::SessionCloseReason, Error, Header, Http3Client, Http3ClientEvent, @@ -16,16 +24,6 @@ use crate::{ RecvStreamStats, SendStreamStats, WebTransportEvent, WebTransportRequest, WebTransportServerEvent, WebTransportSessionAcceptAction, }; -use neqo_crypto::AuthenticationStatus; -use neqo_transport::{ConnectionParameters, StreamId, StreamType}; -use std::cell::RefCell; -use std::rc::Rc; -use std::time::Duration; - -use test_fixture::{ - addr, anti_replay, fixture_init, now, CountingConnectionIdGenerator, DEFAULT_ALPN_H3, - DEFAULT_KEYS, DEFAULT_SERVER_NAME, -}; const DATAGRAM_SIZE: u64 = 1200; @@ -64,8 +62,8 @@ pub fn default_http3_server(server_params: Http3Parameters) -> Http3Server { 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(); + out = client.process(out.as_ref(), now()).dgram(); + out = server.process(out.as_ref(), now()).dgram(); if out.is_none() { break; } @@ -78,28 +76,28 @@ fn connect_with(client: &mut Http3Client, server: &mut Http3Server) { 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()); + let out = server.process(out.as_dgram_ref(), now()); + let out = client.process(out.as_dgram_ref(), now()); + let out = server.process(out.as_dgram_ref(), now()); assert!(out.as_dgram_ref().is_none()); let authentication_needed = |e| matches!(e, Http3ClientEvent::AuthenticationNeeded); assert!(client.events().any(authentication_needed)); client.authenticated(AuthenticationStatus::Ok, now()); - let out = client.process(out.dgram(), now()); + let out = client.process(out.as_dgram_ref(), now()); let connected = |e| matches!(e, Http3ClientEvent::StateChange(Http3State::Connected)); assert!(client.events().any(connected)); assert_eq!(client.state(), Http3State::Connected); // Exchange H3 setttings - let out = server.process(out.dgram(), now()); - let out = client.process(out.dgram(), now()); - let out = server.process(out.dgram(), now()); - let out = client.process(out.dgram(), now()); - let out = server.process(out.dgram(), now()); - std::mem::drop(client.process(out.dgram(), now())); + let out = server.process(out.as_dgram_ref(), now()); + let out = client.process(out.as_dgram_ref(), now()); + let out = server.process(out.as_dgram_ref(), now()); + let out = client.process(out.as_dgram_ref(), now()); + let out = server.process(out.as_dgram_ref(), now()); + std::mem::drop(client.process(out.as_dgram_ref(), now())); } fn connect( @@ -201,10 +199,10 @@ impl WtTest { let mut now = now(); loop { now += RTT / 2; - out = self.client.process(out, now).dgram(); + out = self.client.process(out.as_ref(), now).dgram(); let client_none = out.is_none(); now += RTT / 2; - out = self.server.process(out, now).dgram(); + out = self.server.process(out.as_ref(), now).dgram(); if client_none && out.is_none() { break; } diff --git a/third_party/rust/neqo-http3/src/features/extended_connect/tests/webtransport/negotiation.rs b/third_party/rust/neqo-http3/src/features/extended_connect/tests/webtransport/negotiation.rs index 23784e5609c9..27f669861def 100644 --- a/third_party/rust/neqo-http3/src/features/extended_connect/tests/webtransport/negotiation.rs +++ b/third_party/rust/neqo-http3/src/features/extended_connect/tests/webtransport/negotiation.rs @@ -4,17 +4,19 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::time::Duration; + +use neqo_common::{event::Provider, Encoder}; +use neqo_crypto::AuthenticationStatus; +use neqo_transport::{Connection, ConnectionError, StreamType}; +use test_fixture::{default_server_h3, now}; + 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| { @@ -86,7 +88,7 @@ fn zero_rtt( // exchange 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))); + std::mem::drop(client.process(out.as_dgram_ref(), now() + Duration::from_millis(250))); assert_eq!(client.state(), Http3State::Connected); let token = client .events() @@ -234,8 +236,8 @@ fn zero_rtt_wt_settings() { 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(); + out = client.process(out.as_ref(), now()).dgram(); + out = server.process(out.as_ref(), now()).dgram(); if out.is_none() { break; } diff --git a/third_party/rust/neqo-http3/src/features/extended_connect/tests/webtransport/sessions.rs b/third_party/rust/neqo-http3/src/features/extended_connect/tests/webtransport/sessions.rs index 65572a1c2a8e..5f929d0e4b58 100644 --- a/third_party/rust/neqo-http3/src/features/extended_connect/tests/webtransport/sessions.rs +++ b/third_party/rust/neqo-http3/src/features/extended_connect/tests/webtransport/sessions.rs @@ -4,19 +4,25 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use crate::features::extended_connect::tests::webtransport::{ - default_http3_client, default_http3_server, wt_default_parameters, WtTest, -}; -use crate::{ - features::extended_connect::SessionCloseReason, frames::WebTransportFrame, Error, Header, - Http3ClientEvent, Http3OrWebTransportStream, Http3Server, Http3ServerEvent, Http3State, - Priority, WebTransportEvent, WebTransportServerEvent, WebTransportSessionAcceptAction, -}; +use std::mem; + use neqo_common::{event::Provider, Encoder}; use neqo_transport::StreamType; -use std::mem; use test_fixture::now; +use crate::{ + features::extended_connect::{ + tests::webtransport::{ + default_http3_client, default_http3_server, wt_default_parameters, WtTest, + }, + SessionCloseReason, + }, + frames::WebTransportFrame, + Error, Header, Http3ClientEvent, Http3OrWebTransportStream, Http3Server, Http3ServerEvent, + Http3State, Priority, WebTransportEvent, WebTransportServerEvent, + WebTransportSessionAcceptAction, +}; + #[test] fn wt_session() { let mut wt = WtTest::new(); @@ -419,18 +425,18 @@ fn wt_close_session_cannot_be_sent_at_once() { Err(Error::InvalidStreamId) ); - let out = wt.server.process(None, now()).dgram(); - let out = wt.client.process(out, now()).dgram(); + let out = wt.server.process(None, now()); + let out = wt.client.process(out.as_dgram_ref(), now()); // Client has not received the full CloseSession frame and it can create more streams. let unidi_client = wt.create_wt_stream_client(wt_session.stream_id(), StreamType::UniDi); - let out = wt.server.process(out, now()).dgram(); - let out = wt.client.process(out, now()).dgram(); - let out = wt.server.process(out, now()).dgram(); - let out = wt.client.process(out, now()).dgram(); - let out = wt.server.process(out, now()).dgram(); - let _out = wt.client.process(out, now()).dgram(); + let out = wt.server.process(out.as_dgram_ref(), now()); + let out = wt.client.process(out.as_dgram_ref(), now()); + let out = wt.server.process(out.as_dgram_ref(), now()); + let out = wt.client.process(out.as_dgram_ref(), now()); + let out = wt.server.process(out.as_dgram_ref(), now()); + let _out = wt.client.process(out.as_dgram_ref(), now()); wt.check_events_after_closing_session_client( &[], diff --git a/third_party/rust/neqo-http3/src/features/extended_connect/tests/webtransport/streams.rs b/third_party/rust/neqo-http3/src/features/extended_connect/tests/webtransport/streams.rs index a50c45d5181a..b898dbb31e76 100644 --- a/third_party/rust/neqo-http3/src/features/extended_connect/tests/webtransport/streams.rs +++ b/third_party/rust/neqo-http3/src/features/extended_connect/tests/webtransport/streams.rs @@ -4,11 +4,15 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use crate::features::extended_connect::tests::webtransport::WtTest; -use crate::{features::extended_connect::SessionCloseReason, Error}; -use neqo_transport::StreamType; use std::mem; +use neqo_transport::StreamType; + +use crate::{ + features::extended_connect::{tests::webtransport::WtTest, SessionCloseReason}, + Error, +}; + #[test] fn wt_client_stream_uni() { const BUF_CLIENT: &[u8] = &[0; 10]; @@ -287,13 +291,17 @@ fn wt_server_stream_bidi_stop_sending() { // 1) Both sides of a bidirectional client stream are opened. // 2) A client unidirectional stream is opened. // 3) A client unidirectional stream has been closed and both sides consumed the closing info. -// 4) A client unidirectional stream has been closed, but only the server has consumed the closing info. -// 5) A client unidirectional stream has been closed, but only the client has consum the closing info. +// 4) A client unidirectional stream has been closed, but only the server has consumed the closing +// info. +// 5) A client unidirectional stream has been closed, but only the client has consum the closing +// info. // 6) Both sides of a bidirectional server stream are opened. // 7) A server unidirectional stream is opened. // 8) A server unidirectional stream has been closed and both sides consumed the closing info. -// 9) A server unidirectional stream has been closed, but only the server has consumed the closing info. -// 10) A server unidirectional stream has been closed, but only the client has consumed the closing info. +// 9) A server unidirectional stream has been closed, but only the server has consumed the closing +// info. +// 10) A server unidirectional stream has been closed, but only the client has consumed the closing +// info. // 11) Both sides of a bidirectional stream have been closed and consumed by both sides. // 12) Both sides of a bidirectional stream have been closed, but not consumed by both sides. // 13) Multiples open streams diff --git a/third_party/rust/neqo-http3/src/features/extended_connect/webtransport_session.rs b/third_party/rust/neqo-http3/src/features/extended_connect/webtransport_session.rs index c446fd3843ff..adbdf07e110a 100644 --- a/third_party/rust/neqo-http3/src/features/extended_connect/webtransport_session.rs +++ b/third_party/rust/neqo-http3/src/features/extended_connect/webtransport_session.rs @@ -6,6 +6,12 @@ #![allow(clippy::module_name_repetitions)] +use std::{any::Any, cell::RefCell, collections::BTreeSet, mem, rc::Rc}; + +use neqo_common::{qtrace, Encoder, Header, MessageType, Role}; +use neqo_qpack::{QPackDecoder, QPackEncoder}; +use neqo_transport::{streams::SendOrder, Connection, DatagramTracking, StreamId}; + use super::{ExtendedConnectEvents, ExtendedConnectType, SessionCloseReason}; use crate::{ frames::{FrameReader, StreamReaderRecvStreamWrapper, WebTransportFrame}, @@ -15,14 +21,6 @@ use crate::{ HttpRecvStreamEvents, Priority, PriorityHandler, ReceiveOutput, RecvStream, RecvStreamEvents, Res, SendStream, SendStreamEvents, Stream, }; -use neqo_common::{qtrace, Encoder, Header, MessageType, Role}; -use neqo_qpack::{QPackDecoder, QPackEncoder}; -use neqo_transport::{streams::SendOrder, Connection, DatagramTracking, StreamId}; -use std::any::Any; -use std::cell::RefCell; -use std::collections::BTreeSet; -use std::mem; -use std::rc::Rc; #[derive(Debug, PartialEq)] enum SessionState { @@ -100,6 +98,7 @@ impl WebTransportSession { } /// # Panics + /// /// This function is only called with `RecvStream` and `SendStream` that also implement /// the http specific functions and `http_stream()` will never return `None`. #[must_use] @@ -134,8 +133,11 @@ impl WebTransportSession { } /// # Errors + /// /// The function can only fail if supplied headers are not valid http headers. + /// /// # Panics + /// /// `control_stream_send` implements the http specific functions and `http_stream()` /// will never return `None`. pub fn send_request(&mut self, headers: &[Header], conn: &mut Connection) -> Res<()> { @@ -220,6 +222,7 @@ impl WebTransportSession { } /// # Panics + /// /// This cannot panic because headers are checked before this function called. pub fn maybe_check_headers(&mut self) { if SessionState::Negotiating != self.state { @@ -335,6 +338,7 @@ impl WebTransportSession { } /// # Errors + /// /// It may return an error if the frame is not correctly decoded. pub fn read_control_stream(&mut self, conn: &mut Connection) -> Res<()> { let (f, fin) = self @@ -373,8 +377,9 @@ impl WebTransportSession { } /// # Errors - /// Return an error if the stream was closed on the transport layer, but that information is not yet - /// consumed on the http/3 layer. + /// + /// Return an error if the stream was closed on the transport layer, but that information is not + /// yet consumed on the http/3 layer. pub fn close_session(&mut self, conn: &mut Connection, error: u32, message: &str) -> Res<()> { self.state = SessionState::Done; let close_frame = WebTransportFrame::CloseSession { @@ -399,6 +404,7 @@ impl WebTransportSession { } /// # Errors + /// /// Returns an error if the datagram exceeds the remote datagram size limit. pub fn send_datagram( &self, diff --git a/third_party/rust/neqo-http3/src/features/extended_connect/webtransport_streams.rs b/third_party/rust/neqo-http3/src/features/extended_connect/webtransport_streams.rs index ca918dce9e36..84dcd20618ac 100644 --- a/third_party/rust/neqo-http3/src/features/extended_connect/webtransport_streams.rs +++ b/third_party/rust/neqo-http3/src/features/extended_connect/webtransport_streams.rs @@ -4,15 +4,16 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::{cell::RefCell, rc::Rc}; + +use neqo_common::Encoder; +use neqo_transport::{Connection, RecvStreamStats, SendStreamStats, StreamId}; + use super::WebTransportSession; use crate::{ CloseType, Http3StreamInfo, Http3StreamType, ReceiveOutput, RecvStream, RecvStreamEvents, Res, SendStream, SendStreamEvents, Stream, }; -use neqo_common::Encoder; -use neqo_transport::{Connection, RecvStreamStats, SendStreamStats, StreamId}; -use std::cell::RefCell; -use std::rc::Rc; pub const WEBTRANSPORT_UNI_STREAM: u64 = 0x54; pub const WEBTRANSPORT_STREAM: u64 = 0x41; diff --git a/third_party/rust/neqo-http3/src/features/mod.rs b/third_party/rust/neqo-http3/src/features/mod.rs index 0e045ed80b41..34e21f50acbb 100644 --- a/third_party/rust/neqo-http3/src/features/mod.rs +++ b/third_party/rust/neqo-http3/src/features/mod.rs @@ -4,23 +4,24 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::{fmt::Debug, mem}; + +use neqo_common::qtrace; + use crate::{ client_events::Http3ClientEvents, settings::{HSettingType, HSettings}, }; -use neqo_common::qtrace; -use std::fmt::Debug; -use std::mem; pub mod extended_connect; /// States: /// - `Disable` - it is not turned on for this connection. -/// - `Negotiating` - the feature is enabled locally, but settings from the peer -/// have not been received yet. +/// - `Negotiating` - the feature is enabled locally, but settings from the peer have not been +/// received yet. /// - `Negotiated` - the settings have been received and both sides support the feature. -/// - `NegotiationFailed` - the settings have been received and the peer does not -/// support the feature. +/// - `NegotiationFailed` - the settings have been received and the peer does not support the +/// feature. #[derive(Debug)] pub enum NegotiationState { Disabled, diff --git a/third_party/rust/neqo-http3/src/frames/hframe.rs b/third_party/rust/neqo-http3/src/frames/hframe.rs index 28ce7608f9ab..83e69ba8949a 100644 --- a/third_party/rust/neqo-http3/src/frames/hframe.rs +++ b/third_party/rust/neqo-http3/src/frames/hframe.rs @@ -4,12 +4,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use crate::{frames::reader::FrameDecoder, settings::HSettings, Error, Priority, Res}; +use std::{fmt::Debug, io::Write}; + use neqo_common::{Decoder, Encoder}; use neqo_crypto::random; use neqo_transport::StreamId; -use std::fmt::Debug; -use std::io::Write; + +use crate::{frames::reader::FrameDecoder, settings::HSettings, Error, Priority, Res}; pub(crate) type HFrameType = u64; diff --git a/third_party/rust/neqo-http3/src/frames/reader.rs b/third_party/rust/neqo-http3/src/frames/reader.rs index 9d81f2c1c12e..5017c666a4cc 100644 --- a/third_party/rust/neqo-http3/src/frames/reader.rs +++ b/third_party/rust/neqo-http3/src/frames/reader.rs @@ -6,34 +6,39 @@ #![allow(clippy::module_name_repetitions)] -use crate::{Error, RecvStream, Res}; +use std::{convert::TryFrom, fmt::Debug}; + use neqo_common::{ hex_with_len, qtrace, Decoder, IncrementalDecoderBuffer, IncrementalDecoderIgnore, IncrementalDecoderUint, }; use neqo_transport::{Connection, StreamId}; -use std::convert::TryFrom; -use std::fmt::Debug; + +use crate::{Error, RecvStream, Res}; const MAX_READ_SIZE: usize = 4096; pub(crate) trait FrameDecoder { fn is_known_type(frame_type: u64) -> bool; /// # Errors + /// /// Returns `HttpFrameUnexpected` if frames is not alowed, i.e. is a `H3_RESERVED_FRAME_TYPES`. fn frame_type_allowed(_frame_type: u64) -> Res<()> { Ok(()) } + /// # Errors + /// /// If a frame cannot be properly decoded. fn decode(frame_type: u64, frame_len: u64, data: Option<&[u8]>) -> Res>; } 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 - /// consumed on the http/3 layer. + /// Return an error if the stream was closed on the transport layer, but that information is not + /// yet consumed on the http/3 layer. fn read_data(&mut self, buf: &mut [u8]) -> Res<(usize, bool)>; } @@ -50,6 +55,7 @@ impl<'a> StreamReaderConnectionWrapper<'a> { impl<'a> StreamReader for StreamReaderConnectionWrapper<'a> { /// # Errors + /// /// An error may happen while reading a stream, e.g. early close, protocol error, etc. fn read_data(&mut self, buf: &mut [u8]) -> Res<(usize, bool)> { let res = self.conn.stream_recv(self.stream_id, buf)?; @@ -70,6 +76,7 @@ impl<'a> StreamReaderRecvStreamWrapper<'a> { impl<'a> StreamReader for StreamReaderRecvStreamWrapper<'a> { /// # Errors + /// /// An error may happen while reading a stream, e.g. early close, protocol error, etc. fn read_data(&mut self, buf: &mut [u8]) -> Res<(usize, bool)> { self.recv_stream.read_data(self.conn, buf) @@ -146,7 +153,9 @@ impl FrameReader { } /// returns true if quic stream was closed. + /// /// # Errors + /// /// May return `HttpFrame` if a frame cannot be decoded. /// and `TransportStreamDoesNotExist` if `stream_recv` fails. pub fn receive>( @@ -186,6 +195,7 @@ impl FrameReader { } /// # Errors + /// /// May return `HttpFrame` if a frame cannot be decoded. fn consume>(&mut self, mut input: Decoder) -> Res> { match &mut self.state { diff --git a/third_party/rust/neqo-http3/src/frames/tests/hframe.rs b/third_party/rust/neqo-http3/src/frames/tests/hframe.rs index 54b7c94c8e3d..3da7e7fc3660 100644 --- a/third_party/rust/neqo-http3/src/frames/tests/hframe.rs +++ b/third_party/rust/neqo-http3/src/frames/tests/hframe.rs @@ -4,15 +4,16 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use neqo_common::{Decoder, Encoder}; +use neqo_transport::StreamId; +use test_fixture::fixture_init; + use super::enc_dec_hframe; use crate::{ frames::HFrame, settings::{HSetting, HSettingType, HSettings}, Priority, }; -use neqo_common::{Decoder, Encoder}; -use neqo_transport::StreamId; -use test_fixture::fixture_init; #[test] fn test_data_frame() { diff --git a/third_party/rust/neqo-http3/src/frames/tests/mod.rs b/third_party/rust/neqo-http3/src/frames/tests/mod.rs index 092b3039ec2e..33eea5497a75 100644 --- a/third_party/rust/neqo-http3/src/frames/tests/mod.rs +++ b/third_party/rust/neqo-http3/src/frames/tests/mod.rs @@ -4,15 +4,17 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use crate::frames::{ - reader::FrameDecoder, FrameReader, HFrame, StreamReaderConnectionWrapper, WebTransportFrame, -}; +use std::mem; + use neqo_common::Encoder; use neqo_crypto::AuthenticationStatus; use neqo_transport::StreamType; -use std::mem; use test_fixture::{default_client, default_server, now}; +use crate::frames::{ + reader::FrameDecoder, FrameReader, HFrame, StreamReaderConnectionWrapper, WebTransportFrame, +}; + #[allow(clippy::many_single_char_names)] pub(crate) fn enc_dec>(d: &Encoder, st: &str, remaining: usize) -> T { // For data, headers and push_promise we do not read all bytes from the buffer @@ -22,12 +24,12 @@ pub(crate) fn enc_dec>(d: &Encoder, st: &str, remaining: usiz let mut conn_c = default_client(); let mut conn_s = default_server(); let out = conn_c.process(None, now()); - let out = conn_s.process(out.dgram(), now()); - let out = conn_c.process(out.dgram(), now()); - mem::drop(conn_s.process(out.dgram(), now())); + let out = conn_s.process(out.as_dgram_ref(), now()); + let out = conn_c.process(out.as_dgram_ref(), now()); + mem::drop(conn_s.process(out.as_dgram_ref(), now())); conn_c.authenticated(AuthenticationStatus::Ok, now()); let out = conn_c.process(None, now()); - mem::drop(conn_s.process(out.dgram(), now())); + mem::drop(conn_s.process(out.as_dgram_ref(), now())); // create a stream let stream_id = conn_s.stream_create(StreamType::BiDi).unwrap(); @@ -38,7 +40,7 @@ pub(crate) fn enc_dec>(d: &Encoder, st: &str, remaining: usiz let buf = Encoder::from_hex(st); conn_s.stream_send(stream_id, buf.as_ref()).unwrap(); let out = conn_s.process(None, now()); - mem::drop(conn_c.process(out.dgram(), now())); + mem::drop(conn_c.process(out.as_dgram_ref(), now())); let (frame, fin) = fr .receive::(&mut StreamReaderConnectionWrapper::new( diff --git a/third_party/rust/neqo-http3/src/frames/tests/reader.rs b/third_party/rust/neqo-http3/src/frames/tests/reader.rs index f694e4dbe354..fed1477ba4c1 100644 --- a/third_party/rust/neqo-http3/src/frames/tests/reader.rs +++ b/third_party/rust/neqo-http3/src/frames/tests/reader.rs @@ -4,6 +4,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::{fmt::Debug, mem}; + +use neqo_common::Encoder; +use neqo_transport::{Connection, StreamId, StreamType}; +use test_fixture::{connect, now}; + use crate::{ frames::{ reader::FrameDecoder, FrameReader, HFrame, StreamReaderConnectionWrapper, WebTransportFrame, @@ -11,11 +17,6 @@ use crate::{ settings::{HSetting, HSettingType, HSettings}, Error, }; -use neqo_common::Encoder; -use neqo_transport::{Connection, StreamId, StreamType}; -use std::fmt::Debug; -use std::mem; -use test_fixture::{connect, now}; struct FrameReaderTest { pub fr: FrameReader, @@ -39,7 +40,7 @@ impl FrameReaderTest { fn process>(&mut self, v: &[u8]) -> Option { self.conn_s.stream_send(self.stream_id, v).unwrap(); let out = self.conn_s.process(None, now()); - mem::drop(self.conn_c.process(out.dgram(), now())); + mem::drop(self.conn_c.process(out.as_dgram_ref(), now())); let (frame, fin) = self .fr .receive::(&mut StreamReaderConnectionWrapper::new( @@ -230,12 +231,12 @@ fn test_reading_frame + PartialEq + Debug>( } let out = fr.conn_s.process(None, now()); - mem::drop(fr.conn_c.process(out.dgram(), now())); + mem::drop(fr.conn_c.process(out.as_dgram_ref(), now())); if let FrameReadingTestSend::DataThenFin = test_to_send { fr.conn_s.stream_close_send(fr.stream_id).unwrap(); let out = fr.conn_s.process(None, now()); - mem::drop(fr.conn_c.process(out.dgram(), now())); + mem::drop(fr.conn_c.process(out.as_dgram_ref(), now())); } let rv = fr.fr.receive::(&mut StreamReaderConnectionWrapper::new( @@ -478,11 +479,11 @@ fn test_frame_reading_when_stream_is_closed_before_sending_data() { fr.conn_s.stream_send(fr.stream_id, &[0x00]).unwrap(); let out = fr.conn_s.process(None, now()); - mem::drop(fr.conn_c.process(out.dgram(), now())); + mem::drop(fr.conn_c.process(out.as_dgram_ref(), now())); assert_eq!(Ok(()), fr.conn_c.stream_close_send(fr.stream_id)); let out = fr.conn_c.process(None, now()); - mem::drop(fr.conn_s.process(out.dgram(), now())); + mem::drop(fr.conn_s.process(out.as_dgram_ref(), now())); assert_eq!( Ok((None, true)), fr.fr @@ -501,11 +502,11 @@ fn test_wt_frame_reading_when_stream_is_closed_before_sending_data() { fr.conn_s.stream_send(fr.stream_id, &[0x00]).unwrap(); let out = fr.conn_s.process(None, now()); - mem::drop(fr.conn_c.process(out.dgram(), now())); + mem::drop(fr.conn_c.process(out.as_dgram_ref(), now())); assert_eq!(Ok(()), fr.conn_c.stream_close_send(fr.stream_id)); let out = fr.conn_c.process(None, now()); - mem::drop(fr.conn_s.process(out.dgram(), now())); + mem::drop(fr.conn_s.process(out.as_dgram_ref(), now())); assert_eq!( Ok((None, true)), fr.fr diff --git a/third_party/rust/neqo-http3/src/frames/wtframe.rs b/third_party/rust/neqo-http3/src/frames/wtframe.rs index b5f76161c586..deb7a026a00d 100644 --- a/third_party/rust/neqo-http3/src/frames/wtframe.rs +++ b/third_party/rust/neqo-http3/src/frames/wtframe.rs @@ -4,10 +4,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use crate::{frames::reader::FrameDecoder, Error, Res}; -use neqo_common::{Decoder, Encoder}; use std::convert::TryFrom; +use neqo_common::{Decoder, Encoder}; + +use crate::{frames::reader::FrameDecoder, Error, Res}; + pub(crate) type WebTransportFrameType = u64; const WT_FRAME_CLOSE_SESSION: WebTransportFrameType = 0x2843; diff --git a/third_party/rust/neqo-http3/src/headers_checks.rs b/third_party/rust/neqo-http3/src/headers_checks.rs index 7d679409ad41..9bf661c8fe67 100644 --- a/third_party/rust/neqo-http3/src/headers_checks.rs +++ b/third_party/rust/neqo-http3/src/headers_checks.rs @@ -6,10 +6,12 @@ #![allow(clippy::unused_unit)] // see https://github.com/Lymia/enumset/issues/44 -use crate::{Error, MessageType, Res}; +use std::convert::TryFrom; + use enumset::{enum_set, EnumSet, EnumSetType}; use neqo_common::Header; -use std::convert::TryFrom; + +use crate::{Error, MessageType, Res}; #[derive(EnumSetType, Debug)] enum PseudoHeaderState { @@ -45,7 +47,9 @@ impl TryFrom<(MessageType, &str)> for PseudoHeaderState { } /// Check whether the response is informational(1xx). +/// /// # Errors +/// /// Returns an error if response headers do not contain /// a status header or if the value of the header is 101 or cannot be parsed. pub fn is_interim(headers: &[Header]) -> Res { @@ -89,7 +93,9 @@ fn track_pseudo( /// Checks if request/response headers are well formed, i.e. contain /// allowed pseudo headers and in a right order, etc. +/// /// # Errors +/// /// Returns an error if headers are not well formed. pub fn headers_valid(headers: &[Header], message_type: MessageType) -> Res<()> { let mut method_value: Option<&str> = None; @@ -155,7 +161,9 @@ pub fn headers_valid(headers: &[Header], message_type: MessageType) -> Res<()> { /// Checks if trailers are well formed, i.e. pseudo headers are not /// allowed in trailers. +/// /// # Errors +/// /// Returns an error if trailers are not well formed. pub fn trailers_valid(headers: &[Header]) -> Res<()> { for header in headers { @@ -168,9 +176,10 @@ pub fn trailers_valid(headers: &[Header]) -> Res<()> { #[cfg(test)] mod tests { + use neqo_common::Header; + use super::headers_valid; use crate::MessageType; - use neqo_common::Header; fn create_connect_headers() -> Vec
{ vec![ diff --git a/third_party/rust/neqo-http3/src/lib.rs b/third_party/rust/neqo-http3/src/lib.rs index 76be301a8e04..635707ca7cb4 100644 --- a/third_party/rust/neqo-http3/src/lib.rs +++ b/third_party/rust/neqo-http3/src/lib.rs @@ -35,7 +35,7 @@ supported and can be enabled using [`Http3Parameters`](struct.Http3Parameters.ht 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) +[`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 @@ -160,14 +160,8 @@ mod server_events; mod settings; mod stream_type_reader; -use neqo_qpack::Error as QpackError; -pub use neqo_transport::{streams::SendOrder, Output, StreamId}; -use neqo_transport::{ - AppError, Connection, Error as TransportError, RecvStreamStats, SendStreamStats, -}; -use std::fmt::Debug; +use std::{any::Any, cell::RefCell, fmt::Debug, rc::Rc}; -use crate::priority::PriorityHandler; use buffered_send_stream::BufferedStream; pub use client_events::{Http3ClientEvent, WebTransportEvent}; pub use conn_params::Http3Parameters; @@ -177,23 +171,28 @@ use features::extended_connect::WebTransportSession; use frames::HFrame; pub use neqo_common::Header; use neqo_common::MessageType; +use neqo_qpack::Error as QpackError; +pub use neqo_transport::{streams::SendOrder, Output, StreamId}; +use neqo_transport::{ + AppError, Connection, Error as TransportError, RecvStreamStats, SendStreamStats, +}; pub use priority::Priority; pub use server::Http3Server; pub use server_events::{ Http3OrWebTransportStream, Http3ServerEvent, WebTransportRequest, WebTransportServerEvent, }; -use std::any::Any; -use std::cell::RefCell; -use std::rc::Rc; use stream_type_reader::NewStreamType; +use crate::priority::PriorityHandler; + type Res = Result; #[derive(Clone, Debug, PartialEq, Eq)] pub enum Error { HttpNoError, HttpGeneralProtocol, - HttpGeneralProtocolStream, //this is the same as the above but it should only close a stream not a connection. + HttpGeneralProtocolStream, /* this is the same as the above but it should only close a + * stream not a connection. */ // When using this error, you need to provide a value that is unique, which // will allow the specific error to be identified. This will be validated in CI. HttpInternal(u16), @@ -288,6 +287,7 @@ impl Error { } /// # Panics + /// /// On unexpected errors, in debug mode. #[must_use] pub fn map_stream_send_errors(err: &Error) -> Self { @@ -304,6 +304,7 @@ impl Error { } /// # Panics + /// /// On unexpected errors, in debug mode. #[must_use] pub fn map_stream_create_errors(err: &TransportError) -> Self { @@ -318,6 +319,7 @@ impl Error { } /// # Panics + /// /// On unexpected errors, in debug mode. #[must_use] pub fn map_stream_recv_errors(err: &Error) -> Self { @@ -345,8 +347,11 @@ impl Error { } /// # Errors - /// Any error is mapped to the indicated type. + /// + /// Any error is mapped to the indicated type. + /// /// # Panics + /// /// On internal errors, in debug mode. fn map_error(r: Result>, err: Self) -> Result { r.map_err(|e| { @@ -450,16 +455,23 @@ trait RecvStream: Stream { /// The stream reads data from the corresponding quic stream and returns `ReceiveOutput`. /// The function also returns true as the second parameter if the stream is done and /// could be forgotten, i.e. removed from all records. + /// /// # Errors + /// /// An error may happen while reading a stream, e.g. early close, protocol error, etc. fn receive(&mut self, conn: &mut Connection) -> Res<(ReceiveOutput, bool)>; + /// # Errors + /// /// An error may happen while reading a stream, e.g. early close, etc. fn reset(&mut self, close_type: CloseType) -> Res<()>; + /// The function allows an app to read directly from the quic stream. The function /// returns the number of bytes written into `buf` and true/false if the stream is /// completely done and can be forgotten, i.e. removed from all records. + /// /// # Errors + /// /// An error may happen while reading a stream, e.g. early close, protocol error, etc. fn read_data(&mut self, _conn: &mut Connection, _buf: &mut [u8]) -> Res<(usize, bool)> { Err(Error::InvalidStreamId) @@ -483,7 +495,9 @@ trait HttpRecvStream: RecvStream { /// This function is similar to the receive function and has the same output, i.e. /// a `ReceiveOutput` enum and bool. The bool is true if the stream is completely done /// and can be forgotten, i.e. removed from all records. + /// /// # Errors + /// /// An error may happen while reading a stream, e.g. early close, protocol error, etc. fn header_unblocked(&mut self, conn: &mut Connection) -> Res<(ReceiveOutput, bool)>; @@ -552,6 +566,7 @@ trait HttpRecvStreamEvents: RecvStreamEvents { trait SendStream: Stream { /// # Errors + /// /// Error my occur during sending data, e.g. protocol error, etc. fn send(&mut self, conn: &mut Connection) -> Res<()>; fn has_data_to_send(&self) -> bool; @@ -559,14 +574,19 @@ trait SendStream: Stream { fn done(&self) -> bool; fn set_sendorder(&mut self, conn: &mut Connection, sendorder: Option) -> Res<()>; fn set_fairness(&mut self, conn: &mut Connection, fairness: bool) -> Res<()>; + /// # Errors + /// /// Error my occur during sending data, e.g. protocol error, etc. fn send_data(&mut self, _conn: &mut Connection, _buf: &[u8]) -> Res; /// # Errors + /// /// It may happen that the transport stream is already close. This is unlikely. fn close(&mut self, conn: &mut Connection) -> Res<()>; + /// # Errors + /// /// It may happen that the transport stream is already close. This is unlikely. fn close_with_message( &mut self, @@ -576,6 +596,7 @@ trait SendStream: Stream { ) -> Res<()> { Err(Error::InvalidStreamId) } + /// This function is called when sending side is closed abruptly by the peer or /// the application. fn handle_stop_sending(&mut self, close_type: CloseType); @@ -584,6 +605,7 @@ trait SendStream: Stream { } /// # Errors + /// /// It may happen that the transport stream is already close. This is unlikely. fn send_data_atomic(&mut self, _conn: &mut Connection, _buf: &[u8]) -> Res<()> { Err(Error::InvalidStreamId) @@ -599,7 +621,9 @@ trait HttpSendStream: SendStream { /// This function is used to supply headers to a http message. The /// function is used for request headers, response headers, 1xx response and /// trailers. + /// /// # Errors + /// /// This can also return an error if the underlying stream is closed. fn send_headers(&mut self, headers: &[Header], conn: &mut Connection) -> Res<()>; fn set_new_listener(&mut self, _conn_events: Box) {} diff --git a/third_party/rust/neqo-http3/src/priority.rs b/third_party/rust/neqo-http3/src/priority.rs index 6a391de578ce..f2651d3bb593 100644 --- a/third_party/rust/neqo-http3/src/priority.rs +++ b/third_party/rust/neqo-http3/src/priority.rs @@ -1,8 +1,9 @@ -use crate::{frames::HFrame, Error, Header, Res}; +use std::{convert::TryFrom, fmt}; + use neqo_transport::StreamId; use sfv::{BareItem, Item, ListEntry, Parser}; -use std::convert::TryFrom; -use std::fmt; + +use crate::{frames::HFrame, Error, Header, Res}; #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct Priority { @@ -21,6 +22,7 @@ impl Default for Priority { impl Priority { /// # Panics + /// /// If an invalid urgency (>7 is given) #[must_use] pub fn new(urgency: u8, incremental: bool) -> Priority { @@ -44,9 +46,13 @@ impl Priority { } /// Constructs a priority from raw bytes (either a field value of frame content). + /// /// # Errors + /// /// When the contained syntax is invalid. + /// /// # Panics + /// /// Never, but the compiler is not smart enough to work that out. pub fn from_bytes(bytes: &[u8]) -> Res { let dict = Parser::parse_dictionary(bytes).map_err(|_| Error::HttpFrame)?; @@ -149,10 +155,10 @@ impl PriorityHandler { #[cfg(test)] mod test { - use crate::priority::PriorityHandler; - use crate::{HFrame, Priority}; use neqo_transport::StreamId; + use crate::{priority::PriorityHandler, HFrame, Priority}; + #[test] fn priority_updates_ignore_same() { let mut p = PriorityHandler::new(false, Priority::new(5, false)); @@ -183,7 +189,8 @@ mod test { let mut p = PriorityHandler::new(false, Priority::new(5, false)); assert!(p.maybe_update_priority(Priority::new(6, false))); assert!(p.maybe_update_priority(Priority::new(7, false))); - // updating two times with a different priority -> the last priority update should be in the next frame + // updating two times with a different priority -> the last priority update should be in the + // next frame let expected = HFrame::PriorityUpdateRequest { element_id: 4, priority: Priority::new(7, false), diff --git a/third_party/rust/neqo-http3/src/push_controller.rs b/third_party/rust/neqo-http3/src/push_controller.rs index 79ebab4efcad..c4591991ae22 100644 --- a/third_party/rust/neqo-http3/src/push_controller.rs +++ b/third_party/rust/neqo-http3/src/push_controller.rs @@ -3,28 +3,33 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use crate::client_events::{Http3ClientEvent, Http3ClientEvents}; -use crate::connection::Http3Connection; -use crate::frames::HFrame; -use crate::{CloseType, Error, Http3StreamInfo, HttpRecvStreamEvents, RecvStreamEvents, Res}; +use std::{ + cell::RefCell, + collections::VecDeque, + convert::TryFrom, + fmt::{Debug, Display}, + mem, + rc::Rc, + slice::SliceIndex, +}; + use neqo_common::{qerror, qinfo, qtrace, Header}; use neqo_transport::{Connection, StreamId}; -use std::cell::RefCell; -use std::collections::VecDeque; -use std::convert::TryFrom; -use std::fmt::Debug; -use std::fmt::Display; -use std::mem; -use std::rc::Rc; -use std::slice::SliceIndex; + +use crate::{ + client_events::{Http3ClientEvent, Http3ClientEvents}, + connection::Http3Connection, + frames::HFrame, + CloseType, Error, Http3StreamInfo, HttpRecvStreamEvents, RecvStreamEvents, Res, +}; /// `PushStates`: -/// `Init`: there is no push stream nor a push promise. This state is only used to keep track of opened and closed -/// push streams. +/// `Init`: there is no push stream nor a push promise. This state is only used to keep track of +/// opened and closed push streams. /// `PushPromise`: the push has only ever receive a pushpromise frame -/// `OnlyPushStream`: there is only a push stream. All push stream events, i.e. `PushHeaderReady` and -/// `PushDataReadable` will be delayed until a push promise is received (they are kept in -/// `events`). +/// `OnlyPushStream`: there is only a push stream. All push stream events, i.e. `PushHeaderReady` +/// and `PushDataReadable` will be delayed until a push promise is received +/// (they are kept in `events`). /// `Active`: there is a push steam and at least one push promise frame. /// `Close`: the push stream has been closed or reset already. #[derive(Debug, PartialEq, Clone)] @@ -93,9 +98,7 @@ impl ActivePushStreams { None | Some(PushState::Closed) => None, Some(s) => { let res = mem::replace(s, PushState::Closed); - while self.push_streams.get(0).is_some() - && *self.push_streams.get(0).unwrap() == PushState::Closed - { + while let Some(PushState::Closed) = self.push_streams.front() { self.push_streams.pop_front(); self.first_push_id += 1; } @@ -124,21 +127,22 @@ impl ActivePushStreams { /// `PushController` keeps information about push stream states. /// -/// A `PushStream` calls `add_new_push_stream` that may change the push state from Init to `OnlyPushStream` or from -/// `PushPromise` to `Active`. If a stream has already been closed `add_new_push_stream` returns false (the `PushStream` -/// will close the transport stream). +/// A `PushStream` calls `add_new_push_stream` that may change the push state from Init to +/// `OnlyPushStream` or from `PushPromise` to `Active`. If a stream has already been closed +/// `add_new_push_stream` returns false (the `PushStream` will close the transport stream). /// A `PushStream` calls `push_stream_reset` if the transport stream has been canceled. /// When a push stream is done it calls `close`. /// /// The `PushController` handles: -/// `PUSH_PROMISE` frame: frames may change the push state from Init to `PushPromise` and from `OnlyPushStream` to -/// `Active`. Frames for a closed steams are ignored. -/// `CANCEL_PUSH` frame: (`handle_cancel_push` will be called). If a push is in state `PushPromise` or `Active`, any -/// posted events will be removed and a `PushCanceled` event will be posted. If a push is in -/// state `OnlyPushStream` or `Active` the transport stream and the `PushStream` will be closed. -/// The frame will be ignored for already closed pushes. -/// Application calling cancel: the actions are similar to the `CANCEL_PUSH` frame. The difference is that -/// `PushCanceled` will not be posted and a `CANCEL_PUSH` frame may be sent. +/// `PUSH_PROMISE` frame: frames may change the push state from Init to `PushPromise` and from +/// `OnlyPushStream` to `Active`. Frames for a closed steams are ignored. +/// `CANCEL_PUSH` frame: (`handle_cancel_push` will be called). If a push is in state `PushPromise` +/// or `Active`, any posted events will be removed and a `PushCanceled` event +/// will be posted. If a push is in state `OnlyPushStream` or `Active` the +/// transport stream and the `PushStream` will be closed. The frame will be +/// ignored for already closed pushes. Application calling cancel: the actions are similar to the +/// `CANCEL_PUSH` frame. The difference is that `PushCanceled` will not +/// be posted and a `CANCEL_PUSH` frame may be sent. #[derive(Debug)] pub(crate) struct PushController { max_concurent_push: u64, @@ -147,8 +151,8 @@ pub(crate) struct PushController { // We keep a stream until the stream has been closed. push_streams: ActivePushStreams, // The keeps the next consecutive push_id that should be open. - // All push_id < next_push_id_to_open are in the push_stream lists. If they are not in the list they have - // been already closed. + // All push_id < next_push_id_to_open are in the push_stream lists. If they are not in the list + // they have been already closed. conn_events: Http3ClientEvents, } @@ -171,7 +175,9 @@ impl Display for PushController { impl PushController { /// A new `push_promise` has been received. + /// /// # Errors + /// /// `HttpId` if `push_id` greater than it is allowed has been received. pub fn new_push_promise( &mut self, @@ -340,8 +346,9 @@ impl PushController { match self.push_streams.get(push_id) { None => { qtrace!("Push has already been closed."); - // If we have some events for the push_id in the event queue, the caller still does not - // not know that the push has been closed. Otherwise return InvalidStreamId. + // If we have some events for the push_id in the event queue, the caller still does + // not not know that the push has been closed. Otherwise return + // InvalidStreamId. if self.conn_events.has_push(push_id) { self.conn_events.remove_events_for_push_id(push_id); Ok(()) diff --git a/third_party/rust/neqo-http3/src/qlog.rs b/third_party/rust/neqo-http3/src/qlog.rs index 84c13dad4337..c3a13fd19ff6 100644 --- a/third_party/rust/neqo-http3/src/qlog.rs +++ b/third_party/rust/neqo-http3/src/qlog.rs @@ -8,14 +8,13 @@ use std::convert::TryFrom; +use neqo_common::qlog::NeqoQlog; +use neqo_transport::StreamId; use qlog::{ self, events::{DataRecipient, EventData}, }; -use neqo_common::qlog::NeqoQlog; -use neqo_transport::StreamId; - pub fn h3_data_moved_up(qlog: &mut NeqoQlog, stream_id: StreamId, amount: usize) { qlog.add_event_data(|| { let ev_data = EventData::DataMoved(qlog::events::quic::DataMoved { diff --git a/third_party/rust/neqo-http3/src/qpack_decoder_receiver.rs b/third_party/rust/neqo-http3/src/qpack_decoder_receiver.rs index 3cdfdf74cd17..46b9ca590b85 100644 --- a/third_party/rust/neqo-http3/src/qpack_decoder_receiver.rs +++ b/third_party/rust/neqo-http3/src/qpack_decoder_receiver.rs @@ -4,11 +4,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use crate::{CloseType, Error, Http3StreamType, ReceiveOutput, RecvStream, Res, Stream}; +use std::{cell::RefCell, rc::Rc}; + use neqo_qpack::QPackDecoder; use neqo_transport::{Connection, StreamId}; -use std::cell::RefCell; -use std::rc::Rc; + +use crate::{CloseType, Error, Http3StreamType, ReceiveOutput, RecvStream, Res, Stream}; #[derive(Debug)] pub(crate) struct DecoderRecvStream { diff --git a/third_party/rust/neqo-http3/src/qpack_encoder_receiver.rs b/third_party/rust/neqo-http3/src/qpack_encoder_receiver.rs index efe234173f64..76c779bcf2c2 100644 --- a/third_party/rust/neqo-http3/src/qpack_encoder_receiver.rs +++ b/third_party/rust/neqo-http3/src/qpack_encoder_receiver.rs @@ -4,11 +4,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use crate::{CloseType, Error, Http3StreamType, ReceiveOutput, RecvStream, Res, Stream}; +use std::{cell::RefCell, rc::Rc}; + use neqo_qpack::QPackEncoder; use neqo_transport::{Connection, StreamId}; -use std::cell::RefCell; -use std::rc::Rc; + +use crate::{CloseType, Error, Http3StreamType, ReceiveOutput, RecvStream, Res, Stream}; #[derive(Debug)] pub(crate) struct EncoderRecvStream { diff --git a/third_party/rust/neqo-http3/src/recv_message.rs b/third_party/rust/neqo-http3/src/recv_message.rs index dd27c513373e..36e8f65b19e5 100644 --- a/third_party/rust/neqo-http3/src/recv_message.rs +++ b/third_party/rust/neqo-http3/src/recv_message.rs @@ -4,24 +4,22 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use crate::frames::{FrameReader, HFrame, StreamReaderConnectionWrapper, H3_FRAME_TYPE_HEADERS}; -use crate::push_controller::PushController; -use crate::{ - headers_checks::{headers_valid, is_interim}, - priority::PriorityHandler, - qlog, CloseType, Error, Http3StreamInfo, Http3StreamType, HttpRecvStream, HttpRecvStreamEvents, - MessageType, Priority, ReceiveOutput, RecvStream, Res, Stream, +use std::{ + any::Any, cell::RefCell, cmp::min, collections::VecDeque, convert::TryFrom, fmt::Debug, rc::Rc, }; + use neqo_common::{qdebug, qinfo, qtrace, Header}; use neqo_qpack::decoder::QPackDecoder; use neqo_transport::{Connection, StreamId}; -use std::any::Any; -use std::cell::RefCell; -use std::cmp::min; -use std::collections::VecDeque; -use std::convert::TryFrom; -use std::fmt::Debug; -use std::rc::Rc; + +use crate::{ + frames::{FrameReader, HFrame, StreamReaderConnectionWrapper, H3_FRAME_TYPE_HEADERS}, + headers_checks::{headers_valid, is_interim}, + priority::PriorityHandler, + push_controller::PushController, + qlog, CloseType, Error, Http3StreamInfo, Http3StreamType, HttpRecvStream, HttpRecvStreamEvents, + MessageType, Priority, ReceiveOutput, RecvStream, Res, Stream, +}; #[allow(clippy::module_name_repetitions)] pub(crate) struct RecvMessageInfo { @@ -348,7 +346,8 @@ impl RecvMessage { panic!("Stream readable after being closed!"); } RecvMessageState::ExtendedConnect => { - // Ignore read event, this request is waiting to be picked up by a new WebTransportSession + // Ignore read event, this request is waiting to be picked up by a new + // WebTransportSession break Ok(()); } }; diff --git a/third_party/rust/neqo-http3/src/request_target.rs b/third_party/rust/neqo-http3/src/request_target.rs index a58445b5d74a..28bc22ac2d73 100644 --- a/third_party/rust/neqo-http3/src/request_target.rs +++ b/third_party/rust/neqo-http3/src/request_target.rs @@ -7,6 +7,7 @@ #![allow(clippy::module_name_repetitions)] use std::fmt::{Debug, Formatter}; + use url::{ParseError, Url}; pub trait RequestTarget: Debug { @@ -58,7 +59,9 @@ pub trait AsRequestTarget<'x> { type Target: RequestTarget; type Error; /// Produce a `RequestTarget` that refers to `self`. + /// /// # Errors + /// /// This method can generate an error of type `Self::Error` /// if the conversion is unsuccessful. fn as_request_target(&'x self) -> Result; diff --git a/third_party/rust/neqo-http3/src/send_message.rs b/third_party/rust/neqo-http3/src/send_message.rs index 531f804937cb..96156938a0ca 100644 --- a/third_party/rust/neqo-http3/src/send_message.rs +++ b/third_party/rust/neqo-http3/src/send_message.rs @@ -4,21 +4,18 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use crate::frames::HFrame; -use crate::{ - headers_checks::{headers_valid, is_interim, trailers_valid}, - qlog, BufferedStream, CloseType, Error, Http3StreamInfo, Http3StreamType, HttpSendStream, Res, - SendStream, SendStreamEvents, Stream, -}; +use std::{any::Any, cell::RefCell, cmp::min, fmt::Debug, rc::Rc}; use neqo_common::{qdebug, qinfo, qtrace, Encoder, Header, MessageType}; use neqo_qpack::encoder::QPackEncoder; use neqo_transport::{streams::SendOrder, Connection, StreamId}; -use std::any::Any; -use std::cell::RefCell; -use std::cmp::min; -use std::fmt::Debug; -use std::rc::Rc; + +use crate::{ + frames::HFrame, + headers_checks::{headers_valid, is_interim, trailers_valid}, + qlog, BufferedStream, CloseType, Error, Http3StreamInfo, Http3StreamType, HttpSendStream, Res, + SendStream, SendStreamEvents, Stream, +}; const MAX_DATA_HEADER_SIZE_2: usize = (1 << 6) - 1; // Maximal amount of data with DATA frame header size 2 const MAX_DATA_HEADER_SIZE_2_LIMIT: usize = MAX_DATA_HEADER_SIZE_2 + 3; // 63 + 3 (size of the next buffer data frame header) @@ -134,6 +131,7 @@ impl SendMessage { } /// # Errors + /// /// `ClosedCriticalStream` if the encoder stream is closed. /// `InternalError` if an unexpected error occurred. fn encode( @@ -236,11 +234,13 @@ impl SendStream for SendMessage { } /// # Errors + /// /// `InternalError` if an unexpected error occurred. /// `InvalidStreamId` if the stream does not exist, /// `AlreadyClosed` if the stream has already been closed. - /// `TransportStreamDoesNotExist` if the transport stream does not exist (this may happen if `process_output` - /// has not been called when needed, and HTTP3 layer has not picked up the info that the stream has been closed.) + /// `TransportStreamDoesNotExist` if the transport stream does not exist (this may happen if + /// `process_output` has not been called when needed, and HTTP3 layer has not picked up the + /// info that the stream has been closed.) fn send(&mut self, conn: &mut Connection) -> Res<()> { let sent = Error::map_error(self.stream.send_buffer(conn), Error::HttpInternal(5))?; qlog::h3_data_moved_down(conn.qlog_mut(), self.stream_id(), sent); diff --git a/third_party/rust/neqo-http3/src/server.rs b/third_party/rust/neqo-http3/src/server.rs index e4c1c707bbdb..b29f7154517e 100644 --- a/third_party/rust/neqo-http3/src/server.rs +++ b/third_party/rust/neqo-http3/src/server.rs @@ -6,6 +6,21 @@ #![allow(clippy::module_name_repetitions)] +use std::{ + cell::{RefCell, RefMut}, + collections::HashMap, + path::PathBuf, + rc::Rc, + time::Instant, +}; + +use neqo_common::{qtrace, Datagram}; +use neqo_crypto::{AntiReplay, Cipher, PrivateKey, PublicKey, ZeroRttChecker}; +use neqo_transport::{ + server::{ActiveConnectionRef, Server, ValidateAddress}, + ConnectionIdGenerator, Output, +}; + use crate::{ connection::Http3State, connection_server::Http3ServerHandler, @@ -16,19 +31,6 @@ use crate::{ settings::HttpZeroRttChecker, Http3Parameters, Http3StreamInfo, Res, }; -use neqo_common::{qtrace, Datagram}; -use neqo_crypto::{AntiReplay, Cipher, PrivateKey, PublicKey, ZeroRttChecker}; -use neqo_transport::{ - server::{ActiveConnectionRef, Server, ValidateAddress}, - ConnectionIdGenerator, Output, -}; -use std::{ - cell::{RefCell, RefMut}, - collections::HashMap, - path::PathBuf, - rc::Rc, - time::Instant, -}; type HandlerRef = Rc>; @@ -49,6 +51,7 @@ impl ::std::fmt::Display for Http3Server { impl Http3Server { /// # Errors + /// /// Making a `neqo_transport::Server` may produce an error. This can only be a crypto error if /// the socket can't be created or configured. pub fn new( @@ -92,6 +95,7 @@ impl Http3Server { /// Enable encrypted client hello (ECH). /// /// # Errors + /// /// Only when NSS can't serialize a configuration. pub fn enable_ech( &mut self, @@ -109,7 +113,7 @@ impl Http3Server { self.server.ech_config() } - pub fn process(&mut self, dgram: Option, now: Instant) -> Output { + pub fn process(&mut self, dgram: Option<&Datagram>, now: Instant) -> Output { qtrace!([self], "Process."); let out = self.server.process(dgram, now); self.process_http3(now); @@ -119,7 +123,7 @@ impl Http3Server { qtrace!([self], "Send packet: {:?}", d); Output::Datagram(d) } - _ => self.server.process(None, now), + _ => self.server.process(Option::<&Datagram>::None, now), } } @@ -309,24 +313,26 @@ fn prepare_data( #[cfg(test)] mod tests { - use super::{Http3Server, Http3ServerEvent, Http3State, Rc, RefCell}; - use crate::{Error, HFrame, Header, Http3Parameters, Priority}; + use std::{ + collections::HashMap, + mem, + ops::{Deref, DerefMut}, + }; + use neqo_common::{event::Provider, Encoder}; use neqo_crypto::{AuthenticationStatus, ZeroRttCheckResult, ZeroRttChecker}; use neqo_qpack::{encoder::QPackEncoder, QpackSettings}; use neqo_transport::{ Connection, ConnectionError, ConnectionEvent, State, StreamId, StreamType, ZeroRttState, }; - use std::{ - collections::HashMap, - mem, - ops::{Deref, DerefMut}, - }; use test_fixture::{ anti_replay, default_client, fixture_init, now, CountingConnectionIdGenerator, DEFAULT_ALPN, DEFAULT_KEYS, }; + use super::{Http3Server, Http3ServerEvent, Http3State, Rc, RefCell}; + use crate::{Error, HFrame, Header, Http3Parameters, Priority}; + const DEFAULT_SETTINGS: QpackSettings = QpackSettings { max_table_size_encoder: 100, max_table_size_decoder: 100, @@ -399,29 +405,29 @@ mod tests { const SERVER_SIDE_DECODER_STREAM_ID: StreamId = StreamId::new(11); fn connect_transport(server: &mut Http3Server, client: &mut Connection, resume: bool) { - let c1 = client.process(None, now()).dgram(); - let s1 = server.process(c1, now()).dgram(); - let c2 = client.process(s1, now()).dgram(); + let c1 = client.process(None, now()); + let s1 = server.process(c1.as_dgram_ref(), now()); + let c2 = client.process(s1.as_dgram_ref(), now()); let needs_auth = client .events() .any(|e| e == ConnectionEvent::AuthenticationNeeded); let c2 = if needs_auth { assert!(!resume); // c2 should just be an ACK, so absorb that. - let s_ack = server.process(c2, now()).dgram(); - assert!(s_ack.is_none()); + let s_ack = server.process(c2.as_dgram_ref(), now()); + assert!(s_ack.as_dgram_ref().is_none()); client.authenticated(AuthenticationStatus::Ok, now()); - client.process(None, now()).dgram() + client.process(None, now()) } else { assert!(resume); c2 }; assert!(client.state().connected()); - let s2 = server.process(c2, now()).dgram(); + let s2 = server.process(c2.as_dgram_ref(), now()); assert_connected(server); - let c3 = client.process(s2, now()).dgram(); - assert!(c3.is_none()); + let c3 = client.process(s2.as_dgram_ref(), now()); + assert!(c3.as_dgram_ref().is_none()); } // Start a client/server and check setting frame. @@ -556,8 +562,8 @@ mod tests { sent = neqo_trans_conn.stream_send(decoder_stream, &[0x3]); assert_eq!(sent, Ok(1)); let out1 = neqo_trans_conn.process(None, now()); - let out2 = server.process(out1.dgram(), now()); - mem::drop(neqo_trans_conn.process(out2.dgram(), now())); + let out2 = server.process(out1.as_dgram_ref(), now()); + mem::drop(neqo_trans_conn.process(out2.as_dgram_ref(), now())); // assert no error occured. assert_not_closed(server); @@ -588,7 +594,7 @@ mod tests { let control = peer_conn.control_stream_id; peer_conn.stream_close_send(control).unwrap(); let out = peer_conn.process(None, now()); - hconn.process(out.dgram(), now()); + hconn.process(out.as_dgram_ref(), now()); assert_closed(&mut hconn, &Error::HttpClosedCriticalStream); } @@ -603,7 +609,7 @@ mod tests { let sent = neqo_trans_conn.stream_send(control_stream, &[0x0, 0xd, 0x1, 0xf]); assert_eq!(sent, Ok(4)); let out = neqo_trans_conn.process(None, now()); - hconn.process(out.dgram(), now()); + hconn.process(out.as_dgram_ref(), now()); assert_closed(&mut hconn, &Error::HttpMissingSettings); } @@ -615,7 +621,7 @@ mod tests { // send the second SETTINGS frame. peer_conn.control_send(&[0x4, 0x6, 0x1, 0x40, 0x64, 0x7, 0x40, 0x64]); let out = peer_conn.process(None, now()); - hconn.process(out.dgram(), now()); + hconn.process(out.as_dgram_ref(), now()); assert_closed(&mut hconn, &Error::HttpFrameUnexpected); } @@ -630,7 +636,7 @@ mod tests { frame.encode(&mut e); peer_conn.control_send(e.as_ref()); let out = peer_conn.process(None, now()); - hconn.process(out.dgram(), now()); + hconn.process(out.as_dgram_ref(), now()); // check if the given connection got closed on invalid stream ids if valid { assert_not_closed(&mut hconn); @@ -673,7 +679,7 @@ mod tests { peer_conn.control_send(v); let out = peer_conn.process(None, now()); - hconn.process(out.dgram(), now()); + hconn.process(out.as_dgram_ref(), now()); assert_closed(&mut hconn, &Error::HttpFrameUnexpected); } @@ -707,10 +713,10 @@ mod tests { .stream_send(new_stream_id, &[0x41, 0x19, 0x4, 0x4, 0x6, 0x0, 0x8, 0x0]) .unwrap(); let out = peer_conn.process(None, now()); - let out = hconn.process(out.dgram(), now()); - mem::drop(peer_conn.process(out.dgram(), now())); + let out = hconn.process(out.as_dgram_ref(), now()); + mem::drop(peer_conn.process(out.as_dgram_ref(), now())); let out = hconn.process(None, now()); - mem::drop(peer_conn.process(out.dgram(), now())); + mem::drop(peer_conn.process(out.as_dgram_ref(), now())); // check for stop-sending with Error::HttpStreamCreation. let mut stop_sending_event_found = false; @@ -738,8 +744,8 @@ mod tests { let push_stream_id = peer_conn.stream_create(StreamType::UniDi).unwrap(); _ = peer_conn.stream_send(push_stream_id, &[0x1]).unwrap(); let out = peer_conn.process(None, now()); - let out = hconn.process(out.dgram(), now()); - mem::drop(peer_conn.conn.process(out.dgram(), now())); + let out = hconn.process(out.as_dgram_ref(), now()); + mem::drop(peer_conn.conn.process(out.as_dgram_ref(), now())); assert_closed(&mut hconn, &Error::HttpStreamCreation); } @@ -755,38 +761,38 @@ mod tests { let mut sent = peer_conn.stream_send(control_stream, &[0x0]); assert_eq!(sent, Ok(1)); let out = peer_conn.process(None, now()); - hconn.process(out.dgram(), now()); + hconn.process(out.as_dgram_ref(), now()); // start sending SETTINGS frame sent = peer_conn.stream_send(control_stream, &[0x4]); assert_eq!(sent, Ok(1)); let out = peer_conn.process(None, now()); - hconn.process(out.dgram(), now()); + hconn.process(out.as_dgram_ref(), now()); sent = peer_conn.stream_send(control_stream, &[0x4]); assert_eq!(sent, Ok(1)); let out = peer_conn.process(None, now()); - hconn.process(out.dgram(), now()); + hconn.process(out.as_dgram_ref(), now()); sent = peer_conn.stream_send(control_stream, &[0x6]); assert_eq!(sent, Ok(1)); let out = peer_conn.process(None, now()); - hconn.process(out.dgram(), now()); + hconn.process(out.as_dgram_ref(), now()); sent = peer_conn.stream_send(control_stream, &[0x0]); assert_eq!(sent, Ok(1)); let out = peer_conn.process(None, now()); - hconn.process(out.dgram(), now()); + hconn.process(out.as_dgram_ref(), now()); sent = peer_conn.stream_send(control_stream, &[0x8]); assert_eq!(sent, Ok(1)); let out = peer_conn.process(None, now()); - hconn.process(out.dgram(), now()); + hconn.process(out.as_dgram_ref(), now()); sent = peer_conn.stream_send(control_stream, &[0x0]); assert_eq!(sent, Ok(1)); let out = peer_conn.process(None, now()); - hconn.process(out.dgram(), now()); + hconn.process(out.as_dgram_ref(), now()); assert_not_closed(&mut hconn); @@ -794,37 +800,37 @@ mod tests { sent = peer_conn.stream_send(control_stream, &[0x5]); assert_eq!(sent, Ok(1)); let out = peer_conn.process(None, now()); - hconn.process(out.dgram(), now()); + hconn.process(out.as_dgram_ref(), now()); sent = peer_conn.stream_send(control_stream, &[0x5]); assert_eq!(sent, Ok(1)); let out = peer_conn.process(None, now()); - hconn.process(out.dgram(), now()); + hconn.process(out.as_dgram_ref(), now()); sent = peer_conn.stream_send(control_stream, &[0x4]); assert_eq!(sent, Ok(1)); let out = peer_conn.process(None, now()); - hconn.process(out.dgram(), now()); + hconn.process(out.as_dgram_ref(), now()); sent = peer_conn.stream_send(control_stream, &[0x61]); assert_eq!(sent, Ok(1)); let out = peer_conn.process(None, now()); - hconn.process(out.dgram(), now()); + hconn.process(out.as_dgram_ref(), now()); sent = peer_conn.stream_send(control_stream, &[0x62]); assert_eq!(sent, Ok(1)); let out = peer_conn.process(None, now()); - hconn.process(out.dgram(), now()); + hconn.process(out.as_dgram_ref(), now()); sent = peer_conn.stream_send(control_stream, &[0x63]); assert_eq!(sent, Ok(1)); let out = peer_conn.process(None, now()); - hconn.process(out.dgram(), now()); + hconn.process(out.as_dgram_ref(), now()); sent = peer_conn.stream_send(control_stream, &[0x64]); assert_eq!(sent, Ok(1)); let out = peer_conn.process(None, now()); - hconn.process(out.dgram(), now()); + hconn.process(out.as_dgram_ref(), now()); // PUSH_PROMISE on a control stream will cause an error assert_closed(&mut hconn, &Error::HttpFrameUnexpected); @@ -840,7 +846,7 @@ mod tests { peer_conn.stream_close_send(stream_id).unwrap(); let out = peer_conn.process(None, now()); - hconn.process(out.dgram(), now()); + hconn.process(out.as_dgram_ref(), now()); assert_closed(&mut hconn, &Error::HttpFrame); } @@ -892,7 +898,7 @@ mod tests { peer_conn.stream_close_send(stream_id).unwrap(); let out = peer_conn.process(None, now()); - hconn.process(out.dgram(), now()); + hconn.process(out.as_dgram_ref(), now()); // Check connection event. There should be 1 Header and 2 data events. let mut headers_frames = 0; @@ -943,7 +949,7 @@ mod tests { .unwrap(); let out = peer_conn.process(None, now()); - hconn.process(out.dgram(), now()); + hconn.process(out.as_dgram_ref(), now()); // Check connection event. There should be 1 Header and no data events. let mut headers_frames = 0; @@ -987,8 +993,8 @@ mod tests { .unwrap(); peer_conn.stream_close_send(stream_id).unwrap(); - let out = peer_conn.process(out.dgram(), now()); - hconn.process(out.dgram(), now()); + let out = peer_conn.process(out.as_dgram_ref(), now()); + hconn.process(out.as_dgram_ref(), now()); while let Some(event) = hconn.next_event() { match event { @@ -1020,7 +1026,7 @@ mod tests { .unwrap(); let out = peer_conn.process(None, now()); - hconn.process(out.dgram(), now()); + hconn.process(out.as_dgram_ref(), now()); // Check connection event. There should be 1 Header and no data events. // The server will reset the stream. @@ -1052,8 +1058,8 @@ mod tests { } let out = hconn.process(None, now()); - let out = peer_conn.process(out.dgram(), now()); - hconn.process(out.dgram(), now()); + let out = peer_conn.process(out.as_dgram_ref(), now()); + hconn.process(out.as_dgram_ref(), now()); // Check that STOP_SENDING and REET has been received. let mut reset = 0; @@ -1085,7 +1091,7 @@ mod tests { .stream_reset_send(CLIENT_SIDE_CONTROL_STREAM_ID, Error::HttpNoError.code()) .unwrap(); let out = peer_conn.process(None, now()); - hconn.process(out.dgram(), now()); + hconn.process(out.as_dgram_ref(), now()); assert_closed(&mut hconn, &Error::HttpClosedCriticalStream); } @@ -1098,7 +1104,7 @@ mod tests { .stream_reset_send(CLIENT_SIDE_ENCODER_STREAM_ID, Error::HttpNoError.code()) .unwrap(); let out = peer_conn.process(None, now()); - hconn.process(out.dgram(), now()); + hconn.process(out.as_dgram_ref(), now()); assert_closed(&mut hconn, &Error::HttpClosedCriticalStream); } @@ -1111,7 +1117,7 @@ mod tests { .stream_reset_send(CLIENT_SIDE_DECODER_STREAM_ID, Error::HttpNoError.code()) .unwrap(); let out = peer_conn.process(None, now()); - hconn.process(out.dgram(), now()); + hconn.process(out.as_dgram_ref(), now()); assert_closed(&mut hconn, &Error::HttpClosedCriticalStream); } @@ -1125,7 +1131,7 @@ mod tests { .stream_stop_sending(SERVER_SIDE_CONTROL_STREAM_ID, Error::HttpNoError.code()) .unwrap(); let out = peer_conn.process(None, now()); - hconn.process(out.dgram(), now()); + hconn.process(out.as_dgram_ref(), now()); assert_closed(&mut hconn, &Error::HttpClosedCriticalStream); } @@ -1138,7 +1144,7 @@ mod tests { .stream_stop_sending(SERVER_SIDE_ENCODER_STREAM_ID, Error::HttpNoError.code()) .unwrap(); let out = peer_conn.process(None, now()); - hconn.process(out.dgram(), now()); + hconn.process(out.as_dgram_ref(), now()); assert_closed(&mut hconn, &Error::HttpClosedCriticalStream); } @@ -1151,7 +1157,7 @@ mod tests { .stream_stop_sending(SERVER_SIDE_DECODER_STREAM_ID, Error::HttpNoError.code()) .unwrap(); let out = peer_conn.process(None, now()); - hconn.process(out.dgram(), now()); + hconn.process(out.as_dgram_ref(), now()); assert_closed(&mut hconn, &Error::HttpClosedCriticalStream); } @@ -1259,7 +1265,7 @@ mod tests { .unwrap(); let out = peer_conn.process(None, now()); - hconn.process(out.dgram(), now()); + hconn.process(out.as_dgram_ref(), now()); let mut requests = HashMap::new(); while let Some(event) = hconn.next_event() { diff --git a/third_party/rust/neqo-http3/src/server_connection_events.rs b/third_party/rust/neqo-http3/src/server_connection_events.rs index f56288e2047c..cbc8e6d56e18 100644 --- a/third_party/rust/neqo-http3/src/server_connection_events.rs +++ b/third_party/rust/neqo-http3/src/server_connection_events.rs @@ -4,17 +4,16 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use crate::connection::Http3State; +use std::{cell::RefCell, collections::VecDeque, rc::Rc}; + +use neqo_common::Header; +use neqo_transport::{AppError, StreamId}; + use crate::{ + connection::Http3State, features::extended_connect::{ExtendedConnectEvents, ExtendedConnectType, SessionCloseReason}, CloseType, Http3StreamInfo, HttpRecvStreamEvents, Priority, RecvStreamEvents, SendStreamEvents, }; -use neqo_common::Header; -use neqo_transport::AppError; -use neqo_transport::StreamId; -use std::cell::RefCell; -use std::collections::VecDeque; -use std::rc::Rc; #[derive(Debug, PartialEq, Eq, Clone)] pub(crate) enum Http3ServerConnEvent { diff --git a/third_party/rust/neqo-http3/src/server_events.rs b/third_party/rust/neqo-http3/src/server_events.rs index e0cc84ed4c68..4be48363dfd2 100644 --- a/third_party/rust/neqo-http3/src/server_events.rs +++ b/third_party/rust/neqo-http3/src/server_events.rs @@ -6,20 +6,25 @@ #![allow(clippy::module_name_repetitions)] -use crate::connection::{Http3State, WebTransportSessionAcceptAction}; -use crate::connection_server::Http3ServerHandler; -use crate::{ - features::extended_connect::SessionCloseReason, Http3StreamInfo, Http3StreamType, Priority, Res, +use std::{ + cell::RefCell, + collections::VecDeque, + convert::TryFrom, + ops::{Deref, DerefMut}, + rc::Rc, }; -use neqo_common::{qdebug, qinfo, Encoder, Header}; -use neqo_transport::server::ActiveConnectionRef; -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; +use neqo_common::{qdebug, qinfo, Encoder, Header}; +use neqo_transport::{ + server::ActiveConnectionRef, AppError, Connection, DatagramTracking, StreamId, StreamType, +}; + +use crate::{ + connection::{Http3State, WebTransportSessionAcceptAction}, + connection_server::Http3ServerHandler, + features::extended_connect::SessionCloseReason, + Http3StreamInfo, Http3StreamType, Priority, Res, +}; #[derive(Debug, Clone)] pub struct StreamHandler { @@ -57,7 +62,9 @@ impl StreamHandler { } /// Supply a response header to a request. + /// /// # Errors + /// /// It may return `InvalidStreamId` if a stream does not exist anymore. pub fn send_headers(&mut self, headers: &[Header]) -> Res<()> { self.handler.borrow_mut().send_headers( @@ -68,7 +75,9 @@ impl StreamHandler { } /// Supply response data to a request. + /// /// # Errors + /// /// It may return `InvalidStreamId` if a stream does not exist anymore. pub fn send_data(&mut self, buf: &[u8]) -> Res { self.handler @@ -77,7 +86,9 @@ impl StreamHandler { } /// Close sending side. + /// /// # Errors + /// /// It may return `InvalidStreamId` if a stream does not exist anymore. pub fn stream_close_send(&mut self) -> Res<()> { self.handler @@ -86,7 +97,9 @@ impl StreamHandler { } /// Request a peer to stop sending a stream. + /// /// # Errors + /// /// It may return `InvalidStreamId` if a stream does not exist anymore. pub fn stream_stop_sending(&mut self, app_error: AppError) -> Res<()> { qdebug!( @@ -103,7 +116,9 @@ impl StreamHandler { } /// Reset sending side of a stream. + /// /// # Errors + /// /// It may return `InvalidStreamId` if a stream does not exist anymore. pub fn stream_reset_send(&mut self, app_error: AppError) -> Res<()> { qdebug!( @@ -120,7 +135,9 @@ impl StreamHandler { } /// Reset a stream/request. + /// /// # Errors + /// /// It may return `InvalidStreamId` if a stream does not exist anymore pub fn cancel_fetch(&mut self, app_error: AppError) -> Res<()> { qdebug!([self], "reset error:{}.", app_error); @@ -159,14 +176,18 @@ impl Http3OrWebTransportStream { } /// Supply a response header to a request. + /// /// # Errors + /// /// It may return `InvalidStreamId` if a stream does not exist anymore. pub fn send_headers(&mut self, headers: &[Header]) -> Res<()> { self.stream_handler.send_headers(headers) } /// Supply response data to a request. + /// /// # Errors + /// /// It may return `InvalidStreamId` if a stream does not exist anymore. pub fn send_data(&mut self, data: &[u8]) -> Res { qinfo!([self], "Set new response."); @@ -174,7 +195,9 @@ impl Http3OrWebTransportStream { } /// Close sending side. + /// /// # Errors + /// /// It may return `InvalidStreamId` if a stream does not exist anymore. pub fn stream_close_send(&mut self) -> Res<()> { qinfo!([self], "Set new response."); @@ -243,7 +266,9 @@ 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: &WebTransportSessionAcceptAction) -> Res<()> { qinfo!([self], "Set a response for a WebTransport session."); @@ -258,6 +283,7 @@ impl WebTransportRequest { } /// # Errors + /// /// It may return `InvalidStreamId` if a stream does not exist anymore. /// Also return an error if the stream was closed on the transport layer, /// but that information is not yet consumed on the http/3 layer. @@ -279,7 +305,9 @@ impl WebTransportRequest { } /// Close sending side. + /// /// # Errors + /// /// It may return `InvalidStreamId` if a stream does not exist anymore. pub fn create_stream(&mut self, stream_type: StreamType) -> Res { let session_id = self.stream_handler.stream_id(); @@ -301,7 +329,9 @@ impl WebTransportRequest { } /// 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. @@ -326,9 +356,13 @@ impl WebTransportRequest { /// 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 { let max_size = self.stream_handler.conn.borrow().max_datagram_size()?; diff --git a/third_party/rust/neqo-http3/src/settings.rs b/third_party/rust/neqo-http3/src/settings.rs index 1e952dae6d6e..9cd4b994b793 100644 --- a/third_party/rust/neqo-http3/src/settings.rs +++ b/third_party/rust/neqo-http3/src/settings.rs @@ -6,10 +6,12 @@ #![allow(clippy::module_name_repetitions)] -use crate::{Error, Http3Parameters, Res}; +use std::ops::Deref; + use neqo_common::{Decoder, Encoder}; use neqo_crypto::{ZeroRttCheckResult, ZeroRttChecker}; -use std::ops::Deref; + +use crate::{Error, Http3Parameters, Res}; type SettingsType = u64; @@ -120,6 +122,7 @@ impl HSettings { } /// # Errors + /// /// Returns an error if settings types are reserved of settings value are not permitted. pub fn decode_frame_contents(&mut self, dec: &mut Decoder) -> Res<()> { while dec.remaining() > 0 { diff --git a/third_party/rust/neqo-http3/src/stream_type_reader.rs b/third_party/rust/neqo-http3/src/stream_type_reader.rs index 364064f26b61..f36181d3b126 100644 --- a/third_party/rust/neqo-http3/src/stream_type_reader.rs +++ b/third_party/rust/neqo-http3/src/stream_type_reader.rs @@ -6,14 +6,15 @@ #![allow(clippy::module_name_repetitions)] -use crate::control_stream_local::HTTP3_UNI_STREAM_TYPE_CONTROL; -use crate::frames::H3_FRAME_TYPE_HEADERS; -use crate::{CloseType, Error, Http3StreamType, ReceiveOutput, RecvStream, Res, Stream}; use neqo_common::{qtrace, Decoder, IncrementalDecoderUint, Role}; -use neqo_qpack::decoder::QPACK_UNI_STREAM_TYPE_DECODER; -use neqo_qpack::encoder::QPACK_UNI_STREAM_TYPE_ENCODER; +use neqo_qpack::{decoder::QPACK_UNI_STREAM_TYPE_DECODER, encoder::QPACK_UNI_STREAM_TYPE_ENCODER}; use neqo_transport::{Connection, StreamId, StreamType}; +use crate::{ + control_stream_local::HTTP3_UNI_STREAM_TYPE_CONTROL, frames::H3_FRAME_TYPE_HEADERS, CloseType, + Error, Http3StreamType, ReceiveOutput, RecvStream, Res, Stream, +}; + 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; @@ -33,7 +34,9 @@ impl NewStreamType { /// Get the final `NewStreamType` from a stream type. All streams, except Push stream, /// are identified by the type only. This function will return None for the Push stream /// because it needs the ID besides the type. - /// # Error + /// + /// # Errors + /// /// Push streams received by the server are not allowed and this function will return /// `HttpStreamCreation` error. fn final_stream_type( @@ -67,12 +70,11 @@ impl NewStreamType { /// `NewStreamHeadReader` reads the head of an unidirectional stream to identify the stream. /// There are 2 type of streams: -/// - streams identified by the single type (varint encoded). Most streams belong to -/// this category. The `NewStreamHeadReader` will switch from `ReadType`to `Done` state. -/// - streams identified by the type and the ID (both varint encoded). For example, a -/// push stream is identified by the type and `PushId`. After reading the type in -/// the `ReadType` state, `NewStreamHeadReader` changes to `ReadId` state and from there -/// to `Done` state +/// - streams identified by the single type (varint encoded). Most streams belong to this category. +/// The `NewStreamHeadReader` will switch from `ReadType`to `Done` state. +/// - streams identified by the type and the ID (both varint encoded). For example, a push stream +/// is identified by the type and `PushId`. After reading the type in the `ReadType` state, +/// `NewStreamHeadReader` changes to `ReadId` state and from there to `Done` state #[derive(Debug)] pub(crate) enum NewStreamHeadReader { ReadType { @@ -140,12 +142,12 @@ impl NewStreamHeadReader { role, stream_id, .. } => { // final_stream_type may return: - // - an error if a stream type is not allowed for the role, e.g. Push - // stream received at the server. + // - an error if a stream type is not allowed for the role, e.g. Push stream + // received at the server. // - a final type if a stream is only identify by the type // - None - if a stream is not identified by the type only, but it needs - // additional data from the header to produce the final type, e.g. - // a push stream needs pushId as well. + // additional data from the header to produce the final type, e.g. a push + // stream needs pushId as well. let final_type = NewStreamType::final_stream_type(output, stream_id.stream_type(), *role); match (&final_type, fin) { @@ -234,20 +236,23 @@ impl RecvStream for NewStreamHeadReader { #[cfg(test)] mod tests { + use std::mem; + + use neqo_common::{Encoder, Role}; + use neqo_qpack::{ + decoder::QPACK_UNI_STREAM_TYPE_DECODER, encoder::QPACK_UNI_STREAM_TYPE_ENCODER, + }; + use neqo_transport::{Connection, StreamId, StreamType}; + use test_fixture::{connect, now}; + use super::{ NewStreamHeadReader, HTTP3_UNI_STREAM_TYPE_PUSH, WEBTRANSPORT_STREAM, WEBTRANSPORT_UNI_STREAM, }; - use neqo_transport::{Connection, StreamId, StreamType}; - use std::mem; - use test_fixture::{connect, now}; - - use crate::control_stream_local::HTTP3_UNI_STREAM_TYPE_CONTROL; - use crate::frames::H3_FRAME_TYPE_HEADERS; - use crate::{CloseType, Error, NewStreamType, ReceiveOutput, RecvStream, Res}; - use neqo_common::{Encoder, Role}; - use neqo_qpack::decoder::QPACK_UNI_STREAM_TYPE_DECODER; - use neqo_qpack::encoder::QPACK_UNI_STREAM_TYPE_ENCODER; + use crate::{ + control_stream_local::HTTP3_UNI_STREAM_TYPE_CONTROL, frames::H3_FRAME_TYPE_HEADERS, + CloseType, Error, NewStreamType, ReceiveOutput, RecvStream, Res, + }; struct Test { conn_c: Connection, @@ -262,7 +267,7 @@ mod tests { // create a stream let stream_id = conn_s.stream_create(stream_type).unwrap(); let out = conn_s.process(None, now()); - mem::drop(conn_c.process(out.dgram(), now())); + mem::drop(conn_c.process(out.as_dgram_ref(), now())); Self { conn_c, @@ -285,7 +290,7 @@ mod tests { .stream_send(self.stream_id, &enc[i..=i]) .unwrap(); let out = self.conn_s.process(None, now()); - mem::drop(self.conn_c.process(out.dgram(), now())); + mem::drop(self.conn_c.process(out.as_dgram_ref(), now())); assert_eq!( self.decoder.receive(&mut self.conn_c).unwrap(), (ReceiveOutput::NoOutput, false) @@ -299,7 +304,7 @@ mod tests { self.conn_s.stream_close_send(self.stream_id).unwrap(); } let out = self.conn_s.process(None, now()); - mem::drop(self.conn_c.process(out.dgram(), now())); + mem::drop(self.conn_c.process(out.dgram().as_ref(), now())); assert_eq!(&self.decoder.receive(&mut self.conn_c), outcome); assert_eq!(self.decoder.done(), done); } @@ -397,7 +402,8 @@ mod tests { let mut t = Test::new(StreamType::UniDi, Role::Server); t.decode( - &[H3_FRAME_TYPE_HEADERS], // this is the same as a HTTP3_UNI_STREAM_TYPE_PUSH which is not aallowed on the server side. + &[H3_FRAME_TYPE_HEADERS], /* this is the same as a HTTP3_UNI_STREAM_TYPE_PUSH which + * is not aallowed on the server side. */ false, &Err(Error::HttpStreamCreation), true, @@ -413,7 +419,8 @@ mod tests { let mut t = Test::new(StreamType::UniDi, Role::Client); t.decode( - &[H3_FRAME_TYPE_HEADERS, 0xaaaa_aaaa], // this is the same as a HTTP3_UNI_STREAM_TYPE_PUSH + &[H3_FRAME_TYPE_HEADERS, 0xaaaa_aaaa], /* this is the same as a + * HTTP3_UNI_STREAM_TYPE_PUSH */ false, &Ok(( ReceiveOutput::NewStream(NewStreamType::Push(0xaaaa_aaaa)), diff --git a/third_party/rust/neqo-http3/tests/httpconn.rs b/third_party/rust/neqo-http3/tests/httpconn.rs index c78b3f0be8b5..a0b2bcdb8037 100644 --- a/third_party/rust/neqo-http3/tests/httpconn.rs +++ b/third_party/rust/neqo-http3/tests/httpconn.rs @@ -6,6 +6,11 @@ #![allow(unused_assignments)] +use std::{ + mem, + time::{Duration, Instant}, +}; + use neqo_common::{event::Provider, qtrace, Datagram}; use neqo_crypto::{AuthenticationStatus, ResumptionToken}; use neqo_http3::{ @@ -13,8 +18,6 @@ use neqo_http3::{ Http3ServerEvent, Http3State, Priority, }; 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]; @@ -94,19 +97,19 @@ fn process_client_events(conn: &mut Http3Client) { fn connect_peers(hconn_c: &mut Http3Client, hconn_s: &mut Http3Server) -> Option { assert_eq!(hconn_c.state(), Http3State::Initializing); let out = hconn_c.process(None, now()); // Initial - let out = hconn_s.process(out.dgram(), now()); // Initial + Handshake - let out = hconn_c.process(out.dgram(), now()); // ACK - mem::drop(hconn_s.process(out.dgram(), now())); //consume ACK + let out = hconn_s.process(out.as_dgram_ref(), now()); // Initial + Handshake + let out = hconn_c.process(out.as_dgram_ref(), now()); // ACK + mem::drop(hconn_s.process(out.as_dgram_ref(), now())); // consume ACK let authentication_needed = |e| matches!(e, Http3ClientEvent::AuthenticationNeeded); assert!(hconn_c.events().any(authentication_needed)); hconn_c.authenticated(AuthenticationStatus::Ok, now()); let out = hconn_c.process(None, now()); // Handshake assert_eq!(hconn_c.state(), Http3State::Connected); - let out = hconn_s.process(out.dgram(), now()); // Handshake - let out = hconn_c.process(out.dgram(), now()); - let out = hconn_s.process(out.dgram(), now()); + let out = hconn_s.process(out.as_dgram_ref(), now()); // Handshake + let out = hconn_c.process(out.as_dgram_ref(), now()); + let out = hconn_s.process(out.as_dgram_ref(), now()); // assert!(hconn_s.settings_received); - let out = hconn_c.process(out.dgram(), now()); + let out = hconn_c.process(out.as_dgram_ref(), now()); // assert!(hconn_c.settings_received); out.dgram() @@ -122,11 +125,11 @@ fn connect_peers_with_network_propagation_delay( 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 + let out = hconn_s.process(out.as_dgram_ref(), now); // Initial + Handshake now += net_delay; - let out = hconn_c.process(out.dgram(), now); // ACK + let out = hconn_c.process(out.as_dgram_ref(), now); // ACK now += net_delay; - let out = hconn_s.process(out.dgram(), now); //consume ACK + let out = hconn_s.process(out.as_dgram_ref(), now); // consume ACK assert!(out.dgram().is_none()); let authentication_needed = |e| matches!(e, Http3ClientEvent::AuthenticationNeeded); assert!(hconn_c.events().any(authentication_needed)); @@ -135,13 +138,13 @@ fn connect_peers_with_network_propagation_delay( 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 + let out = hconn_s.process(out.as_dgram_ref(), now); // HANDSHAKE_DONE now += net_delay; - let out = hconn_c.process(out.dgram(), now); // Consume HANDSHAKE_DONE, send control streams. + let out = hconn_c.process(out.as_dgram_ref(), now); // Consume HANDSHAKE_DONE, send control streams. now += net_delay; - let out = hconn_s.process(out.dgram(), now); // consume and send control streams. + let out = hconn_s.process(out.as_dgram_ref(), now); // consume and send control streams. now += net_delay; - let out = hconn_c.process(out.dgram(), now); // consume control streams. + let out = hconn_c.process(out.as_dgram_ref(), now); // consume control streams. (out.dgram(), now) } @@ -156,8 +159,8 @@ fn connect() -> (Http3Client, Http3Server, Option) { fn exchange_packets(client: &mut Http3Client, server: &mut Http3Server, out_ex: Option) { let mut out = out_ex; loop { - out = client.process(out, now()).dgram(); - out = server.process(out, now()).dgram(); + out = client.process(out.as_ref(), now()).dgram(); + out = server.process(out.as_ref(), now()).dgram(); if out.is_none() { break; } @@ -185,17 +188,17 @@ fn test_fetch() { .unwrap(); assert_eq!(req, 0); hconn_c.stream_close_send(req).unwrap(); - let out = hconn_c.process(dgram, now()); + let out = hconn_c.process(dgram.as_ref(), now()); qtrace!("-----server"); - let out = hconn_s.process(out.dgram(), now()); - mem::drop(hconn_c.process(out.dgram(), now())); + let out = hconn_s.process(out.as_dgram_ref(), now()); + mem::drop(hconn_c.process(out.as_dgram_ref(), now())); process_server_events(&mut hconn_s); let out = hconn_s.process(None, now()); qtrace!("-----client"); - mem::drop(hconn_c.process(out.dgram(), now())); + mem::drop(hconn_c.process(out.as_dgram_ref(), now())); let out = hconn_s.process(None, now()); - mem::drop(hconn_c.process(out.dgram(), now())); + mem::drop(hconn_c.process(out.as_dgram_ref(), now())); process_client_events(&mut hconn_c); } @@ -214,10 +217,10 @@ fn test_103_response() { .unwrap(); assert_eq!(req, 0); hconn_c.stream_close_send(req).unwrap(); - let out = hconn_c.process(dgram, now()); + let out = hconn_c.process(dgram.as_ref(), now()); - let out = hconn_s.process(out.dgram(), now()); - mem::drop(hconn_c.process(out.dgram(), now())); + let out = hconn_s.process(out.as_dgram_ref(), now()); + mem::drop(hconn_c.process(out.as_dgram_ref(), now())); let mut request = receive_request(&mut hconn_s).unwrap(); let info_headers = [ @@ -228,7 +231,7 @@ fn test_103_response() { request.send_headers(&info_headers).unwrap(); let out = hconn_s.process(None, now()); - mem::drop(hconn_c.process(out.dgram(), now())); + mem::drop(hconn_c.process(out.as_dgram_ref(), now())); let info_headers_event = |e| { matches!(e, Http3ClientEvent::HeaderReady { headers, @@ -239,8 +242,8 @@ fn test_103_response() { set_response(&mut request); let out = hconn_s.process(None, now()); - mem::drop(hconn_c.process(out.dgram(), now())); - process_client_events(&mut hconn_c) + mem::drop(hconn_c.process(out.as_dgram_ref(), now())); + process_client_events(&mut hconn_c); } #[test] @@ -371,8 +374,8 @@ fn zerortt() { .unwrap(); hconn_c.stream_close_send(req).unwrap(); - let out = hconn_c.process(dgram, now()); - let out = hconn_s.process(out.dgram(), now()); + let out = hconn_c.process(dgram.as_ref(), now()); + let out = hconn_s.process(out.as_dgram_ref(), now()); let mut request_stream = None; let mut zerortt_state_change = false; @@ -436,7 +439,7 @@ fn fetch_noresponse_will_idletimeout() { .unwrap(); assert_eq!(req, 0); hconn_c.stream_close_send(req).unwrap(); - let _out = hconn_c.process(dgram, now); + let _out = hconn_c.process(dgram.as_ref(), now); qtrace!("-----server"); let mut done = false; diff --git a/third_party/rust/neqo-http3/tests/priority.rs b/third_party/rust/neqo-http3/tests/priority.rs index df9259ad4b8e..cdec16105896 100644 --- a/third_party/rust/neqo-http3/tests/priority.rs +++ b/third_party/rust/neqo-http3/tests/priority.rs @@ -4,22 +4,21 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use neqo_common::event::Provider; +use std::time::Instant; +use neqo_common::event::Provider; use neqo_crypto::AuthenticationStatus; use neqo_http3::{ Header, Http3Client, Http3ClientEvent, Http3Server, Http3ServerEvent, Http3State, Priority, }; - -use std::time::Instant; use test_fixture::*; fn exchange_packets(client: &mut Http3Client, server: &mut Http3Server) { let mut out = None; loop { - out = client.process(out, now()).dgram(); + out = client.process(out.as_ref(), now()).dgram(); let client_done = out.is_none(); - out = server.process(out, now()).dgram(); + out = server.process(out.as_ref(), now()).dgram(); if out.is_none() && client_done { break; } @@ -32,26 +31,26 @@ fn connect_with(client: &mut Http3Client, server: &mut Http3Server) { 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()); + let out = server.process(out.as_dgram_ref(), now()); + let out = client.process(out.as_dgram_ref(), now()); + let out = server.process(out.as_dgram_ref(), now()); assert!(out.as_dgram_ref().is_none()); let authentication_needed = |e| matches!(e, Http3ClientEvent::AuthenticationNeeded); assert!(client.events().any(authentication_needed)); client.authenticated(AuthenticationStatus::Ok, now()); - let out = client.process(out.dgram(), now()); + let out = client.process(out.as_dgram_ref(), now()); let connected = |e| matches!(e, Http3ClientEvent::StateChange(Http3State::Connected)); assert!(client.events().any(connected)); assert_eq!(client.state(), Http3State::Connected); // Exchange H3 setttings - let out = server.process(out.dgram(), now()); - let out = client.process(out.dgram(), now()); - let out = server.process(out.dgram(), now()); - let out = client.process(out.dgram(), now()); - _ = server.process(out.dgram(), now()); + let out = server.process(out.as_dgram_ref(), now()); + let out = client.process(out.as_dgram_ref(), now()); + let out = server.process(out.as_dgram_ref(), now()); + let out = client.process(out.as_dgram_ref(), now()); + _ = server.process(out.as_dgram_ref(), now()); } fn connect() -> (Http3Client, Http3Server) { diff --git a/third_party/rust/neqo-http3/tests/send_message.rs b/third_party/rust/neqo-http3/tests/send_message.rs index ef4a571dff51..507c4bd55218 100644 --- a/third_party/rust/neqo-http3/tests/send_message.rs +++ b/third_party/rust/neqo-http3/tests/send_message.rs @@ -28,8 +28,8 @@ lazy_static! { 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(); + out = client.process(out.as_ref(), now()).dgram(); + out = server.process(out.as_ref(), now()).dgram(); if out.is_none() { break; } diff --git a/third_party/rust/neqo-http3/tests/webtransport.rs b/third_party/rust/neqo-http3/tests/webtransport.rs index e0556708f181..4e943d86cb07 100644 --- a/third_party/rust/neqo-http3/tests/webtransport.rs +++ b/third_party/rust/neqo-http3/tests/webtransport.rs @@ -4,6 +4,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::{cell::RefCell, rc::Rc}; + use neqo_common::{event::Provider, Header}; use neqo_crypto::AuthenticationStatus; use neqo_http3::{ @@ -12,8 +14,6 @@ use neqo_http3::{ 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, @@ -44,16 +44,16 @@ fn connect() -> (Http3Client, Http3Server) { 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()); + let out = server.process(out.as_dgram_ref(), now()); + let out = client.process(out.as_dgram_ref(), now()); + let out = server.process(out.as_dgram_ref(), 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 mut out = client.process(out.as_dgram_ref(), now()).dgram(); let connected = |e| matches!(e, Http3ClientEvent::StateChange(Http3State::Connected)); assert!(client.events().any(connected)); @@ -61,9 +61,9 @@ fn connect() -> (Http3Client, Http3Server) { // Exchange H3 setttings loop { - out = server.process(out, now()).dgram(); + out = server.process(out.as_ref(), now()).dgram(); let dgram_present = out.is_some(); - out = client.process(out, now()).dgram(); + out = client.process(out.as_ref(), now()).dgram(); if out.is_none() && !dgram_present { break; } @@ -74,8 +74,8 @@ fn connect() -> (Http3Client, Http3Server) { 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(); + out = client.process(out.as_ref(), now()).dgram(); + out = server.process(out.as_ref(), now()).dgram(); if out.is_none() { break; } diff --git a/third_party/rust/neqo-qpack/.cargo-checksum.json b/third_party/rust/neqo-qpack/.cargo-checksum.json index 4f67bad77bf2..2bbec5df4383 100644 --- a/third_party/rust/neqo-qpack/.cargo-checksum.json +++ b/third_party/rust/neqo-qpack/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"179c65b51b783d59b44cda97ecdaee2f2d16aacd55f27fb07e1704ad84ff46b8","src/decoder.rs":"04dca25dd8a8d0423da0abebb0cbe0498e9ce977616bf9b2b3cf1f460dc60585","src/decoder_instructions.rs":"2205c7635b8f0c568f6fe9a63c17028eaf8d29a9b5ac7136b6554cc7fbf35038","src/encoder.rs":"45436fecbb475f2ac0e5d3500b6e4b515e5b74f48f17789edf3e2ffb1571e1e3","src/encoder_instructions.rs":"1eb4f6eee2d9ff16f96dc5bf80dae9bc04316126f6eca933fb51dbd9218a439c","src/header_block.rs":"906e418548279057bd3cc1b6bb4b16db6cf9548d9b2552f9111a6411b5548d0e","src/huffman.rs":"3a9edaf827343ec6e43cfd50fcc0d0077287947160ae630da5c3ddaaefedd010","src/huffman_decode_helper.rs":"2970c57f052878b727c2f764490c54184f5c2608e1d6aa961c3b01509e290122","src/huffman_table.rs":"06fea766a6276ac56c7ee0326faed800a742c15fda1f33bf2513e6cc6a5e6d27","src/lib.rs":"f28baa9cd92b94a3e3c513882b857038ffb9de20369dd7a4e5776d9fcd8322a9","src/prefix.rs":"72c587c40aef4ed38cf13b2de91091d671611679be2a9da6f0b24abafaf50dc5","src/qlog.rs":"211cbc385d2ea925153397205c762ff55956d9871308cdfd29f31bb354e7dbd4","src/qpack_send_buf.rs":"bc86cce786b6c8a468aed8d436ec4a8a86b3d4a917495fff7931ba4026c36c47","src/reader.rs":"5d7d785cf2eabc92d784ec1d1f428bcee5a814a99c903688371115a546e760dc","src/static_table.rs":"fda9d5c6f38f94b0bf92d3afdf8432dce6e27e189736596e16727090c77b78ec","src/stats.rs":"624dfa3b40858c304097bb0ce5b1be1bb4d7916b1abfc222f1aa705907009730","src/table.rs":"370d76fe72273549e2bdf3efcfb83172e656b7a426c26726d85f47037a69f150"},"package":null} \ No newline at end of file +{"files":{"Cargo.toml":"2eabb2ad2846a08b51b306634ed75dc14ab3a43738b1190e3b4c4f2beb00b8e2","src/decoder.rs":"7e468d59adff1fa9373cbb703d13a7503f721a89bebafd049feaf0308a39b606","src/decoder_instructions.rs":"d991d70e51f079bc5b30d3982fd0176edfa9bb7ba14c17a20ec3eea878c56206","src/encoder.rs":"e026da38c2c3410a4e9aa330cda09ac411008772dd66d262d6c375601cebf481","src/encoder_instructions.rs":"86e3abbd9cf94332041326ac6cf806ed64623e3fd38dbc0385b1f63c37e73fd9","src/header_block.rs":"3925476df69b90d950594faadc5cb24c374d46de8c75a374a235f0d27323a7d8","src/huffman.rs":"8b0b2ea069c2a6eb6677b076b99b08ac0d29eabe1f2bbbab37f18f49187ef276","src/huffman_decode_helper.rs":"81309e27ff8f120a10c0b1598888ded21b76e297dc02cea8c7378d6a6627d62a","src/huffman_table.rs":"06fea766a6276ac56c7ee0326faed800a742c15fda1f33bf2513e6cc6a5e6d27","src/lib.rs":"196114397c2b1bf6ef154206018f519b012789cf712e89b069a7616d7278ef3a","src/prefix.rs":"fb4a9acbcf6fd3178f4474404cd3d3b131abca934f69fe14a9d744bc7e636dc5","src/qlog.rs":"e320007ea8309546b26f9c0019ab8722da80dbd38fa976233fd8ae19a0af637c","src/qpack_send_buf.rs":"14d71310c260ee15ea40a783998b507c968eef12db2892b47c689e872b5242a5","src/reader.rs":"b9a7dccd726f471fc24f1d3304f03ac0a039c0828aac7b33c927be07d395c655","src/static_table.rs":"fda9d5c6f38f94b0bf92d3afdf8432dce6e27e189736596e16727090c77b78ec","src/stats.rs":"624dfa3b40858c304097bb0ce5b1be1bb4d7916b1abfc222f1aa705907009730","src/table.rs":"05dbec6483bb24c9fc8d721b70fdfefc2df53b458488b55104147f13c386a47d"},"package":null} \ No newline at end of file diff --git a/third_party/rust/neqo-qpack/Cargo.toml b/third_party/rust/neqo-qpack/Cargo.toml index 4bd88292897b..7df63b7bf6a0 100644 --- a/third_party/rust/neqo-qpack/Cargo.toml +++ b/third_party/rust/neqo-qpack/Cargo.toml @@ -11,19 +11,18 @@ [package] edition = "2018" -rust-version = "1.65.0" +rust-version = "1.70.0" name = "neqo-qpack" -version = "0.6.8" +version = "0.7.0" authors = ["Dragana Damjanovic "] license = "MIT OR Apache-2.0" [dependencies] -lazy_static = "1.3.0" -qlog = "0.9.0" -static_assertions = "1.1.0" +lazy_static = "~1.4.0" +static_assertions = "~1.1.0" [dependencies.log] -version = "0.4.0" +version = "~0.4.17" default-features = false [dependencies.neqo-common] @@ -35,6 +34,10 @@ path = "./../neqo-crypto" [dependencies.neqo-transport] path = "./../neqo-transport" +[dependencies.qlog] +git = "https://github.com/cloudflare/quiche" +rev = "09ea4b244096a013071cfe2175bbf2945fb7f8d1" + [dev-dependencies.test-fixture] path = "../test-fixture" diff --git a/third_party/rust/neqo-qpack/src/decoder.rs b/third_party/rust/neqo-qpack/src/decoder.rs index 5971545938c4..2119db025647 100644 --- a/third_party/rust/neqo-qpack/src/decoder.rs +++ b/third_party/rust/neqo-qpack/src/decoder.rs @@ -4,6 +4,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::convert::TryFrom; + +use neqo_common::{qdebug, Header}; +use neqo_transport::{Connection, StreamId}; + use crate::{ decoder_instructions::DecoderInstruction, encoder_instructions::{DecodedEncoderInstruction, EncoderInstructionReader}, @@ -14,9 +19,6 @@ use crate::{ table::HeaderTable, Error, QpackSettings, Res, }; -use neqo_common::{qdebug, Header}; -use neqo_transport::{Connection, StreamId}; -use std::convert::TryFrom; pub const QPACK_UNI_STREAM_TYPE_DECODER: u64 = 0x3; @@ -30,12 +32,13 @@ pub struct QPackDecoder { local_stream_id: Option, max_table_size: u64, max_blocked_streams: usize, - blocked_streams: Vec<(StreamId, u64)>, //stream_id and requested inserts count. + blocked_streams: Vec<(StreamId, u64)>, // stream_id and requested inserts count. stats: Stats, } impl QPackDecoder { /// # Panics + /// /// If settings include invalid values. #[must_use] pub fn new(qpack_settings: &QpackSettings) -> Self { @@ -50,7 +53,7 @@ impl QPackDecoder { send_buf, local_stream_id: None, max_table_size: qpack_settings.max_table_size_decoder, - max_blocked_streams: usize::try_from(qpack_settings.max_blocked_streams).unwrap(), + max_blocked_streams: usize::from(qpack_settings.max_blocked_streams), blocked_streams: Vec::new(), stats: Stats::default(), } @@ -67,6 +70,7 @@ impl QPackDecoder { } /// # Panics + /// /// If the number of blocked streams is too large. #[must_use] pub fn get_blocked_streams(&self) -> u16 { @@ -74,7 +78,9 @@ impl QPackDecoder { } /// returns a list of unblocked streams + /// /// # Errors + /// /// May return: `ClosedCriticalStream` if stream has been closed or `EncoderStream` /// in case of any other transport error. pub fn receive(&mut self, conn: &mut Connection, stream_id: StreamId) -> Res> { @@ -164,8 +170,11 @@ impl QPackDecoder { } /// # Errors + /// /// May return an error in case of any transport error. TODO: define transport errors. + /// /// # Panics + /// /// Never, but rust doesn't know that. #[allow(clippy::map_err_ignore)] pub fn send(&mut self, conn: &mut Connection) -> Res<()> { @@ -186,6 +195,7 @@ impl QPackDecoder { } /// # Errors + /// /// May return `DecompressionFailed` if header block is incorrect or incomplete. pub fn refers_dynamic_table(&self, buf: &[u8]) -> Res { HeaderDecoder::new(buf).refers_dynamic_table(self.max_entries, self.table.base()) @@ -193,9 +203,13 @@ impl QPackDecoder { /// This function returns None if the stream is blocked waiting for table insertions. /// 'buf' must contain the complete header block. + /// /// # Errors + /// /// May return `DecompressionFailed` if header block is incorrect or incomplete. + /// /// # Panics + /// /// When there is a programming error. pub fn decode_header_block( &mut self, @@ -236,6 +250,7 @@ impl QPackDecoder { } /// # Panics + /// /// When a stream has already been added. pub fn add_send_stream(&mut self, stream_id: StreamId) { assert!( @@ -272,13 +287,15 @@ fn map_error(err: &Error) -> Error { #[cfg(test)] mod tests { - use super::{Connection, Error, QPackDecoder, Res}; - use crate::QpackSettings; + use std::{convert::TryFrom, mem}; + use neqo_common::Header; use neqo_transport::{StreamId, StreamType}; - use std::{convert::TryFrom, mem}; use test_fixture::now; + use super::{Connection, Error, QPackDecoder, Res}; + use crate::QpackSettings; + const STREAM_0: StreamId = StreamId::new(0); struct TestDecoder { @@ -319,7 +336,7 @@ mod tests { .stream_send(decoder.recv_stream_id, encoder_instruction) .unwrap(); let out = decoder.peer_conn.process(None, now()); - mem::drop(decoder.conn.process(out.dgram(), now())); + mem::drop(decoder.conn.process(out.as_dgram_ref(), now())); assert_eq!( decoder .decoder @@ -331,7 +348,7 @@ mod tests { fn send_instructions_and_check(decoder: &mut TestDecoder, decoder_instruction: &[u8]) { decoder.decoder.send(&mut decoder.conn).unwrap(); let out = decoder.conn.process(None, now()); - mem::drop(decoder.peer_conn.process(out.dgram(), now())); + mem::drop(decoder.peer_conn.process(out.as_dgram_ref(), now())); let mut buf = [0_u8; 100]; let (amount, fin) = decoder .peer_conn @@ -434,7 +451,8 @@ mod tests { ); } - // this test tests header decoding, the header acks command and the insert count increment command. + // this test tests header decoding, the header acks command and the insert count increment + // command. #[test] fn test_duplicate() { let mut decoder = connect(); @@ -467,8 +485,8 @@ mod tests { fn test_encode_incr_encode_header_ack_some() { // 1. Decoder receives an instruction (header and value both as literal) // 2. Decoder process the instruction and sends an increment instruction. - // 3. Decoder receives another two instruction (header and value both as literal) and - // a header block. + // 3. Decoder receives another two instruction (header and value both as literal) and a + // header block. // 4. Now it sends only a header ack and an increment instruction with increment==1. let headers = vec![ Header::new("my-headera", "my-valuea"), @@ -504,8 +522,8 @@ mod tests { fn test_encode_incr_encode_header_ack_all() { // 1. Decoder receives an instruction (header and value both as literal) // 2. Decoder process the instruction and sends an increment instruction. - // 3. Decoder receives another instruction (header and value both as literal) and - // a header block. + // 3. Decoder receives another instruction (header and value both as literal) and a header + // block. // 4. Now it sends only a header ack. let headers = vec![ Header::new("my-headera", "my-valuea"), @@ -604,7 +622,8 @@ mod tests { ], encoder_inst: &[], }, - // test adding a new header and encode_post_base_index, also test fix_header_block_prefix + // test adding a new header and encode_post_base_index, also test + // fix_header_block_prefix TestElement { headers: vec![Header::new("my-header", "my-value")], header_block: &[0x02, 0x80, 0x10], @@ -683,7 +702,8 @@ mod tests { ], encoder_inst: &[], }, - // test adding a new header and encode_post_base_index, also test fix_header_block_prefix + // test adding a new header and encode_post_base_index, also test + // fix_header_block_prefix TestElement { headers: vec![Header::new("my-header", "my-value")], header_block: &[0x02, 0x80, 0x10], diff --git a/third_party/rust/neqo-qpack/src/decoder_instructions.rs b/third_party/rust/neqo-qpack/src/decoder_instructions.rs index eb8a331f3ab6..029cd61db637 100644 --- a/third_party/rust/neqo-qpack/src/decoder_instructions.rs +++ b/third_party/rust/neqo-qpack/src/decoder_instructions.rs @@ -4,15 +4,17 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use crate::prefix::{ - DECODER_HEADER_ACK, DECODER_INSERT_COUNT_INCREMENT, DECODER_STREAM_CANCELLATION, -}; -use crate::qpack_send_buf::QpackData; -use crate::reader::{IntReader, ReadByte}; -use crate::Res; +use std::mem; + use neqo_common::{qdebug, qtrace}; use neqo_transport::StreamId; -use std::mem; + +use crate::{ + prefix::{DECODER_HEADER_ACK, DECODER_INSERT_COUNT_INCREMENT, DECODER_STREAM_CANCELLATION}, + qpack_send_buf::QpackData, + reader::{IntReader, ReadByte}, + Res, +}; #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum DecoderInstruction { @@ -81,10 +83,11 @@ impl DecoderInstructionReader { } } - /// ### Errors - /// 1) `NeedMoreData` if the reader needs more data - /// 2) `ClosedCriticalStream` - /// 3) other errors will be translated to `DecoderStream` by the caller of this function. + /// # Errors + /// + /// 1) `NeedMoreData` if the reader needs more data + /// 2) `ClosedCriticalStream` + /// 3) other errors will be translated to `DecoderStream` by the caller of this function. pub fn read_instructions(&mut self, recv: &mut R) -> Res { qdebug!([self], "read a new instraction"); loop { @@ -137,11 +140,11 @@ impl DecoderInstructionReader { #[cfg(test)] mod test { - use super::{DecoderInstruction, DecoderInstructionReader, QpackData}; - use crate::reader::test_receiver::TestReceiver; - use crate::Error; use neqo_transport::StreamId; + use super::{DecoderInstruction, DecoderInstructionReader, QpackData}; + use crate::{reader::test_receiver::TestReceiver, Error}; + fn test_encoding_decoding(instruction: DecoderInstruction) { let mut buf = QpackData::default(); instruction.marshal(&mut buf); diff --git a/third_party/rust/neqo-qpack/src/encoder.rs b/third_party/rust/neqo-qpack/src/encoder.rs index 211a41fc1236..c7921ee2c01b 100644 --- a/third_party/rust/neqo-qpack/src/encoder.rs +++ b/third_party/rust/neqo-qpack/src/encoder.rs @@ -4,19 +4,25 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use crate::decoder_instructions::{DecoderInstruction, DecoderInstructionReader}; -use crate::encoder_instructions::EncoderInstruction; -use crate::header_block::HeaderEncoder; -use crate::qlog; -use crate::qpack_send_buf::QpackData; -use crate::reader::ReceiverConnWrapper; -use crate::stats::Stats; -use crate::table::{HeaderTable, LookupResult, ADDITIONAL_TABLE_ENTRY_SIZE}; -use crate::{Error, QpackSettings, Res}; +use std::{ + collections::{HashMap, HashSet, VecDeque}, + convert::TryFrom, +}; + use neqo_common::{qdebug, qerror, qlog::NeqoQlog, qtrace, Header}; use neqo_transport::{Connection, Error as TransportError, StreamId}; -use std::collections::{HashMap, HashSet, VecDeque}; -use std::convert::TryFrom; + +use crate::{ + decoder_instructions::{DecoderInstruction, DecoderInstructionReader}, + encoder_instructions::EncoderInstruction, + header_block::HeaderEncoder, + qlog, + qpack_send_buf::QpackData, + reader::ReceiverConnWrapper, + stats::Stats, + table::{HeaderTable, LookupResult, ADDITIONAL_TABLE_ENTRY_SIZE}, + Error, QpackSettings, Res, +}; pub const QPACK_UNI_STREAM_TYPE_ENCODER: u64 = 0x2; @@ -45,9 +51,9 @@ pub struct QPackEncoder { local_stream: LocalStreamState, max_blocked_streams: u16, // Remember header blocks that are referring to dynamic table. - // There can be multiple header blocks in one stream, headers, trailer, push stream request, etc. - // This HashMap maps a stream ID to a list of header blocks. Each header block is a list of - // referenced dynamic table entries. + // There can be multiple header blocks in one stream, headers, trailer, push stream request, + // etc. This HashMap maps a stream ID to a list of header blocks. Each header block is a + // list of referenced dynamic table entries. unacked_header_blocks: HashMap>>, blocked_stream_cnt: u16, use_huffman: bool, @@ -75,7 +81,9 @@ impl QPackEncoder { /// This function is use for setting encoders table max capacity. The value is received as /// a `SETTINGS_QPACK_MAX_TABLE_CAPACITY` setting parameter. + /// /// # Errors + /// /// `EncoderStream` if value is too big. /// `ChangeCapacity` if table capacity cannot be reduced. pub fn set_max_capacity(&mut self, cap: u64) -> Res<()> { @@ -103,7 +111,9 @@ impl QPackEncoder { /// This function is use for setting encoders max blocked streams. The value is received as /// a `SETTINGS_QPACK_BLOCKED_STREAMS` setting parameter. + /// /// # Errors + /// /// `EncoderStream` if value is too big. pub fn set_max_blocked_streams(&mut self, blocked_streams: u64) -> Res<()> { self.max_blocked_streams = u16::try_from(blocked_streams).or(Err(Error::EncoderStream))?; @@ -111,7 +121,9 @@ impl QPackEncoder { } /// Reads decoder instructions. + /// /// # Errors + /// /// May return: `ClosedCriticalStream` if stream has been closed or `DecoderStream` /// in case of any other transport error. pub fn receive(&mut self, conn: &mut Connection, stream_id: StreamId) -> Res<()> { @@ -221,14 +233,20 @@ impl QPackEncoder { } } - /// Inserts a new entry into a table and sends the corresponding instruction to a peer. An entry is added only - /// if it is possible to send the corresponding instruction immediately, i.e. the encoder stream is not - /// blocked by the flow control (or stream internal buffer(this is very unlikely)). - /// ### Errors + /// Inserts a new entry into a table and sends the corresponding instruction to a peer. An entry + /// is added only if it is possible to send the corresponding instruction immediately, i.e. + /// the encoder stream is not blocked by the flow control (or stream internal buffer(this is + /// very unlikely)). + /// + /// # Errors + /// /// `EncoderStreamBlocked` if the encoder stream is blocked by the flow control. /// `DynamicTableFull` if the dynamic table does not have enough space for the entry. - /// The function can return transport errors: `InvalidStreamId`, `InvalidInput` and `FinalSizeError`. + /// The function can return transport errors: `InvalidStreamId`, `InvalidInput` and + /// `FinalSizeError`. + /// /// # Panics + /// /// When the insertion fails (it should not). pub fn send_and_insert( &mut self, @@ -279,7 +297,8 @@ impl QPackEncoder { stream_id: StreamId, ) -> Res<()> { if let Some(cap) = self.next_capacity { - // Check if it is possible to reduce the capacity, e.g. if enough space can be make free for the reduction. + // Check if it is possible to reduce the capacity, e.g. if enough space can be make free + // for the reduction. if cap < self.table.capacity() && !self.table.can_evict_to(cap) { return Err(Error::DynamicTableFull); } @@ -293,7 +312,7 @@ impl QPackEncoder { false, "can_evict_to should have checked and make sure this operation is possible" ); - return Err(Error::InternalError(1)); + return Err(Error::InternalError); } self.max_entries = cap / 32; self.next_capacity = None; @@ -302,7 +321,9 @@ impl QPackEncoder { } /// Sends any qpack encoder instructions. + /// /// # Errors + /// /// returns `EncoderStream` in case of an error. pub fn send_encoder_updates(&mut self, conn: &mut Connection) -> Res<()> { match self.local_stream { @@ -338,10 +359,14 @@ impl QPackEncoder { } /// Encodes headers + /// /// # Errors + /// /// `ClosedCriticalStream` if the encoder stream is closed. /// `InternalError` if an unexpected error occurred. + /// /// # Panics + /// /// If there is a programming error. pub fn encode_header_block( &mut self, @@ -358,11 +383,9 @@ impl QPackEncoder { // to write to the encoder stream AND if it can't uses // literal instructions. // The errors can be: - // 1) `EncoderStreamBlocked` - this is an error that - // can occur. + // 1) `EncoderStreamBlocked` - this is an error that can occur. // 2) `InternalError` - this is unexpected error. - // 3) `ClosedCriticalStream` - this is error that should - // close the HTTP/3 session. + // 3) `ClosedCriticalStream` - this is error that should close the HTTP/3 session. // The last 2 errors are ignored here and will be picked up // by the main loop. encoder_blocked = true; @@ -406,8 +429,9 @@ impl QPackEncoder { self.table.add_ref(index); } } else if can_block && !encoder_blocked { - // Insert using an InsertWithNameLiteral instruction. This entry name does not match any name in the - // tables therefore we cannot use any other instruction. + // Insert using an InsertWithNameLiteral instruction. This entry name does not match + // any name in the tables therefore we cannot use any other + // instruction. if let Ok(index) = self.send_and_insert(conn, &name, &value) { encoded_h.encode_indexed_dynamic(index); ref_entries.insert(index); @@ -417,16 +441,15 @@ impl QPackEncoder { // to write to the encoder stream AND if it can't uses // literal instructions. // The errors can be: - // 1) `EncoderStreamBlocked` - this is an error that - // can occur. - // 2) `DynamicTableFull` - this is an error that - // can occur. + // 1) `EncoderStreamBlocked` - this is an error that can occur. + // 2) `DynamicTableFull` - this is an error that can occur. // 3) `InternalError` - this is unexpected error. - // 4) `ClosedCriticalStream` - this is error that should - // close the HTTP/3 session. + // 4) `ClosedCriticalStream` - this is error that should close the HTTP/3 + // session. // The last 2 errors are ignored here and will be picked up // by the main loop. - // As soon as one of the instructions cannot be written or the table is full, do not try again. + // As soon as one of the instructions cannot be written or the table is full, do + // not try again. encoder_blocked = true; encoded_h.encode_literal_with_name_literal(&name, &value); } @@ -458,7 +481,9 @@ impl QPackEncoder { } /// Encoder stream has been created. Add the stream id. + /// /// # Panics + /// /// If a stream has already been added. pub fn add_send_stream(&mut self, stream_id: StreamId) { if self.local_stream == LocalStreamState::NoStream { @@ -505,18 +530,20 @@ fn map_stream_send_atomic_error(err: &TransportError) -> Error { } _ => { debug_assert!(false, "Unexpected error"); - Error::InternalError(2) + Error::InternalError } } } #[cfg(test)] mod tests { + use std::mem; + + use neqo_transport::{ConnectionParameters, StreamId, StreamType}; + use test_fixture::{default_client, default_server, handshake, new_server, now, DEFAULT_ALPN}; + use super::{Connection, Error, Header, QPackEncoder, Res}; use crate::QpackSettings; - use neqo_transport::{ConnectionParameters, StreamId, StreamType}; - use std::mem; - use test_fixture::{default_client, default_server, handshake, new_server, now, DEFAULT_ALPN}; struct TestEncoder { encoder: QPackEncoder, @@ -529,7 +556,8 @@ mod tests { impl TestEncoder { pub fn change_capacity(&mut self, capacity: u64) -> Res<()> { self.encoder.set_max_capacity(capacity).unwrap(); - // We will try to really change the table only when we send the change capacity instruction. + // We will try to really change the table only when we send the change capacity + // instruction. self.encoder.send_encoder_updates(&mut self.conn) } @@ -556,8 +584,8 @@ mod tests { pub fn send_instructions(&mut self, encoder_instruction: &[u8]) { self.encoder.send_encoder_updates(&mut self.conn).unwrap(); let out = self.conn.process(None, now()); - let out2 = self.peer_conn.process(out.dgram(), now()); - mem::drop(self.conn.process(out2.dgram(), now())); + let out2 = self.peer_conn.process(out.as_dgram_ref(), now()); + mem::drop(self.conn.process(out2.as_dgram_ref(), now())); let mut buf = [0_u8; 100]; let (amount, fin) = self .peer_conn @@ -619,7 +647,7 @@ mod tests { .stream_send(encoder.recv_stream_id, decoder_instruction) .unwrap(); let out = encoder.peer_conn.process(None, now()); - mem::drop(encoder.conn.process(out.dgram(), now())); + mem::drop(encoder.conn.process(out.as_dgram_ref(), now())); assert!(encoder .encoder .read_instructions(&mut encoder.conn, encoder.recv_stream_id) @@ -722,7 +750,8 @@ mod tests { ], encoder_inst: &[], }, - // test adding a new header and encode_post_base_index, also test fix_header_block_prefix + // test adding a new header and encode_post_base_index, also test + // fix_header_block_prefix TestElement { headers: vec![Header::new("my-header", "my-value")], header_block: &[0x02, 0x80, 0x10], @@ -796,7 +825,8 @@ mod tests { ], encoder_inst: &[], }, - // test adding a new header and encode_post_base_index, also test fix_header_block_prefix + // test adding a new header and encode_post_base_index, also test + // fix_header_block_prefix TestElement { headers: vec![Header::new("my-header", "my-value")], header_block: &[0x02, 0x80, 0x10], @@ -870,7 +900,8 @@ mod tests { assert!(res.is_ok()); encoder.send_instructions(HEADER_CONTENT_LENGTH_VALUE_1_NAME_LITERAL); - // insert "content-length: 12345 which will fail because the ntry in the table cannot be evicted. + // insert "content-length: 12345 which will fail because the ntry in the table cannot be + // evicted. let res = encoder .encoder @@ -921,7 +952,8 @@ mod tests { assert_eq!(&buf[..], ENCODE_INDEXED_REF_DYNAMIC); encoder.send_instructions(&[]); - // insert "content-length: 12345 which will fail because the entry in the table cannot be evicted + // insert "content-length: 12345 which will fail because the entry in the table cannot be + // evicted let res = encoder .encoder @@ -1004,8 +1036,8 @@ mod tests { encoder.send_instructions(&[]); - // The next one will not use the dynamic entry because it is exceeding the max_blocked_streams - // limit. + // The next one will not use the dynamic entry because it is exceeding the + // max_blocked_streams limit. let buf = encoder.encoder.encode_header_block( &mut encoder.conn, &[Header::new("content-length", "1234")], @@ -1099,7 +1131,8 @@ mod tests { assert_eq!(encoder.encoder.blocked_stream_cnt(), 1); - // The next one will not create a new entry because the encoder is on max_blocked_streams limit. + // The next one will not create a new entry because the encoder is on max_blocked_streams + // limit. let buf = encoder.encoder.encode_header_block( &mut encoder.conn, &[Header::new("name2", "value2")], @@ -1274,8 +1307,8 @@ mod tests { assert_eq!(encoder.encoder.blocked_stream_cnt(), 2); // receive a stream cancel for the first stream. - // This will remove the first stream as blocking but it will not mark the instruction as acked. - // and the second steam will still be blocking. + // This will remove the first stream as blocking but it will not mark the instruction as + // acked. and the second steam will still be blocking. recv_instruction(&mut encoder, STREAM_CANCELED_ID_1); // The stream is not blocking anymore because header ack also acks the instruction. @@ -1507,9 +1540,10 @@ mod tests { assert!(encoder.encoder.set_max_capacity(1000).is_ok()); encoder.send_instructions(CAP_INSTRUCTION_1000); - // Encode a header block with 2 headers. The first header will be added to the dynamic table. - // The second will not be added to the dynamic table, because the corresponding instruction - // cannot be written immediately due to the flow control limit. + // Encode a header block with 2 headers. The first header will be added to the dynamic + // table. The second will not be added to the dynamic table, because the + // corresponding instruction cannot be written immediately due to the flow control + // limit. let buf1 = encoder.encoder.encode_header_block( &mut encoder.conn, &[ @@ -1524,7 +1558,8 @@ mod tests { // Assert that the second header is encoded as a literal with a name literal assert_eq!(buf1[3] & 0xf0, 0x20); - // Try to encode another header block. Here both headers will be encoded as a literal with a name literal + // Try to encode another header block. Here both headers will be encoded as a literal with a + // name literal let buf2 = encoder.encoder.encode_header_block( &mut encoder.conn, &[ @@ -1540,10 +1575,10 @@ mod tests { // exchange a flow control update. let out = encoder.peer_conn.process(None, now()); - mem::drop(encoder.conn.process(out.dgram(), now())); + mem::drop(encoder.conn.process(out.as_dgram_ref(), now())); - // Try writing a new header block. Now, headers will be added to the dynamic table again, because - // instructions can be sent. + // Try writing a new header block. Now, headers will be added to the dynamic table again, + // because instructions can be sent. let buf3 = encoder.encoder.encode_header_block( &mut encoder.conn, &[ @@ -1587,7 +1622,7 @@ mod tests { .send_encoder_updates(&mut encoder.conn) .unwrap(); let out = encoder.conn.process(None, now()); - mem::drop(encoder.peer_conn.process(out.dgram(), now())); + mem::drop(encoder.peer_conn.process(out.as_dgram_ref(), now())); // receive an insert count increment. recv_instruction(&mut encoder, &[0x01]); diff --git a/third_party/rust/neqo-qpack/src/encoder_instructions.rs b/third_party/rust/neqo-qpack/src/encoder_instructions.rs index 93be06bf7f2b..5564af969ea8 100644 --- a/third_party/rust/neqo-qpack/src/encoder_instructions.rs +++ b/third_party/rust/neqo-qpack/src/encoder_instructions.rs @@ -4,16 +4,20 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use crate::prefix::{ - ENCODER_CAPACITY, ENCODER_DUPLICATE, ENCODER_INSERT_WITH_NAME_LITERAL, - ENCODER_INSERT_WITH_NAME_REF_DYNAMIC, ENCODER_INSERT_WITH_NAME_REF_STATIC, NO_PREFIX, -}; -use crate::qpack_send_buf::QpackData; -use crate::reader::{IntReader, LiteralReader, ReadByte, Reader}; -use crate::Res; -use neqo_common::{qdebug, qtrace}; use std::mem; +use neqo_common::{qdebug, qtrace}; + +use crate::{ + prefix::{ + ENCODER_CAPACITY, ENCODER_DUPLICATE, ENCODER_INSERT_WITH_NAME_LITERAL, + ENCODER_INSERT_WITH_NAME_REF_DYNAMIC, ENCODER_INSERT_WITH_NAME_REF_STATIC, NO_PREFIX, + }, + qpack_send_buf::QpackData, + reader::{IntReader, LiteralReader, ReadByte, Reader}, + Res, +}; + // The encoder only uses InsertWithNameLiteral, therefore clippy is complaining about dead_code. // We may decide to use othe instruction in the future. // All instructions are used for testing, therefore they are defined. @@ -183,10 +187,11 @@ impl EncoderInstructionReader { Ok(()) } - /// ### Errors - /// 1) `NeedMoreData` if the reader needs more data - /// 2) `ClosedCriticalStream` - /// 3) other errors will be translated to `EncoderStream` by the caller of this function. + /// # Errors + /// + /// 1) `NeedMoreData` if the reader needs more data + /// 2) `ClosedCriticalStream` + /// 3) other errors will be translated to `EncoderStream` by the caller of this function. pub fn read_instructions( &mut self, recv: &mut T, @@ -265,8 +270,7 @@ impl EncoderInstructionReader { mod test { use super::{EncoderInstruction, EncoderInstructionReader, QpackData}; - use crate::reader::test_receiver::TestReceiver; - use crate::Error; + use crate::{reader::test_receiver::TestReceiver, Error}; fn test_encoding_decoding(instruction: &EncoderInstruction, use_huffman: bool) { let mut buf = QpackData::default(); diff --git a/third_party/rust/neqo-qpack/src/header_block.rs b/third_party/rust/neqo-qpack/src/header_block.rs index 3b37db120eda..2e15bdf1fe30 100644 --- a/third_party/rust/neqo-qpack/src/header_block.rs +++ b/third_party/rust/neqo-qpack/src/header_block.rs @@ -4,6 +4,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::{ + mem, + ops::{Deref, Div}, +}; + +use neqo_common::{qtrace, Header}; + use crate::{ prefix::{ BASE_PREFIX_NEGATIVE, BASE_PREFIX_POSITIVE, HEADER_FIELD_INDEX_DYNAMIC, @@ -17,11 +24,6 @@ use crate::{ table::HeaderTable, Error, Res, }; -use neqo_common::{qtrace, Header}; -use std::{ - mem, - ops::{Deref, Div}, -}; #[derive(Default, Debug, PartialEq)] pub struct HeaderEncoder { diff --git a/third_party/rust/neqo-qpack/src/huffman.rs b/third_party/rust/neqo-qpack/src/huffman.rs index 31657ca8269f..283a501b3283 100644 --- a/third_party/rust/neqo-qpack/src/huffman.rs +++ b/third_party/rust/neqo-qpack/src/huffman.rs @@ -4,11 +4,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use crate::huffman_decode_helper::{HuffmanDecoderNode, HUFFMAN_DECODE_ROOT}; -use crate::huffman_table::HUFFMAN_TABLE; -use crate::{Error, Res}; use std::convert::TryFrom; +use crate::{ + huffman_decode_helper::{HuffmanDecoderNode, HUFFMAN_DECODE_ROOT}, + huffman_table::HUFFMAN_TABLE, + Error, Res, +}; + struct BitReader<'a> { input: &'a [u8], offset: usize, @@ -65,9 +68,14 @@ impl<'a> BitReader<'a> { } /// Decodes huffman encoded input. +/// /// # Errors -/// This function may return `HuffmanDecompressionFailed` if `input` is not a correct huffman-encoded array of bits. +/// +/// This function may return `HuffmanDecompressionFailed` if `input` is not a correct +/// huffman-encoded array of bits. +/// /// # Panics +/// /// Never, but rust can't know that. pub fn decode_huffman(input: &[u8]) -> Res> { let mut reader = BitReader::new(input); @@ -109,6 +117,7 @@ fn decode_character(reader: &mut BitReader) -> Res> { } /// # Panics +/// /// Never, but rust doesn't know that. #[must_use] pub fn encode_huffman(input: &[u8]) -> Vec { diff --git a/third_party/rust/neqo-qpack/src/huffman_decode_helper.rs b/third_party/rust/neqo-qpack/src/huffman_decode_helper.rs index 7589ebd11ae8..122226dd1fd7 100644 --- a/third_party/rust/neqo-qpack/src/huffman_decode_helper.rs +++ b/third_party/rust/neqo-qpack/src/huffman_decode_helper.rs @@ -4,10 +4,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use crate::huffman_table::HUFFMAN_TABLE; -use lazy_static::lazy_static; use std::convert::TryFrom; +use lazy_static::lazy_static; + +use crate::huffman_table::HUFFMAN_TABLE; + pub struct HuffmanDecoderNode { pub next: [Option>; 2], pub value: Option, diff --git a/third_party/rust/neqo-qpack/src/lib.rs b/third_party/rust/neqo-qpack/src/lib.rs index 86ccb11ff86a..1581712017d5 100644 --- a/third_party/rust/neqo-qpack/src/lib.rs +++ b/third_party/rust/neqo-qpack/src/lib.rs @@ -6,7 +6,8 @@ #![cfg_attr(feature = "deny-warnings", deny(warnings))] #![warn(clippy::pedantic)] -// This is because of Encoder and Decoder structs. TODO: think about a better namings for crate and structs. +// This is because of Encoder and Decoder structs. TODO: think about a better namings for crate and +// structs. #![allow(clippy::module_name_repetitions)] pub mod decoder; @@ -44,10 +45,11 @@ pub enum Error { EncoderStream, DecoderStream, ClosedCriticalStream, - InternalError(u16), + InternalError, // These are internal errors, they will be transformed into one of the above. - NeedMoreData, // Return when an input stream does not have more data that a decoder needs.(It does not mean that a stream is closed.) + NeedMoreData, /* Return when an input stream does not have more data that a decoder + * needs.(It does not mean that a stream is closed.) */ HeaderLookup, HuffmanDecompressionFailed, BadUtf8, @@ -78,7 +80,8 @@ impl Error { } /// # Errors - /// Any error is mapped to the indicated type. + /// + /// Any error is mapped to the indicated type. fn map_error(r: Result, err: Self) -> Result { r.map_err(|e| { if matches!(e, Self::ClosedCriticalStream) { diff --git a/third_party/rust/neqo-qpack/src/prefix.rs b/third_party/rust/neqo-qpack/src/prefix.rs index ee0826850d91..0085de0df918 100644 --- a/third_party/rust/neqo-qpack/src/prefix.rs +++ b/third_party/rust/neqo-qpack/src/prefix.rs @@ -6,6 +6,8 @@ #[derive(Copy, Clone, Debug)] pub struct Prefix { + #[allow(unknown_lints)] // available with Rust v1.75 + #[allow(clippy::struct_field_names)] prefix: u8, len: u8, mask: u8, @@ -14,9 +16,10 @@ pub struct Prefix { impl Prefix { pub fn new(prefix: u8, len: u8) -> Self { // len should never be larger than 7. - // Most of Prefixes are instantiated as consts bellow. The only place where this construcrtor is used - // is in tests and when literals are encoded and the Huffman bit is added to one of the consts bellow. - // create_prefix guaranty that all const have len < 7 so we can safely assert that len is <=7. + // Most of Prefixes are instantiated as consts bellow. The only place where this + // construcrtor is used is in tests and when literals are encoded and the Huffman + // bit is added to one of the consts bellow. create_prefix guaranty that all const + // have len < 7 so we can safely assert that len is <=7. assert!(len <= 7); assert!((len == 0) || (prefix & ((1 << (8 - len)) - 1) == 0)); Self { @@ -108,7 +111,7 @@ create_prefix!(ENCODER_INSERT_WITH_NAME_LITERAL, 0x40, 2); create_prefix!(ENCODER_DUPLICATE, 0x00, 3); //===================================================================== -//Header block encoding prefixes +// Header block encoding prefixes //===================================================================== create_prefix!(BASE_PREFIX_POSITIVE, 0x00, 1); @@ -135,5 +138,6 @@ create_prefix!(HEADER_FIELD_LITERAL_NAME_REF_DYNAMIC, 0x40, 4, 0xD0); create_prefix!(HEADER_FIELD_LITERAL_NAME_REF_DYNAMIC_POST, 0x00, 5, 0xF0); // | 0 | 0 | 1 | N | H | Index(3+) | -// N is ignored and H is not relevant for decoding this prefix, therefore the mask is 1110 0000 = 0xE0 +// N is ignored and H is not relevant for decoding this prefix, therefore the mask is 1110 0000 = +// 0xE0 create_prefix!(HEADER_FIELD_LITERAL_NAME_LITERAL, 0x20, 4, 0xE0); diff --git a/third_party/rust/neqo-qpack/src/qlog.rs b/third_party/rust/neqo-qpack/src/qlog.rs index c6ae6b5d0f6e..8d48efb0aa06 100644 --- a/third_party/rust/neqo-qpack/src/qlog.rs +++ b/third_party/rust/neqo-qpack/src/qlog.rs @@ -6,11 +6,9 @@ // Functions that handle capturing QLOG traces. -use neqo_common::hex; -use neqo_common::qlog::NeqoQlog; +use neqo_common::{hex, qlog::NeqoQlog}; use qlog::events::{ - qpack::QpackInstructionTypeName, - qpack::{QPackInstruction, QpackInstructionParsed}, + qpack::{QPackInstruction, QpackInstructionParsed, QpackInstructionTypeName}, EventData, RawInfo, }; diff --git a/third_party/rust/neqo-qpack/src/qpack_send_buf.rs b/third_party/rust/neqo-qpack/src/qpack_send_buf.rs index 4fbdbf12bd5b..a4438590817f 100644 --- a/third_party/rust/neqo-qpack/src/qpack_send_buf.rs +++ b/third_party/rust/neqo-qpack/src/qpack_send_buf.rs @@ -4,11 +4,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use crate::huffman::encode_huffman; -use crate::prefix::Prefix; +use std::{convert::TryFrom, ops::Deref}; + use neqo_common::Encoder; -use std::convert::TryFrom; -use std::ops::Deref; + +use crate::{huffman::encode_huffman, prefix::Prefix}; #[derive(Default, Debug, PartialEq)] pub(crate) struct QpackData { diff --git a/third_party/rust/neqo-qpack/src/reader.rs b/third_party/rust/neqo-qpack/src/reader.rs index f47471005d5b..ff9c42b246c3 100644 --- a/third_party/rust/neqo-qpack/src/reader.rs +++ b/third_party/rust/neqo-qpack/src/reader.rs @@ -4,22 +4,26 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use crate::{huffman::decode_huffman, prefix::Prefix, Error, Res}; +use std::{convert::TryInto, mem, str}; + use neqo_common::{qdebug, qerror}; use neqo_transport::{Connection, StreamId}; -use std::{convert::TryInto, mem, str}; + +use crate::{huffman::decode_huffman, prefix::Prefix, Error, Res}; pub trait ReadByte { /// # Errors - /// Return error occurred while reading a byte. - /// The exact error depends on trait implementation. + /// + /// Return error occurred while reading a byte. + /// The exact error depends on trait implementation. fn read_byte(&mut self) -> Res; } pub trait Reader { /// # Errors - /// Return error occurred while reading date into a buffer. - /// The exact error depends on trait implementation. + /// + /// Return error occurred while reading date into a buffer. + /// The exact error depends on trait implementation. fn read(&mut self, buf: &mut [u8]) -> Res; } @@ -154,7 +158,9 @@ pub struct IntReader { impl IntReader { /// `IntReader` is created by suppling the first byte anf prefix length. /// A varint may take only one byte, In that case already the first by has set state to done. + /// /// # Panics + /// /// When `prefix_len` is 8 or larger. #[must_use] pub fn new(first_byte: u8, prefix_len: u8) -> Self { @@ -174,6 +180,7 @@ impl IntReader { } /// # Panics + /// /// Never, but rust doesn't know that. #[must_use] pub fn make(first_byte: u8, prefixes: &[Prefix]) -> Self { @@ -187,7 +194,9 @@ impl IntReader { /// This function reads bytes until the varint is decoded or until stream/buffer does not /// have any more date. + /// /// # Errors + /// /// Possible errors are: /// 1) `NeedMoreData` if the reader needs more data, /// 2) `IntegerOverflow`, @@ -245,7 +254,9 @@ impl LiteralReader { /// Creates `LiteralReader` with the first byte. This constructor is always used /// when a litreral has a prefix. /// For literals without a prefix please use the default constructor. + /// /// # Panics + /// /// If `prefix_len` is 8 or more. #[must_use] pub fn new_with_first_byte(first_byte: u8, prefix_len: u8) -> Self { @@ -261,13 +272,17 @@ impl LiteralReader { /// This function reads bytes until the literal is decoded or until stream/buffer does not /// have any more date ready. + /// /// # Errors + /// /// Possible errors are: /// 1) `NeedMoreData` if the reader needs more data, /// 2) `IntegerOverflow` /// 3) Any `ReadByte`'s error /// It returns value if reading the literal is done or None if it needs more data. + /// /// # Panics + /// /// When this object is complete. pub fn read(&mut self, s: &mut T) -> Res> { loop { @@ -309,7 +324,9 @@ impl LiteralReader { /// This is a helper function used only by `ReceiverBufferWrapper`, therefore it returns /// `DecompressionFailed` if any error happens. +/// /// # Errors +/// /// If an parsing error occurred, the function returns `BadUtf8`. pub fn parse_utf8(v: &[u8]) -> Res<&str> { str::from_utf8(v).map_err(|_| Error::BadUtf8) @@ -318,9 +335,10 @@ pub fn parse_utf8(v: &[u8]) -> Res<&str> { #[cfg(test)] pub(crate) mod test_receiver { - use super::{Error, ReadByte, Reader, Res}; use std::collections::VecDeque; + use super::{Error, ReadByte, Reader, Res}; + #[derive(Default)] pub struct TestReceiver { buf: VecDeque, @@ -358,11 +376,12 @@ pub(crate) mod test_receiver { #[cfg(test)] mod tests { + use test_receiver::TestReceiver; + use super::{ parse_utf8, str, test_receiver, Error, IntReader, LiteralReader, ReadByte, ReceiverBufferWrapper, Res, }; - use test_receiver::TestReceiver; const TEST_CASES_NUMBERS: [(&[u8], u8, u64); 7] = [ (&[0xEA], 3, 10), diff --git a/third_party/rust/neqo-qpack/src/table.rs b/third_party/rust/neqo-qpack/src/table.rs index cc9844ee2744..7ce8572542cc 100644 --- a/third_party/rust/neqo-qpack/src/table.rs +++ b/third_party/rust/neqo-qpack/src/table.rs @@ -4,11 +4,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use crate::static_table::{StaticTableEntry, HEADER_STATIC_TABLE}; -use crate::{Error, Res}; +use std::{collections::VecDeque, convert::TryFrom}; + use neqo_common::qtrace; -use std::collections::VecDeque; -use std::convert::TryFrom; + +use crate::{ + static_table::{StaticTableEntry, HEADER_STATIC_TABLE}, + Error, Res, +}; pub const ADDITIONAL_TABLE_ENTRY_SIZE: usize = 32; @@ -106,9 +109,12 @@ impl HeaderTable { } /// Change the dynamic table capacity. - /// ### Errors + /// + /// # Errors + /// /// `ChangeCapacity` if table capacity cannot be reduced. - /// The table cannot be reduce if there are entries that are referred at the moment or their inserts are unacked. + /// The table cannot be reduce if there are entries that are referred at the moment or their + /// inserts are unacked. pub fn set_capacity(&mut self, cap: u64) -> Res<()> { qtrace!([self], "set capacity to {}", cap); if !self.evict_to(cap) { @@ -119,7 +125,9 @@ impl HeaderTable { } /// Get a static entry with `index`. - /// ### Errors + /// + /// # Errors + /// /// `HeaderLookup` if the index does not exist in the static table. pub fn get_static(index: u64) -> Res<&'static StaticTableEntry> { let inx = usize::try_from(index).or(Err(Error::HeaderLookup))?; @@ -151,7 +159,9 @@ impl HeaderTable { } /// Get a entry in the dynamic table. - /// ### Errors + /// + /// # Errors + /// /// `HeaderLookup` if entry does not exist. pub fn get_dynamic(&self, index: u64, base: u64, post: bool) -> Res<&DynamicTableEntry> { let inx = if post { @@ -186,8 +196,8 @@ impl HeaderTable { } /// Look for a header pair. - /// The function returns `LookupResult`: `index`, `static_table` (if it is a static table entry) and `value_matches` - /// (if the header value matches as well not only header name) + /// The function returns `LookupResult`: `index`, `static_table` (if it is a static table entry) + /// and `value_matches` (if the header value matches as well not only header name) pub fn lookup(&mut self, name: &[u8], value: &[u8], can_block: bool) -> Option { qtrace!( [self], @@ -280,9 +290,11 @@ impl HeaderTable { } /// Insert a new entry. - /// ### Errors - /// `DynamicTableFull` if an entry cannot be added to the table because there is not enough space and/or - /// other entry cannot be evicted. + /// + /// # Errors + /// + /// `DynamicTableFull` if an entry cannot be added to the table because there is not enough + /// space and/or other entry cannot be evicted. pub fn insert(&mut self, name: &[u8], value: &[u8]) -> Res { qtrace!([self], "insert name={:?} value={:?}", name, value); let entry = DynamicTableEntry { @@ -304,9 +316,11 @@ impl HeaderTable { } /// Insert a new entry with the name refer to by a index to static or dynamic table. - /// ### Errors - /// `DynamicTableFull` if an entry cannot be added to the table because there is not enough space and/or - /// other entry cannot be evicted. + /// + /// # Errors + /// + /// `DynamicTableFull` if an entry cannot be added to the table because there is not enough + /// space and/or other entry cannot be evicted. /// `HeaderLookup` if the index dos not exits in the static/dynamic table. pub fn insert_with_name_ref( &mut self, @@ -336,9 +350,11 @@ impl HeaderTable { } /// Duplicate an entry. - /// ### Errors - /// `DynamicTableFull` if an entry cannot be added to the table because there is not enough space and/or - /// other entry cannot be evicted. + /// + /// # Errors + /// + /// `DynamicTableFull` if an entry cannot be added to the table because there is not enough + /// space and/or other entry cannot be evicted. /// `HeaderLookup` if the index dos not exits in the static/dynamic table. pub fn duplicate(&mut self, index: u64) -> Res { qtrace!([self], "duplicate entry={}", index); @@ -355,7 +371,9 @@ impl HeaderTable { } /// Increment number of acknowledge entries. - /// ### Errors + /// + /// # Errors + /// /// `IncrementAck` if ack is greater than actual number of inserts. pub fn increment_acked(&mut self, increment: u64) -> Res<()> { qtrace!([self], "increment acked by {}", increment); diff --git a/third_party/rust/neqo-transport/.cargo-checksum.json b/third_party/rust/neqo-transport/.cargo-checksum.json index f42f9094370f..ba33141b7a96 100644 --- a/third_party/rust/neqo-transport/.cargo-checksum.json +++ b/third_party/rust/neqo-transport/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"9f313e52f3df5e301b92f08ed552cca08b67608529eeb34517f439dea2cf2eee","TODO":"d759cb804b32fa9d96ea8d3574a3c4073da9fe6a0b02b708a0e22cce5a5b4a0f","src/ackrate.rs":"219162186ccd67f652638d0a163aaa4815b2d2d4ed89085bed4f3bd816b8b6c9","src/addr_valid.rs":"d6008b0af47276b1ff3747ebbfa6fabe72aa4002116ae24c4b4de60090dc78e6","src/cc/classic_cc.rs":"17025aede05fceda15f0628c1abfd9fadba54c2ee6bbd00e62cba8376752440c","src/cc/cubic.rs":"230be4be046156909720fc5516810a743844b4ddd3bbdfd391cd6c26ad2d3eea","src/cc/mod.rs":"e8f2a1fc728943f7ee79bcb59c21c0bf6b046f003edd137cde2a90159fcc4652","src/cc/new_reno.rs":"f4c93ccb5a132eafcff5f358b80efda31ab1aa5667c79b351d4cadefc33bbb7f","src/cc/tests/cubic.rs":"3e494981cfa28624b43becd3180ca01dbb068cf96d6bcfe080c3b8efeedc95fc","src/cc/tests/mod.rs":"1567bf0ddaff5cb679217f2fd65f01e15a302b9b9e68b69f3e617dcaf7b3e5ec","src/cc/tests/new_reno.rs":"562223ddd5d7db317179dcb833f6e82294df7cf664851f3d80ad64b2fdc003a2","src/cid.rs":"a3e82bbd720e477447a0b458fe9fc89138e8c2855559fce4f0caf2329556e7a1","src/connection/idle.rs":"6ea9a71db6cd29036c3e4f8922bd33d0bfc33ac065a93c94d3cc8721a52db0fc","src/connection/mod.rs":"8ab9d6fd8a077995964e78aea709aa1b05aa76f05add3e0a1809cc30b4501dc5","src/connection/params.rs":"68435805fc96d84cc55257c9eef73ecdfa52344188e0e7378177d2defb6b0b81","src/connection/saved.rs":"f611ab477541262ac7605fa481a92c4b4d3bcd54fff58a4b97f51df86c752563","src/connection/state.rs":"3bf306b97ff009f8c490c34940fb38562d2bff63953f6913e06b135e39b77ed0","src/connection/test_internal.rs":"f3ebfe97b25c9c716d41406066295e5aff4e96a3051ef4e2b5fb258282bbc14c","src/connection/tests/ackrate.rs":"849dc6e583807a98913dc324be0c1e9c72d186d6bd3785f88178ba739cd84445","src/connection/tests/cc.rs":"c36d724dbc2aeb4d40b89d7e822382e6711db34b0f7226dfc906966730f46610","src/connection/tests/close.rs":"c55788c917ce6c2345651bc6527254e5579ae917c816a12b64756fa366a343f7","src/connection/tests/datagram.rs":"997b29b19d5ec151a046de8f468651430604154df2631f57b1747415d1ccf750","src/connection/tests/fuzzing.rs":"ef96fb5c3af63fbc44da70bdbc73a16234443332ba812282db6da876b07db861","src/connection/tests/handshake.rs":"e8ca28ee7ebacf2b25755314ebc6e02c6dbb55cc48338f7f52d7bb214894d097","src/connection/tests/idle.rs":"f24d316a796c9d1e3783090ff8b06455c5b7899630921406c3b8a70e991276c5","src/connection/tests/keys.rs":"5cf6423ca3143d5d66ea7ffd02424633adc4757b5eb40d95389914d49cc28537","src/connection/tests/migration.rs":"33b411e76c28d750ba5bb0634afa49bf04cf12c58a49c587d5b5a827835cd57e","src/connection/tests/mod.rs":"5045eace6fe7cb113f13ac88d8309bb42d73c50df90d50b56c039c1e389bd2a7","src/connection/tests/priority.rs":"50818c0378cbb659c34d60f204fde8e80c32879bc1e2db7e6b71eb10dad0b088","src/connection/tests/recovery.rs":"42df4f73ab58de4778402adde2036ed0475b5c0be246d1abb263bcb78eac00a4","src/connection/tests/resumption.rs":"cbdb48ee36c4fa13614bee421ad49cafc1aeee830fb2cefc58ba4a55c10f7e64","src/connection/tests/stream.rs":"c611117acbde4de0ead4c0c5c23d560da060fa2ac48a90c560a4da793bec60dd","src/connection/tests/vn.rs":"0f7d8438a39eb32ea98bb9ea55c44194b958da0591598ef2a991c6dc359bff8d","src/connection/tests/zerortt.rs":"14578e0dad2def5a9542423c1a34f9c9e3d453fe9dd158defcae347c6c14da27","src/crypto.rs":"b72e4c656dbb2939218223b24b28c0234b3868559385bbdb2ea2c9ed334ed356","src/dump.rs":"d08f15a2ef4cfd61ed34e1fd3a0937f0334c3c1a074fb76c5f004cf515154e5c","src/events.rs":"5373063c64581e9a06cd7aa42b0a84a152ef7441067499183bd9bd253bf1f32a","src/fc.rs":"f24251108ea7f301920f0f49d1072d0e6f78ecc591431ea281c4047c3ef4a659","src/frame.rs":"61f1f83152ce78e4cbd548414fd4eadba1b5dd0a83012bdfacf9e5a6e37d7c2c","src/lib.rs":"273cda3b58f17d7156024c5fc5981c87fcf22cf8dad23add85b78b264937d8a1","src/pace.rs":"6c6a67ae4300f0af3670bae76478741348e56d2090245ce6c014fa85f066fe01","src/packet/mod.rs":"22e822794ea5cdcf93ecb8411c388ab782fa65572dfa3328f662ab6343f97512","src/packet/retry.rs":"b658349eac5a78529c6c16f56dc436af975a78fe4eac15ba24017730bb7807ca","src/path.rs":"8f39bd9fc138bac6263655b1b487eef1b42f03347f5f5aa35979c8b32da70cdf","src/qlog.rs":"ccd41bc73102e595b8b196844b95869ca0a5960e21648fbdcde3c39e8bc7688e","src/quic_datagrams.rs":"b65fafd57550a87a311b4e2706cde5a153040689d73b5eb2bf9ba28e9ef8f2c6","src/recovery.rs":"bef5ab5742c2a8577ef887775afbfbf1d843053440c983441ddef13a490d32c8","src/recv_stream.rs":"6db66625fad3b991abbf572c35f00b577181d7b18ec67e30abb9ee711106d1b1","src/rtt.rs":"688bccee574b9782399882c6a9e34058d75f2ac7c31008bcd1f36fed12676299","src/send_stream.rs":"18063fc497b10c8be29d4c11d7767aa094bcaf30e982a0277637b8480aacdccc","src/sender.rs":"07f8030e2a341ac0496016d46a06b2b1778f268c8b9abc8ae66c1482fdc72653","src/server.rs":"6737bf2a3daba0f2912d14ddcf1937b13fface0ef2960a419a7e3904d2515b3b","src/stats.rs":"680c26ca1f7a6374425631ca0dcf21e96565f5dd36962737f45693d98cf5576f","src/stream_id.rs":"eeb8311caae29f40066d03e41a4a2a5793406135d410b156c36197c5dbb833da","src/streams.rs":"01f0fda4620cde2d28c7f97a8df3f1e44df7c4921e76f21d540671429121d2b2","src/tparams.rs":"f967eb5d843dd00d612a3fef37529a8138776016c03ab35d9a589c48f1983dc3","src/tracking.rs":"0911edf932005ad6eb36006c28bcafae9019bffdd7168829c274cb7a50fb4a4f","src/version.rs":"3f5ae26db18fcfac719efc1a68ee2d6f2aaec74ccd75c82d20976ba96cadc136","tests/common/mod.rs":"d7425cbbc82d6ea9a538db087d025d33e89125a55a0c050a0ac118c6860dc681","tests/conn_vectors.rs":"72d8d5132b0468facc58d764e52a29fbfc0004f2272df6087b73b53c018591a6","tests/connection.rs":"565a8b3f37fa22a95b42fe5248c3bd78d13271f24f68ccac83429c89916cdfe8","tests/network.rs":"b94f1ebc94153cb4e67a25d2ce3615c13fa23f96071b092e557b8b8dda57bcf4","tests/retry.rs":"a35853cb4c9443c5e84352bb56e1aed6ce423c4456a3e68f547d9bf0d8ea4181","tests/server.rs":"27f7417ccd438e3d1865fe0f3bb91ae293bc3c0f98535e51bfca74fcf678f4d2","tests/sim/connection.rs":"63657160e84158dffa96ce3aec0a5aca40f66199a1ef30d54292089c5fccc441","tests/sim/delay.rs":"9efa722adb89e37262369e9f3c67405f0acc8c24997271811e48df9e856e5a8d","tests/sim/drop.rs":"bd89e5c71cdd1b27cd755faaedd87d5feadf2f424df721a7df41a51bcebcbb58","tests/sim/mod.rs":"5f29310329c74c85c73bafd29742e781011d2607fed59873b342ab3f8ca3b8dd","tests/sim/net.rs":"597f4d37bc26c3d82eeeaa6d14dd03bc2be3930686df2b293748b43c07c497d7","tests/sim/rng.rs":"2c90b0bbaf0c952ebee232deb3594f7a86af387737b15474de3e97ee6b623d90","tests/sim/taildrop.rs":"5c505d150f0071e8cc2d540b3a817a6942fdf13df32f1fbc6822952f2e146176"},"package":null} \ No newline at end of file +{"files":{"Cargo.toml":"d31e1132faa19d4a3025d3b7a98a38d09591b8b75735896d5afbd7e8fdb4434d","benches/rx_stream_orderer.rs":"5f32aba0066bca15aedbf059f9b00f64ced11aa7222c0b0c5ea202bdd9e6ef14","src/ackrate.rs":"c8d8933ccd8255e5b0712a4a7c4a7304de16a430325d5125fdc538a623874279","src/addr_valid.rs":"d1badfd0ab71ad8c6368a398f52d23f817d70e70653a3313353af34542525603","src/cc/classic_cc.rs":"15b735d6c7054489fd0fadc25cbee8b88b4efe1ee0dcc43354b1552183a8b2d8","src/cc/cubic.rs":"f6669242f6566b1de711b8ff59051919a5aa9106da43ed16ae83d6fe614cec11","src/cc/mod.rs":"0141bcadb719a7fe75d037f4ebe19c7f7bdbf9177314ea5b97ee7244b14b162b","src/cc/new_reno.rs":"1d2790260fe8147b4a40c1792e862ab30b204cf4cf8fef45f5d50d6975011ec2","src/cc/tests/cubic.rs":"5367da8fa627046379bacba45a0f993b7305aef48d954c13004cb7ae88dc04ec","src/cc/tests/mod.rs":"1567bf0ddaff5cb679217f2fd65f01e15a302b9b9e68b69f3e617dcaf7b3e5ec","src/cc/tests/new_reno.rs":"7e8a81c3f16d1f21f8b42b2abba4cf8ea6f78cb2ea05c4d85d7c1cb71c1db464","src/cid.rs":"91ed2b635aabde43ed5e9d383d26e9b3a28e92f218adb8feea650d9c4e55ec0a","src/connection/dump.rs":"aea2f97fa78f3d9e0fe32c2a58ce70a7050aced3abde8b06183ed88d02571ec1","src/connection/idle.rs":"b3bc2ad1290e54278d8703092d135eda973eb12316d1f6dffedaffdf25e2a47e","src/connection/mod.rs":"c38de7f0114d2218f3fc5024bd7570199712f57c399642a7b3be0a107845d947","src/connection/params.rs":"c6433e78953df329fa241c7eba0220743f8028d0ca9c1da0021c7f5973aae5c8","src/connection/saved.rs":"97eb19792be3c4d721057021a43ea50a52f89a3cfa583d3d3dcf5d9144b332f5","src/connection/state.rs":"04352beb60ec9b51b41ae2999acb0086f3f90dc94fa1b2becf3921ec0e6ba5b1","src/connection/test_internal.rs":"f3ebfe97b25c9c716d41406066295e5aff4e96a3051ef4e2b5fb258282bbc14c","src/connection/tests/ackrate.rs":"aa92c91185a74eeb2abcc86d19d746b8de3feb7ad507494be9042a6ec37b491e","src/connection/tests/cc.rs":"ee567e43b626353beaae2f0a9e09266bbb8d62bc14178743fc3606bc53c5b6b1","src/connection/tests/close.rs":"c309974598b0b51793d54be1470338d692f1111f79ea985a5c73d62d780d15f7","src/connection/tests/datagram.rs":"ae2853c4b8dbae4b00940adcc8bd114065f134944f182270987d55daa1b27adb","src/connection/tests/fuzzing.rs":"a877ce6cb005099eb4ae1f5649c63c4b7a5c108c9459a3bb36490965712f5310","src/connection/tests/handshake.rs":"1bed309e8358dfb5026e12da7ea2f8bdf42e910fb8b41809b554c0a484a264e8","src/connection/tests/idle.rs":"30077587ed3e22934c82d675119bdcc696a91d4d0d1908fb5f4c9f8b085fd8d9","src/connection/tests/keys.rs":"792cf24ac51daff62c19dcb8c266824a9fd22cb1b715d416551ee270a26c9eb2","src/connection/tests/migration.rs":"33e0442b0d2d3e940ba4a42313419dd2d183174bede0b3046165410ce05b21b1","src/connection/tests/mod.rs":"f64c200035396f96adb6b86742c7117dc96cf500969c1eae2bddcb1d1c1c43f3","src/connection/tests/priority.rs":"e2299a873dca794573a10b0f94bbc5fdf8b75ed7531ee931c31ad7507bc71e6f","src/connection/tests/recovery.rs":"7f28767f3cca2ff60e3dcfa803e12ef043486a222f54681a8faf2ea2fee564a1","src/connection/tests/resumption.rs":"94550cd961b98fba6ab30ff97a538919c76112394470ac00568ea1ac66e6e323","src/connection/tests/stream.rs":"8d3b6fa884847de15910b005c5f9cdfcbdf5eecec5bb84804a842a85f075b0c3","src/connection/tests/vn.rs":"d2539caf17b455c9259d7cfbd823e110da463890997e578f8e74af5f468a4a7b","src/connection/tests/zerortt.rs":"73180babcf24b0eccef91656acfaac953d3abeab52d7d14cede0188ea9d40fc6","src/crypto.rs":"2070f445d4b25d6dc500ba9cf5dcf29383aba217e4ba2e0e312a45e073b28dc6","src/events.rs":"70f989e60004f62d39398c557e36337457810c5942dcfb9631f707f7ac59466d","src/fc.rs":"6c4cd4a3854e5442b33f309a24c0b1a040cdc8193611ea0e05c66b7f6fa1f68c","src/frame.rs":"5e2d28051ef585fdcfb47e7ed666f5000ad38b5407356b07da4ccd6595d3cc34","src/lib.rs":"ef0481f82f5d2173aa202fad0376dbf99e14ae3d58b3bfca4f3da8ec4e07ce8c","src/pace.rs":"05e38e711af705ea52b75843c2226c86fba3605f00d56be00038f5f769a5c8a2","src/packet/mod.rs":"a52648a30a2f066d74a7021b97b99163cf8037644faddef8138ee2dca8ec6ffa","src/packet/retry.rs":"d4cd0f68af678d212040a9e21440697cddb61811261d6e5e9117b47926b49eda","src/path.rs":"fb5240ec491f087eaa86bc737fdfaa3d2c063d69ab12c11d54186a9c8e68714f","src/qlog.rs":"83a605006a98bedd1ed13de8fc1b46aca6b0eaf26a1af5ce8bb936d1dcd6ed9a","src/quic_datagrams.rs":"bd035ac89cf4c7f94d9b839c54cc0298c5db76f7c9e55138455e3246aac01e1e","src/recovery.rs":"fdd85ae2c11bb4efa0f83fec8723a55466089384bea72f86fd1c921d586fe692","src/recv_stream.rs":"40e9da357e43fe668853f2f8251b012cea8e1331230148c448def092551f3b49","src/rtt.rs":"39039f8140e0729085e8090a7f3572cc9f1334936d2f04ff221d387abaecb282","src/send_stream.rs":"da5564521eb7ecfd31326a168c6bc957ec6e1ac832e885d7877570a5fae77222","src/sender.rs":"fe3970579b1d3869ca967058db1261b710f5b8ab6a3f47199e0db5ed5bae75ce","src/server.rs":"b2d9acbe5455255187611599f3c21af2c706968a1b042bdde9a59bdb96b5ac2a","src/stats.rs":"7e7aabe62b0d67151fdfd6b2024230ea1418955ed0ed2f03cbaef0d0877d4221","src/stream_id.rs":"188a8177fd7819e9206bab359ff3002516ecc87322211f536c2bd766a5b7d9d8","src/streams.rs":"476460ce385cfd403656af37f544f75f25bfd261a25fe528df6e926fecd7c785","src/tparams.rs":"08359a7a24f51d741c38294b362c5a0206a2ed9335f0ef61502c7a2d6447b6d8","src/tracking.rs":"6b12710c73344b366c985ff01511601cd35137359c4be015a505e01b082f4f88","src/version.rs":"afe8b5db2a9303e411a24071539cbc58c857fdecd19b3898b37ee3ecac24253c","tests/common/mod.rs":"922872201a673248889c9f511ecc995831d0316e2c5dd2918194b63ee28560ac","tests/conn_vectors.rs":"6b3a1177ca0cb123008ee52a89606d94c19ee6f36f92c040266ce4b6ea13904b","tests/connection.rs":"601100578c1c8f8562917e585654309a8f2bc268c4bc6ab37e29af33b8328eac","tests/network.rs":"1d07b5db4079db3766443b773e661a7f070d402d51be94fb75c6f96696a6b016","tests/retry.rs":"b1077e3500f7387bdd23a65481b5d937cd3f4b0b220f635f2b1db27966a5d538","tests/server.rs":"d46a736c03855f06634b3aedba9d32563d861d4408ad3bb875450d723ea3742a","tests/sim/connection.rs":"6a1ffe23fbbcae957eacf0f9d53672e3b3a0dc133e5b3bb3e2aaba872b597a67","tests/sim/delay.rs":"31171d53ced9609c4f327cef48950dbbe9fecad0d208cbcd2b1bfee474d90e31","tests/sim/drop.rs":"02e6471466f4168d6a507c621dd10d0dfeb26f13ae08ed97401c7e3857a1f43a","tests/sim/mod.rs":"fd62f126e7ddef141fe07c7ea9b4c8ba00dfc4002698a3431eaf3badebca1a53","tests/sim/net.rs":"597f4d37bc26c3d82eeeaa6d14dd03bc2be3930686df2b293748b43c07c497d7","tests/sim/rng.rs":"4d5ef201e51b5ed5a0c63ad83cf514c9b117c9d6a07da94d91acc538edb56633","tests/sim/taildrop.rs":"638adda0a3f295550692a471d471a6d0673e1e61c96f5cf6f013a98f6641201c"},"package":null} \ No newline at end of file diff --git a/third_party/rust/neqo-transport/Cargo.toml b/third_party/rust/neqo-transport/Cargo.toml index 5ea619e7d16a..f993a858b8f5 100644 --- a/third_party/rust/neqo-transport/Cargo.toml +++ b/third_party/rust/neqo-transport/Cargo.toml @@ -11,23 +11,27 @@ [package] edition = "2018" -rust-version = "1.65.0" +rust-version = "1.70.0" name = "neqo-transport" -version = "0.6.8" +version = "0.7.0" authors = [ "EKR ", "Andy Grover ", ] license = "MIT OR Apache-2.0" +[[bench]] +name = "rx_stream_orderer" +harness = false +required-features = ["bench"] + [dependencies] -indexmap = "1.0" -lazy_static = "1.3.0" -qlog = "0.9.0" -smallvec = "1.0.0" +indexmap = "1.9.3" +lazy_static = "1.4" +smallvec = "1.11.1" [dependencies.log] -version = "0.4.0" +version = "0.4.17" default-features = false [dependencies.neqo-common] @@ -36,9 +40,18 @@ path = "../neqo-common" [dependencies.neqo-crypto] path = "../neqo-crypto" +[dependencies.qlog] +git = "https://github.com/cloudflare/quiche" +rev = "09ea4b244096a013071cfe2175bbf2945fb7f8d1" + +[dev-dependencies] +criterion = "0.5.1" +enum-map = "2.7" + [dev-dependencies.test-fixture] path = "../test-fixture" [features] +bench = [] deny-warnings = [] fuzzing = ["neqo-crypto/fuzzing"] diff --git a/third_party/rust/neqo-transport/TODO b/third_party/rust/neqo-transport/TODO deleted file mode 100755 index 151dbd175338..000000000000 --- a/third_party/rust/neqo-transport/TODO +++ /dev/null @@ -1,9 +0,0 @@ -Use stream events in h3 // grover or dragana? -harmonize our rust usage: - - use foo::* or use foo::{bar, baz} and ordering/grouping - - remove extern crate - - sort #[derive()] args -cleanup public API -write docs for public API -write docs for everything else -CI diff --git a/third_party/rust/neqo-transport/benches/rx_stream_orderer.rs b/third_party/rust/neqo-transport/benches/rx_stream_orderer.rs new file mode 100644 index 000000000000..03b401ba06da --- /dev/null +++ b/third_party/rust/neqo-transport/benches/rx_stream_orderer.rs @@ -0,0 +1,20 @@ +use criterion::{criterion_group, criterion_main, Criterion}; +use neqo_transport::recv_stream::RxStreamOrderer; + +fn rx_stream_orderer() { + let mut rx = RxStreamOrderer::new(); + let data: &[u8] = &[0; 1337]; + + for i in 0..100000 { + rx.inbound_frame(i * 1337, data); + } +} + +fn criterion_benchmark(c: &mut Criterion) { + c.bench_function("RxStreamOrderer::inbound_frame()", |b| { + b.iter(rx_stream_orderer) + }); +} + +criterion_group!(benches, criterion_benchmark); +criterion_main!(benches); diff --git a/third_party/rust/neqo-transport/src/ackrate.rs b/third_party/rust/neqo-transport/src/ackrate.rs index 6c4ae44f863a..cf68f9021ff2 100644 --- a/third_party/rust/neqo-transport/src/ackrate.rs +++ b/third_party/rust/neqo-transport/src/ackrate.rs @@ -7,16 +7,14 @@ // Management of the peer's ack rate. #![deny(clippy::pedantic)] -use crate::connection::params::ACK_RATIO_SCALE; -use crate::frame::FRAME_TYPE_ACK_FREQUENCY; -use crate::packet::PacketBuilder; -use crate::recovery::RecoveryToken; -use crate::stats::FrameStats; +use std::{cmp::max, convert::TryFrom, time::Duration}; use neqo_common::qtrace; -use std::cmp::max; -use std::convert::TryFrom; -use std::time::Duration; + +use crate::{ + connection::params::ACK_RATIO_SCALE, frame::FRAME_TYPE_ACK_FREQUENCY, packet::PacketBuilder, + recovery::RecoveryToken, stats::FrameStats, +}; #[derive(Debug, Clone)] pub struct AckRate { diff --git a/third_party/rust/neqo-transport/src/addr_valid.rs b/third_party/rust/neqo-transport/src/addr_valid.rs index fcb8106742ee..b5ed2d07d159 100644 --- a/third_party/rust/neqo-transport/src/addr_valid.rs +++ b/third_party/rust/neqo-transport/src/addr_valid.rs @@ -6,22 +6,22 @@ // This file implements functions necessary for address validation. +use std::{ + convert::TryFrom, + net::{IpAddr, SocketAddr}, + time::{Duration, Instant}, +}; + use neqo_common::{qinfo, qtrace, Decoder, Encoder, Role}; use neqo_crypto::{ constants::{TLS_AES_128_GCM_SHA256, TLS_VERSION_1_3}, selfencrypt::SelfEncrypt, }; - -use crate::cid::ConnectionId; -use crate::packet::PacketBuilder; -use crate::recovery::RecoveryToken; -use crate::stats::FrameStats; -use crate::{Error, Res}; - use smallvec::SmallVec; -use std::convert::TryFrom; -use std::net::{IpAddr, SocketAddr}; -use std::time::{Duration, Instant}; + +use crate::{ + cid::ConnectionId, packet::PacketBuilder, recovery::RecoveryToken, stats::FrameStats, Res, +}; /// A prefix we add to Retry tokens to distinguish them from NEW_TOKEN tokens. const TOKEN_IDENTIFIER_RETRY: &[u8] = &[0x52, 0x65, 0x74, 0x72, 0x79]; @@ -433,9 +433,6 @@ impl NewTokenSender { builder.encode_varint(crate::frame::FRAME_TYPE_NEW_TOKEN); builder.encode_vvec(&t.token); - if builder.len() > builder.limit() { - return Err(Error::InternalError(7)); - } tokens.push(RecoveryToken::NewToken(t.seqno)); stats.new_token += 1; @@ -460,9 +457,10 @@ impl NewTokenSender { #[cfg(test)] mod tests { - use super::NewTokenState; use neqo_common::Role; + use super::NewTokenState; + const ONE: &[u8] = &[1, 2, 3]; const TWO: &[u8] = &[4, 5]; diff --git a/third_party/rust/neqo-transport/src/cc/classic_cc.rs b/third_party/rust/neqo-transport/src/cc/classic_cc.rs index 8465d6b0afde..6f4a01d795cc 100644 --- a/third_party/rust/neqo-transport/src/cc/classic_cc.rs +++ b/third_party/rust/neqo-transport/src/cc/classic_cc.rs @@ -14,14 +14,15 @@ use std::{ }; use super::CongestionControl; - use crate::{ cc::MAX_DATAGRAM_SIZE, packet::PacketNumber, qlog::{self, QlogMetric}, + rtt::RttEstimate, sender::PACING_BURST_SIZE, tracking::SentPacket, }; +#[rustfmt::skip] // to keep `::` and thus prevent conflict with `crate::qlog` use ::qlog::events::{quic::CongestionStateUpdated, EventData}; use neqo_common::{const_max, const_min, qdebug, qinfo, qlog::NeqoQlog, qtrace}; @@ -160,17 +161,18 @@ impl CongestionControl for ClassicCongestionControl { } // Multi-packet version of OnPacketAckedCC - fn on_packets_acked(&mut self, acked_pkts: &[SentPacket], min_rtt: Duration, now: Instant) { + fn on_packets_acked(&mut self, acked_pkts: &[SentPacket], rtt_est: &RttEstimate, now: Instant) { let mut is_app_limited = true; let mut new_acked = 0; for pkt in acked_pkts { qinfo!( - "packet_acked this={:p}, pn={}, ps={}, ignored={}, lost={}", + "packet_acked this={:p}, pn={}, ps={}, ignored={}, lost={}, rtt_est={:?}", self, pkt.pn, pkt.size, i32::from(!pkt.cc_outstanding()), - i32::from(pkt.lost()) + i32::from(pkt.lost()), + rtt_est, ); if !pkt.cc_outstanding() { continue; @@ -221,7 +223,7 @@ impl CongestionControl for ClassicCongestionControl { let bytes_for_increase = self.cc_algorithm.bytes_for_cwnd_increase( self.congestion_window, new_acked, - min_rtt, + rtt_est.minimum(), now, ); debug_assert!(bytes_for_increase > 0); @@ -534,6 +536,14 @@ impl ClassicCongestionControl { #[cfg(test)] mod tests { + use std::{ + convert::TryFrom, + time::{Duration, Instant}, + }; + + use neqo_common::qinfo; + use test_fixture::now; + use super::{ ClassicCongestionControl, WindowAdjustment, CWND_INITIAL, CWND_MIN, PERSISTENT_CONG_THRESH, }; @@ -545,17 +555,13 @@ mod tests { CongestionControl, CongestionControlAlgorithm, CWND_INITIAL_PKTS, MAX_DATAGRAM_SIZE, }, packet::{PacketNumber, PacketType}, + rtt::RttEstimate, tracking::SentPacket, }; - use neqo_common::qinfo; - use std::{ - convert::TryFrom, - time::{Duration, Instant}, - }; - use test_fixture::now; const PTO: Duration = Duration::from_millis(100); const RTT: Duration = Duration::from_millis(98); + const RTT_ESTIMATE: RttEstimate = RttEstimate::from_duration(Duration::from_millis(98)); const ZERO: Duration = Duration::from_secs(0); const EPSILON: Duration = Duration::from_nanos(1); const GAP: Duration = Duration::from_secs(1); @@ -1024,7 +1030,7 @@ mod tests { } assert_eq!(cc.bytes_in_flight(), packet_burst_size * MAX_DATAGRAM_SIZE); now += RTT; - cc.on_packets_acked(&pkts, RTT, now); + cc.on_packets_acked(&pkts, &RTT_ESTIMATE, now); assert_eq!(cc.bytes_in_flight(), 0); assert_eq!(cc.acked_bytes, 0); assert_eq!(cwnd, cc.congestion_window); // CWND doesn't grow because we're app limited @@ -1053,7 +1059,7 @@ mod tests { now += RTT; // Check if congestion window gets increased for all packets currently in flight for (i, pkt) in pkts.into_iter().enumerate() { - cc.on_packets_acked(&[pkt], RTT, now); + cc.on_packets_acked(&[pkt], &RTT_ESTIMATE, now); assert_eq!( cc.bytes_in_flight(), @@ -1100,7 +1106,7 @@ mod tests { ); cc.on_packet_sent(&p_not_lost); now += RTT; - cc.on_packets_acked(&[p_not_lost], RTT, now); + cc.on_packets_acked(&[p_not_lost], &RTT_ESTIMATE, now); cwnd_is_halved(&cc); // cc is app limited therefore cwnd in not increased. assert_eq!(cc.acked_bytes, 0); @@ -1128,7 +1134,7 @@ mod tests { assert_eq!(cc.bytes_in_flight(), packet_burst_size * MAX_DATAGRAM_SIZE); now += RTT; for (i, pkt) in pkts.into_iter().enumerate() { - cc.on_packets_acked(&[pkt], RTT, now); + cc.on_packets_acked(&[pkt], &RTT_ESTIMATE, now); assert_eq!( cc.bytes_in_flight(), @@ -1163,7 +1169,7 @@ mod tests { let mut last_acked_bytes = 0; // Check if congestion window gets increased for all packets currently in flight for (i, pkt) in pkts.into_iter().enumerate() { - cc.on_packets_acked(&[pkt], RTT, now); + cc.on_packets_acked(&[pkt], &RTT_ESTIMATE, now); assert_eq!( cc.bytes_in_flight(), diff --git a/third_party/rust/neqo-transport/src/cc/cubic.rs b/third_party/rust/neqo-transport/src/cc/cubic.rs index a7d7b845fd0d..c04a29b4430f 100644 --- a/third_party/rust/neqo-transport/src/cc/cubic.rs +++ b/third_party/rust/neqo-transport/src/cc/cubic.rs @@ -6,12 +6,15 @@ #![deny(clippy::pedantic)] -use std::fmt::{self, Display}; -use std::time::{Duration, Instant}; +use std::{ + convert::TryFrom, + fmt::{self, Display}, + time::{Duration, Instant}, +}; + +use neqo_common::qtrace; use crate::cc::{classic_cc::WindowAdjustment, MAX_DATAGRAM_SIZE_F64}; -use neqo_common::qtrace; -use std::convert::TryFrom; // CUBIC congestion control @@ -39,9 +42,9 @@ const EXPONENTIAL_GROWTH_REDUCTION: f64 = 2.0; /// This has the effect of reducing larger values to `1<<53`. /// If you have a congestion window that large, something is probably wrong. fn convert_to_f64(v: usize) -> f64 { - let mut f_64 = f64::try_from(u32::try_from(v >> 21).unwrap_or(u32::MAX)).unwrap(); + let mut f_64 = f64::from(u32::try_from(v >> 21).unwrap_or(u32::MAX)); f_64 *= 2_097_152.0; // f_64 <<= 21 - f_64 += f64::try_from(u32::try_from(v & 0x1f_ffff).unwrap()).unwrap(); + f_64 += f64::from(u32::try_from(v & 0x1f_ffff).unwrap()); f_64 } @@ -163,8 +166,8 @@ impl WindowAdjustment for Cubic { // of `MAX_DATAGRAM_SIZE` to match the increase of `target - cwnd / cwnd` as defined // in the specification (Sections 4.4 and 4.5). // The amount of data required therefore reduces asymptotically as the target increases. - // If the target is not significantly higher than the congestion window, require a very large - // amount of acknowledged data (effectively block increases). + // If the target is not significantly higher than the congestion window, require a very + // large amount of acknowledged data (effectively block increases). let mut acked_to_increase = MAX_DATAGRAM_SIZE_F64 * curr_cwnd_f64 / (target_cwnd - curr_cwnd_f64).max(1.0); @@ -178,9 +181,10 @@ impl WindowAdjustment for Cubic { fn reduce_cwnd(&mut self, curr_cwnd: usize, acked_bytes: usize) -> (usize, usize) { let curr_cwnd_f64 = convert_to_f64(curr_cwnd); // Fast Convergence - // If congestion event occurs before the maximum congestion window before the last congestion event, - // we reduce the the maximum congestion window and thereby W_max. - // check cwnd + MAX_DATAGRAM_SIZE instead of cwnd because with cwnd in bytes, cwnd may be slightly off. + // If congestion event occurs before the maximum congestion window before the last + // congestion event, we reduce the the maximum congestion window and thereby W_max. + // check cwnd + MAX_DATAGRAM_SIZE instead of cwnd because with cwnd in bytes, cwnd may be + // slightly off. self.last_max_cwnd = if curr_cwnd_f64 + MAX_DATAGRAM_SIZE_F64 < self.last_max_cwnd { curr_cwnd_f64 * CUBIC_FAST_CONVERGENCE } else { diff --git a/third_party/rust/neqo-transport/src/cc/mod.rs b/third_party/rust/neqo-transport/src/cc/mod.rs index 5cd56767475c..a1a43bd157b5 100644 --- a/third_party/rust/neqo-transport/src/cc/mod.rs +++ b/third_party/rust/neqo-transport/src/cc/mod.rs @@ -7,20 +7,23 @@ // Congestion control #![deny(clippy::pedantic)] -use crate::{path::PATH_MTU_V6, tracking::SentPacket, Error}; -use neqo_common::qlog::NeqoQlog; - use std::{ fmt::{Debug, Display}, str::FromStr, time::{Duration, Instant}, }; +use neqo_common::qlog::NeqoQlog; + +use crate::{path::PATH_MTU_V6, rtt::RttEstimate, tracking::SentPacket, Error}; + mod classic_cc; mod cubic; mod new_reno; -pub use classic_cc::{ClassicCongestionControl, CWND_INITIAL, CWND_INITIAL_PKTS, CWND_MIN}; +pub use classic_cc::ClassicCongestionControl; +#[cfg(test)] +pub use classic_cc::{CWND_INITIAL, CWND_INITIAL_PKTS, CWND_MIN}; pub use cubic::Cubic; pub use new_reno::NewReno; @@ -40,7 +43,7 @@ pub trait CongestionControl: Display + Debug { #[must_use] fn cwnd_avail(&self) -> usize; - fn on_packets_acked(&mut self, acked_pkts: &[SentPacket], min_rtt: Duration, now: Instant); + fn on_packets_acked(&mut self, acked_pkts: &[SentPacket], rtt_est: &RttEstimate, now: Instant); /// Returns true if the congestion window was reduced. fn on_packets_lost( diff --git a/third_party/rust/neqo-transport/src/cc/new_reno.rs b/third_party/rust/neqo-transport/src/cc/new_reno.rs index d34cdfbab972..e51b3d6cc006 100644 --- a/third_party/rust/neqo-transport/src/cc/new_reno.rs +++ b/third_party/rust/neqo-transport/src/cc/new_reno.rs @@ -7,10 +7,12 @@ // Congestion control #![deny(clippy::pedantic)] -use std::fmt::{self, Display}; +use std::{ + fmt::{self, Display}, + time::{Duration, Instant}, +}; use crate::cc::classic_cc::WindowAdjustment; -use std::time::{Duration, Instant}; #[derive(Debug, Default)] pub struct NewReno {} diff --git a/third_party/rust/neqo-transport/src/cc/tests/cubic.rs b/third_party/rust/neqo-transport/src/cc/tests/cubic.rs index d93643583d28..0c82e478170b 100644 --- a/third_party/rust/neqo-transport/src/cc/tests/cubic.rs +++ b/third_party/rust/neqo-transport/src/cc/tests/cubic.rs @@ -7,6 +7,14 @@ #![allow(clippy::cast_possible_truncation)] #![allow(clippy::cast_sign_loss)] +use std::{ + convert::TryFrom, + ops::Sub, + time::{Duration, Instant}, +}; + +use test_fixture::now; + use crate::{ cc::{ classic_cc::{ClassicCongestionControl, CWND_INITIAL}, @@ -17,16 +25,12 @@ use crate::{ CongestionControl, MAX_DATAGRAM_SIZE, MAX_DATAGRAM_SIZE_F64, }, packet::PacketType, + rtt::RttEstimate, tracking::SentPacket, }; -use std::{ - convert::TryFrom, - ops::Sub, - time::{Duration, Instant}, -}; -use test_fixture::now; const RTT: Duration = Duration::from_millis(100); +const RTT_ESTIMATE: RttEstimate = RttEstimate::from_duration(Duration::from_millis(100)); const CWND_INITIAL_F64: f64 = 10.0 * MAX_DATAGRAM_SIZE_F64; const CWND_INITIAL_10_F64: f64 = 10.0 * CWND_INITIAL_F64; const CWND_INITIAL_10: usize = 10 * CWND_INITIAL; @@ -59,7 +63,7 @@ fn ack_packet(cc: &mut ClassicCongestionControl, pn: u64, now: Instant) { Vec::new(), // tokens MAX_DATAGRAM_SIZE, // size ); - cc.on_packets_acked(&[acked], RTT, now); + cc.on_packets_acked(&[acked], &RTT_ESTIMATE, now); } fn packet_lost(cc: &mut ClassicCongestionControl, pn: u64) { @@ -76,9 +80,7 @@ fn packet_lost(cc: &mut ClassicCongestionControl, pn: u64) { } fn expected_tcp_acks(cwnd_rtt_start: usize) -> u64 { - (f64::try_from(i32::try_from(cwnd_rtt_start).unwrap()).unwrap() - / MAX_DATAGRAM_SIZE_F64 - / CUBIC_ALPHA) + (f64::from(i32::try_from(cwnd_rtt_start).unwrap()) / MAX_DATAGRAM_SIZE_F64 / CUBIC_ALPHA) .round() as u64 } @@ -109,7 +111,7 @@ fn tcp_phase() { for _ in 0..num_tcp_increases { let cwnd_rtt_start = cubic.cwnd(); - //Expected acks during a period of RTT / CUBIC_ALPHA. + // Expected acks during a period of RTT / CUBIC_ALPHA. let acks = expected_tcp_acks(cwnd_rtt_start); // The time between acks if they are ideally paced over a RTT. let time_increase = RTT / u32::try_from(cwnd_rtt_start / MAX_DATAGRAM_SIZE).unwrap(); @@ -145,9 +147,10 @@ fn tcp_phase() { let expected_ack_tcp_increase = expected_tcp_acks(cwnd_rtt_start); assert!(num_acks < expected_ack_tcp_increase); - // This first increase after a TCP phase may be shorter than what it would take by a regular cubic phase, - // because of the proper byte counting and the credit it already had before entering this phase. Therefore - // We will perform another round and compare it to expected increase using the cubic equation. + // This first increase after a TCP phase may be shorter than what it would take by a regular + // cubic phase, because of the proper byte counting and the credit it already had before + // entering this phase. Therefore We will perform another round and compare it to expected + // increase using the cubic equation. let cwnd_rtt_start_after_tcp = cubic.cwnd(); let elapsed_time = now - start_time; @@ -167,12 +170,12 @@ fn tcp_phase() { let expected_ack_tcp_increase2 = expected_tcp_acks(cwnd_rtt_start_after_tcp); assert!(num_acks2 < expected_ack_tcp_increase2); - // The time needed to increase cwnd by MAX_DATAGRAM_SIZE using the cubic equation will be calculates from: - // W_cubic(elapsed_time + t_to_increase) - W_cubis(elapsed_time) = MAX_DATAGRAM_SIZE => - // CUBIC_C * (elapsed_time + t_to_increase)^3 * MAX_DATAGRAM_SIZE + CWND_INITIAL - - // CUBIC_C * elapsed_time^3 * MAX_DATAGRAM_SIZE + CWND_INITIAL = MAX_DATAGRAM_SIZE => - // t_to_increase = cbrt((1 + CUBIC_C * elapsed_time^3) / CUBIC_C) - elapsed_time - // (t_to_increase is in seconds) + // The time needed to increase cwnd by MAX_DATAGRAM_SIZE using the cubic equation will be + // calculates from: W_cubic(elapsed_time + t_to_increase) - W_cubis(elapsed_time) = + // MAX_DATAGRAM_SIZE => CUBIC_C * (elapsed_time + t_to_increase)^3 * MAX_DATAGRAM_SIZE + + // CWND_INITIAL - CUBIC_C * elapsed_time^3 * MAX_DATAGRAM_SIZE + CWND_INITIAL = + // MAX_DATAGRAM_SIZE => t_to_increase = cbrt((1 + CUBIC_C * elapsed_time^3) / CUBIC_C) - + // elapsed_time (t_to_increase is in seconds) // number of ack needed is t_to_increase / time_increase. let expected_ack_cubic_increase = ((((1.0 + CUBIC_C * (elapsed_time).as_secs_f64().powi(3)) / CUBIC_C).cbrt() @@ -180,15 +183,16 @@ fn tcp_phase() { / time_increase.as_secs_f64()) .ceil() as u64; // num_acks is very close to the calculated value. The exact value is hard to calculate - // because the proportional increase(i.e. curr_cwnd_f64 / (target - curr_cwnd_f64) * MAX_DATAGRAM_SIZE_F64) - // and the byte counting. + // because the proportional increase(i.e. curr_cwnd_f64 / (target - curr_cwnd_f64) * + // MAX_DATAGRAM_SIZE_F64) and the byte counting. assert_eq!(num_acks2, expected_ack_cubic_increase + 2); } #[test] fn cubic_phase() { let mut cubic = ClassicCongestionControl::new(Cubic::default()); - // Set last_max_cwnd to a higher number make sure that cc is the cubic phase (cwnd is calculated by the cubic equation). + // Set last_max_cwnd to a higher number make sure that cc is the cubic phase (cwnd is calculated + // by the cubic equation). cubic.set_last_max_cwnd(CWND_INITIAL_10_F64); // Set ssthresh to something small to make sure that cc is in the congection avoidance phase. cubic.set_ssthresh(1); @@ -205,7 +209,7 @@ fn cubic_phase() { let num_rtts_w_max = (k / RTT.as_secs_f64()).round() as u64; for _ in 0..num_rtts_w_max { let cwnd_rtt_start = cubic.cwnd(); - //Expected acks + // Expected acks let acks = cwnd_rtt_start / MAX_DATAGRAM_SIZE; let time_increase = RTT / u32::try_from(acks).unwrap(); for _ in 0..acks { @@ -264,7 +268,8 @@ fn congestion_event_congestion_avoidance() { // Set ssthresh to something small to make sure that cc is in the congection avoidance phase. cubic.set_ssthresh(1); - // Set last_max_cwnd to something smaller than cwnd so that the fast convergence is not triggered. + // Set last_max_cwnd to something smaller than cwnd so that the fast convergence is not + // triggered. cubic.set_last_max_cwnd(3.0 * MAX_DATAGRAM_SIZE_F64); _ = fill_cwnd(&mut cubic, 0, now()); diff --git a/third_party/rust/neqo-transport/src/cc/tests/new_reno.rs b/third_party/rust/neqo-transport/src/cc/tests/new_reno.rs index 0e4322c08c0b..a73844a75501 100644 --- a/third_party/rust/neqo-transport/src/cc/tests/new_reno.rs +++ b/third_party/rust/neqo-transport/src/cc/tests/new_reno.rs @@ -7,15 +7,23 @@ // Congestion control #![deny(clippy::pedantic)] -use crate::cc::new_reno::NewReno; -use crate::cc::{ClassicCongestionControl, CongestionControl, CWND_INITIAL, MAX_DATAGRAM_SIZE}; -use crate::packet::PacketType; -use crate::tracking::SentPacket; use std::time::Duration; + use test_fixture::now; +use crate::{ + cc::{ + new_reno::NewReno, ClassicCongestionControl, CongestionControl, CWND_INITIAL, + MAX_DATAGRAM_SIZE, + }, + packet::PacketType, + rtt::RttEstimate, + tracking::SentPacket, +}; + const PTO: Duration = Duration::from_millis(100); const RTT: Duration = Duration::from_millis(98); +const RTT_ESTIMATE: RttEstimate = RttEstimate::from_duration(Duration::from_millis(98)); fn cwnd_is_default(cc: &ClassicCongestionControl) { assert_eq!(cc.cwnd(), CWND_INITIAL); @@ -117,7 +125,7 @@ fn issue_876() { assert_eq!(cc.bytes_in_flight(), 6 * MAX_DATAGRAM_SIZE - 5); // and ack it. cwnd increases slightly - cc.on_packets_acked(&sent_packets[6..], RTT, time_now); + cc.on_packets_acked(&sent_packets[6..], &RTT_ESTIMATE, time_now); assert_eq!(cc.acked_bytes(), sent_packets[6].size); cwnd_is_halved(&cc); assert_eq!(cc.bytes_in_flight(), 5 * MAX_DATAGRAM_SIZE - 2); @@ -162,8 +170,8 @@ fn issue_1465() { cwnd_is_default(&cc); assert_eq!(cc.bytes_in_flight(), 3 * MAX_DATAGRAM_SIZE); - // advance one rtt to detect lost packet there this simplifies the timers, because on_packet_loss - // would only be called after RTO, but that is not relevant to the problem + // advance one rtt to detect lost packet there this simplifies the timers, because + // on_packet_loss would only be called after RTO, but that is not relevant to the problem now += RTT; cc.on_packets_lost(Some(now), None, PTO, &[p1]); @@ -181,7 +189,7 @@ fn issue_1465() { // the acked packets before on_packet_sent were the cause of // https://github.com/mozilla/neqo/pull/1465 - cc.on_packets_acked(&[p2], RTT, now); + cc.on_packets_acked(&[p2], &RTT_ESTIMATE, now); assert_eq!(cc.bytes_in_flight(), 0); @@ -189,7 +197,7 @@ fn issue_1465() { let p4 = send_next(&mut cc, now); cc.on_packet_sent(&p4); now += RTT; - cc.on_packets_acked(&[p4], RTT, now); + cc.on_packets_acked(&[p4], &RTT_ESTIMATE, now); // do the same as in the first rtt but now the bug appears let p5 = send_next(&mut cc, now); diff --git a/third_party/rust/neqo-transport/src/cid.rs b/third_party/rust/neqo-transport/src/cid.rs index 38157419de39..be202daf253e 100644 --- a/third_party/rust/neqo-transport/src/cid.rs +++ b/third_party/rust/neqo-transport/src/cid.rs @@ -6,24 +6,23 @@ // Representation and management of connection IDs. -use crate::frame::FRAME_TYPE_NEW_CONNECTION_ID; -use crate::packet::PacketBuilder; -use crate::recovery::RecoveryToken; -use crate::stats::FrameStats; -use crate::{Error, Res}; +use std::{ + borrow::Borrow, + cell::{Ref, RefCell}, + cmp::{max, min}, + convert::{AsRef, TryFrom}, + ops::Deref, + rc::Rc, +}; use neqo_common::{hex, hex_with_len, qinfo, Decoder, Encoder}; use neqo_crypto::random; - use smallvec::SmallVec; -use std::borrow::Borrow; -use std::cell::{Ref, RefCell}; -use std::cmp::max; -use std::cmp::min; -use std::convert::AsRef; -use std::convert::TryFrom; -use std::ops::Deref; -use std::rc::Rc; + +use crate::{ + frame::FRAME_TYPE_NEW_CONNECTION_ID, packet::PacketBuilder, recovery::RecoveryToken, + stats::FrameStats, Error, Res, +}; pub const MAX_CONNECTION_ID_LEN: usize = 20; pub const LOCAL_ACTIVE_CID_LIMIT: usize = 8; @@ -88,8 +87,8 @@ impl + ?Sized> From<&T> for ConnectionId { } } -impl<'a> From<&ConnectionIdRef<'a>> for ConnectionId { - fn from(cidref: &ConnectionIdRef<'a>) -> Self { +impl<'a> From> for ConnectionId { + fn from(cidref: ConnectionIdRef<'a>) -> Self { Self::from(SmallVec::from(cidref.cid)) } } @@ -120,7 +119,7 @@ impl<'a> PartialEq> for ConnectionId { } } -#[derive(Hash, Eq, PartialEq)] +#[derive(Hash, Eq, PartialEq, Clone, Copy)] pub struct ConnectionIdRef<'a> { cid: &'a [u8], } @@ -324,6 +323,10 @@ impl ConnectionIdEntry { pub fn connection_id(&self) -> &ConnectionId { &self.cid } + + pub fn reset_token(&self) -> &SRT { + &self.srt + } } pub type RemoteConnectionIdEntry = ConnectionIdEntry<[u8; 16]>; @@ -340,8 +343,8 @@ impl ConnectionIdStore { self.cids.retain(|c| c.seqno != seqno); } - pub fn contains(&self, cid: &ConnectionIdRef) -> bool { - self.cids.iter().any(|c| &c.cid == cid) + pub fn contains(&self, cid: ConnectionIdRef) -> bool { + self.cids.iter().any(|c| c.cid == cid) } pub fn next(&mut self) -> Option> { @@ -418,8 +421,9 @@ pub struct ConnectionIdManager { /// The `ConnectionIdGenerator` instance that is used to create connection IDs. generator: Rc>, /// The connection IDs that we will accept. - /// This includes any we advertise in `NEW_CONNECTION_ID` that haven't been bound to a path yet. - /// During the handshake at the server, it also includes the randomized DCID pick by the client. + /// This includes any we advertise in `NEW_CONNECTION_ID` that haven't been bound to a path + /// yet. During the handshake at the server, it also includes the randomized DCID pick by + /// the client. connection_ids: ConnectionIdStore<()>, /// The maximum number of connection IDs this will accept. This is at least 2 and won't /// be more than `LOCAL_ACTIVE_CID_LIMIT`. @@ -479,7 +483,7 @@ impl ConnectionIdManager { } } - pub fn is_valid(&self, cid: &ConnectionIdRef) -> bool { + pub fn is_valid(&self, cid: ConnectionIdRef) -> bool { self.connection_ids.contains(cid) } @@ -528,10 +532,6 @@ impl ConnectionIdManager { builder.encode_varint(0u64); builder.encode_vec(1, &entry.cid); builder.encode(&entry.srt); - if builder.len() > builder.limit() { - return Err(Error::InternalError(8)); - } - stats.new_connection_id += 1; Ok(true) } @@ -592,9 +592,10 @@ impl ConnectionIdManager { #[cfg(test)] mod tests { - use super::*; use test_fixture::fixture_init; + use super::*; + #[test] fn generate_initial_cid() { fixture_init(); diff --git a/third_party/rust/neqo-transport/src/dump.rs b/third_party/rust/neqo-transport/src/connection/dump.rs similarity index 73% rename from third_party/rust/neqo-transport/src/dump.rs rename to third_party/rust/neqo-transport/src/connection/dump.rs index fceb6b6f5db5..77d51c605c8f 100644 --- a/third_party/rust/neqo-transport/src/dump.rs +++ b/third_party/rust/neqo-transport/src/connection/dump.rs @@ -7,13 +7,16 @@ // Enable just this file for logging to just see packets. // e.g. "RUST_LOG=neqo_transport::dump neqo-client ..." -use crate::connection::Connection; -use crate::frame::Frame; -use crate::packet::{PacketNumber, PacketType}; -use crate::path::PathRef; +use std::fmt::Write; + use neqo_common::{qdebug, Decoder}; -use std::fmt::Write; +use crate::{ + connection::Connection, + frame::Frame, + packet::{PacketNumber, PacketType}, + path::PathRef, +}; #[allow(clippy::module_name_repetitions)] pub fn dump_packet( @@ -24,19 +27,16 @@ pub fn dump_packet( pn: PacketNumber, payload: &[u8], ) { - if ::log::Level::Debug > ::log::max_level() { + if !log::log_enabled!(log::Level::Debug) { return; } let mut s = String::from(""); let mut d = Decoder::from(payload); while d.remaining() > 0 { - let f = match Frame::decode(&mut d) { - Ok(f) => f, - Err(_) => { - s.push_str(" [broken]..."); - break; - } + let Ok(f) = Frame::decode(&mut d) else { + s.push_str(" [broken]..."); + break; }; if let Some(x) = f.dump() { write!(&mut s, "\n {} {}", dir, &x).unwrap(); diff --git a/third_party/rust/neqo-transport/src/connection/idle.rs b/third_party/rust/neqo-transport/src/connection/idle.rs index da1c5207774c..e33f3defb348 100644 --- a/third_party/rust/neqo-transport/src/connection/idle.rs +++ b/third_party/rust/neqo-transport/src/connection/idle.rs @@ -4,13 +4,15 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use crate::recovery::RecoveryToken; -use neqo_common::qtrace; use std::{ cmp::{max, min}, time::{Duration, Instant}, }; +use neqo_common::qtrace; + +use crate::recovery::RecoveryToken; + #[derive(Debug, Clone)] /// There's a little bit of different behavior for resetting idle timeout. See /// -transport 10.2 ("Idle Timeout"). diff --git a/third_party/rust/neqo-transport/src/connection/mod.rs b/third_party/rust/neqo-transport/src/connection/mod.rs index abb7e590ad4d..2de388418a9a 100644 --- a/third_party/rust/neqo-transport/src/connection/mod.rs +++ b/third_party/rust/neqo-transport/src/connection/mod.rs @@ -6,6 +6,29 @@ // The class implementing a QUIC connection. +use std::{ + cell::RefCell, + cmp::{max, min}, + convert::TryFrom, + fmt::{self, Debug}, + mem, + net::{IpAddr, SocketAddr}, + ops::RangeInclusive, + rc::{Rc, Weak}, + time::{Duration, Instant}, +}; + +use neqo_common::{ + event::Provider as EventProvider, hex, hex_snip_middle, hrtime, qdebug, qerror, qinfo, + qlog::NeqoQlog, qtrace, qwarn, Datagram, Decoder, Encoder, Role, +}; +use neqo_crypto::{ + agent::CertificateInfo, random, Agent, AntiReplay, AuthenticationStatus, Cipher, Client, Group, + HandshakeState, PrivateKey, PublicKey, ResumptionToken, SecretAgentInfo, SecretAgentPreInfo, + Server, ZeroRttChecker, +}; +use smallvec::SmallVec; + use crate::{ addr_valid::{AddressValidation, NewTokenState}, cid::{ @@ -13,7 +36,6 @@ use crate::{ ConnectionIdRef, ConnectionIdStore, LOCAL_ACTIVE_CID_LIMIT, }, crypto::{Crypto, CryptoDxState, CryptoSpace}, - dump::*, events::{ConnectionEvent, ConnectionEvents, OutgoingDatagramOutcome}, frame::{ CloseError, Frame, FrameType, FRAME_TYPE_CONNECTION_CLOSE_APPLICATION, @@ -37,43 +59,24 @@ use crate::{ version::{Version, WireVersion}, AppError, ConnectionError, Error, Res, StreamId, }; -use neqo_common::{ - event::Provider as EventProvider, hex, hex_snip_middle, hrtime, qdebug, qerror, qinfo, - qlog::NeqoQlog, qtrace, qwarn, Datagram, Decoder, Encoder, Role, -}; -use neqo_crypto::{ - agent::CertificateInfo, random, Agent, AntiReplay, AuthenticationStatus, Cipher, Client, - HandshakeState, PrivateKey, PublicKey, ResumptionToken, SecretAgentInfo, SecretAgentPreInfo, - Server, ZeroRttChecker, -}; -use smallvec::SmallVec; -use std::{ - cell::RefCell, - cmp::{max, min}, - convert::TryFrom, - fmt::{self, Debug}, - mem, - net::{IpAddr, SocketAddr}, - ops::RangeInclusive, - rc::{Rc, Weak}, - time::{Duration, Instant}, -}; - +mod dump; mod idle; pub mod params; mod saved; mod state; #[cfg(test)] pub mod test_internal; - -pub use crate::send_stream::{RetransmissionPriority, SendStreamStats, TransmissionPriority}; -pub use params::{ConnectionParameters, ACK_RATIO_SCALE}; -pub use state::{ClosingFrame, State}; - +use dump::dump_packet; use idle::IdleTimeout; +pub use params::ConnectionParameters; use params::PreferredAddressConfig; +#[cfg(test)] +pub use params::ACK_RATIO_SCALE; use saved::SavedDatagrams; use state::StateSignaling; +pub use state::{ClosingFrame, State}; + +pub use crate::send_stream::{RetransmissionPriority, SendStreamStats, TransmissionPriority}; #[derive(Debug, Default)] struct Packet(Vec); @@ -326,6 +329,7 @@ impl Connection { local_addr, remote_addr, c.conn_params.get_cc_algorithm(), + c.conn_params.pacing_enabled(), NeqoQlog::default(), now, ); @@ -419,7 +423,7 @@ impl Connection { #[cfg(test)] test_frame_writer: None, }; - c.stats.borrow_mut().init(format!("{}", c)); + c.stats.borrow_mut().init(format!("{c}")); Ok(c) } @@ -473,7 +477,9 @@ impl Connection { /// Set a local transport parameter, possibly overriding a default value. /// This only sets transport parameters without dealing with other aspects of /// setting the value. + /// /// # Panics + /// /// This panics if the transport parameter is known to this crate. pub fn set_local_tparam(&self, tp: TransportParameterId, value: TransportParameter) -> Res<()> { #[cfg(not(test))] @@ -491,9 +497,9 @@ impl Connection { } /// `odcid` is their original choice for our CID, which we get from the Retry token. - /// `remote_cid` is the value from the Source Connection ID field of - /// an incoming packet: what the peer wants us to use now. - /// `retry_cid` is what we asked them to use when we sent the Retry. + /// `remote_cid` is the value from the Source Connection ID field of an incoming packet: what + /// the peer wants us to use now. `retry_cid` is what we asked them to use when we sent the + /// Retry. pub(crate) fn set_retry_cids( &mut self, odcid: ConnectionId, @@ -542,6 +548,26 @@ impl Connection { Ok(()) } + /// Enable a set of key exchange groups. + pub fn set_groups(&mut self, groups: &[Group]) -> Res<()> { + if self.state != State::Init { + qerror!([self], "Cannot enable groups in state {:?}", self.state); + return Err(Error::ConnectionState); + } + self.crypto.tls.set_groups(groups)?; + Ok(()) + } + + /// Set the number of additional key shares to send in the client hello. + pub fn send_additional_key_shares(&mut self, count: usize) -> Res<()> { + if self.state != State::Init { + qerror!([self], "Cannot enable groups in state {:?}", self.state); + return Err(Error::ConnectionState); + } + self.crypto.tls.send_additional_key_shares(count)?; + Ok(()) + } + fn make_resumption_token(&mut self) -> ResumptionToken { debug_assert_eq!(self.role, Role::Client); debug_assert!(self.crypto.has_resumption_token()); @@ -619,7 +645,9 @@ impl Connection { /// 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 { assert_eq!(self.role, Role::Client); @@ -815,7 +843,7 @@ impl Connection { ) -> Res { if let Err(v) = &res { #[cfg(debug_assertions)] - let msg = format!("{:?}", v); + let msg = format!("{v:?}"); #[cfg(not(debug_assertions))] let msg = ""; let error = ConnectionError::Transport(v.clone()); @@ -826,8 +854,8 @@ impl Connection { qwarn!([self], "Closing again after error {:?}", err); } State::Init => { - // We have not even sent anything just close the connection without sending any error. - // This may happen when client_start fails. + // We have not even sent anything just close the connection without sending any + // error. This may happen when client_start fails. self.set_state(State::Closed(error)); } State::WaitInitial => { @@ -915,12 +943,30 @@ impl Connection { } /// Process new input datagrams on the connection. - pub fn process_input(&mut self, d: Datagram, now: Instant) { + pub fn process_input(&mut self, d: &Datagram, now: Instant) { self.input(d, now, now); self.process_saved(now); self.streams.cleanup_closed_streams(); } + /// Process new input datagrams on the connection. + pub fn process_multiple_input<'a, I>(&mut self, dgrams: I, now: Instant) + where + I: IntoIterator, + I::IntoIter: ExactSizeIterator, + { + let dgrams = dgrams.into_iter(); + if dgrams.len() == 0 { + return; + } + + for d in dgrams { + self.input(d, now, now); + } + self.process_saved(now); + self.streams.cleanup_closed_streams(); + } + /// Get the time that we next need to be called back, relative to `now`. fn next_delay(&mut self, now: Instant, paced: bool) -> Duration { qtrace!([self], "Get callback delay {:?}", now); @@ -998,7 +1044,7 @@ impl Connection { let res = self.client_start(now); self.absorb_error(now, res); } - (State::Init, Role::Server) | (State::WaitInitial, Role::Server) => { + (State::Init | State::WaitInitial, Role::Server) => { return Output::None; } _ => { @@ -1020,7 +1066,7 @@ impl Connection { /// Process input and generate output. #[must_use = "Output of the process function must be handled"] - pub fn process(&mut self, dgram: Option, now: Instant) -> Output { + pub fn process(&mut self, dgram: Option<&Datagram>, now: Instant) -> Output { if let Some(d) = dgram { self.input(d, now, now); self.process_saved(now); @@ -1119,18 +1165,24 @@ impl Connection { debug_assert!(self.crypto.states.rx_hp(self.version, cspace).is_some()); for saved in self.saved_datagrams.take_saved() { qtrace!([self], "input saved @{:?}: {:?}", saved.t, saved.d); - self.input(saved.d, saved.t, now); + self.input(&saved.d, saved.t, now); } } } /// In case a datagram arrives that we can only partially process, save any /// part that we don't have keys for. - fn save_datagram(&mut self, cspace: CryptoSpace, d: Datagram, remaining: usize, now: Instant) { + fn save_datagram(&mut self, cspace: CryptoSpace, d: &Datagram, remaining: usize, now: Instant) { let d = if remaining < d.len() { - Datagram::new(d.source(), d.destination(), &d[d.len() - remaining..]) + Datagram::new( + d.source(), + d.destination(), + d.tos(), + d.ttl(), + &d[d.len() - remaining..], + ) } else { - d + d.clone() }; self.saved_datagrams.save(cspace, d, now); self.stats.borrow_mut().saved_datagrams += 1; @@ -1163,6 +1215,12 @@ impl Connection { .get_versions_mut() .set_initial(self.conn_params.get_versions().initial()); mem::swap(self, &mut c); + qlog::client_version_information_negotiated( + &mut self.qlog, + self.conn_params.get_versions().all(), + supported, + version, + ); Ok(()) } else { qinfo!([self], "Version negotiation: failed with {:?}", supported); @@ -1183,7 +1241,7 @@ impl Connection { dcid: Option<&ConnectionId>, now: Instant, ) -> Res { - if dcid.map_or(false, |d| d != packet.dcid()) { + if dcid.map_or(false, |d| d != &packet.dcid()) { self.stats .borrow_mut() .pkt_dropped("Coalesced packet has different DCID"); @@ -1230,43 +1288,34 @@ impl Connection { self.tps.borrow_mut().local.set_bytes( tparams::ORIGINAL_DESTINATION_CONNECTION_ID, packet.dcid().to_vec(), - ) + ); } } (PacketType::VersionNegotiation, State::WaitInitial, Role::Client) => { - match packet.supported_versions() { - Ok(versions) => { - if versions.is_empty() - || versions.contains(&self.version().wire_version()) - || versions.contains(&0) - || packet.scid() != self.odcid().unwrap() - || matches!( - self.address_validation, - AddressValidationInfo::Retry { .. } - ) - { - // Ignore VersionNegotiation packets that contain the current version. - // Or don't have the right connection ID. - // Or are received after a Retry. - self.stats.borrow_mut().pkt_dropped("Invalid VN"); - return Ok(PreprocessResult::End); - } - + if let Ok(versions) = packet.supported_versions() { + if versions.is_empty() + || versions.contains(&self.version().wire_version()) + || versions.contains(&0) + || &packet.scid() != self.odcid().unwrap() + || matches!(self.address_validation, AddressValidationInfo::Retry { .. }) + { + // Ignore VersionNegotiation packets that contain the current version. + // Or don't have the right connection ID. + // Or are received after a Retry. + self.stats.borrow_mut().pkt_dropped("Invalid VN"); + } else { self.version_negotiation(&versions, now)?; - return Ok(PreprocessResult::End); } - Err(_) => { - self.stats.borrow_mut().pkt_dropped("VN with no versions"); - return Ok(PreprocessResult::End); - } - } + } else { + self.stats.borrow_mut().pkt_dropped("VN with no versions"); + }; + return Ok(PreprocessResult::End); } (PacketType::Retry, State::WaitInitial, Role::Client) => { self.handle_retry(packet, now); return Ok(PreprocessResult::Next); } - (PacketType::Handshake, State::WaitInitial, Role::Client) - | (PacketType::Short, State::WaitInitial, Role::Client) => { + (PacketType::Handshake | PacketType::Short, State::WaitInitial, Role::Client) => { // This packet can't be processed now, but it could be a sign // that Initial packets were lost. // Resend Initial CRYPTO frames immediately a few times just @@ -1279,9 +1328,7 @@ impl Connection { self.crypto.resend_unacked(PacketNumberSpace::Initial); } } - (PacketType::VersionNegotiation, ..) - | (PacketType::Retry, ..) - | (PacketType::OtherVersion, ..) => { + (PacketType::VersionNegotiation | PacketType::Retry | PacketType::OtherVersion, ..) => { self.stats .borrow_mut() .pkt_dropped(format!("{:?}", packet.packet_type())); @@ -1346,7 +1393,7 @@ impl Connection { self.handle_migration(path, d, migrate, now); } else if self.role != Role::Client && (packet.packet_type() == PacketType::Handshake - || (packet.dcid().len() >= 8 && packet.dcid() == &self.local_initial_source_cid)) + || (packet.dcid().len() >= 8 && packet.dcid() == self.local_initial_source_cid)) { // We only allow one path during setup, so apply handshake // path validation to this path. @@ -1356,12 +1403,13 @@ impl Connection { /// Take a datagram as input. This reports an error if the packet was bad. /// This takes two times: when the datagram was received, and the current time. - fn input(&mut self, d: Datagram, received: Instant, now: Instant) { + fn input(&mut self, d: &Datagram, received: Instant, now: Instant) { // First determine the path. let path = self.paths.find_path_with_rebinding( d.destination(), d.source(), self.conn_params.get_cc_algorithm(), + self.conn_params.pacing_enabled(), now, ); path.borrow_mut().add_received(d.len()); @@ -1369,7 +1417,7 @@ impl Connection { self.capture_error(Some(path), now, 0, res).ok(); } - fn input_path(&mut self, path: &PathRef, d: Datagram, now: Instant) -> Res<()> { + fn input_path(&mut self, path: &PathRef, d: &Datagram, now: Instant) -> Res<()> { let mut slc = &d[..]; let mut dcid = None; @@ -1417,7 +1465,7 @@ impl Connection { self.stats.borrow_mut().dups_rx += 1; } else { match self.process_packet(path, &payload, now) { - Ok(migrate) => self.postprocess_packet(path, &d, &packet, migrate, now), + Ok(migrate) => self.postprocess_packet(path, d, &packet, migrate, now), Err(e) => { self.ensure_error_path(path, &packet, now); return Err(e); @@ -1449,7 +1497,7 @@ impl Connection { // Decryption failure, or not having keys is not fatal. // If the state isn't available, or we can't decrypt the packet, drop // the rest of the datagram on the floor, but don't generate an error. - self.check_stateless_reset(path, &d, dcid.is_none(), now)?; + self.check_stateless_reset(path, d, dcid.is_none(), now)?; self.stats.borrow_mut().pkt_dropped("Decryption failure"); qlog::packet_dropped(&mut self.qlog, &packet); } @@ -1457,7 +1505,7 @@ impl Connection { slc = remainder; dcid = Some(ConnectionId::from(packet.dcid())); } - self.check_stateless_reset(path, &d, dcid.is_none(), now)?; + self.check_stateless_reset(path, d, dcid.is_none(), now)?; Ok(()) } @@ -1629,6 +1677,7 @@ impl Connection { /// Either way, the path is probed and will be abandoned if the probe fails. /// /// # Errors + /// /// Fails if this is not a client, not confirmed, or there are not enough connection /// IDs available to use. pub fn migrate( @@ -1667,9 +1716,13 @@ impl Connection { return Err(Error::InvalidMigration); } - let path = self - .paths - .find_path(local, remote, self.conn_params.get_cc_algorithm(), now); + let path = self.paths.find_path( + local, + remote, + self.conn_params.get_cc_algorithm(), + self.conn_params.pacing_enabled(), + now, + ); self.ensure_permanent(&path)?; qinfo!( [self], @@ -1844,12 +1897,9 @@ impl Connection { let grease_quic_bit = self.can_grease_quic_bit(); let version = self.version(); for space in PacketNumberSpace::iter() { - let (cspace, tx) = - if let Some(crypto) = self.crypto.states.select_tx_mut(self.version, *space) { - crypto - } else { - continue; - }; + let Some((cspace, tx)) = self.crypto.states.select_tx_mut(self.version, *space) else { + continue; + }; let path = close.path().borrow(); let (_, mut builder) = Self::build_packet_header( @@ -1884,9 +1934,6 @@ impl Connection { .as_ref() .unwrap_or(&close) .write_frame(&mut builder); - if builder.len() > builder.limit() { - return Err(Error::InternalError(10)); - } encoder = builder.build(tx)?; } @@ -1931,7 +1978,7 @@ impl Connection { if builder.is_full() { return Ok(()); } - self.paths.write_frames(builder, tokens, frame_stats)?; + self.paths.write_frames(builder, tokens, frame_stats); if builder.is_full() { return Ok(()); } @@ -2056,7 +2103,7 @@ impl Connection { builder, &mut tokens, stats, - )?; + ); } let ack_end = builder.len(); @@ -2071,7 +2118,7 @@ impl Connection { &mut self.stats.borrow_mut().frame_tx, full_mtu, now, - )? { + ) { builder.enable_padding(true); } } @@ -2132,12 +2179,9 @@ impl Connection { let mut encoder = Encoder::with_capacity(profile.limit()); for space in PacketNumberSpace::iter() { // Ensure we have tx crypto state for this epoch, or skip it. - let (cspace, tx) = - if let Some(crypto) = self.crypto.states.select_tx_mut(self.version, *space) { - crypto - } else { - continue; - }; + let Some((cspace, tx)) = self.crypto.states.select_tx_mut(self.version, *space) else { + continue; + }; let header_start = encoder.len(); let (pt, mut builder) = Self::build_packet_header( @@ -2239,6 +2283,7 @@ impl Connection { } if encoder.is_empty() { + qinfo!("TX blocked, profile={:?} ", profile); Ok(SendOption::No(profile.paced())) } else { // Perform additional padding for Initial packets as necessary. @@ -2282,6 +2327,7 @@ impl Connection { qinfo!([self], "client_start"); debug_assert_eq!(self.role, Role::Client); qlog::client_connection_started(&mut self.qlog, &self.paths.primary()); + qlog::client_version_information_initiated(&mut self.qlog, self.conn_params.get_versions()); self.handshake(now, self.version, PacketNumberSpace::Initial, None)?; self.set_state(State::WaitInitial); @@ -2630,7 +2676,7 @@ impl Connection { &data ); self.stats.borrow_mut().frame_rx.crypto += 1; - self.crypto.streams.inbound_frame(space, offset, data); + self.crypto.streams.inbound_frame(space, offset, data)?; if self.crypto.streams.data_ready(space) { let mut buf = Vec::new(); let read = self.crypto.streams.read_to_end(space, &mut buf); @@ -2905,7 +2951,7 @@ impl Connection { self.streams.clear_streams(); } self.events.connection_state_change(state); - qlog::connection_state_updated(&mut self.qlog, &self.state) + qlog::connection_state_updated(&mut self.qlog, &self.state); } else if mem::discriminant(&state) != mem::discriminant(&self.state) { // Only tolerate a regression in state if the new state is closing // and the connection is already closed. @@ -2919,7 +2965,9 @@ impl Connection { /// Create a stream. /// Returns new stream id + /// /// # Errors + /// /// `ConnectionState` if the connecton stat does not allow to create streams. /// `StreamLimitError` if we are limiied by server's stream concurence. pub fn stream_create(&mut self, st: StreamType) -> Res { @@ -2941,7 +2989,9 @@ impl Connection { } /// Set the priority of a stream. + /// /// # Errors + /// /// `InvalidStreamId` the stream does not exist. pub fn stream_priority( &mut self, @@ -2956,7 +3006,9 @@ impl Connection { } /// Set the SendOrder of a stream. Re-enqueues to keep the ordering correct + /// /// # Errors + /// /// Returns InvalidStreamId if the stream id doesn't exist pub fn stream_sendorder( &mut self, @@ -2967,7 +3019,9 @@ impl Connection { } /// Set the Fairness of a stream + /// /// # Errors + /// /// Returns InvalidStreamId if the stream id doesn't exist pub fn stream_fairness(&mut self, stream_id: StreamId, fairness: bool) -> Res<()> { self.streams.set_fairness(stream_id, fairness) @@ -2986,7 +3040,9 @@ impl Connection { /// Send data on a stream. /// Returns how many bytes were successfully sent. Could be less /// than total, based on receiver credit space available, etc. + /// /// # Errors + /// /// `InvalidStreamId` the stream does not exist, /// `InvalidInput` if length of `data` is zero, /// `FinalSizeError` if the stream has already been closed. @@ -2997,7 +3053,9 @@ impl Connection { /// Send all data or nothing on a stream. May cause DATA_BLOCKED or /// STREAM_DATA_BLOCKED frames to be sent. /// Returns true if data was successfully sent, otherwise false. + /// /// # Errors + /// /// `InvalidStreamId` the stream does not exist, /// `InvalidInput` if length of `data` is zero, /// `FinalSizeError` if the stream has already been closed. @@ -3038,7 +3096,9 @@ impl Connection { /// Read buffered data from stream. bool says whether read bytes includes /// the final data on stream. + /// /// # Errors + /// /// `InvalidStreamId` if the stream does not exist. /// `NoMoreData` if data and fin bit were previously read by the application. pub fn stream_recv(&mut self, stream_id: StreamId, data: &mut [u8]) -> Res<(usize, bool)> { @@ -3057,7 +3117,9 @@ impl Connection { } /// Increases `max_stream_data` for a `stream_id`. + /// /// # Errors + /// /// Returns `InvalidStreamId` if a stream does not exist or the receiving /// side is closed. pub fn set_stream_max_data(&mut self, stream_id: StreamId, max_data: u64) -> Res<()> { @@ -3071,7 +3133,9 @@ impl Connection { /// (if `keep` is `true`) or no longer important (if `keep` is `false`). If any /// stream is marked this way, PING frames will be used to keep the connection /// alive, even when there is no activity. + /// /// # Errors + /// /// Returns `InvalidStreamId` if a stream does not exist or the receiving /// side is closed. pub fn stream_keep_alive(&mut self, stream_id: StreamId, keep: bool) -> Res<()> { @@ -3085,7 +3149,9 @@ impl Connection { /// 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. + /// /// # Error + /// /// The function returns `NotAvailable` if datagrams are not enabled. pub fn max_datagram_size(&self) -> Res { let max_dgram_size = self.quic_datagrams.remote_datagram_size(); @@ -3093,13 +3159,11 @@ impl Connection { return Err(Error::NotAvailable); } let version = self.version(); - let (cspace, tx) = if let Some(crypto) = self + let Some((cspace, tx)) = self .crypto .states .select_tx(self.version, PacketNumberSpace::ApplicationData) - { - crypto - } else { + else { return Err(Error::NotAvailable); }; let path = self.paths.primary_fallible().ok_or(Error::NotAvailable)?; @@ -3128,7 +3192,9 @@ impl Connection { } /// Queue a datagram for sending. + /// /// # Error + /// /// The function returns `TooMuchData` if the supply buffer is bigger than /// the allowed remote datagram size. The funcion does not check if the /// datagram can fit into a packet (i.e. MTU limit). This is checked during diff --git a/third_party/rust/neqo-transport/src/connection/params.rs b/third_party/rust/neqo-transport/src/connection/params.rs index 9d0db0f45d1a..48aba4303b27 100644 --- a/third_party/rust/neqo-transport/src/connection/params.rs +++ b/third_party/rust/neqo-transport/src/connection/params.rs @@ -4,18 +4,19 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use crate::connection::{ConnectionIdManager, Role, LOCAL_ACTIVE_CID_LIMIT}; +use std::{cmp::max, convert::TryFrom, time::Duration}; + pub use crate::recovery::FAST_PTO_SCALE; -use crate::recv_stream::RECV_BUFFER_SIZE; -use crate::rtt::GRANULARITY; -use crate::stream_id::StreamType; -use crate::tparams::{self, PreferredAddress, TransportParameter, TransportParametersHandler}; -use crate::tracking::DEFAULT_ACK_DELAY; -use crate::version::{Version, VersionConfig}; -use crate::{CongestionControlAlgorithm, Res}; -use std::cmp::max; -use std::convert::TryFrom; -use std::time::Duration; +use crate::{ + connection::{ConnectionIdManager, Role, LOCAL_ACTIVE_CID_LIMIT}, + recv_stream::RECV_BUFFER_SIZE, + rtt::GRANULARITY, + stream_id::StreamType, + tparams::{self, PreferredAddress, TransportParameter, TransportParametersHandler}, + tracking::DEFAULT_ACK_DELAY, + version::{Version, VersionConfig}, + CongestionControlAlgorithm, Res, +}; const LOCAL_MAX_DATA: u64 = 0x3FFF_FFFF_FFFF_FFFF; // 2^62-1 const LOCAL_STREAM_LIMIT_BIDI: u64 = 16; @@ -49,11 +50,14 @@ pub struct ConnectionParameters { cc_algorithm: CongestionControlAlgorithm, /// Initial connection-level flow control limit. max_data: u64, - /// Initial flow control limit for receiving data on bidirectional streams that the peer creates. + /// Initial flow control limit for receiving data on bidirectional streams that the peer + /// creates. max_stream_data_bidi_remote: u64, - /// Initial flow control limit for receiving data on bidirectional streams that this endpoint creates. + /// Initial flow control limit for receiving data on bidirectional streams that this endpoint + /// creates. max_stream_data_bidi_local: u64, - /// Initial flow control limit for receiving data on unidirectional streams that the peer creates. + /// Initial flow control limit for receiving data on unidirectional streams that the peer + /// creates. max_stream_data_uni: u64, /// Initial limit on bidirectional streams that the peer creates. max_streams_bidi: u64, @@ -75,6 +79,7 @@ pub struct ConnectionParameters { fast_pto: u8, fuzzing: bool, grease: bool, + pacing: bool, } impl Default for ConnectionParameters { @@ -97,6 +102,7 @@ impl Default for ConnectionParameters { fast_pto: FAST_PTO_SCALE, fuzzing: false, grease: true, + pacing: true, } } } @@ -145,6 +151,7 @@ impl ConnectionParameters { } /// # Panics + /// /// If v > 2^60 (the maximum allowed by the protocol). pub fn max_streams(mut self, stream_type: StreamType, v: u64) -> Self { assert!(v <= (1 << 60), "max_streams is too large"); @@ -160,7 +167,9 @@ impl ConnectionParameters { } /// Get the maximum stream data that we will accept on different types of streams. + /// /// # Panics + /// /// If `StreamType::UniDi` and `false` are passed as that is not a valid combination. pub fn get_max_stream_data(&self, stream_type: StreamType, remote: bool) -> u64 { match (stream_type, remote) { @@ -174,7 +183,9 @@ impl ConnectionParameters { } /// Set the maximum stream data that we will accept on different types of streams. + /// /// # Panics + /// /// If `StreamType::UniDi` and `false` are passed as that is not a valid combination /// or if v >= 62 (the maximum allowed by the protocol). pub fn max_stream_data(mut self, stream_type: StreamType, remote: bool, v: u64) -> Self { @@ -222,6 +233,7 @@ impl ConnectionParameters { } /// # Panics + /// /// If `timeout` is 2^62 milliseconds or more. pub fn idle_timeout(mut self, timeout: Duration) -> Self { assert!(timeout.as_millis() < (1 << 62), "idle timeout is too long"); @@ -279,6 +291,7 @@ impl ConnectionParameters { /// congestion. /// /// # Panics + /// /// A value of 0 is invalid and will cause a panic. pub fn fast_pto(mut self, scale: u8) -> Self { assert_ne!(scale, 0); @@ -304,6 +317,15 @@ impl ConnectionParameters { self } + pub fn pacing_enabled(&self) -> bool { + self.pacing + } + + pub fn pacing(mut self, pacing: bool) -> Self { + self.pacing = pacing; + self + } + pub fn create_transport_parameter( &self, role: Role, diff --git a/third_party/rust/neqo-transport/src/connection/saved.rs b/third_party/rust/neqo-transport/src/connection/saved.rs index 368a859f5d53..f5616c732a68 100644 --- a/third_party/rust/neqo-transport/src/connection/saved.rs +++ b/third_party/rust/neqo-transport/src/connection/saved.rs @@ -4,11 +4,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::mem; -use std::time::Instant; +use std::{mem, time::Instant}; + +use neqo_common::{qdebug, qinfo, Datagram}; use crate::crypto::CryptoSpace; -use neqo_common::{qdebug, qinfo, Datagram}; /// The number of datagrams that are saved during the handshake when /// keys to decrypt them are not yet available. diff --git a/third_party/rust/neqo-transport/src/connection/state.rs b/third_party/rust/neqo-transport/src/connection/state.rs index a34c91865e00..9afb42174f11 100644 --- a/third_party/rust/neqo-transport/src/connection/state.rs +++ b/third_party/rust/neqo-transport/src/connection/state.rs @@ -4,20 +4,25 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use neqo_common::Encoder; -use std::cmp::{min, Ordering}; -use std::mem; -use std::rc::Rc; -use std::time::Instant; - -use crate::frame::{ - FrameType, FRAME_TYPE_CONNECTION_CLOSE_APPLICATION, FRAME_TYPE_CONNECTION_CLOSE_TRANSPORT, - FRAME_TYPE_HANDSHAKE_DONE, +use std::{ + cmp::{min, Ordering}, + mem, + rc::Rc, + time::Instant, +}; + +use neqo_common::Encoder; + +use crate::{ + frame::{ + FrameType, FRAME_TYPE_CONNECTION_CLOSE_APPLICATION, FRAME_TYPE_CONNECTION_CLOSE_TRANSPORT, + FRAME_TYPE_HANDSHAKE_DONE, + }, + packet::PacketBuilder, + path::PathRef, + recovery::RecoveryToken, + ConnectionError, Error, Res, }; -use crate::packet::PacketBuilder; -use crate::path::PathRef; -use crate::recovery::RecoveryToken; -use crate::{ConnectionError, Error, Res}; #[derive(Clone, Debug, PartialEq, Eq)] /// The state of the Connection. @@ -206,16 +211,13 @@ impl StateSignaling { debug_assert!(false, "StateSignaling must be in Idle state."); return; } - *self = Self::HandshakeDone + *self = Self::HandshakeDone; } pub fn write_done(&mut self, builder: &mut PacketBuilder) -> Res> { if matches!(self, Self::HandshakeDone) && builder.remaining() >= 1 { *self = Self::Idle; builder.encode_varint(FRAME_TYPE_HANDSHAKE_DONE); - if builder.len() > builder.limit() { - return Err(Error::InternalError(14)); - } Ok(Some(RecoveryToken::HandshakeDone)) } else { Ok(None) diff --git a/third_party/rust/neqo-transport/src/connection/tests/ackrate.rs b/third_party/rust/neqo-transport/src/connection/tests/ackrate.rs index 8d0f73f15417..1b83d42acda7 100644 --- a/third_party/rust/neqo-transport/src/connection/tests/ackrate.rs +++ b/third_party/rust/neqo-transport/src/connection/tests/ackrate.rs @@ -4,6 +4,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::{mem, time::Duration}; + +use test_fixture::{addr_v4, assertions}; + use super::{ super::{ConnectionParameters, ACK_RATIO_SCALE}, ack_bytes, connect_rtt_idle, default_client, default_server, fill_cwnd, increase_cwnd, @@ -11,9 +15,6 @@ use super::{ }; use crate::stream_id::StreamType; -use std::{mem, time::Duration}; -use test_fixture::{addr_v4, assertions}; - /// With the default RTT here (100ms) and default ratio (4), endpoints won't send /// `ACK_FREQUENCY` as the ACK delay isn't different enough from the default. #[test] @@ -71,7 +72,7 @@ fn ack_rate_exit_slow_start() { // and to send ACK_FREQUENCY. now += DEFAULT_RTT / 2; assert_eq!(client.stats().frame_tx.ack_frequency, 0); - let af = client.process(Some(ack), now).dgram(); + let af = client.process(Some(&ack), now).dgram(); assert!(af.is_some()); assert_eq!(client.stats().frame_tx.ack_frequency, 1); } @@ -120,11 +121,11 @@ fn ack_rate_client_one_rtt() { // The first packet will elicit an immediate ACK however, so do this twice. let d = send_something(&mut client, now); now += RTT / 2; - let ack = server.process(Some(d), now).dgram(); + let ack = server.process(Some(&d), now).dgram(); assert!(ack.is_some()); let d = send_something(&mut client, now); now += RTT / 2; - let delay = server.process(Some(d), now).callback(); + let delay = server.process(Some(&d), now).callback(); assert_eq!(delay, RTT); assert_eq!(client.stats().frame_tx.ack_frequency, 1); @@ -143,11 +144,11 @@ fn ack_rate_server_half_rtt() { now += RTT / 2; // The client now will acknowledge immediately because it has been more than // an RTT since it last sent an acknowledgment. - let ack = client.process(Some(d), now); + let ack = client.process(Some(&d), now); assert!(ack.as_dgram_ref().is_some()); let d = send_something(&mut server, now); now += RTT / 2; - let delay = client.process(Some(d), now).callback(); + let delay = client.process(Some(&d), now).callback(); assert_eq!(delay, RTT / 2); assert_eq!(server.stats().frame_tx.ack_frequency, 1); @@ -171,7 +172,7 @@ fn migrate_ack_delay() { let client2 = send_something(&mut client, now); assertions::assert_v4_path(&client2, false); // Doesn't. Is dropped. now += DEFAULT_RTT / 2; - server.process_input(client1, now); + server.process_input(&client1, now); let stream = client.stream_create(StreamType::UniDi).unwrap(); let now = increase_cwnd(&mut client, &mut server, stream, now); @@ -187,7 +188,7 @@ fn migrate_ack_delay() { // After noticing this new loss, the client sends ACK_FREQUENCY. // It has sent a few before (as we dropped `client2`), so ignore those. let ad_before = client.stats().frame_tx.ack_frequency; - let af = client.process(Some(ack), now).dgram(); + let af = client.process(Some(&ack), now).dgram(); assert!(af.is_some()); assert_eq!(client.stats().frame_tx.ack_frequency, ad_before + 1); } diff --git a/third_party/rust/neqo-transport/src/connection/tests/cc.rs b/third_party/rust/neqo-transport/src/connection/tests/cc.rs index 26e4dbd014ff..b3467ea67ca7 100644 --- a/third_party/rust/neqo-transport/src/connection/tests/cc.rs +++ b/third_party/rust/neqo-transport/src/connection/tests/cc.rs @@ -4,23 +4,23 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use super::super::Output; -use super::{ - ack_bytes, assert_full_cwnd, connect_rtt_idle, cwnd, cwnd_avail, cwnd_packets, default_client, - default_server, fill_cwnd, induce_persistent_congestion, send_something, DEFAULT_RTT, - FORCE_IDLE_CLIENT_1RTT_PACKETS, POST_HANDSHAKE_CWND, -}; -use crate::cc::MAX_DATAGRAM_SIZE; -use crate::packet::PacketNumber; -use crate::recovery::{ACK_ONLY_SIZE_LIMIT, PACKET_THRESHOLD}; -use crate::sender::PACING_BURST_SIZE; -use crate::stream_id::StreamType; -use crate::tracking::DEFAULT_ACK_PACKET_TOLERANCE; +use std::{convert::TryFrom, mem, time::Duration}; use neqo_common::{qdebug, qinfo, Datagram}; -use std::convert::TryFrom; -use std::mem; -use std::time::Duration; + +use super::{ + super::Output, ack_bytes, assert_full_cwnd, connect_rtt_idle, cwnd, cwnd_avail, cwnd_packets, + default_client, default_server, fill_cwnd, induce_persistent_congestion, send_something, + CLIENT_HANDSHAKE_1RTT_PACKETS, DEFAULT_RTT, POST_HANDSHAKE_CWND, +}; +use crate::{ + cc::MAX_DATAGRAM_SIZE, + packet::PacketNumber, + recovery::{ACK_ONLY_SIZE_LIMIT, PACKET_THRESHOLD}, + sender::PACING_BURST_SIZE, + stream_id::StreamType, + tracking::DEFAULT_ACK_PACKET_TOLERANCE, +}; #[test] /// Verify initial CWND is honored. @@ -54,7 +54,7 @@ fn cc_slow_start_to_cong_avoidance_recovery_period() { // We have already sent packets in `connect_rtt_idle`, // so include a fudge factor. let flight1_largest = - PacketNumber::try_from(c_tx_dgrams.len() + FORCE_IDLE_CLIENT_1RTT_PACKETS).unwrap(); + PacketNumber::try_from(c_tx_dgrams.len() + CLIENT_HANDSHAKE_1RTT_PACKETS).unwrap(); // Server: Receive and generate ack now += DEFAULT_RTT / 2; @@ -66,7 +66,7 @@ fn cc_slow_start_to_cong_avoidance_recovery_period() { // Client: Process ack now += DEFAULT_RTT / 2; - client.process_input(s_ack, now); + client.process_input(&s_ack, now); assert_eq!( client.stats().frame_rx.largest_acknowledged, flight1_largest @@ -88,7 +88,7 @@ fn cc_slow_start_to_cong_avoidance_recovery_period() { // Client: Process ack now += DEFAULT_RTT / 2; - client.process_input(s_ack, now); + client.process_input(&s_ack, now); assert_eq!( client.stats().frame_rx.largest_acknowledged, flight2_largest @@ -118,7 +118,7 @@ fn cc_cong_avoidance_recovery_period_unchanged() { // Server: Receive and generate ack let s_ack = ack_bytes(&mut server, stream_id, c_tx_dgrams, now); - client.process_input(s_ack, now); + client.process_input(&s_ack, now); let cwnd1 = cwnd(&client); @@ -126,7 +126,7 @@ fn cc_cong_avoidance_recovery_period_unchanged() { let s_ack = ack_bytes(&mut server, stream_id, c_tx_dgrams2, now); // ACK more packets but they were sent before end of recovery period - client.process_input(s_ack, now); + client.process_input(&s_ack, now); // cwnd should not have changed since ACKed packets were sent before // recovery period expired @@ -156,12 +156,12 @@ fn single_packet_on_recovery() { // Acknowledge just one packet and cause one packet to be declared lost. // The length is the amount of credit the client should have. - let ack = server.process(Some(delivered), now).dgram(); + let ack = server.process(Some(&delivered), now).dgram(); assert!(ack.is_some()); // The client should see the loss and enter recovery. // As there are many outstanding packets, there should be no available cwnd. - client.process_input(ack.unwrap(), now); + client.process_input(&ack.unwrap(), now); assert_eq!(cwnd_avail(&client), 0); // The client should send one packet, ignoring the cwnd. @@ -193,7 +193,7 @@ fn cc_cong_avoidance_recovery_period_to_cong_avoidance() { // Client: Process ack now += DEFAULT_RTT / 2; - client.process_input(s_ack, now); + client.process_input(&s_ack, now); // Should be in CARP now. now += DEFAULT_RTT / 2; @@ -227,7 +227,7 @@ fn cc_cong_avoidance_recovery_period_to_cong_avoidance() { let most = c_tx_dgrams.len() - usize::try_from(DEFAULT_ACK_PACKET_TOLERANCE).unwrap() - 1; let s_ack = ack_bytes(&mut server, stream_id, c_tx_dgrams.drain(..most), now); assert_eq!(cwnd(&client), expected_cwnd); - client.process_input(s_ack, now); + client.process_input(&s_ack, now); // make sure to fill cwnd again. let (mut new_pkts, next_now) = fill_cwnd(&mut client, stream_id, now); now = next_now; @@ -235,7 +235,7 @@ fn cc_cong_avoidance_recovery_period_to_cong_avoidance() { let s_ack = ack_bytes(&mut server, stream_id, c_tx_dgrams, now); assert_eq!(cwnd(&client), expected_cwnd); - client.process_input(s_ack, now); + client.process_input(&s_ack, now); // make sure to fill cwnd again. let (mut new_pkts, next_now) = fill_cwnd(&mut client, stream_id, now); now = next_now; @@ -287,7 +287,7 @@ fn cc_slow_start_to_persistent_congestion_some_acks() { let s_ack = ack_bytes(&mut server, stream, c_tx_dgrams, now); now += Duration::from_millis(100); - client.process_input(s_ack, now); + client.process_input(&s_ack, now); // send bytes that will be lost let (_, next_now) = fill_cwnd(&mut client, stream, now); @@ -333,7 +333,7 @@ fn cc_persistent_congestion_to_slow_start() { // No longer in CARP. (pkts acked from after start of CARP) // Should be in slow start now. - client.process_input(s_ack, now); + client.process_input(&s_ack, now); // ACKing 2 packets should let client send 4. let (c_tx_dgrams, _) = fill_cwnd(&mut client, stream, now); @@ -371,11 +371,11 @@ fn ack_are_not_cc() { // The client can ack the server packet even if cc windows is full. qdebug!([client], "Process ack-eliciting"); - let ack_pkt = client.process(ack_eliciting_packet, now).dgram(); + let ack_pkt = client.process(ack_eliciting_packet.as_ref(), now).dgram(); assert!(ack_pkt.is_some()); qdebug!([server], "Handle ACK"); let prev_ack_count = server.stats().frame_rx.ack; - server.process_input(ack_pkt.unwrap(), now); + server.process_input(&ack_pkt.unwrap(), now); assert_eq!(server.stats().frame_rx.ack, prev_ack_count + 1); } diff --git a/third_party/rust/neqo-transport/src/connection/tests/close.rs b/third_party/rust/neqo-transport/src/connection/tests/close.rs index a9f1fafa2577..f45e77e549f9 100644 --- a/third_party/rust/neqo-transport/src/connection/tests/close.rs +++ b/third_party/rust/neqo-transport/src/connection/tests/close.rs @@ -4,14 +4,18 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use super::super::{Connection, Output, State}; -use super::{connect, connect_force_idle, default_client, default_server, send_something}; -use crate::tparams::{self, TransportParameter}; -use crate::{AppError, ConnectionError, Error, ERROR_APPLICATION_CLOSE}; - -use neqo_common::Datagram; use std::time::Duration; -use test_fixture::{self, addr, now}; + +use test_fixture::{self, datagram, now}; + +use super::{ + super::{Connection, Output, State}, + connect, connect_force_idle, default_client, default_server, send_something, +}; +use crate::{ + tparams::{self, TransportParameter}, + AppError, ConnectionError, Error, ERROR_APPLICATION_CLOSE, +}; fn assert_draining(c: &Connection, expected: &Error) { assert!(c.state().closed()); @@ -38,7 +42,7 @@ fn connection_close() { let out = client.process(None, now); - server.process_input(out.dgram().unwrap(), now); + server.process_input(&out.dgram().unwrap(), now); assert_draining(&server, &Error::PeerApplicationError(42)); } @@ -55,7 +59,7 @@ fn connection_close_with_long_reason_string() { let out = client.process(None, now); - server.process_input(out.dgram().unwrap(), now); + server.process_input(&out.dgram().unwrap(), now); assert_draining(&server, &Error::PeerApplicationError(42)); } @@ -68,7 +72,7 @@ fn early_application_close() { // One flight each. let dgram = client.process(None, now()).dgram(); assert!(dgram.is_some()); - let dgram = server.process(dgram, now()).dgram(); + let dgram = server.process(dgram.as_ref(), now()).dgram(); assert!(dgram.is_some()); server.close(now(), 77, String::new()); @@ -76,7 +80,7 @@ fn early_application_close() { let dgram = server.process(None, now()).dgram(); assert!(dgram.is_some()); - client.process_input(dgram.unwrap(), now()); + client.process_input(&dgram.unwrap(), now()); assert_draining(&client, &Error::PeerError(ERROR_APPLICATION_CLOSE)); } @@ -93,13 +97,13 @@ fn bad_tls_version() { let dgram = client.process(None, now()).dgram(); assert!(dgram.is_some()); - let dgram = server.process(dgram, now()).dgram(); + let dgram = server.process(dgram.as_ref(), now()).dgram(); assert_eq!( *server.state(), State::Closed(ConnectionError::Transport(Error::ProtocolViolation)) ); assert!(dgram.is_some()); - client.process_input(dgram.unwrap(), now()); + client.process_input(&dgram.unwrap(), now()); assert_draining(&client, &Error::PeerError(Error::ProtocolViolation.code())); } @@ -116,11 +120,11 @@ fn closing_timers_interation() { // We're going to induce time-based loss recovery so that timer is set. let _p1 = send_something(&mut client, now); let p2 = send_something(&mut client, now); - let ack = server.process(Some(p2), now).dgram(); + let ack = server.process(Some(&p2), now).dgram(); assert!(ack.is_some()); // This is an ACK. // After processing the ACK, we should be on the loss recovery timer. - let cb = client.process(ack, now).callback(); + let cb = client.process(ack.as_ref(), now).callback(); assert_ne!(cb, Duration::from_secs(0)); now += cb; @@ -153,7 +157,7 @@ fn closing_and_draining() { // The client will spit out the same packet in response to anything it receives. let p3 = send_something(&mut server, now()); - let client_close2 = client.process(Some(p3), now()).dgram(); + let client_close2 = client.process(Some(&p3), now()).dgram(); assert_eq!( client_close.as_ref().unwrap().len(), client_close2.as_ref().unwrap().len() @@ -168,14 +172,14 @@ fn closing_and_draining() { ); // When the server receives the close, it too should generate CONNECTION_CLOSE. - let server_close = server.process(client_close, now()).dgram(); + let server_close = server.process(client_close.as_ref(), now()).dgram(); assert!(server.state().closed()); assert!(server_close.is_some()); // .. but it ignores any further close packets. - let server_close_timer = server.process(client_close2, now()).callback(); + let server_close_timer = server.process(client_close2.as_ref(), now()).callback(); assert_ne!(server_close_timer, Duration::from_secs(0)); // Even a legitimate packet without a close in it. - let server_close_timer2 = server.process(Some(p1), now()).callback(); + let server_close_timer2 = server.process(Some(&p1), now()).callback(); assert_eq!(server_close_timer, server_close_timer2); let end = server.process(None, now() + server_close_timer); @@ -201,6 +205,6 @@ fn stateless_reset_client() { .unwrap(); connect_force_idle(&mut client, &mut server); - client.process_input(Datagram::new(addr(), addr(), vec![77; 21]), now()); + client.process_input(&datagram(vec![77; 21]), now()); assert_draining(&client, &Error::StatelessReset); } diff --git a/third_party/rust/neqo-transport/src/connection/tests/datagram.rs b/third_party/rust/neqo-transport/src/connection/tests/datagram.rs index 4348f2dd3b8a..5b7b8dc0b4fe 100644 --- a/third_party/rust/neqo-transport/src/connection/tests/datagram.rs +++ b/third_party/rust/neqo-transport/src/connection/tests/datagram.rs @@ -4,21 +4,23 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::{cell::RefCell, convert::TryFrom, rc::Rc}; + +use neqo_common::event::Provider; +use test_fixture::now; + use super::{ assert_error, connect_force_idle, default_client, default_server, new_client, new_server, AT_LEAST_PTO, }; -use crate::events::{ConnectionEvent, OutgoingDatagramOutcome}; -use crate::frame::FRAME_TYPE_DATAGRAM; -use crate::packet::PacketBuilder; -use crate::quic_datagrams::MAX_QUIC_DATAGRAM; use crate::{ + events::{ConnectionEvent, OutgoingDatagramOutcome}, + frame::FRAME_TYPE_DATAGRAM, + packet::PacketBuilder, + quic_datagrams::MAX_QUIC_DATAGRAM, send_stream::{RetransmissionPriority, TransmissionPriority}, Connection, ConnectionError, ConnectionParameters, Error, StreamType, }; -use neqo_common::event::Provider; -use std::{cell::RefCell, convert::TryFrom, rc::Rc}; -use test_fixture::now; const DATAGRAM_LEN_MTU: u64 = 1310; const DATA_MTU: &[u8] = &[1; 1310]; @@ -80,7 +82,7 @@ fn datagram_enabled_on_client() { let out = server.process_output(now()).dgram().unwrap(); assert_eq!(server.stats().frame_tx.datagram, dgram_sent + 1); - client.process_input(out, now()); + client.process_input(&out, now()); assert!(matches!( client.next_event().unwrap(), ConnectionEvent::Datagram(data) if data == DATA_SMALLER_THAN_MTU @@ -108,7 +110,7 @@ fn datagram_enabled_on_server() { let out = client.process_output(now()).dgram().unwrap(); assert_eq!(client.stats().frame_tx.datagram, dgram_sent + 1); - server.process_input(out, now()); + server.process_input(&out, now()); assert!(matches!( server.next_event().unwrap(), ConnectionEvent::Datagram(data) if data == DATA_SMALLER_THAN_MTU @@ -205,7 +207,7 @@ fn datagram_acked() { assert_eq!(client.stats().frame_tx.datagram, dgram_sent + 1); let dgram_received = server.stats().frame_rx.datagram; - server.process_input(out.unwrap(), now()); + server.process_input(&out.unwrap(), now()); assert_eq!(server.stats().frame_rx.datagram, dgram_received + 1); let now = now() + AT_LEAST_PTO; // Ack should be sent @@ -218,7 +220,7 @@ fn datagram_acked() { ConnectionEvent::Datagram(data) if data == DATA_SMALLER_THAN_MTU )); - client.process_input(out.unwrap(), now); + client.process_input(&out.unwrap(), now); assert!(matches!( client.next_event().unwrap(), ConnectionEvent::OutgoingDatagramOutcome { id, outcome } if id == 1 && outcome == OutgoingDatagramOutcome::Acked @@ -230,7 +232,7 @@ fn send_packet_and_get_server_event( server: &mut Connection, ) -> ConnectionEvent { let out = client.process_output(now()).dgram(); - server.process_input(out.unwrap(), now()); + server.process_input(&out.unwrap(), now()); let mut events: Vec<_> = server .events() .filter_map(|evt| match evt { @@ -323,7 +325,7 @@ fn datagram_lost() { let pings_sent = client.stats().frame_tx.ping; let dgram_lost = client.stats().datagram_tx.lost; let out = client.process_output(now).dgram(); - assert!(out.is_some()); //PING probing + assert!(out.is_some()); // PING probing // Datagram is not sent again. assert_eq!(client.stats().frame_tx.ping, pings_sent + 1); assert_eq!(client.stats().frame_tx.datagram, dgram_sent2); @@ -358,7 +360,7 @@ fn dgram_no_allowed() { let out = server.process_output(now()).dgram().unwrap(); server.test_frame_writer = None; - client.process_input(out, now()); + client.process_input(&out, now()); assert_error( &client, @@ -379,7 +381,7 @@ fn dgram_too_big() { let out = server.process_output(now()).dgram().unwrap(); server.test_frame_writer = None; - client.process_input(out, now()); + client.process_input(&out, now()); assert_error( &client, @@ -414,7 +416,7 @@ fn outgoing_datagram_queue_full() { // Send DATA_SMALLER_THAN_MTU_2 datagram let out = client.process_output(now()).dgram(); assert_eq!(client.stats().frame_tx.datagram, dgram_sent + 1); - server.process_input(out.unwrap(), now()); + server.process_input(&out.unwrap(), now()); assert!(matches!( server.next_event().unwrap(), ConnectionEvent::Datagram(data) if data == DATA_SMALLER_THAN_MTU_2 @@ -424,7 +426,7 @@ fn outgoing_datagram_queue_full() { let dgram_sent2 = client.stats().frame_tx.datagram; let out = client.process_output(now()).dgram(); assert_eq!(client.stats().frame_tx.datagram, dgram_sent2 + 1); - server.process_input(out.unwrap(), now()); + server.process_input(&out.unwrap(), now()); assert!(matches!( server.next_event().unwrap(), ConnectionEvent::Datagram(data) if data == DATA_MTU @@ -438,7 +440,7 @@ fn send_datagram(sender: &mut Connection, receiver: &mut Connection, data: &[u8] assert_eq!(sender.stats().frame_tx.datagram, dgram_sent + 1); let dgram_received = receiver.stats().frame_rx.datagram; - receiver.process_input(out, now()); + receiver.process_input(&out, now()); assert_eq!(receiver.stats().frame_rx.datagram, dgram_received + 1); } @@ -552,7 +554,7 @@ fn multiple_quic_datagrams_in_one_packet() { let out = client.process_output(now()).dgram(); assert_eq!(client.stats().frame_tx.datagram, dgram_sent + 2); - server.process_input(out.unwrap(), now()); + server.process_input(&out.unwrap(), now()); let datagram = |e: &_| matches!(e, ConnectionEvent::Datagram(..)); assert_eq!(server.events().filter(datagram).count(), 2); } diff --git a/third_party/rust/neqo-transport/src/connection/tests/fuzzing.rs b/third_party/rust/neqo-transport/src/connection/tests/fuzzing.rs index 24201eff2618..5425e1a16e0e 100644 --- a/third_party/rust/neqo-transport/src/connection/tests/fuzzing.rs +++ b/third_party/rust/neqo-transport/src/connection/tests/fuzzing.rs @@ -8,11 +8,12 @@ #![warn(clippy::pedantic)] #![cfg(feature = "fuzzing")] -use super::{connect_force_idle, default_client, default_server}; -use crate::StreamType; use neqo_crypto::FIXED_TAG_FUZZING; use test_fixture::now; +use super::{connect_force_idle, default_client, default_server}; +use crate::StreamType; + #[test] fn no_encryption() { const DATA_CLIENT: &[u8] = &[2; 40]; @@ -27,7 +28,7 @@ fn no_encryption() { let client_pkt = client.process_output(now()).dgram().unwrap(); assert!(client_pkt[..client_pkt.len() - FIXED_TAG_FUZZING.len()].ends_with(DATA_CLIENT)); - server.process_input(client_pkt, now()); + server.process_input(&client_pkt, now()); let mut buf = vec![0; 100]; let (len, _) = server.stream_recv(stream_id, &mut buf).unwrap(); assert_eq!(len, DATA_CLIENT.len()); @@ -36,7 +37,7 @@ fn no_encryption() { let server_pkt = server.process_output(now()).dgram().unwrap(); assert!(server_pkt[..server_pkt.len() - FIXED_TAG_FUZZING.len()].ends_with(DATA_SERVER)); - client.process_input(server_pkt, now()); + client.process_input(&server_pkt, now()); let (len, _) = client.stream_recv(stream_id, &mut buf).unwrap(); assert_eq!(len, DATA_SERVER.len()); assert_eq!(&buf[..len], DATA_SERVER); diff --git a/third_party/rust/neqo-transport/src/connection/tests/handshake.rs b/third_party/rust/neqo-transport/src/connection/tests/handshake.rs index 5083ee7dcbba..93385ac1bcfa 100644 --- a/third_party/rust/neqo-transport/src/connection/tests/handshake.rs +++ b/third_party/rust/neqo-transport/src/connection/tests/handshake.rs @@ -4,33 +4,39 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use super::super::{Connection, Output, State}; -use super::{ - assert_error, connect, connect_force_idle, connect_with_rtt, default_client, default_server, - get_tokens, handshake, maybe_authenticate, resumed_server, send_something, - CountingConnectionIdGenerator, AT_LEAST_PTO, DEFAULT_RTT, DEFAULT_STREAM_DATA, -}; -use crate::connection::AddressValidation; -use crate::events::ConnectionEvent; -use crate::path::PATH_MTU_V6; -use crate::server::ValidateAddress; -use crate::tparams::{TransportParameter, MIN_ACK_DELAY}; -use crate::tracking::DEFAULT_ACK_DELAY; -use crate::{ - ConnectionError, ConnectionParameters, EmptyConnectionIdGenerator, Error, StreamType, Version, +use std::{ + cell::RefCell, + convert::TryFrom, + mem, + net::{IpAddr, Ipv6Addr, SocketAddr}, + rc::Rc, + time::Duration, }; use neqo_common::{event::Provider, qdebug, Datagram}; use neqo_crypto::{ constants::TLS_CHACHA20_POLY1305_SHA256, generate_ech_keys, AuthenticationStatus, }; -use std::cell::RefCell; -use std::convert::TryFrom; -use std::mem; -use std::net::{IpAddr, Ipv6Addr, SocketAddr}; -use std::rc::Rc; -use std::time::Duration; -use test_fixture::{self, addr, assertions, fixture_init, now, split_datagram}; +use test_fixture::{ + self, addr, assertions, assertions::assert_coalesced_0rtt, datagram, fixture_init, now, + split_datagram, +}; + +use super::{ + super::{Connection, Output, State}, + assert_error, connect, connect_force_idle, connect_with_rtt, default_client, default_server, + get_tokens, handshake, maybe_authenticate, resumed_server, send_something, + CountingConnectionIdGenerator, AT_LEAST_PTO, DEFAULT_RTT, DEFAULT_STREAM_DATA, +}; +use crate::{ + connection::AddressValidation, + events::ConnectionEvent, + path::PATH_MTU_V6, + server::ValidateAddress, + tparams::{TransportParameter, MIN_ACK_DELAY}, + tracking::DEFAULT_ACK_DELAY, + ConnectionError, ConnectionParameters, EmptyConnectionIdGenerator, Error, StreamType, Version, +}; const ECH_CONFIG_ID: u8 = 7; const ECH_PUBLIC_NAME: &str = "public.example"; @@ -45,31 +51,31 @@ fn full_handshake() { qdebug!("---- server: CH -> SH, EE, CERT, CV, FIN"); let mut server = default_server(); - let out = server.process(out.dgram(), now()); + let out = server.process(out.as_dgram_ref(), now()); assert!(out.as_dgram_ref().is_some()); assert_eq!(out.as_dgram_ref().unwrap().len(), PATH_MTU_V6); qdebug!("---- client: cert verification"); - let out = client.process(out.dgram(), now()); + let out = client.process(out.as_dgram_ref(), now()); assert!(out.as_dgram_ref().is_some()); - let out = server.process(out.dgram(), now()); + let out = server.process(out.as_dgram_ref(), now()); assert!(out.as_dgram_ref().is_none()); assert!(maybe_authenticate(&mut client)); qdebug!("---- client: SH..FIN -> FIN"); - let out = client.process(out.dgram(), now()); + let out = client.process(out.as_dgram_ref(), now()); assert!(out.as_dgram_ref().is_some()); assert_eq!(*client.state(), State::Connected); qdebug!("---- server: FIN -> ACKS"); - let out = server.process(out.dgram(), now()); + let out = server.process(out.as_dgram_ref(), now()); assert!(out.as_dgram_ref().is_some()); assert_eq!(*server.state(), State::Confirmed); qdebug!("---- client: ACKS -> 0"); - let out = client.process(out.dgram(), now()); + let out = client.process(out.as_dgram_ref(), now()); assert!(out.as_dgram_ref().is_none()); assert_eq!(*client.state(), State::Confirmed); } @@ -83,14 +89,14 @@ fn handshake_failed_authentication() { qdebug!("---- server: CH -> SH, EE, CERT, CV, FIN"); let mut server = default_server(); - let out = server.process(out.dgram(), now()); + let out = server.process(out.as_dgram_ref(), now()); assert!(out.as_dgram_ref().is_some()); qdebug!("---- client: cert verification"); - let out = client.process(out.dgram(), now()); + let out = client.process(out.as_dgram_ref(), now()); assert!(out.as_dgram_ref().is_some()); - let out = server.process(out.dgram(), now()); + let out = server.process(out.as_dgram_ref(), now()); assert!(out.as_dgram_ref().is_none()); let authentication_needed = |e| matches!(e, ConnectionEvent::AuthenticationNeeded); @@ -103,7 +109,7 @@ fn handshake_failed_authentication() { assert!(out.as_dgram_ref().is_some()); qdebug!("---- server: Alert(certificate_revoked)"); - let out = server.process(out.dgram(), now()); + let out = server.process(out.as_dgram_ref(), now()); assert!(out.as_dgram_ref().is_some()); assert_error(&client, &ConnectionError::Transport(Error::CryptoAlert(44))); assert_error(&server, &ConnectionError::Transport(Error::PeerError(300))); @@ -127,7 +133,7 @@ fn no_alpn() { handshake(&mut client, &mut server, now(), Duration::new(0, 0)); // TODO (mt): errors are immediate, which means that we never send CONNECTION_CLOSE // and the client never sees the server's rejection of its handshake. - //assert_error(&client, ConnectionError::Transport(Error::CryptoAlert(120))); + // assert_error(&client, ConnectionError::Transport(Error::CryptoAlert(120))); assert_error( &server, &ConnectionError::Transport(Error::CryptoAlert(120)), @@ -145,16 +151,16 @@ fn dup_server_flight1() { qdebug!("---- server: CH -> SH, EE, CERT, CV, FIN"); let mut server = default_server(); - let out_to_rep = server.process(out.dgram(), now()); + let out_to_rep = server.process(out.as_dgram_ref(), now()); assert!(out_to_rep.as_dgram_ref().is_some()); qdebug!("Output={:0x?}", out_to_rep.as_dgram_ref()); qdebug!("---- client: cert verification"); - let out = client.process(Some(out_to_rep.as_dgram_ref().unwrap().clone()), now()); + let out = client.process(Some(out_to_rep.as_dgram_ref().unwrap()), now()); assert!(out.as_dgram_ref().is_some()); qdebug!("Output={:0x?}", out.as_dgram_ref()); - let out = server.process(out.dgram(), now()); + let out = server.process(out.as_dgram_ref(), now()); assert!(out.as_dgram_ref().is_none()); assert!(maybe_authenticate(&mut client)); @@ -169,7 +175,7 @@ fn dup_server_flight1() { assert_eq!(1, client.stats().dropped_rx); qdebug!("---- Dup, ignored"); - let out = client.process(out_to_rep.dgram(), now()); + let out = client.process(out_to_rep.as_dgram_ref(), now()); assert!(out.as_dgram_ref().is_none()); qdebug!("Output={:0x?}", out.as_dgram_ref()); @@ -199,12 +205,12 @@ fn crypto_frame_split() { // The entire server flight doesn't fit in a single packet because the // certificate is large, therefore the server will produce 2 packets. - let server1 = server.process(client1.dgram(), now()); + let server1 = server.process(client1.as_dgram_ref(), now()); assert!(server1.as_dgram_ref().is_some()); let server2 = server.process(None, now()); assert!(server2.as_dgram_ref().is_some()); - let client2 = client.process(server1.dgram(), now()); + let client2 = client.process(server1.as_dgram_ref(), now()); // This is an ack. assert!(client2.as_dgram_ref().is_some()); // The client might have the certificate now, so we can't guarantee that @@ -213,11 +219,11 @@ fn crypto_frame_split() { assert_eq!(*client.state(), State::Handshaking); // let server process the ack for the first packet. - let server3 = server.process(client2.dgram(), now()); + let server3 = server.process(client2.as_dgram_ref(), now()); assert!(server3.as_dgram_ref().is_none()); // Consume the second packet from the server. - let client3 = client.process(server2.dgram(), now()); + let client3 = client.process(server2.as_dgram_ref(), now()); // Check authentication. let auth2 = maybe_authenticate(&mut client); @@ -225,13 +231,13 @@ fn crypto_frame_split() { // Now client has all data to finish handshake. assert_eq!(*client.state(), State::Connected); - let client4 = client.process(server3.dgram(), now()); + let client4 = client.process(server3.as_dgram_ref(), now()); // One of these will contain data depending on whether Authentication was completed // after the first or second server packet. assert!(client3.as_dgram_ref().is_some() ^ client4.as_dgram_ref().is_some()); - mem::drop(server.process(client3.dgram(), now())); - mem::drop(server.process(client4.dgram(), now())); + mem::drop(server.process(client3.as_dgram_ref(), now())); + mem::drop(server.process(client4.as_dgram_ref(), now())); assert_eq!(*client.state(), State::Connected); assert_eq!(*server.state(), State::Confirmed); @@ -263,19 +269,19 @@ fn send_05rtt() { let c1 = client.process(None, now()).dgram(); assert!(c1.is_some()); - let s1 = server.process(c1, now()).dgram().unwrap(); + let s1 = server.process(c1.as_ref(), now()).dgram().unwrap(); assert_eq!(s1.len(), PATH_MTU_V6); // The server should accept writes at this point. let s2 = send_something(&mut server, now()); // Complete the handshake at the client. - client.process_input(s1, now()); + client.process_input(&s1, now()); maybe_authenticate(&mut client); assert_eq!(*client.state(), State::Connected); // The client should receive the 0.5-RTT data now. - client.process_input(s2, now()); + client.process_input(&s2, now()); let mut buf = vec![0; DEFAULT_STREAM_DATA.len() + 1]; let stream_id = client .events() @@ -300,19 +306,19 @@ fn reorder_05rtt() { let c1 = client.process(None, now()).dgram(); assert!(c1.is_some()); - let s1 = server.process(c1, now()).dgram().unwrap(); + let s1 = server.process(c1.as_ref(), now()).dgram().unwrap(); // The server should accept writes at this point. let s2 = send_something(&mut server, now()); // We can't use the standard facility to complete the handshake, so // drive it as aggressively as possible. - client.process_input(s2, now()); + client.process_input(&s2, now()); assert_eq!(client.stats().saved_datagrams, 1); // After processing the first packet, the client should go back and // process the 0.5-RTT packet data, which should make data available. - client.process_input(s1, now()); + client.process_input(&s1, now()); // We can't use `maybe_authenticate` here as that consumes events. client.authenticated(AuthenticationStatus::Ok, now()); assert_eq!(*client.state(), State::Connected); @@ -350,7 +356,7 @@ fn reorder_05rtt_with_0rtt() { server.send_ticket(now, &[]).unwrap(); let ticket = server.process_output(now).dgram().unwrap(); now += RTT / 2; - client.process_input(ticket, now); + client.process_input(&ticket, now); let token = get_tokens(&mut client).pop().unwrap(); let mut client = default_client(); @@ -367,35 +373,35 @@ fn reorder_05rtt_with_0rtt() { // Handle the first packet and send 0.5-RTT in response. Drop the response. now += RTT / 2; - mem::drop(server.process(Some(c1), now).dgram().unwrap()); + mem::drop(server.process(Some(&c1), now).dgram().unwrap()); // The gap in 0-RTT will result in this 0.5 RTT containing an ACK. - server.process_input(c2, now); + server.process_input(&c2, now); let s2 = send_something(&mut server, now); // Save the 0.5 RTT. now += RTT / 2; - client.process_input(s2, now); + client.process_input(&s2, now); assert_eq!(client.stats().saved_datagrams, 1); // Now PTO at the client and cause the server to re-send handshake packets. now += AT_LEAST_PTO; let c3 = client.process(None, now).dgram(); + assert_coalesced_0rtt(c3.as_ref().unwrap()); now += RTT / 2; - let s3 = server.process(c3, now).dgram().unwrap(); - assertions::assert_no_1rtt(&s3[..]); + let s3 = server.process(c3.as_ref(), now).dgram().unwrap(); // The client should be able to process the 0.5 RTT now. // This should contain an ACK, so we are processing an ACK from the past. now += RTT / 2; - client.process_input(s3, now); + client.process_input(&s3, now); maybe_authenticate(&mut client); let c4 = client.process(None, now).dgram(); assert_eq!(*client.state(), State::Connected); assert_eq!(client.paths.rtt(), RTT); now += RTT / 2; - server.process_input(c4.unwrap(), now); + server.process_input(&c4.unwrap(), now); assert_eq!(*server.state(), State::Confirmed); // Don't check server RTT as it will be massively inflated by a // poor initial estimate received when the server dropped the @@ -416,7 +422,7 @@ fn coalesce_05rtt() { let c1 = client.process(None, now).dgram(); assert!(c1.is_some()); now += RTT / 2; - let s1 = server.process(c1, now).dgram(); + let s1 = server.process(c1.as_ref(), now).dgram(); assert!(s1.is_some()); // Drop the server flight. Then send some data. @@ -431,7 +437,7 @@ fn coalesce_05rtt() { let c2 = client.process(None, now).dgram(); assert!(c2.is_some()); now += RTT / 2; - let s2 = server.process(c2, now).dgram(); + let s2 = server.process(c2.as_ref(), now).dgram(); // Even though there is a 1-RTT packet at the end of the datagram, the // flight should be padded to full size. assert_eq!(s2.as_ref().unwrap().len(), PATH_MTU_V6); @@ -440,7 +446,7 @@ fn coalesce_05rtt() { // packet until authentication completes though. So it saves it. now += RTT / 2; assert_eq!(client.stats().dropped_rx, 0); - mem::drop(client.process(s2, now).dgram()); + mem::drop(client.process(s2.as_ref(), now).dgram()); // This packet will contain an ACK, but we can ignore it. assert_eq!(client.stats().dropped_rx, 0); assert_eq!(client.stats().packets_rx, 3); @@ -457,11 +463,11 @@ fn coalesce_05rtt() { // Allow the handshake to complete. now += RTT / 2; - let s3 = server.process(c3, now).dgram(); + let s3 = server.process(c3.as_ref(), now).dgram(); assert!(s3.is_some()); assert_eq!(*server.state(), State::Confirmed); now += RTT / 2; - mem::drop(client.process(s3, now).dgram()); + mem::drop(client.process(s3.as_ref(), now).dgram()); assert_eq!(*client.state(), State::Confirmed); assert_eq!(client.stats().dropped_rx, 0); // No dropped packets. @@ -478,7 +484,7 @@ fn reorder_handshake() { assert!(c1.is_some()); now += RTT / 2; - let s1 = server.process(c1, now).dgram(); + let s1 = server.process(c1.as_ref(), now).dgram(); assert!(s1.is_some()); // Drop the Initial packet from this. @@ -488,7 +494,7 @@ fn reorder_handshake() { // Pass just the handshake packet in and the client can't handle it yet. // It can only send another Initial packet. now += RTT / 2; - let dgram = client.process(s_hs, now).dgram(); + let dgram = client.process(s_hs.as_ref(), now).dgram(); assertions::assert_initial(dgram.as_ref().unwrap(), false); assert_eq!(client.stats().saved_datagrams, 1); assert_eq!(client.stats().packets_rx, 1); @@ -499,7 +505,7 @@ fn reorder_handshake() { now += AT_LEAST_PTO; let c2 = client.process(None, now).dgram(); now += RTT / 2; - let s2 = server.process(c2, now).dgram(); + let s2 = server.process(c2.as_ref(), now).dgram(); assert!(s2.is_some()); let (s_init, s_hs) = split_datagram(&s2.unwrap()); @@ -507,11 +513,11 @@ fn reorder_handshake() { // Processing the Handshake packet first should save it. now += RTT / 2; - client.process_input(s_hs.unwrap(), now); + client.process_input(&s_hs.unwrap(), now); assert_eq!(client.stats().saved_datagrams, 2); assert_eq!(client.stats().packets_rx, 2); - client.process_input(s_init, now); + client.process_input(&s_init, now); // Each saved packet should now be "received" again. assert_eq!(client.stats().packets_rx, 7); maybe_authenticate(&mut client); @@ -521,14 +527,14 @@ fn reorder_handshake() { // Note that though packets were saved and processed very late, // they don't cause the RTT to change. now += RTT / 2; - let s3 = server.process(c3, now).dgram(); + let s3 = server.process(c3.as_ref(), now).dgram(); assert_eq!(*server.state(), State::Confirmed); // Don't check server RTT estimate as it will be inflated due to // it making a guess based on retransmissions when it dropped // the Initial packet number space. now += RTT / 2; - client.process_input(s3.unwrap(), now); + client.process_input(&s3.unwrap(), now); assert_eq!(*client.state(), State::Confirmed); assert_eq!(client.paths.rtt(), RTT); } @@ -545,11 +551,11 @@ fn reorder_1rtt() { assert!(c1.is_some()); now += RTT / 2; - let s1 = server.process(c1, now).dgram(); + let s1 = server.process(c1.as_ref(), now).dgram(); assert!(s1.is_some()); now += RTT / 2; - client.process_input(s1.unwrap(), now); + client.process_input(&s1.unwrap(), now); maybe_authenticate(&mut client); let c2 = client.process(None, now).dgram(); assert!(c2.is_some()); @@ -558,7 +564,7 @@ fn reorder_1rtt() { // Give them to the server before giving it `c2`. for _ in 0..PACKETS { let d = send_something(&mut client, now); - server.process_input(d, now + RTT / 2); + server.process_input(&d, now + RTT / 2); } // The server has now received those packets, and saved them. // The two extra received are Initial + the junk we use for padding. @@ -567,7 +573,7 @@ fn reorder_1rtt() { assert_eq!(server.stats().dropped_rx, 1); now += RTT / 2; - let s2 = server.process(c2, now).dgram(); + let s2 = server.process(c2.as_ref(), now).dgram(); // The server has now received those packets, and saved them. // The two additional are a Handshake and a 1-RTT (w/ NEW_CONNECTION_ID). assert_eq!(server.stats().packets_rx, PACKETS * 2 + 4); @@ -577,7 +583,7 @@ fn reorder_1rtt() { assert_eq!(server.paths.rtt(), RTT); now += RTT / 2; - client.process_input(s2.unwrap(), now); + client.process_input(&s2.unwrap(), now); assert_eq!(client.paths.rtt(), RTT); // All the stream data that was sent should now be available. @@ -615,8 +621,8 @@ fn corrupted_initial() { .find(|(_, &v)| v != 0) .unwrap(); corrupted[idx] ^= 0x76; - let dgram = Datagram::new(d.source(), d.destination(), corrupted); - server.process_input(dgram, now()); + let dgram = Datagram::new(d.source(), d.destination(), d.tos(), d.ttl(), corrupted); + server.process_input(&dgram, now()); // The server should have received two packets, // the first should be dropped, the second saved. assert_eq!(server.stats().packets_rx, 2); @@ -654,7 +660,7 @@ fn extra_initial_hs() { let c_init = client.process(None, now).dgram(); assert!(c_init.is_some()); now += DEFAULT_RTT / 2; - let s_init = server.process(c_init, now).dgram(); + let s_init = server.process(c_init.as_ref(), now).dgram(); assert!(s_init.is_some()); now += DEFAULT_RTT / 2; @@ -666,13 +672,13 @@ fn extra_initial_hs() { // Do that EXTRA_INITIALS times and each time the client will emit // another Initial packet. for _ in 0..=super::super::EXTRA_INITIALS { - let c_init = client.process(undecryptable.clone(), now).dgram(); + let c_init = client.process(undecryptable.as_ref(), now).dgram(); assertions::assert_initial(c_init.as_ref().unwrap(), false); now += DEFAULT_RTT / 10; } // After EXTRA_INITIALS, the client stops sending Initial packets. - let nothing = client.process(undecryptable, now).dgram(); + let nothing = client.process(undecryptable.as_ref(), now).dgram(); assert!(nothing.is_none()); // Until PTO, where another Initial can be used to complete the handshake. @@ -680,14 +686,14 @@ fn extra_initial_hs() { let c_init = client.process(None, now).dgram(); assertions::assert_initial(c_init.as_ref().unwrap(), false); now += DEFAULT_RTT / 2; - let s_init = server.process(c_init, now).dgram(); + let s_init = server.process(c_init.as_ref(), now).dgram(); now += DEFAULT_RTT / 2; - client.process_input(s_init.unwrap(), now); + client.process_input(&s_init.unwrap(), now); maybe_authenticate(&mut client); let c_fin = client.process_output(now).dgram(); assert_eq!(*client.state(), State::Connected); now += DEFAULT_RTT / 2; - server.process_input(c_fin.unwrap(), now); + server.process_input(&c_fin.unwrap(), now); assert_eq!(*server.state(), State::Confirmed); } @@ -700,7 +706,7 @@ fn extra_initial_invalid_cid() { let c_init = client.process(None, now).dgram(); assert!(c_init.is_some()); now += DEFAULT_RTT / 2; - let s_init = server.process(c_init, now).dgram(); + let s_init = server.process(c_init.as_ref(), now).dgram(); assert!(s_init.is_some()); now += DEFAULT_RTT / 2; @@ -711,8 +717,8 @@ fn extra_initial_invalid_cid() { let mut copy = hs.to_vec(); assert_ne!(copy[5], 0); // The DCID should be non-zero length. copy[6] ^= 0xc4; - let dgram_copy = Datagram::new(hs.destination(), hs.source(), copy); - let nothing = client.process(Some(dgram_copy), now).dgram(); + let dgram_copy = Datagram::new(hs.destination(), hs.source(), hs.tos(), hs.ttl(), copy); + let nothing = client.process(Some(&dgram_copy), now).dgram(); assert!(nothing.is_none()); } @@ -761,7 +767,7 @@ fn anti_amplification() { let c_init = client.process_output(now).dgram(); now += DEFAULT_RTT / 2; - let s_init1 = server.process(c_init, now).dgram().unwrap(); + let s_init1 = server.process(c_init.as_ref(), now).dgram().unwrap(); assert_eq!(s_init1.len(), PATH_MTU_V6); let s_init2 = server.process_output(now).dgram().unwrap(); assert_eq!(s_init2.len(), PATH_MTU_V6); @@ -777,11 +783,11 @@ fn anti_amplification() { assert_ne!(cb, Duration::new(0, 0)); now += DEFAULT_RTT / 2; - client.process_input(s_init1, now); - client.process_input(s_init2, now); + client.process_input(&s_init1, now); + client.process_input(&s_init2, now); let ack_count = client.stats().frame_tx.ack; let frame_count = client.stats().frame_tx.all; - let ack = client.process(Some(s_init3), now).dgram().unwrap(); + let ack = client.process(Some(&s_init3), now).dgram().unwrap(); assert!(!maybe_authenticate(&mut client)); // No need yet. // The client sends a padded datagram, with just ACK for Handshake. @@ -790,16 +796,16 @@ fn anti_amplification() { assert_ne!(ack.len(), PATH_MTU_V6); // Not padded (it includes Handshake). now += DEFAULT_RTT / 2; - let remainder = server.process(Some(ack), now).dgram(); + let remainder = server.process(Some(&ack), now).dgram(); now += DEFAULT_RTT / 2; - client.process_input(remainder.unwrap(), now); + client.process_input(&remainder.unwrap(), now); assert!(maybe_authenticate(&mut client)); // OK, we have all of it. let fin = client.process_output(now).dgram(); assert_eq!(*client.state(), State::Connected); now += DEFAULT_RTT / 2; - server.process_input(fin.unwrap(), now); + server.process_input(&fin.unwrap(), now); assert_eq!(*server.state(), State::Confirmed); } @@ -814,8 +820,8 @@ fn garbage_initial() { let mut corrupted = Vec::from(&initial[..initial.len() - 1]); corrupted.push(initial[initial.len() - 1] ^ 0xb7); corrupted.extend_from_slice(rest.as_ref().map_or(&[], |r| &r[..])); - let garbage = Datagram::new(addr(), addr(), corrupted); - assert_eq!(Output::None, server.process(Some(garbage), now())); + let garbage = datagram(corrupted); + assert_eq!(Output::None, server.process(Some(&garbage), now())); } #[test] @@ -825,17 +831,19 @@ fn drop_initial_packet_from_wrong_address() { assert!(out.as_dgram_ref().is_some()); let mut server = default_server(); - let out = server.process(out.dgram(), now()); + let out = server.process(out.as_dgram_ref(), now()); assert!(out.as_dgram_ref().is_some()); let p = out.dgram().unwrap(); let dgram = Datagram::new( SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 2)), 443), p.destination(), + p.tos(), + p.ttl(), &p[..], ); - let out = client.process(Some(dgram), now()); + let out = client.process(Some(&dgram), now()); assert!(out.as_dgram_ref().is_none()); } @@ -846,22 +854,24 @@ fn drop_handshake_packet_from_wrong_address() { assert!(out.as_dgram_ref().is_some()); let mut server = default_server(); - let out = server.process(out.dgram(), now()); + let out = server.process(out.as_dgram_ref(), now()); assert!(out.as_dgram_ref().is_some()); let (s_in, s_hs) = split_datagram(&out.dgram().unwrap()); // Pass the initial packet. - mem::drop(client.process(Some(s_in), now()).dgram()); + mem::drop(client.process(Some(&s_in), now()).dgram()); let p = s_hs.unwrap(); let dgram = Datagram::new( SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 2)), 443), p.destination(), + p.tos(), + p.ttl(), &p[..], ); - let out = client.process(Some(dgram), now()); + let out = client.process(Some(&dgram), now()); assert!(out.as_dgram_ref().is_none()); } @@ -910,8 +920,8 @@ fn ech_retry() { .unwrap(); let dgram = client.process_output(now()).dgram(); - let dgram = server.process(dgram, now()).dgram(); - client.process_input(dgram.unwrap(), now()); + let dgram = server.process(dgram.as_ref(), now()).dgram(); + client.process_input(&dgram.unwrap(), now()); let auth_event = ConnectionEvent::EchFallbackAuthenticationNeeded { public_name: String::from(ECH_PUBLIC_NAME), }; @@ -921,7 +931,7 @@ fn ech_retry() { // Tell the server about the error. let dgram = client.process_output(now()).dgram(); - server.process_input(dgram.unwrap(), now()); + server.process_input(&dgram.unwrap(), now()); assert_eq!( server.state().error(), Some(&ConnectionError::Transport(Error::PeerError(0x100 + 121))) @@ -965,8 +975,8 @@ fn ech_retry_fallback_rejected() { .unwrap(); let dgram = client.process_output(now()).dgram(); - let dgram = server.process(dgram, now()).dgram(); - client.process_input(dgram.unwrap(), now()); + let dgram = server.process(dgram.as_ref(), now()).dgram(); + client.process_input(&dgram.unwrap(), now()); let auth_event = ConnectionEvent::EchFallbackAuthenticationNeeded { public_name: String::from(ECH_PUBLIC_NAME), }; @@ -980,7 +990,7 @@ fn ech_retry_fallback_rejected() { // Pass the error on. let dgram = client.process_output(now()).dgram(); - server.process_input(dgram.unwrap(), now()); + server.process_input(&dgram.unwrap(), now()); assert_eq!( server.state().error(), Some(&ConnectionError::Transport(Error::PeerError(298))) @@ -999,13 +1009,13 @@ fn bad_min_ack_delay() { let mut client = default_client(); let dgram = client.process_output(now()).dgram(); - let dgram = server.process(dgram, now()).dgram(); - client.process_input(dgram.unwrap(), now()); + let dgram = server.process(dgram.as_ref(), now()).dgram(); + client.process_input(&dgram.unwrap(), now()); client.authenticated(AuthenticationStatus::Ok, now()); assert_eq!(client.state().error(), Some(&EXPECTED_ERROR)); let dgram = client.process_output(now()).dgram(); - server.process_input(dgram.unwrap(), now()); + server.process_input(&dgram.unwrap(), now()); assert_eq!( server.state().error(), Some(&ConnectionError::Transport(Error::PeerError( @@ -1025,7 +1035,7 @@ fn only_server_initial() { let client_dgram = client.process_output(now).dgram(); // Now fetch two flights of messages from the server. - let server_dgram1 = server.process(client_dgram, now).dgram(); + let server_dgram1 = server.process(client_dgram.as_ref(), now).dgram(); let server_dgram2 = server.process_output(now + AT_LEAST_PTO).dgram(); // Only pass on the Initial from the first. We should get a Handshake in return. @@ -1035,7 +1045,7 @@ fn only_server_initial() { // The client will not acknowledge the Initial as it discards keys. // It sends a Handshake probe instead, containing just a PING frame. assert_eq!(client.stats().frame_tx.ping, 0); - let probe = client.process(Some(initial), now).dgram(); + let probe = client.process(Some(&initial), now).dgram(); assertions::assert_handshake(&probe.unwrap()); assert_eq!(client.stats().dropped_rx, 0); assert_eq!(client.stats().frame_tx.ping, 1); @@ -1047,17 +1057,17 @@ fn only_server_initial() { now += AT_LEAST_PTO; assert_eq!(client.stats().frame_tx.ping, 1); let discarded = client.stats().dropped_rx; - let probe = client.process(Some(initial), now).dgram(); + let probe = client.process(Some(&initial), now).dgram(); assertions::assert_handshake(&probe.unwrap()); assert_eq!(client.stats().frame_tx.ping, 2); assert_eq!(client.stats().dropped_rx, discarded + 1); // Pass the Handshake packet and complete the handshake. - client.process_input(handshake.unwrap(), now); + client.process_input(&handshake.unwrap(), now); maybe_authenticate(&mut client); let dgram = client.process_output(now).dgram(); - let dgram = server.process(dgram, now).dgram(); - client.process_input(dgram.unwrap(), now); + let dgram = server.process(dgram.as_ref(), now).dgram(); + client.process_input(&dgram.unwrap(), now); assert_eq!(*client.state(), State::Confirmed); assert_eq!(*server.state(), State::Confirmed); @@ -1083,25 +1093,25 @@ fn no_extra_probes_after_confirmed() { // Finally, run the handshake. now += AT_LEAST_PTO * 2; let dgram = client.process_output(now).dgram(); - let dgram = server.process(dgram, now).dgram(); + let dgram = server.process(dgram.as_ref(), now).dgram(); // The server should have dropped the Initial keys now, so passing in the Initial // should elicit a retransmit rather than having it completely ignored. - let spare_handshake = server.process(Some(replay_initial), now).dgram(); + let spare_handshake = server.process(Some(&replay_initial), now).dgram(); assert!(spare_handshake.is_some()); - client.process_input(dgram.unwrap(), now); + client.process_input(&dgram.unwrap(), now); maybe_authenticate(&mut client); let dgram = client.process_output(now).dgram(); - let dgram = server.process(dgram, now).dgram(); - client.process_input(dgram.unwrap(), now); + let dgram = server.process(dgram.as_ref(), now).dgram(); + client.process_input(&dgram.unwrap(), now); assert_eq!(*client.state(), State::Confirmed); assert_eq!(*server.state(), State::Confirmed); - let probe = server.process(spare_initial, now).dgram(); + let probe = server.process(spare_initial.as_ref(), now).dgram(); assert!(probe.is_none()); - let probe = client.process(spare_handshake, now).dgram(); + let probe = client.process(spare_handshake.as_ref(), now).dgram(); assert!(probe.is_none()); } @@ -1114,12 +1124,12 @@ fn implicit_rtt_server() { let dgram = client.process_output(now).dgram(); now += RTT / 2; - let dgram = server.process(dgram, now).dgram(); + let dgram = server.process(dgram.as_ref(), now).dgram(); now += RTT / 2; - let dgram = client.process(dgram, now).dgram(); + let dgram = client.process(dgram.as_ref(), now).dgram(); assertions::assert_handshake(dgram.as_ref().unwrap()); now += RTT / 2; - server.process_input(dgram.unwrap(), now); + server.process_input(&dgram.unwrap(), now); // The server doesn't receive any acknowledgments, but it can infer // an RTT estimate from having discarded the Initial packet number space. diff --git a/third_party/rust/neqo-transport/src/connection/tests/idle.rs b/third_party/rust/neqo-transport/src/connection/tests/idle.rs index a9643c284fb4..c33726917a4b 100644 --- a/third_party/rust/neqo-transport/src/connection/tests/idle.rs +++ b/third_party/rust/neqo-transport/src/connection/tests/idle.rs @@ -4,6 +4,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::{ + mem, + time::{Duration, Instant}, +}; + +use neqo_common::{qtrace, Encoder}; +use test_fixture::{self, now, split_datagram}; + use super::{ super::{Connection, ConnectionParameters, IdleTimeout, Output, State}, connect, connect_force_idle, connect_rtt_idle, connect_with_rtt, default_client, @@ -18,13 +26,6 @@ use crate::{ tracking::PacketNumberSpace, }; -use neqo_common::{qtrace, Encoder}; -use std::{ - mem, - time::{Duration, Instant}, -}; -use test_fixture::{self, now, split_datagram}; - fn default_timeout() -> Duration { ConnectionParameters::default().get_idle_timeout() } @@ -107,15 +108,18 @@ fn asymmetric_idle_timeout() { connect(&mut client, &mut server); let c1 = send_something(&mut client, now()); let c2 = send_something(&mut client, now()); - server.process_input(c2, now()); - server.process_input(c1, now()); + server.process_input(&c2, now()); + server.process_input(&c1, now()); let s1 = send_something(&mut server, now()); let s2 = send_something(&mut server, now()); - client.process_input(s2, now()); - let ack = client.process(Some(s1), now()).dgram(); + client.process_input(&s2, now()); + let ack = client.process(Some(&s1), now()).dgram(); assert!(ack.is_some()); // Now both should have received ACK frames so should be idle. - assert_eq!(server.process(ack, now()), Output::Callback(LOWER_TIMEOUT)); + assert_eq!( + server.process(ack.as_ref(), now()), + Output::Callback(LOWER_TIMEOUT) + ); assert_eq!(client.process(None, now()), Output::Callback(LOWER_TIMEOUT)); } @@ -144,13 +148,13 @@ fn tiny_idle_timeout() { let c1 = send_something(&mut client, now); let c2 = send_something(&mut client, now); now += RTT / 2; - server.process_input(c2, now); - server.process_input(c1, now); + server.process_input(&c2, now); + server.process_input(&c1, now); let s1 = send_something(&mut server, now); let s2 = send_something(&mut server, now); now += RTT / 2; - client.process_input(s2, now); - let ack = client.process(Some(s1), now).dgram(); + client.process_input(&s2, now); + let ack = client.process(Some(&s1), now).dgram(); assert!(ack.is_some()); // The client should be idle now, but with a different timer. @@ -162,7 +166,7 @@ fn tiny_idle_timeout() { // The server should go idle after the ACK, but again with a larger timeout. now += RTT / 2; - if let Output::Callback(t) = client.process(ack, now) { + if let Output::Callback(t) = client.process(ack.as_ref(), now) { assert!(t > LOWER_TIMEOUT); } else { panic!("Client not idle"); @@ -257,11 +261,11 @@ fn idle_recv_packet() { // Otherwise, the eventual timeout will be extended (and we're not testing that). now += Duration::from_secs(10); let out = client.process(None, now); - server.process_input(out.dgram().unwrap(), now); + server.process_input(&out.dgram().unwrap(), now); assert_eq!(server.stream_send(stream, b"world").unwrap(), 5); let out = server.process_output(now); assert_ne!(out.as_dgram_ref(), None); - mem::drop(client.process(out.dgram(), now)); + mem::drop(client.process(out.as_dgram_ref(), now)); assert!(matches!(client.state(), State::Confirmed)); // Add a little less than the idle timeout and we're still connected. @@ -288,9 +292,9 @@ fn idle_caching() { // Perform the first round trip, but drop the Initial from the server. // The client then caches the Handshake packet. let dgram = client.process_output(start).dgram(); - let dgram = server.process(dgram, start).dgram(); + let dgram = server.process(dgram.as_ref(), start).dgram(); let (_, handshake) = split_datagram(&dgram.unwrap()); - client.process_input(handshake.unwrap(), start); + client.process_input(&handshake.unwrap(), start); // Perform an exchange and keep the connection alive. // Only allow a packet containing a PING to pass. @@ -303,7 +307,7 @@ fn idle_caching() { // Now let the server process the client PING. This causes the server // to send CRYPTO frames again, so manually extract and discard those. let ping_before_s = server.stats().frame_rx.ping; - server.process_input(dgram.unwrap(), middle); + server.process_input(&dgram.unwrap(), middle); assert_eq!(server.stats().frame_rx.ping, ping_before_s + 1); let mut tokens = Vec::new(); server @@ -336,7 +340,7 @@ fn idle_caching() { let (initial, _) = split_datagram(&dgram.unwrap()); let ping_before_c = client.stats().frame_rx.ping; let ack_before = client.stats().frame_rx.ack; - client.process_input(initial, middle); + client.process_input(&initial, middle); assert_eq!(client.stats().frame_rx.ping, ping_before_c + 1); assert_eq!(client.stats().frame_rx.ack, ack_before + 1); @@ -345,11 +349,11 @@ fn idle_caching() { let dgram = server.process_output(end).dgram(); let (initial, _) = split_datagram(&dgram.unwrap()); neqo_common::qwarn!("client ingests initial, finally"); - mem::drop(client.process(Some(initial), end)); + mem::drop(client.process(Some(&initial), end)); maybe_authenticate(&mut client); let dgram = client.process_output(end).dgram(); - let dgram = server.process(dgram, end).dgram(); - client.process_input(dgram.unwrap(), end); + let dgram = server.process(dgram.as_ref(), end).dgram(); + client.process_input(&dgram.unwrap(), end); assert_eq!(*client.state(), State::Confirmed); assert_eq!(*server.state(), State::Confirmed); } @@ -378,7 +382,7 @@ fn create_stream_idle_rtt( _ = initiator.stream_send(stream, DEFAULT_STREAM_DATA).unwrap(); let req = initiator.process_output(now).dgram(); now += rtt / 2; - responder.process_input(req.unwrap(), now); + responder.process_input(&req.unwrap(), now); // Reordering two packets from the responder forces the initiator to be idle. _ = responder.stream_send(stream, DEFAULT_STREAM_DATA).unwrap(); @@ -387,15 +391,15 @@ fn create_stream_idle_rtt( let resp2 = responder.process_output(now).dgram(); now += rtt / 2; - initiator.process_input(resp2.unwrap(), now); - initiator.process_input(resp1.unwrap(), now); + initiator.process_input(&resp2.unwrap(), now); + initiator.process_input(&resp1.unwrap(), now); let ack = initiator.process_output(now).dgram(); assert!(ack.is_some()); check_idle(initiator, now); // Receiving the ACK should return the responder to idle too. now += rtt / 2; - responder.process_input(ack.unwrap(), now); + responder.process_input(&ack.unwrap(), now); check_idle(responder, now); (now, stream) @@ -431,9 +435,9 @@ fn keep_alive_initiator() { assert_eq!(server.stats().frame_tx.ping, pings_before + 1); // Exchange ack for the PING. - let out = client.process(ping, now).dgram(); - let out = server.process(out, now).dgram(); - assert!(client.process(out, now).dgram().is_none()); + let out = client.process(ping.as_ref(), now).dgram(); + let out = server.process(out.as_ref(), now).dgram(); + assert!(client.process(out.as_ref(), now).dgram().is_none()); // Check that there will be next keep-alive ping after default_timeout() / 2. assert_idle(&mut server, now, default_timeout() / 2); @@ -473,12 +477,12 @@ fn keep_alive_lost() { assert_eq!(server.stats().frame_tx.ping, pings_before2 + 1); // Exchange ack for the PING. - let out = client.process(ping, now).dgram(); + let out = client.process(ping.as_ref(), now).dgram(); now += Duration::from_millis(20); - let out = server.process(out, now).dgram(); + let out = server.process(out.as_ref(), now).dgram(); - assert!(client.process(out, now).dgram().is_none()); + assert!(client.process(out.as_ref(), now).dgram().is_none()); // TODO: if we run server.process with current value of now, the server will // return some small timeout for the recovry although it does not have @@ -531,10 +535,10 @@ fn keep_alive_unmark() { fn transfer_force_idle(sender: &mut Connection, receiver: &mut Connection) { let dgram = sender.process_output(now()).dgram(); let chaff = send_something(sender, now()); - receiver.process_input(chaff, now()); - receiver.process_input(dgram.unwrap(), now()); + receiver.process_input(&chaff, now()); + receiver.process_input(&dgram.unwrap(), now()); let ack = receiver.process_output(now()).dgram(); - sender.process_input(ack.unwrap(), now()); + sender.process_input(&ack.unwrap(), now()); } /// Receiving the end of the stream stops keep-alives for that stream. @@ -602,7 +606,7 @@ fn keep_alive_stop_sending() { // The server will have sent RESET_STREAM, which the client will // want to acknowledge, so force that out. let junk = send_something(&mut server, now()); - let ack = client.process(Some(junk), now()).dgram(); + let ack = client.process(Some(&junk), now()).dgram(); assert!(ack.is_some()); // Now the client should be idle. @@ -665,7 +669,7 @@ fn keep_alive_uni() { _ = client.stream_send(stream, DEFAULT_STREAM_DATA).unwrap(); let dgram = client.process_output(now()).dgram(); - server.process_input(dgram.unwrap(), now()); + server.process_input(&dgram.unwrap(), now()); server.stream_keep_alive(stream, true).unwrap(); } @@ -675,11 +679,14 @@ fn keep_alive_uni() { 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: + // 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 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); diff --git a/third_party/rust/neqo-transport/src/connection/tests/keys.rs b/third_party/rust/neqo-transport/src/connection/tests/keys.rs index 7e04aaf191be..c247bba670cb 100644 --- a/third_party/rust/neqo-transport/src/connection/tests/keys.rs +++ b/third_party/rust/neqo-transport/src/connection/tests/keys.rs @@ -4,23 +4,28 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use super::super::super::{ConnectionError, ERROR_AEAD_LIMIT_REACHED}; -use super::super::{Connection, ConnectionParameters, Error, Output, State, StreamType}; +use std::mem; + +use neqo_common::{qdebug, Datagram}; +use test_fixture::{self, now}; + use super::{ + super::{ + super::{ConnectionError, ERROR_AEAD_LIMIT_REACHED}, + Connection, ConnectionParameters, Error, Output, State, StreamType, + }, connect, connect_force_idle, default_client, default_server, maybe_authenticate, send_and_receive, send_something, AT_LEAST_PTO, }; -use crate::crypto::{OVERWRITE_INVOCATIONS, UPDATE_WRITE_KEYS_AT}; -use crate::packet::PacketNumber; -use crate::path::PATH_MTU_V6; - -use neqo_common::{qdebug, Datagram}; -use std::mem; -use test_fixture::{self, now}; +use crate::{ + crypto::{OVERWRITE_INVOCATIONS, UPDATE_WRITE_KEYS_AT}, + packet::PacketNumber, + path::PATH_MTU_V6, +}; fn check_discarded( peer: &mut Connection, - pkt: Datagram, + pkt: &Datagram, response: bool, dropped: usize, dups: usize, @@ -59,11 +64,11 @@ fn discarded_initial_keys() { qdebug!("---- server: CH -> SH, EE, CERT, CV, FIN"); let mut server = default_server(); - let init_pkt_s = server.process(init_pkt_c.clone(), now()).dgram(); + let init_pkt_s = server.process(init_pkt_c.as_ref(), now()).dgram(); assert!(init_pkt_s.is_some()); qdebug!("---- client: cert verification"); - let out = client.process(init_pkt_s.clone(), now()).dgram(); + let out = client.process(init_pkt_s.as_ref(), now()).dgram(); assert!(out.is_some()); // The client has received a handshake packet. It will remove the Initial keys. @@ -71,7 +76,7 @@ fn discarded_initial_keys() { // The initial packet should be dropped. The packet contains a Handshake packet as well, which // will be marked as dup. And it will contain padding, which will be "dropped". // The client will generate a Handshake packet here to avoid stalling. - check_discarded(&mut client, init_pkt_s.unwrap(), true, 2, 1); + check_discarded(&mut client, &init_pkt_s.unwrap(), true, 2, 1); assert!(maybe_authenticate(&mut client)); @@ -79,7 +84,7 @@ fn discarded_initial_keys() { // packet from the client. // We will check this by processing init_pkt_c a second time. // The dropped packet is padding. The Initial packet has been mark dup. - check_discarded(&mut server, init_pkt_c.clone().unwrap(), false, 1, 1); + check_discarded(&mut server, &init_pkt_c.clone().unwrap(), false, 1, 1); qdebug!("---- client: SH..FIN -> FIN"); let out = client.process(None, now()).dgram(); @@ -87,14 +92,14 @@ fn discarded_initial_keys() { // The server will process the first Handshake packet. // After this the Initial keys will be dropped. - let out = server.process(out, now()).dgram(); + let out = server.process(out.as_ref(), now()).dgram(); assert!(out.is_some()); // Check that the Initial keys are dropped at the server // We will check this by processing init_pkt_c a third time. // The Initial packet has been dropped and padding that follows it. // There is no dups, everything has been dropped. - check_discarded(&mut server, init_pkt_c.unwrap(), false, 1, 0); + check_discarded(&mut server, &init_pkt_c.unwrap(), false, 1, 0); } #[test] @@ -151,7 +156,7 @@ fn key_update_client() { // The previous PTO packet (see above) was dropped, so we should get an ACK here. let dgram = send_and_receive(&mut client, &mut server, now); assert!(dgram.is_some()); - let res = client.process(dgram, now); + let res = client.process(dgram.as_ref(), now); // This is the first packet that the client has received from the server // with new keys, so its read timer just started. if let Output::Callback(t) = res { @@ -190,7 +195,7 @@ fn key_update_consecutive() { assert_eq!(client.get_epochs(), (Some(4), Some(3))); // Have the server process the ACK. - if let Output::Callback(_) = server.process(dgram, now) { + if let Output::Callback(_) = server.process(dgram.as_ref(), now) { assert_eq!(server.get_epochs(), (Some(4), Some(3))); // Now move the server temporarily into the future so that it // rotates the keys. The client stays in the present. @@ -208,7 +213,7 @@ fn key_update_consecutive() { // However, as the server didn't wait long enough to update again, the // client hasn't rotated its keys, so the packet gets dropped. - check_discarded(&mut client, dgram, false, 1, 0); + check_discarded(&mut client, &dgram, false, 1, 0); } // Key updates can't be initiated too early. @@ -225,12 +230,12 @@ fn key_update_before_confirmed() { assert_update_blocked(&mut client); // Server Initial + Handshake - let dgram = server.process(dgram, now()).dgram(); + let dgram = server.process(dgram.as_ref(), now()).dgram(); assert!(dgram.is_some()); assert_update_blocked(&mut server); // Client Handshake - client.process_input(dgram.unwrap(), now()); + client.process_input(&dgram.unwrap(), now()); assert_update_blocked(&mut client); assert!(maybe_authenticate(&mut client)); @@ -241,12 +246,12 @@ fn key_update_before_confirmed() { assert_update_blocked(&mut client); // Server HANDSHAKE_DONE - let dgram = server.process(dgram, now()).dgram(); + let dgram = server.process(dgram.as_ref(), now()).dgram(); assert!(dgram.is_some()); assert!(server.initiate_key_update().is_ok()); // Client receives HANDSHAKE_DONE - let dgram = client.process(dgram, now()).dgram(); + let dgram = client.process(dgram.as_ref(), now()).dgram(); assert!(dgram.is_none()); assert!(client.initiate_key_update().is_ok()); } @@ -277,13 +282,13 @@ fn exhaust_read_keys() { let dgram = send_something(&mut client, now()); overwrite_invocations(0); - let dgram = server.process(Some(dgram), now()).dgram(); + let dgram = server.process(Some(&dgram), now()).dgram(); assert!(matches!( server.state(), State::Closed(ConnectionError::Transport(Error::KeysExhausted)) )); - client.process_input(dgram.unwrap(), now()); + client.process_input(&dgram.unwrap(), now()); assert!(matches!( client.state(), State::Draining { diff --git a/third_party/rust/neqo-transport/src/connection/tests/migration.rs b/third_party/rust/neqo-transport/src/connection/tests/migration.rs index 9d662da0b836..8307a7dd844f 100644 --- a/third_party/rust/neqo-transport/src/connection/tests/migration.rs +++ b/third_party/rust/neqo-transport/src/connection/tests/migration.rs @@ -4,6 +4,20 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::{ + cell::RefCell, + net::{IpAddr, Ipv6Addr, SocketAddr}, + rc::Rc, + time::{Duration, Instant}, +}; + +use neqo_common::{Datagram, Decoder}; +use test_fixture::{ + self, addr, addr_v4, + assertions::{assert_v4_path, assert_v6_path}, + fixture_init, new_neqo_qlog, now, +}; + use super::{ super::{Connection, Output, State, StreamType}, connect_fail, connect_force_idle, connect_rtt_idle, default_client, default_server, @@ -20,19 +34,6 @@ use crate::{ ConnectionParameters, EmptyConnectionIdGenerator, Error, }; -use neqo_common::{Datagram, Decoder}; -use std::{ - cell::RefCell, - net::{IpAddr, Ipv6Addr, SocketAddr}, - rc::Rc, - time::{Duration, Instant}, -}; -use test_fixture::{ - self, addr, addr_v4, - assertions::{assert_v4_path, assert_v6_path}, - fixture_init, now, -}; - /// This should be a valid-seeming transport parameter. /// And it should have different values to `addr` and `addr_v4`. const SAMPLE_PREFERRED_ADDRESS: &[u8] = &[ @@ -52,7 +53,7 @@ fn loopback() -> SocketAddr { } fn change_path(d: &Datagram, a: SocketAddr) -> Datagram { - Datagram::new(a, a, &d[..]) + Datagram::new(a, a, d.tos(), d.ttl(), &d[..]) } fn new_port(a: SocketAddr) -> SocketAddr { @@ -61,7 +62,13 @@ fn new_port(a: SocketAddr) -> SocketAddr { } fn change_source_port(d: &Datagram) -> Datagram { - Datagram::new(new_port(d.source()), d.destination(), &d[..]) + Datagram::new( + new_port(d.source()), + d.destination(), + d.tos(), + d.ttl(), + &d[..], + ) } /// As these tests use a new path, that path often has a non-zero RTT. @@ -81,7 +88,7 @@ fn rebinding_port() { let dgram = send_something(&mut client, now()); let dgram = change_source_port(&dgram); - server.process_input(dgram, now()); + server.process_input(&dgram, now()); // Have the server send something so that it generates a packet. let stream_id = server.stream_create(StreamType::UniDi).unwrap(); server.stream_close_send(stream_id).unwrap(); @@ -103,7 +110,7 @@ fn path_forwarding_attack() { let dgram = send_something(&mut client, now); let dgram = change_path(&dgram, addr_v4()); - server.process_input(dgram, now); + server.process_input(&dgram, now); // The server now probes the new (primary) path. let new_probe = server.process_output(now).dgram().unwrap(); @@ -123,14 +130,14 @@ fn path_forwarding_attack() { // The client should respond to the challenge on the new path. // The server couldn't pad, so the client is also amplification limited. - let new_resp = client.process(Some(new_probe), now).dgram().unwrap(); + let new_resp = client.process(Some(&new_probe), now).dgram().unwrap(); assert_eq!(client.stats().frame_rx.path_challenge, 1); assert_eq!(client.stats().frame_tx.path_challenge, 1); assert_eq!(client.stats().frame_tx.path_response, 1); assert_v4_path(&new_resp, false); // The client also responds to probes on the old path. - let old_resp = client.process(Some(old_probe), now).dgram().unwrap(); + let old_resp = client.process(Some(&old_probe), now).dgram().unwrap(); assert_eq!(client.stats().frame_rx.path_challenge, 2); assert_eq!(client.stats().frame_tx.path_challenge, 1); assert_eq!(client.stats().frame_tx.path_response, 2); @@ -143,12 +150,12 @@ fn path_forwarding_attack() { // Receiving the PATH_RESPONSE from the client opens the amplification // limit enough for the server to respond. // This is padded because it includes PATH_CHALLENGE. - let server_data1 = server.process(Some(new_resp), now).dgram().unwrap(); + let server_data1 = server.process(Some(&new_resp), now).dgram().unwrap(); assert_v4_path(&server_data1, true); assert_eq!(server.stats().frame_tx.path_challenge, 3); // The client responds to this probe on the new path. - client.process_input(server_data1, now); + client.process_input(&server_data1, now); let stream_before = client.stats().frame_tx.stream; let padded_resp = send_something(&mut client, now); assert_eq!(stream_before, client.stats().frame_tx.stream); @@ -164,7 +171,7 @@ fn path_forwarding_attack() { assert_v4_path(&server_data2, false); // Until new data is received from the client on the old path. - server.process_input(client_data2, now); + server.process_input(&client_data2, now); // The server sends a probe on the "old" path. let server_data3 = send_something(&mut server, now); assert_v4_path(&server_data3, true); @@ -192,7 +199,7 @@ fn migrate_immediate() { let server_delayed = send_something(&mut server, now); // The server accepts the first packet and migrates (but probes). - let server1 = server.process(Some(client1), now).dgram().unwrap(); + let server1 = server.process(Some(&client1), now).dgram().unwrap(); assert_v4_path(&server1, true); let server2 = server.process_output(now).dgram().unwrap(); assert_v6_path(&server2, true); @@ -200,13 +207,13 @@ fn migrate_immediate() { // The second packet has no real effect, it just elicits an ACK. let all_before = server.stats().frame_tx.all; let ack_before = server.stats().frame_tx.ack; - let server3 = server.process(Some(client2), now).dgram(); + let server3 = server.process(Some(&client2), now).dgram(); assert!(server3.is_some()); assert_eq!(server.stats().frame_tx.all, all_before + 1); assert_eq!(server.stats().frame_tx.ack, ack_before + 1); // Receiving a packet sent by the server before migration doesn't change path. - client.process_input(server_delayed, now); + client.process_input(&server_delayed, now); // The client has sent two unpaced packets and this new path has no RTT estimate // so this might be paced. let (client3, _t) = send_something_paced(&mut client, now, true); @@ -293,13 +300,13 @@ fn migrate_same() { assert_v6_path(&probe, true); // Contains PATH_CHALLENGE. assert_eq!(client.stats().frame_tx.path_challenge, 1); - let resp = server.process(Some(probe), now).dgram().unwrap(); + let resp = server.process(Some(&probe), now).dgram().unwrap(); assert_v6_path(&resp, true); assert_eq!(server.stats().frame_tx.path_response, 1); assert_eq!(server.stats().frame_tx.path_challenge, 0); // Everything continues happily. - client.process_input(resp, now); + client.process_input(&resp, now); let contd = send_something(&mut client, now); assert_v6_path(&contd, false); } @@ -374,9 +381,9 @@ fn migration(mut client: Connection) { let probe = client.process_output(now).dgram().unwrap(); assert_v4_path(&probe, true); // Contains PATH_CHALLENGE. assert_eq!(client.stats().frame_tx.path_challenge, 1); - let probe_cid = ConnectionId::from(&get_cid(&probe)); + let probe_cid = ConnectionId::from(get_cid(&probe)); - let resp = server.process(Some(probe), now).dgram().unwrap(); + let resp = server.process(Some(&probe), now).dgram().unwrap(); assert_v4_path(&resp, true); assert_eq!(server.stats().frame_tx.path_response, 1); assert_eq!(server.stats().frame_tx.path_challenge, 1); @@ -385,12 +392,12 @@ fn migration(mut client: Connection) { let client_data = send_something(&mut client, now); assert_ne!(get_cid(&client_data), probe_cid); assert_v6_path(&client_data, false); - server.process_input(client_data, now); + server.process_input(&client_data, now); let server_data = send_something(&mut server, now); assert_v6_path(&server_data, false); // Once the client receives the probe response, it migrates to the new path. - client.process_input(resp, now); + client.process_input(&resp, now); assert_eq!(client.stats().frame_rx.path_challenge, 1); let migrate_client = send_something(&mut client, now); assert_v4_path(&migrate_client, true); // Responds to server probe. @@ -399,7 +406,7 @@ fn migration(mut client: Connection) { // However, it will probe the old path again, even though it has just // received a response to its last probe, because it needs to verify // that the migration is genuine. - server.process_input(migrate_client, now); + server.process_input(&migrate_client, now); let stream_before = server.stats().frame_tx.stream; let probe_old_server = send_something(&mut server, now); // This is just the double-check probe; no STREAM frames. @@ -414,8 +421,8 @@ fn migration(mut client: Connection) { assert_eq!(server.stats().frame_tx.stream, stream_before + 1); // The client receives these checks and responds to the probe, but uses the new path. - client.process_input(migrate_server, now); - client.process_input(probe_old_server, now); + client.process_input(&migrate_server, now); + client.process_input(&probe_old_server, now); let old_probe_resp = send_something(&mut client, now); assert_v6_path(&old_probe_resp, true); let client_confirmation = client.process_output(now).dgram().unwrap(); @@ -455,11 +462,11 @@ fn migration_client_empty_cid() { /// Returns the packet containing `HANDSHAKE_DONE` from the server. fn fast_handshake(client: &mut Connection, server: &mut Connection) -> Option { let dgram = client.process_output(now()).dgram(); - let dgram = server.process(dgram, now()).dgram(); - client.process_input(dgram.unwrap(), now()); + let dgram = server.process(dgram.as_ref(), now()).dgram(); + client.process_input(&dgram.unwrap(), now()); assert!(maybe_authenticate(client)); let dgram = client.process_output(now()).dgram(); - server.process(dgram, now()).dgram() + server.process(dgram.as_ref(), now()).dgram() } fn preferred_address(hs_client: SocketAddr, hs_server: SocketAddr, preferred: SocketAddr) { @@ -498,6 +505,7 @@ fn preferred_address(hs_client: SocketAddr, hs_server: SocketAddr, preferred: So }; fixture_init(); + let (log, _contents) = new_neqo_qlog(); let mut client = Connection::new_client( test_fixture::DEFAULT_SERVER_NAME, test_fixture::DEFAULT_ALPN, @@ -508,6 +516,7 @@ fn preferred_address(hs_client: SocketAddr, hs_server: SocketAddr, preferred: So now(), ) .unwrap(); + client.set_qlog(log); let spa = match preferred { SocketAddr::V6(v6) => PreferredAddress::new(None, Some(v6)), SocketAddr::V4(v4) => PreferredAddress::new(Some(v4), None), @@ -518,7 +527,7 @@ fn preferred_address(hs_client: SocketAddr, hs_server: SocketAddr, preferred: So // The client is about to process HANDSHAKE_DONE. // It should start probing toward the server's preferred address. - let probe = client.process(dgram, now()).dgram().unwrap(); + let probe = client.process(dgram.as_ref(), now()).dgram().unwrap(); assert_toward_spa(&probe, true); assert_eq!(client.stats().frame_tx.path_challenge, 1); assert_ne!(client.process_output(now()).callback(), Duration::new(0, 0)); @@ -528,26 +537,26 @@ fn preferred_address(hs_client: SocketAddr, hs_server: SocketAddr, preferred: So assert_orig_path(&data, false); // The server responds to the probe. - let resp = server.process(Some(probe), now()).dgram().unwrap(); + let resp = server.process(Some(&probe), now()).dgram().unwrap(); assert_from_spa(&resp, true); assert_eq!(server.stats().frame_tx.path_challenge, 1); assert_eq!(server.stats().frame_tx.path_response, 1); // Data continues on the main path for the server. - server.process_input(data, now()); + server.process_input(&data, now()); let data = send_something(&mut server, now()); assert_orig_path(&data, false); // Client gets the probe response back and it migrates. - client.process_input(resp, now()); - client.process_input(data, now()); + client.process_input(&resp, now()); + client.process_input(&data, now()); let data = send_something(&mut client, now()); assert_toward_spa(&data, true); assert_eq!(client.stats().frame_tx.stream, 2); assert_eq!(client.stats().frame_tx.path_response, 1); // The server sees the migration and probes the old path. - let probe = server.process(Some(data), now()).dgram().unwrap(); + let probe = server.process(Some(&data), now()).dgram().unwrap(); assert_orig_path(&probe, true); assert_eq!(server.stats().frame_tx.path_challenge, 2); @@ -589,7 +598,7 @@ fn expect_no_migration(client: &mut Connection, server: &mut Connection) { let dgram = fast_handshake(client, server); // The client won't probe now, though it could; it remains idle. - let out = client.process(dgram, now()); + let out = client.process(dgram.as_ref(), now()); assert_ne!(out.callback(), Duration::new(0, 0)); // Data continues on the main path for the client. @@ -716,12 +725,12 @@ fn migration_invalid_state() { .is_err()); let close = client.process(None, now()).dgram(); - let dgram = server.process(close, now()).dgram(); + let dgram = server.process(close.as_ref(), now()).dgram(); assert!(server .migrate(Some(addr()), Some(addr()), false, now()) .is_err()); - client.process_input(dgram.unwrap(), now()); + client.process_input(&dgram.unwrap(), now()); assert!(client .migrate(Some(addr()), Some(addr()), false, now()) .is_err()); @@ -814,7 +823,7 @@ fn retire_all() { .unwrap(); connect_force_idle(&mut client, &mut server); - let original_cid = ConnectionId::from(&get_cid(&send_something(&mut client, now()))); + let original_cid = ConnectionId::from(get_cid(&send_something(&mut client, now()))); server.test_frame_writer = Some(Box::new(RetireAll { cid_gen })); let ncid = send_something(&mut server, now()); @@ -822,7 +831,7 @@ fn retire_all() { let new_cid_before = client.stats().frame_rx.new_connection_id; let retire_cid_before = client.stats().frame_tx.retire_connection_id; - client.process_input(ncid, now()); + client.process_input(&ncid, now()); let retire = send_something(&mut client, now()); assert_eq!( client.stats().frame_rx.new_connection_id, @@ -852,7 +861,7 @@ fn retire_prior_to_migration_failure() { .unwrap(); connect_force_idle(&mut client, &mut server); - let original_cid = ConnectionId::from(&get_cid(&send_something(&mut client, now()))); + let original_cid = ConnectionId::from(get_cid(&send_something(&mut client, now()))); client .migrate(Some(addr_v4()), Some(addr_v4()), false, now()) @@ -862,7 +871,7 @@ fn retire_prior_to_migration_failure() { let probe = client.process_output(now()).dgram().unwrap(); assert_v4_path(&probe, true); assert_eq!(client.stats().frame_tx.path_challenge, 1); - let probe_cid = ConnectionId::from(&get_cid(&probe)); + let probe_cid = ConnectionId::from(get_cid(&probe)); assert_ne!(original_cid, probe_cid); // Have the server receive the probe, but separately have it decide to @@ -871,17 +880,17 @@ fn retire_prior_to_migration_failure() { let retire_all = send_something(&mut server, now()); server.test_frame_writer = None; - let resp = server.process(Some(probe), now()).dgram().unwrap(); + let resp = server.process(Some(&probe), now()).dgram().unwrap(); assert_v4_path(&resp, true); assert_eq!(server.stats().frame_tx.path_response, 1); assert_eq!(server.stats().frame_tx.path_challenge, 1); // Have the client receive the NEW_CONNECTION_ID with Retire Prior To. - client.process_input(retire_all, now()); + client.process_input(&retire_all, now()); // This packet contains the probe response, which should be fine, but it // also includes PATH_CHALLENGE for the new path, and the client can't // respond without a connection ID. We treat this as a connection error. - client.process_input(resp, now()); + client.process_input(&resp, now()); assert!(matches!( client.state(), State::Closing { @@ -907,7 +916,7 @@ fn retire_prior_to_migration_success() { .unwrap(); connect_force_idle(&mut client, &mut server); - let original_cid = ConnectionId::from(&get_cid(&send_something(&mut client, now()))); + let original_cid = ConnectionId::from(get_cid(&send_something(&mut client, now()))); client .migrate(Some(addr_v4()), Some(addr_v4()), false, now()) @@ -917,7 +926,7 @@ fn retire_prior_to_migration_success() { let probe = client.process_output(now()).dgram().unwrap(); assert_v4_path(&probe, true); assert_eq!(client.stats().frame_tx.path_challenge, 1); - let probe_cid = ConnectionId::from(&get_cid(&probe)); + let probe_cid = ConnectionId::from(get_cid(&probe)); assert_ne!(original_cid, probe_cid); // Have the server receive the probe, but separately have it decide to @@ -926,15 +935,15 @@ fn retire_prior_to_migration_success() { let retire_all = send_something(&mut server, now()); server.test_frame_writer = None; - let resp = server.process(Some(probe), now()).dgram().unwrap(); + let resp = server.process(Some(&probe), now()).dgram().unwrap(); assert_v4_path(&resp, true); assert_eq!(server.stats().frame_tx.path_response, 1); assert_eq!(server.stats().frame_tx.path_challenge, 1); // Have the client receive the NEW_CONNECTION_ID with Retire Prior To second. // As this occurs in a very specific order, migration succeeds. - client.process_input(resp, now()); - client.process_input(retire_all, now()); + client.process_input(&resp, now()); + client.process_input(&retire_all, now()); // Migration succeeds and the new path gets the last connection ID. let dgram = send_something(&mut client, now()); diff --git a/third_party/rust/neqo-transport/src/connection/tests/mod.rs b/third_party/rust/neqo-transport/src/connection/tests/mod.rs index 72fe9d1db846..8a999f40481f 100644 --- a/third_party/rust/neqo-transport/src/connection/tests/mod.rs +++ b/third_party/rust/neqo-transport/src/connection/tests/mod.rs @@ -6,19 +6,6 @@ #![deny(clippy::pedantic)] -use super::{Connection, ConnectionError, ConnectionId, Output, State}; -use crate::{ - addr_valid::{AddressValidation, ValidateAddress}, - cc::{CWND_INITIAL_PKTS, CWND_MIN}, - cid::ConnectionIdRef, - events::ConnectionEvent, - path::PATH_MTU_V6, - recovery::ACK_ONLY_SIZE_LIMIT, - stats::{FrameStats, Stats, MAX_PTO_COUNTS}, - ConnectionIdDecoder, ConnectionIdGenerator, ConnectionParameters, Error, StreamId, StreamType, - Version, -}; - use std::{ cell::RefCell, cmp::min, @@ -28,9 +15,25 @@ use std::{ time::{Duration, Instant}, }; +use enum_map::enum_map; use neqo_common::{event::Provider, qdebug, qtrace, Datagram, Decoder, Role}; use neqo_crypto::{random, AllowZeroRtt, AuthenticationStatus, ResumptionToken}; -use test_fixture::{self, addr, fixture_init, now}; +use test_fixture::{self, addr, fixture_init, new_neqo_qlog, now}; + +use super::{Connection, ConnectionError, ConnectionId, Output, State}; +use crate::{ + addr_valid::{AddressValidation, ValidateAddress}, + cc::{CWND_INITIAL_PKTS, CWND_MIN}, + cid::ConnectionIdRef, + events::ConnectionEvent, + frame::FRAME_TYPE_PING, + packet::PacketBuilder, + path::PATH_MTU_V6, + recovery::ACK_ONLY_SIZE_LIMIT, + stats::{FrameStats, Stats, MAX_PTO_COUNTS}, + ConnectionIdDecoder, ConnectionIdGenerator, ConnectionParameters, Error, StreamId, StreamType, + Version, +}; // All the tests. mod ackrate; @@ -53,7 +56,7 @@ const DEFAULT_RTT: Duration = Duration::from_millis(100); const AT_LEAST_PTO: Duration = Duration::from_secs(1); const DEFAULT_STREAM_DATA: &[u8] = b"message"; /// The number of 1-RTT packets sent in `force_idle` by a client. -const FORCE_IDLE_CLIENT_1RTT_PACKETS: usize = 3; +const CLIENT_HANDSHAKE_1RTT_PACKETS: usize = 1; /// WARNING! In this module, this version of the generator needs to be used. /// This copies the implementation from @@ -99,7 +102,8 @@ impl ConnectionIdGenerator for CountingConnectionIdGenerator { // These are a direct copy of those functions. pub fn new_client(params: ConnectionParameters) -> Connection { fixture_init(); - Connection::new_client( + let (log, _contents) = new_neqo_qlog(); + let mut client = Connection::new_client( test_fixture::DEFAULT_SERVER_NAME, test_fixture::DEFAULT_ALPN, Rc::new(RefCell::new(CountingConnectionIdGenerator::default())), @@ -108,15 +112,18 @@ pub fn new_client(params: ConnectionParameters) -> Connection { params, now(), ) - .expect("create a default client") + .expect("create a default client"); + client.set_qlog(log); + client } + pub fn default_client() -> Connection { new_client(ConnectionParameters::default()) } pub fn new_server(params: ConnectionParameters) -> Connection { fixture_init(); - + let (log, _contents) = new_neqo_qlog(); let mut c = Connection::new_server( test_fixture::DEFAULT_KEYS, test_fixture::DEFAULT_ALPN, @@ -124,6 +131,7 @@ pub fn new_server(params: ConnectionParameters) -> Connection { params, ) .expect("create a default server"); + c.set_qlog(log); c.server_enable_0rtt(&test_fixture::anti_replay(), AllowZeroRtt {}) .expect("enable 0-RTT"); c @@ -146,6 +154,25 @@ pub fn maybe_authenticate(conn: &mut Connection) -> bool { false } +/// Compute the RTT variance after `n` ACKs or other RTT updates. +pub fn rttvar_after_n_updates(n: usize, rtt: Duration) -> Duration { + assert!(n > 0); + let mut rttvar = rtt / 2; + for _ in 1..n { + rttvar = rttvar * 3 / 4; + } + rttvar +} + +/// This inserts a PING frame into packets. +struct PingWriter {} + +impl crate::connection::test_internal::FrameWriter for PingWriter { + fn write_frames(&mut self, builder: &mut PacketBuilder) { + builder.encode_varint(FRAME_TYPE_PING); + } +} + /// Drive the handshake between the client and server. fn handshake( client: &mut Connection, @@ -165,10 +192,28 @@ fn handshake( ) }; + let mut did_ping = enum_map! {_ => false}; while !is_done(a) { _ = maybe_authenticate(a); let had_input = input.is_some(); - let output = a.process(input, now).dgram(); + // Insert a PING frame into the first application data packet an endpoint sends, + // in order to force the peer to ACK it. For the server, this is depending on the + // client's connection state, which is accessible during the tests. + // + // We're doing this to prevent packet loss from delaying ACKs, which would cause + // cwnd to shrink, and also to prevent the delayed ACK timer from being armed after + // the handshake, which is not something the tests are written to account for. + let should_ping = !did_ping[a.role()] + && (a.role() == Role::Client && *a.state() == State::Connected + || (a.role() == Role::Server && *b.state() == State::Connected)); + if should_ping { + a.test_frame_writer = Some(Box::new(PingWriter {})); + } + let output = a.process(input.as_ref(), now).dgram(); + if should_ping { + a.test_frame_writer = None; + did_ping[a.role()] = true; + } assert!(had_input || output.is_some()); input = output; qtrace!("handshake: t += {:?}", rtt / 2); @@ -176,7 +221,7 @@ fn handshake( mem::swap(&mut a, &mut b); } if let Some(d) = input { - a.process_input(d, now); + a.process_input(&d, now); } now } @@ -200,9 +245,9 @@ fn connect_with_rtt( ) -> Instant { fn check_rtt(stats: &Stats, rtt: Duration) { assert_eq!(stats.rtt, rtt); - // Confirmation takes 2 round trips, - // so rttvar is reduced by 1/4 (from rtt/2). - assert_eq!(stats.rttvar, rtt * 3 / 8); + // Validate that rttvar has been computed correctly based on the number of RTT updates. + let n = stats.frame_rx.ack + usize::from(stats.rtt_init_guess); + assert_eq!(stats.rttvar, rttvar_after_n_updates(n, rtt)); } let now = handshake(client, server, now, rtt); assert_eq!(*client.state(), State::Confirmed); @@ -237,53 +282,31 @@ fn exchange_ticket( server.send_ticket(now, &[]).expect("can send ticket"); let ticket = server.process_output(now).dgram(); assert!(ticket.is_some()); - client.process_input(ticket.unwrap(), now); + client.process_input(&ticket.unwrap(), now); assert_eq!(*client.state(), State::Confirmed); get_tokens(client).pop().expect("should have token") } -/// Getting the client and server to reach an idle state is surprisingly hard. -/// The server sends `HANDSHAKE_DONE` at the end of the handshake, and the client -/// doesn't immediately acknowledge it. Reordering packets does the trick. -fn force_idle( - client: &mut Connection, - server: &mut Connection, - rtt: Duration, - mut now: Instant, -) -> Instant { - // The client has sent NEW_CONNECTION_ID, so ensure that the server generates - // an acknowledgment by sending some reordered packets. - qtrace!("force_idle: send reordered client packets"); - let c1 = send_something(client, now); - let c2 = send_something(client, now); - now += rtt / 2; - server.process_input(c2, now); - server.process_input(c1, now); - - // Now do the same for the server. (The ACK is in the first one.) - qtrace!("force_idle: send reordered server packets"); - let s1 = send_something(server, now); - let s2 = send_something(server, now); - now += rtt / 2; - // Delivering s2 first at the client causes it to want to ACK. - client.process_input(s2, now); - // Delivering s1 should not have the client change its mind about the ACK. - let ack = client.process(Some(s1), now).dgram(); - assert!(ack.is_some()); +/// The `handshake` method inserts PING frames into the first application data packets, +/// which forces each peer to ACK them. As a side effect, that causes both sides of the +/// connection to be idle aftwerwards. This method simply verifies that this is the case. +fn assert_idle(client: &mut Connection, server: &mut Connection, rtt: Duration, now: Instant) { let idle_timeout = min( client.conn_params.get_idle_timeout(), server.conn_params.get_idle_timeout(), ); - assert_eq!(client.process_output(now), Output::Callback(idle_timeout)); - now += rtt / 2; - assert_eq!(server.process(ack, now), Output::Callback(idle_timeout)); - now + // Client started its idle period half an RTT before now. + assert_eq!( + client.process_output(now), + Output::Callback(idle_timeout - rtt / 2) + ); + assert_eq!(server.process_output(now), Output::Callback(idle_timeout)); } /// Connect with an RTT and then force both peers to be idle. fn connect_rtt_idle(client: &mut Connection, server: &mut Connection, rtt: Duration) -> Instant { let now = connect_with_rtt(client, server, now(), rtt); - let now = force_idle(client, server, rtt, now); + assert_idle(client, server, rtt, now); // Drain events from both as well. _ = client.events().count(); _ = server.events().count(); @@ -359,7 +382,7 @@ fn increase_cwnd( let pkt = sender.process_output(now); match pkt { Output::Datagram(dgram) => { - receiver.process_input(dgram, now + DEFAULT_RTT / 2); + receiver.process_input(&dgram, now + DEFAULT_RTT / 2); } Output::Callback(t) => { if t < DEFAULT_RTT { @@ -376,12 +399,14 @@ fn increase_cwnd( now += DEFAULT_RTT / 2; let ack = receiver.process_output(now).dgram(); now += DEFAULT_RTT / 2; - sender.process_input(ack.unwrap(), now); + sender.process_input(&ack.unwrap(), now); now } /// Receive multiple packets and generate an ack-only packet. +/// /// # Panics +/// /// The caller is responsible for ensuring that `dest` has received /// enough data that it wants to generate an ACK. This panics if /// no ACK frame is generated. @@ -395,7 +420,7 @@ where let in_dgrams = in_dgrams.into_iter(); qdebug!([dest], "ack_bytes {} datagrams", in_dgrams.len()); for dgram in in_dgrams { - dest.process_input(dgram, now); + dest.process_input(&dgram, now); } loop { @@ -461,7 +486,7 @@ fn induce_persistent_congestion( // An ACK for the third PTO causes persistent congestion. let s_ack = ack_bytes(server, stream, c_tx_dgrams, now); - client.process_input(s_ack, now); + client.process_input(&s_ack, now); assert_eq!(cwnd(client), CWND_MIN); now } @@ -542,7 +567,7 @@ fn send_and_receive( now: Instant, ) -> Option { let dgram = send_something(sender, now); - receiver.process(Some(dgram), now).dgram() + receiver.process(Some(&dgram), now).dgram() } fn get_tokens(client: &mut Connection) -> Vec { diff --git a/third_party/rust/neqo-transport/src/connection/tests/priority.rs b/third_party/rust/neqo-transport/src/connection/tests/priority.rs index 2b0b5ecdc220..1f86aa22e5ba 100644 --- a/third_party/rust/neqo-transport/src/connection/tests/priority.rs +++ b/third_party/rust/neqo-transport/src/connection/tests/priority.rs @@ -4,6 +4,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::{cell::RefCell, mem, rc::Rc}; + +use neqo_common::event::Provider; +use test_fixture::{self, now}; + use super::{ super::{Connection, Error, Output}, connect, default_client, default_server, fill_cwnd, maybe_authenticate, @@ -14,10 +19,6 @@ use crate::{ ConnectionEvent, StreamId, StreamType, }; -use neqo_common::event::Provider; -use std::{cell::RefCell, mem, rc::Rc}; -use test_fixture::{self, now}; - const BLOCK_SIZE: usize = 4_096; fn fill_stream(c: &mut Connection, id: StreamId) { @@ -40,7 +41,7 @@ fn receive_stream() { assert_eq!(MESSAGE.len(), client.stream_send(id, MESSAGE).unwrap()); let dgram = client.process_output(now()).dgram(); - server.process_input(dgram.unwrap(), now()); + server.process_input(&dgram.unwrap(), now()); assert_eq!( server .stream_priority( @@ -82,7 +83,7 @@ fn relative() { .unwrap(); let dgram = client.process_output(now()).dgram(); - server.process_input(dgram.unwrap(), now()); + server.process_input(&dgram.unwrap(), now()); // The "id_normal" stream will get a `NewStream` event, but no data. for e in server.events() { @@ -113,7 +114,7 @@ fn reprioritize() { .unwrap(); let dgram = client.process_output(now()).dgram(); - server.process_input(dgram.unwrap(), now()); + server.process_input(&dgram.unwrap(), now()); // The "id_normal" stream will get a `NewStream` event, but no data. for e in server.events() { @@ -132,7 +133,7 @@ fn reprioritize() { ) .unwrap(); let dgram = client.process_output(now()).dgram(); - server.process_input(dgram.unwrap(), now()); + server.process_input(&dgram.unwrap(), now()); for e in server.events() { if let ConnectionEvent::RecvStreamReadable { stream_id } = e { @@ -163,7 +164,7 @@ fn repairing_loss() { let _lost = client.process_output(now).dgram(); for _ in 0..5 { match client.process_output(now) { - Output::Datagram(d) => server.process_input(d, now), + Output::Datagram(d) => server.process_input(&d, now), Output::Callback(delay) => now += delay, Output::None => unreachable!(), } @@ -176,9 +177,9 @@ fn repairing_loss() { let id_normal = client.stream_create(StreamType::UniDi).unwrap(); fill_stream(&mut client, id_normal); - let dgram = client.process(ack, now).dgram(); + let dgram = client.process(ack.as_ref(), now).dgram(); assert_eq!(client.stats().lost, 1); // Client should have noticed the loss. - server.process_input(dgram.unwrap(), now); + server.process_input(&dgram.unwrap(), now); // Only the low priority stream has data as the retransmission of the data from // the lost packet is now more important than new data from the high priority stream. @@ -194,7 +195,7 @@ fn repairing_loss() { // the retransmitted data into a second packet, it will also contain data from the // normal priority stream. let dgram = client.process_output(now).dgram(); - server.process_input(dgram.unwrap(), now); + server.process_input(&dgram.unwrap(), now); assert!(server.events().any( |e| matches!(e, ConnectionEvent::RecvStreamReadable { stream_id } if stream_id == id_normal), )); @@ -209,8 +210,8 @@ fn critical() { // Rather than connect, send stream data in 0.5-RTT. // That allows this to test that critical streams pre-empt most frame types. let dgram = client.process_output(now).dgram(); - let dgram = server.process(dgram, now).dgram(); - client.process_input(dgram.unwrap(), now); + let dgram = server.process(dgram.as_ref(), now).dgram(); + client.process_input(&dgram.unwrap(), now); maybe_authenticate(&mut client); let id = server.stream_create(StreamType::UniDi).unwrap(); @@ -237,8 +238,8 @@ fn critical() { assert_eq!(stats_after.handshake_done, 0); // Complete the handshake. - let dgram = client.process(dgram, now).dgram(); - server.process_input(dgram.unwrap(), now); + let dgram = client.process(dgram.as_ref(), now).dgram(); + server.process_input(&dgram.unwrap(), now); // Critical beats everything but HANDSHAKE_DONE. let stats_before = server.stats().frame_tx; @@ -260,8 +261,8 @@ fn important() { // Rather than connect, send stream data in 0.5-RTT. // That allows this to test that important streams pre-empt most frame types. let dgram = client.process_output(now).dgram(); - let dgram = server.process(dgram, now).dgram(); - client.process_input(dgram.unwrap(), now); + let dgram = server.process(dgram.as_ref(), now).dgram(); + client.process_input(&dgram.unwrap(), now); maybe_authenticate(&mut client); let id = server.stream_create(StreamType::UniDi).unwrap(); @@ -289,8 +290,8 @@ fn important() { assert_eq!(stats_after.stream, stats_before.stream + 1); // Complete the handshake. - let dgram = client.process(dgram, now).dgram(); - server.process_input(dgram.unwrap(), now); + let dgram = client.process(dgram.as_ref(), now).dgram(); + server.process_input(&dgram.unwrap(), now); // Important beats everything but flow control. let stats_before = server.stats().frame_tx; @@ -313,8 +314,8 @@ fn high_normal() { // Rather than connect, send stream data in 0.5-RTT. // That allows this to test that important streams pre-empt most frame types. let dgram = client.process_output(now).dgram(); - let dgram = server.process(dgram, now).dgram(); - client.process_input(dgram.unwrap(), now); + let dgram = server.process(dgram.as_ref(), now).dgram(); + client.process_input(&dgram.unwrap(), now); maybe_authenticate(&mut client); let id = server.stream_create(StreamType::UniDi).unwrap(); @@ -342,8 +343,8 @@ fn high_normal() { assert_eq!(stats_after.stream, stats_before.stream + 1); // Complete the handshake. - let dgram = client.process(dgram, now).dgram(); - server.process_input(dgram.unwrap(), now); + let dgram = client.process(dgram.as_ref(), now).dgram(); + server.process_input(&dgram.unwrap(), now); // High or Normal doesn't beat NEW_CONNECTION_ID, // but they beat CRYPTO/NEW_TOKEN. diff --git a/third_party/rust/neqo-transport/src/connection/tests/recovery.rs b/third_party/rust/neqo-transport/src/connection/tests/recovery.rs index 44e77cab1ecd..0f12d0310759 100644 --- a/third_party/rust/neqo-transport/src/connection/tests/recovery.rs +++ b/third_party/rust/neqo-transport/src/connection/tests/recovery.rs @@ -4,6 +4,18 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::{ + mem, + time::{Duration, Instant}, +}; + +use neqo_common::qdebug; +use neqo_crypto::AuthenticationStatus; +use test_fixture::{ + assertions::{assert_handshake, assert_initial}, + now, split_datagram, +}; + use super::{ super::{Connection, ConnectionParameters, Output, State}, assert_full_cwnd, connect, connect_force_idle, connect_rtt_idle, connect_with_rtt, cwnd, @@ -13,7 +25,9 @@ use super::{ use crate::{ cc::CWND_MIN, path::PATH_MTU_V6, - recovery::{FAST_PTO_SCALE, MAX_OUTSTANDING_UNACK, MIN_OUTSTANDING_UNACK, PTO_PACKET_COUNT}, + recovery::{ + FAST_PTO_SCALE, MAX_OUTSTANDING_UNACK, MAX_PTO_PACKET_COUNT, MIN_OUTSTANDING_UNACK, + }, rtt::GRANULARITY, stats::MAX_PTO_COUNTS, tparams::TransportParameter, @@ -21,14 +35,6 @@ use crate::{ StreamType, }; -use neqo_common::qdebug; -use neqo_crypto::AuthenticationStatus; -use std::{ - mem, - time::{Duration, Instant}, -}; -use test_fixture::{self, now, split_datagram}; - #[test] fn pto_works_basic() { let mut client = default_client(); @@ -63,7 +69,7 @@ fn pto_works_basic() { let out = client.process(None, now); let stream_before = server.stats().frame_rx.stream; - server.process_input(out.dgram().unwrap(), now); + server.process_input(&out.dgram().unwrap(), now); assert_eq!(server.stats().frame_rx.stream, stream_before + 2); } @@ -88,7 +94,7 @@ fn pto_works_full_cwnd() { // Both datagrams contain one or more STREAM frames. for d in dgrams { let stream_before = server.stats().frame_rx.stream; - server.process_input(d, now); + server.process_input(&d, now); assert!(server.stats().frame_rx.stream > stream_before); } } @@ -114,32 +120,32 @@ fn pto_works_ping() { assert_eq!(cb, GRANULARITY * 2); // Process these by server, skipping pkt0 - let srv0 = server.process(Some(pkt1), now).dgram(); + let srv0 = server.process(Some(&pkt1), now).dgram(); assert!(srv0.is_some()); // ooo, ack client pkt1 now += Duration::from_millis(20); // process pkt2 (immediate ack because last ack was more than an RTT ago; RTT=0) - let srv1 = server.process(Some(pkt2), now).dgram(); + let srv1 = server.process(Some(&pkt2), now).dgram(); assert!(srv1.is_some()); // this is now dropped now += Duration::from_millis(20); // process pkt3 (acked for same reason) - let srv2 = server.process(Some(pkt3), now).dgram(); + let srv2 = server.process(Some(&pkt3), now).dgram(); // ack client pkt 2 & 3 assert!(srv2.is_some()); // client processes ack - let pkt4 = client.process(srv2, now).dgram(); + let pkt4 = client.process(srv2.as_ref(), now).dgram(); // client resends data from pkt0 assert!(pkt4.is_some()); // server sees ooo pkt0 and generates immediate ack - let srv3 = server.process(Some(pkt0), now).dgram(); + let srv3 = server.process(Some(&pkt0), now).dgram(); assert!(srv3.is_some()); // Accept the acknowledgment. - let pkt5 = client.process(srv3, now).dgram(); + let pkt5 = client.process(srv3.as_ref(), now).dgram(); assert!(pkt5.is_none()); now += Duration::from_millis(70); @@ -149,7 +155,7 @@ fn pto_works_ping() { assert_eq!(client.stats().frame_tx.ping, client_pings + 1); let server_pings = server.stats().frame_rx.ping; - server.process_input(pkt6.unwrap(), now); + server.process_input(&pkt6.unwrap(), now); assert_eq!(server.stats().frame_rx.ping, server_pings + 1); } @@ -173,24 +179,20 @@ fn pto_initial() { assert!(pkt2.is_some()); assert_eq!(pkt2.unwrap().len(), PATH_MTU_V6); - let pkt3 = client.process(None, now).dgram(); - assert!(pkt3.is_some()); - assert_eq!(pkt3.unwrap().len(), PATH_MTU_V6); - let delay = client.process(None, now).callback(); // PTO has doubled. assert_eq!(delay, INITIAL_PTO * 2); // Server process the first initial pkt. let mut server = default_server(); - let out = server.process(pkt1, now).dgram(); + let out = server.process(pkt1.as_ref(), now).dgram(); assert!(out.is_some()); // Client receives ack for the first initial packet as well a Handshake packet. // After the handshake packet the initial keys and the crypto stream for the initial // packet number space will be discarded. // Here only an ack for the Handshake packet will be sent. - let out = client.process(out, now).dgram(); + let out = client.process(out.as_ref(), now).dgram(); assert!(out.is_some()); // We do not have PTO for the resent initial packet any more, but @@ -212,14 +214,17 @@ fn pto_handshake_complete() { let mut server = default_server(); let pkt = client.process(None, now).dgram(); + assert_initial(pkt.as_ref().unwrap(), false); let cb = client.process(None, now).callback(); assert_eq!(cb, Duration::from_millis(300)); now += HALF_RTT; - let pkt = server.process(pkt, now).dgram(); + let pkt = server.process(pkt.as_ref(), now).dgram(); + assert_initial(pkt.as_ref().unwrap(), false); now += HALF_RTT; - let pkt = client.process(pkt, now).dgram(); + let pkt = client.process(pkt.as_ref(), now).dgram(); + assert_handshake(pkt.as_ref().unwrap()); let cb = client.process(None, now).callback(); // The client now has a single RTT estimate (20ms), so @@ -227,7 +232,7 @@ fn pto_handshake_complete() { assert_eq!(cb, HALF_RTT * 6); now += HALF_RTT; - let pkt = server.process(pkt, now).dgram(); + let pkt = server.process(pkt.as_ref(), now).dgram(); assert!(pkt.is_none()); now += HALF_RTT; @@ -235,7 +240,7 @@ fn pto_handshake_complete() { qdebug!("---- client: SH..FIN -> FIN"); let pkt1 = client.process(None, now).dgram(); - assert!(pkt1.is_some()); + assert_handshake(pkt1.as_ref().unwrap()); assert_eq!(*client.state(), State::Connected); let cb = client.process(None, now).callback(); @@ -249,6 +254,7 @@ fn pto_handshake_complete() { qdebug!("---- client: PTO"); now += HALF_RTT * 6; let pkt2 = client.process(None, now).dgram(); + assert_handshake(pkt2.as_ref().unwrap()); pto_counts[0] = 1; assert_eq!(client.stats.borrow().pto_counts, pto_counts); @@ -259,7 +265,10 @@ fn pto_handshake_complete() { let stream_id = client.stream_create(StreamType::UniDi).unwrap(); client.stream_close_send(stream_id).unwrap(); let pkt3 = client.process(None, now).dgram(); + assert_handshake(pkt3.as_ref().unwrap()); let (pkt3_hs, pkt3_1rtt) = split_datagram(&pkt3.unwrap()); + assert_handshake(&pkt3_hs); + assert!(pkt3_1rtt.is_some()); // PTO has been doubled. let cb = client.process(None, now).callback(); @@ -276,8 +285,8 @@ fn pto_handshake_complete() { // This should remove the 1-RTT PTO from messing this test up. let server_acks = server.stats().frame_tx.ack; let server_done = server.stats().frame_tx.handshake_done; - server.process_input(pkt3_1rtt.unwrap(), now); - let ack = server.process(pkt1, now).dgram(); + server.process_input(&pkt3_1rtt.unwrap(), now); + let ack = server.process(pkt1.as_ref(), now).dgram(); assert!(ack.is_some()); assert_eq!(server.stats().frame_tx.ack, server_acks + 2); assert_eq!(server.stats().frame_tx.handshake_done, server_done + 1); @@ -285,22 +294,27 @@ fn pto_handshake_complete() { // Check that the other packets (pkt2, pkt3) are Handshake packets. // The server discarded the Handshake keys already, therefore they are dropped. // Note that these don't include 1-RTT packets, because 1-RTT isn't send on PTO. + let (pkt2_hs, pkt2_1rtt) = split_datagram(&pkt2.unwrap()); + assert_handshake(&pkt2_hs); + assert!(pkt2_1rtt.is_some()); let dropped_before1 = server.stats().dropped_rx; let server_frames = server.stats().frame_rx.all; - server.process_input(pkt2.unwrap(), now); + server.process_input(&pkt2_hs, now); assert_eq!(1, server.stats().dropped_rx - dropped_before1); assert_eq!(server.stats().frame_rx.all, server_frames); + server.process_input(&pkt2_1rtt.unwrap(), now); + let server_frames2 = server.stats().frame_rx.all; let dropped_before2 = server.stats().dropped_rx; - server.process_input(pkt3_hs, now); + server.process_input(&pkt3_hs, now); assert_eq!(1, server.stats().dropped_rx - dropped_before2); - assert_eq!(server.stats().frame_rx.all, server_frames); + assert_eq!(server.stats().frame_rx.all, server_frames2); now += HALF_RTT; // Let the client receive the ACK. // It should now be wait to acknowledge the HANDSHAKE_DONE. - let cb = client.process(ack, now).callback(); + let cb = client.process(ack.as_ref(), now).callback(); // The default ack delay is the RTT divided by the default ACK ratio of 4. let expected_ack_delay = HALF_RTT * 2 / 4; assert_eq!(cb, expected_ack_delay); @@ -309,13 +323,6 @@ fn pto_handshake_complete() { now += cb; let out = client.process(None, now).dgram(); assert!(out.is_some()); - let cb = client.process(None, now).callback(); - // The handshake keys are discarded, but now we're back to the idle timeout. - // We don't send another PING because the handshake space is done and there - // is nothing to probe for. - - let idle_timeout = ConnectionParameters::default().get_idle_timeout(); - assert_eq!(cb, idle_timeout - expected_ack_delay); } /// Test that PTO in the Handshake space contains the right frames. @@ -329,14 +336,14 @@ fn pto_handshake_frames() { now += Duration::from_millis(10); qdebug!("---- server: CH -> SH, EE, CERT, CV, FIN"); let mut server = default_server(); - let pkt = server.process(pkt.dgram(), now); + let pkt = server.process(pkt.as_dgram_ref(), now); now += Duration::from_millis(10); qdebug!("---- client: cert verification"); - let pkt = client.process(pkt.dgram(), now); + let pkt = client.process(pkt.as_dgram_ref(), now); now += Duration::from_millis(10); - mem::drop(server.process(pkt.dgram(), now)); + mem::drop(server.process(pkt.as_dgram_ref(), now)); now += Duration::from_millis(10); client.authenticated(AuthenticationStatus::Ok, now); @@ -359,7 +366,7 @@ fn pto_handshake_frames() { now += Duration::from_millis(10); let crypto_before = server.stats().frame_rx.crypto; - server.process_input(pkt2.unwrap(), now); + server.process_input(&pkt2.unwrap(), now); assert_eq!(server.stats().frame_rx.crypto, crypto_before + 1); } @@ -381,7 +388,7 @@ fn handshake_ack_pto() { let c1 = client.process(None, now).dgram(); now += RTT / 2; - let s1 = server.process(c1, now).dgram(); + let s1 = server.process(c1.as_ref(), now).dgram(); assert!(s1.is_some()); let s2 = server.process(None, now).dgram(); assert!(s1.is_some()); @@ -389,8 +396,8 @@ fn handshake_ack_pto() { // Now let the client have the Initial, but drop the first coalesced Handshake packet. now += RTT / 2; let (initial, _) = split_datagram(&s1.unwrap()); - client.process_input(initial, now); - let c2 = client.process(s2, now).dgram(); + client.process_input(&initial, now); + let c2 = client.process(s2.as_ref(), now).dgram(); assert!(c2.is_some()); // This is an ACK. Drop it. let delay = client.process(None, now).callback(); assert_eq!(delay, RTT * 3); @@ -405,7 +412,7 @@ fn handshake_ack_pto() { now += RTT / 2; let ping_before = server.stats().frame_rx.ping; - server.process_input(c3.unwrap(), now); + server.process_input(&c3.unwrap(), now); assert_eq!(server.stats().frame_rx.ping, ping_before + 1); pto_counts[0] = 1; @@ -413,13 +420,13 @@ fn handshake_ack_pto() { // Now complete the handshake as cheaply as possible. let dgram = server.process(None, now).dgram(); - client.process_input(dgram.unwrap(), now); + client.process_input(&dgram.unwrap(), now); maybe_authenticate(&mut client); let dgram = client.process(None, now).dgram(); assert_eq!(*client.state(), State::Connected); - let dgram = server.process(dgram, now).dgram(); + let dgram = server.process(dgram.as_ref(), now).dgram(); assert_eq!(*server.state(), State::Confirmed); - client.process_input(dgram.unwrap(), now); + client.process_input(&dgram.unwrap(), now); assert_eq!(*client.state(), State::Confirmed); assert_eq!(client.stats.borrow().pto_counts, pto_counts); @@ -440,7 +447,7 @@ fn loss_recovery_crash() { assert!(ack.is_some()); // Have the server process the ACK. - let cb = server.process(ack, now).callback(); + let cb = server.process(ack.as_ref(), now).callback(); assert!(cb > Duration::from_secs(0)); // Now we leap into the future. The server should regard the first @@ -468,7 +475,8 @@ fn ack_after_pto() { // Jump forward to the PTO and drain the PTO packets. now += AT_LEAST_PTO; - for _ in 0..PTO_PACKET_COUNT { + // We can use MAX_PTO_PACKET_COUNT, because we know the handshake is over. + for _ in 0..MAX_PTO_PACKET_COUNT { let dgram = client.process(None, now).dgram(); assert!(dgram.is_some()); } @@ -484,13 +492,13 @@ fn ack_after_pto() { // The client is now after a PTO, but if it receives something // that demands acknowledgment, it will send just the ACK. - let ack = client.process(Some(dgram), now).dgram(); + let ack = client.process(Some(&dgram), now).dgram(); assert!(ack.is_some()); // Make sure that the packet only contained an ACK frame. let all_frames_before = server.stats().frame_rx.all; let ack_before = server.stats().frame_rx.ack; - server.process_input(ack.unwrap(), now); + server.process_input(&ack.unwrap(), now); assert_eq!(server.stats().frame_rx.all, all_frames_before + 1); assert_eq!(server.stats().frame_rx.ack, ack_before + 1); } @@ -511,7 +519,7 @@ fn lost_but_kept_and_lr_timer() { // At t=RTT/2 the server receives the packet and ACKs it. now += RTT / 2; - let ack = server.process(Some(p2), now).dgram(); + let ack = server.process(Some(&p2), now).dgram(); assert!(ack.is_some()); // The client also sends another two packets (p3, p4), again losing the first. let _p3 = send_something(&mut client, now); @@ -520,14 +528,14 @@ fn lost_but_kept_and_lr_timer() { // At t=RTT the client receives the ACK and goes into timed loss recovery. // The client doesn't call p1 lost at this stage, but it will soon. now += RTT / 2; - let res = client.process(ack, now); + let res = client.process(ack.as_ref(), now); // The client should be on a loss recovery timer as p1 is missing. let lr_timer = res.callback(); // Loss recovery timer should be RTT/8, but only check for 0 or >=RTT/2. assert_ne!(lr_timer, Duration::from_secs(0)); assert!(lr_timer < (RTT / 2)); // The server also receives and acknowledges p4, again sending an ACK. - let ack = server.process(Some(p4), now).dgram(); + let ack = server.process(Some(&p4), now).dgram(); assert!(ack.is_some()); // At t=RTT*3/2 the client should declare p1 to be lost. @@ -537,7 +545,7 @@ fn lost_but_kept_and_lr_timer() { assert!(res.dgram().is_some()); // When the client processes the ACK, it should engage the // loss recovery timer for p3, not p1 (even though it still tracks p1). - let res = client.process(ack, now); + let res = client.process(ack.as_ref(), now); let lr_timer2 = res.callback(); assert_eq!(lr_timer, lr_timer2); } @@ -560,7 +568,7 @@ fn loss_time_past_largest_acked() { // Start the handshake. let c_in = client.process(None, now).dgram(); now += RTT / 2; - let s_hs1 = server.process(c_in, now).dgram(); + let s_hs1 = server.process(c_in.as_ref(), now).dgram(); // Get some spare server handshake packets for the client to ACK. // This involves a time machine, so be a little cautious. @@ -583,7 +591,7 @@ fn loss_time_past_largest_acked() { // to generate an ack-eliciting packet. For that, we use the Finished message. // Reordering delivery ensures that the later packet is also acknowledged. now += RTT / 2; - let c_hs1 = client.process(s_hs1, now).dgram(); + let c_hs1 = client.process(s_hs1.as_ref(), now).dgram(); assert!(c_hs1.is_some()); // This comes first, so it's useless. maybe_authenticate(&mut client); let c_hs2 = client.process(None, now).dgram(); @@ -592,17 +600,17 @@ fn loss_time_past_largest_acked() { // The we need the outstanding packet to be sent after the // application data packet, so space these out a tiny bit. let _p1 = send_something(&mut client, now + INCR); - let c_hs3 = client.process(s_hs2, now + (INCR * 2)).dgram(); + let c_hs3 = client.process(s_hs2.as_ref(), now + (INCR * 2)).dgram(); assert!(c_hs3.is_some()); // This will be left outstanding. - let c_hs4 = client.process(s_hs3, now + (INCR * 3)).dgram(); + let c_hs4 = client.process(s_hs3.as_ref(), now + (INCR * 3)).dgram(); assert!(c_hs4.is_some()); // This will be acknowledged. // Process c_hs2 and c_hs4, but skip c_hs3. // Then get an ACK for the client. now += RTT / 2; // Deliver c_hs4 first, but don't generate a packet. - server.process_input(c_hs4.unwrap(), now); - let s_ack = server.process(c_hs2, now).dgram(); + server.process_input(&c_hs4.unwrap(), now); + let s_ack = server.process(c_hs2.as_ref(), now).dgram(); assert!(s_ack.is_some()); // This includes an ACK, but it also includes HANDSHAKE_DONE, // which we need to remove because that will cause the Handshake loss @@ -611,20 +619,12 @@ fn loss_time_past_largest_acked() { // Now the client should start its loss recovery timer based on the ACK. now += RTT / 2; - let c_ack = client.process(Some(s_hs_ack), now).dgram(); + let c_ack = client.process(Some(&s_hs_ack), now).dgram(); assert!(c_ack.is_none()); // The client should now have the loss recovery timer active. let lr_time = client.process(None, now).callback(); assert_ne!(lr_time, Duration::from_secs(0)); assert!(lr_time < (RTT / 2)); - - // Skipping forward by the loss recovery timer should cause the client to - // mark packets as lost and retransmit, after which we should be on the PTO - // timer. - now += lr_time; - let delay = client.process(None, now).callback(); - assert_ne!(delay, Duration::from_secs(0)); - assert!(delay > lr_time); } /// `sender` sends a little, `receiver` acknowledges it. @@ -636,12 +636,12 @@ fn trickle(sender: &mut Connection, receiver: &mut Connection, mut count: usize, while count > 0 { qdebug!("trickle: remaining={}", count); assert_eq!(sender.stream_send(id, &[9]).unwrap(), 1); - let dgram = sender.process(maybe_ack, now).dgram(); + let dgram = sender.process(maybe_ack.as_ref(), now).dgram(); - maybe_ack = receiver.process(dgram, now).dgram(); + maybe_ack = receiver.process(dgram.as_ref(), now).dgram(); count -= usize::from(maybe_ack.is_some()); } - sender.process_input(maybe_ack.unwrap(), now); + sender.process_input(&maybe_ack.unwrap(), now); } /// Ensure that a PING frame is sent with ACK sometimes. @@ -757,7 +757,7 @@ fn fast_pto() { let dgram = client.process(None, now).dgram(); let stream_before = server.stats().frame_rx.stream; - server.process_input(dgram.unwrap(), now); + server.process_input(&dgram.unwrap(), now); assert_eq!(server.stats().frame_rx.stream, stream_before + 1); } @@ -797,8 +797,8 @@ fn fast_pto_persistent_congestion() { // Now acknowledge the tail packet and enter persistent congestion. now += DEFAULT_RTT / 2; - let ack = server.process(Some(dgram), now).dgram(); + let ack = server.process(Some(&dgram), now).dgram(); now += DEFAULT_RTT / 2; - client.process_input(ack.unwrap(), now); + client.process_input(&ack.unwrap(), now); assert_eq!(cwnd(&client), CWND_MIN); } diff --git a/third_party/rust/neqo-transport/src/connection/tests/resumption.rs b/third_party/rust/neqo-transport/src/connection/tests/resumption.rs index 0c34f3448d27..a8c45a9f06a4 100644 --- a/third_party/rust/neqo-transport/src/connection/tests/resumption.rs +++ b/third_party/rust/neqo-transport/src/connection/tests/resumption.rs @@ -4,18 +4,18 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::{cell::RefCell, mem, rc::Rc, time::Duration}; + +use test_fixture::{self, assertions, now}; + use super::{ connect, connect_with_rtt, default_client, default_server, exchange_ticket, get_tokens, new_client, resumed_server, send_something, AT_LEAST_PTO, }; -use crate::addr_valid::{AddressValidation, ValidateAddress}; -use crate::{ConnectionParameters, Error, Version}; - -use std::cell::RefCell; -use std::mem; -use std::rc::Rc; -use std::time::Duration; -use test_fixture::{self, assertions, now}; +use crate::{ + addr_valid::{AddressValidation, ValidateAddress}, + ConnectionParameters, Error, Version, +}; #[test] fn resume() { @@ -55,7 +55,7 @@ fn remember_smoothed_rtt() { let ticket = server.process_output(now).dgram(); assert!(ticket.is_some()); now += RTT1 / 2; - client.process_input(ticket.unwrap(), now); + client.process_input(&ticket.unwrap(), now); let token = get_tokens(&mut client).pop().unwrap(); let mut client = default_client(); @@ -122,7 +122,7 @@ fn two_tickets_on_timer() { let pkt = send_something(&mut server, now()); // process() will return an ack first - assert!(client.process(Some(pkt), now()).dgram().is_some()); + assert!(client.process(Some(&pkt), now()).dgram().is_some()); // We do not have a ResumptionToken event yet, because NEW_TOKEN was not sent. assert_eq!(get_tokens(&mut client).len(), 0); @@ -163,7 +163,7 @@ fn two_tickets_with_new_token() { server.send_ticket(now(), &[]).expect("send ticket2"); let pkt = send_something(&mut server, now()); - client.process_input(pkt, now()); + client.process_input(&pkt, now()); let mut all_tokens = get_tokens(&mut client); assert_eq!(all_tokens.len(), 2); let token1 = all_tokens.pop().unwrap(); @@ -184,7 +184,7 @@ fn take_token() { server.send_ticket(now(), &[]).unwrap(); let dgram = server.process(None, now()).dgram(); - client.process_input(dgram.unwrap(), now()); + client.process_input(&dgram.unwrap(), now()); // There should be no ResumptionToken event here. let tokens = get_tokens(&mut client); diff --git a/third_party/rust/neqo-transport/src/connection/tests/stream.rs b/third_party/rust/neqo-transport/src/connection/tests/stream.rs index 980077e5aa12..586a537b9dd8 100644 --- a/third_party/rust/neqo-transport/src/connection/tests/stream.rs +++ b/third_party/rust/neqo-transport/src/connection/tests/stream.rs @@ -4,6 +4,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::{cmp::max, collections::HashMap, convert::TryFrom, mem}; + +use neqo_common::{event::Provider, qdebug}; +use test_fixture::now; + use super::{ super::State, assert_error, connect, connect_force_idle, default_client, default_server, maybe_authenticate, new_client, new_server, send_something, DEFAULT_STREAM_DATA, @@ -22,11 +27,6 @@ use crate::{ StreamId, StreamType, }; -use std::collections::HashMap; - -use neqo_common::{event::Provider, qdebug}; -use std::{cmp::max, convert::TryFrom, mem}; -use test_fixture::now; #[test] fn stream_create() { @@ -34,10 +34,10 @@ fn stream_create() { let out = client.process(None, now()); let mut server = default_server(); - let out = server.process(out.dgram(), now()); + let out = server.process(out.as_dgram_ref(), now()); - let out = client.process(out.dgram(), now()); - mem::drop(server.process(out.dgram(), now())); + let out = client.process(out.as_dgram_ref(), now()); + mem::drop(server.process(out.as_dgram_ref(), now())); assert!(maybe_authenticate(&mut client)); let out = client.process(None, now()); @@ -47,7 +47,7 @@ fn stream_create() { assert_eq!(client.stream_create(StreamType::BiDi).unwrap(), 0); assert_eq!(client.stream_create(StreamType::BiDi).unwrap(), 4); - mem::drop(server.process(out.dgram(), now())); + mem::drop(server.process(out.as_dgram_ref(), now())); // server now in State::Connected assert_eq!(server.stream_create(StreamType::UniDi).unwrap(), 3); assert_eq!(server.stream_create(StreamType::UniDi).unwrap(), 7); @@ -86,7 +86,7 @@ fn transfer() { qdebug!("---- server receives"); for d in datagrams { - let out = server.process(Some(d), now()); + let out = server.process(Some(&d), now()); // With an RTT of zero, the server will acknowledge every packet immediately. assert!(out.as_dgram_ref().is_some()); qdebug!("Output={:0x?}", out.as_dgram_ref()); @@ -156,8 +156,8 @@ fn sendorder_test(order_of_sendorder: &[Option]) { assert_eq!(*client.state(), State::Confirmed); qdebug!("---- server receives"); - for (_, d) in datagrams.into_iter().enumerate() { - let out = server.process(Some(d), now()); + for d in datagrams { + let out = server.process(Some(&d), now()); qdebug!("Output={:0x?}", out.as_dgram_ref()); } assert_eq!(*server.state(), State::Confirmed); @@ -324,11 +324,11 @@ fn report_fin_when_stream_closed_wo_data() { let stream_id = client.stream_create(StreamType::BiDi).unwrap(); client.stream_send(stream_id, &[0x00]).unwrap(); let out = client.process(None, now()); - mem::drop(server.process(out.dgram(), now())); + mem::drop(server.process(out.as_dgram_ref(), now())); server.stream_close_send(stream_id).unwrap(); let out = server.process(None, now()); - mem::drop(client.process(out.dgram(), now())); + mem::drop(client.process(out.as_dgram_ref(), now())); let stream_readable = |e| matches!(e, ConnectionEvent::RecvStreamReadable { .. }); assert!(client.events().any(stream_readable)); } @@ -336,9 +336,9 @@ fn report_fin_when_stream_closed_wo_data() { fn exchange_data(client: &mut Connection, server: &mut Connection) { let mut input = None; loop { - let out = client.process(input, now()).dgram(); + let out = client.process(input.as_ref(), now()).dgram(); let c_done = out.is_none(); - let out = server.process(out, now()).dgram(); + let out = server.process(out.as_ref(), now()).dgram(); if out.is_none() && c_done { break; } @@ -380,7 +380,7 @@ fn sending_max_data() { assert!(!fin); let out = server.process(None, now()).dgram(); - client.process_input(out.unwrap(), now()); + client.process_input(&out.unwrap(), now()); assert_eq!( client @@ -521,7 +521,7 @@ fn do_not_accept_data_after_stop_sending() { let stream_id = client.stream_create(StreamType::BiDi).unwrap(); client.stream_send(stream_id, &[0x00]).unwrap(); let out = client.process(None, now()); - mem::drop(server.process(out.dgram(), now())); + mem::drop(server.process(out.as_dgram_ref(), now())); let stream_readable = |e| matches!(e, ConnectionEvent::RecvStreamReadable { .. }); assert!(server.events().any(stream_readable)); @@ -538,10 +538,10 @@ fn do_not_accept_data_after_stop_sending() { // Receive the second data frame. The frame should be ignored and // DataReadable events shouldn't be posted. - let out = server.process(out_second_data_frame.dgram(), now()); + let out = server.process(out_second_data_frame.as_dgram_ref(), now()); assert!(!server.events().any(stream_readable)); - mem::drop(client.process(out.dgram(), now())); + mem::drop(client.process(out.as_dgram_ref(), now())); assert_eq!( Err(Error::FinalSizeError), client.stream_send(stream_id, &[0x00]) @@ -559,7 +559,7 @@ fn simultaneous_stop_sending_and_reset() { let stream_id = client.stream_create(StreamType::BiDi).unwrap(); client.stream_send(stream_id, &[0x00]).unwrap(); let out = client.process(None, now()); - let ack = server.process(out.dgram(), now()).dgram(); + let ack = server.process(out.as_dgram_ref(), now()).dgram(); let stream_readable = |e| matches!(e, ConnectionEvent::RecvStreamReadable { stream_id: id } if id == stream_id); @@ -568,23 +568,23 @@ fn simultaneous_stop_sending_and_reset() { // The client resets the stream. The packet with reset should arrive after the server // has already requested stop_sending. client.stream_reset_send(stream_id, 0).unwrap(); - let out_reset_frame = client.process(ack, now()).dgram(); + let out_reset_frame = client.process(ack.as_ref(), now()).dgram(); // Send something out of order to force the server to generate an // acknowledgment at the next opportunity. let force_ack = send_something(&mut client, now()); - server.process_input(force_ack, now()); + server.process_input(&force_ack, now()); // Call stop sending. server.stream_stop_sending(stream_id, 0).unwrap(); // Receive the second data frame. The frame should be ignored and // DataReadable events shouldn't be posted. - let ack = server.process(out_reset_frame, now()).dgram(); + let ack = server.process(out_reset_frame.as_ref(), now()).dgram(); assert!(ack.is_some()); assert!(!server.events().any(stream_readable)); // The client gets the STOP_SENDING frame. - client.process_input(ack.unwrap(), now()); + client.process_input(&ack.unwrap(), now()); assert_eq!( Err(Error::InvalidStreamId), client.stream_send(stream_id, &[0x00]) @@ -600,13 +600,13 @@ fn client_fin_reorder() { let client_hs = client.process(None, now()); assert!(client_hs.as_dgram_ref().is_some()); - let server_hs = server.process(client_hs.dgram(), now()); + let server_hs = server.process(client_hs.as_dgram_ref(), now()); assert!(server_hs.as_dgram_ref().is_some()); // ServerHello, etc... - let client_ack = client.process(server_hs.dgram(), now()); + let client_ack = client.process(server_hs.as_dgram_ref(), now()); assert!(client_ack.as_dgram_ref().is_some()); - let server_out = server.process(client_ack.dgram(), now()); + let server_out = server.process(client_ack.as_dgram_ref(), now()); assert!(server_out.as_dgram_ref().is_none()); assert!(maybe_authenticate(&mut client)); @@ -621,11 +621,11 @@ fn client_fin_reorder() { assert!(client_stream_data.as_dgram_ref().is_some()); // Now stream data gets before client_fin - let server_out = server.process(client_stream_data.dgram(), now()); + let server_out = server.process(client_stream_data.as_dgram_ref(), now()); assert!(server_out.as_dgram_ref().is_none()); // the packet will be discarded assert_eq!(*server.state(), State::Handshaking); - let server_out = server.process(client_fin.dgram(), now()); + let server_out = server.process(client_fin.as_dgram_ref(), now()); assert!(server_out.as_dgram_ref().is_some()); } @@ -641,7 +641,7 @@ fn after_fin_is_read_conn_events_for_stream_should_be_removed() { let out = server.process(None, now()).dgram(); assert!(out.is_some()); - mem::drop(client.process(out, now())); + mem::drop(client.process(out.as_ref(), now())); // read from the stream before checking connection events. let mut buf = vec![0; 4000]; @@ -666,7 +666,7 @@ fn after_stream_stop_sending_is_called_conn_events_for_stream_should_be_removed( let out = server.process(None, now()).dgram(); assert!(out.is_some()); - mem::drop(client.process(out, now())); + mem::drop(client.process(out.as_ref(), now())); // send stop seending. client @@ -695,7 +695,7 @@ fn stream_data_blocked_generates_max_stream_data() { assert!(dgram.is_some()); // Consume the data. - client.process_input(dgram.unwrap(), now); + client.process_input(&dgram.unwrap(), now); let mut buf = [0; 10]; let (count, end) = client.stream_recv(stream_id, &mut buf[..]).unwrap(); assert_eq!(count, DEFAULT_STREAM_DATA.len()); @@ -712,14 +712,14 @@ fn stream_data_blocked_generates_max_stream_data() { assert!(dgram.is_some()); let sdb_before = client.stats().frame_rx.stream_data_blocked; - let dgram = client.process(dgram, now).dgram(); + let dgram = client.process(dgram.as_ref(), now).dgram(); assert_eq!(client.stats().frame_rx.stream_data_blocked, sdb_before + 1); assert!(dgram.is_some()); // Client should have sent a MAX_STREAM_DATA frame with just a small increase // on the default window size. let msd_before = server.stats().frame_rx.max_stream_data; - server.process_input(dgram.unwrap(), now); + server.process_input(&dgram.unwrap(), now); assert_eq!(server.stats().frame_rx.max_stream_data, msd_before + 1); // Test that the entirety of the receive buffer is available now. @@ -754,19 +754,19 @@ fn max_streams_after_bidi_closed() { let dgram = client.process(None, now()).dgram(); // Now handle the stream and send an incomplete response. - server.process_input(dgram.unwrap(), now()); + server.process_input(&dgram.unwrap(), now()); server.stream_send(stream_id, RESPONSE).unwrap(); let dgram = server.process_output(now()).dgram(); // The server shouldn't have released more stream credit. - client.process_input(dgram.unwrap(), now()); + client.process_input(&dgram.unwrap(), now()); let e = client.stream_create(StreamType::BiDi).unwrap_err(); assert!(matches!(e, Error::StreamLimitError)); // Closing the stream isn't enough. server.stream_close_send(stream_id).unwrap(); let dgram = server.process_output(now()).dgram(); - client.process_input(dgram.unwrap(), now()); + client.process_input(&dgram.unwrap(), now()); assert!(client.stream_create(StreamType::BiDi).is_err()); // The server needs to see an acknowledgment from the client for its @@ -780,12 +780,12 @@ fn max_streams_after_bidi_closed() { // We need an ACK from the client now, but that isn't guaranteed, // so give the client one more packet just in case. let dgram = send_something(&mut server, now()); - client.process_input(dgram, now()); + client.process_input(&dgram, now()); // Now get the client to send the ACK and have the server handle that. let dgram = send_something(&mut client, now()); - let dgram = server.process(Some(dgram), now()).dgram(); - client.process_input(dgram.unwrap(), now()); + let dgram = server.process(Some(&dgram), now()).dgram(); + client.process_input(&dgram.unwrap(), now()); assert!(client.stream_create(StreamType::BiDi).is_ok()); assert!(client.stream_create(StreamType::BiDi).is_err()); } @@ -800,7 +800,7 @@ fn no_dupdata_readable_events() { let stream_id = client.stream_create(StreamType::BiDi).unwrap(); client.stream_send(stream_id, &[0x00]).unwrap(); let out = client.process(None, now()); - mem::drop(server.process(out.dgram(), now())); + mem::drop(server.process(out.as_dgram_ref(), now())); // We have a data_readable event. let stream_readable = |e| matches!(e, ConnectionEvent::RecvStreamReadable { .. }); @@ -810,7 +810,7 @@ fn no_dupdata_readable_events() { // therefore there should not be a new DataReadable event. client.stream_send(stream_id, &[0x00]).unwrap(); let out_second_data_frame = client.process(None, now()); - mem::drop(server.process(out_second_data_frame.dgram(), now())); + mem::drop(server.process(out_second_data_frame.as_dgram_ref(), now())); assert!(!server.events().any(stream_readable)); // One more frame with a fin will not produce a new DataReadable event, because the @@ -818,7 +818,7 @@ fn no_dupdata_readable_events() { client.stream_send(stream_id, &[0x00]).unwrap(); client.stream_close_send(stream_id).unwrap(); let out_third_data_frame = client.process(None, now()); - mem::drop(server.process(out_third_data_frame.dgram(), now())); + mem::drop(server.process(out_third_data_frame.as_dgram_ref(), now())); assert!(!server.events().any(stream_readable)); } @@ -832,7 +832,7 @@ fn no_dupdata_readable_events_empty_last_frame() { let stream_id = client.stream_create(StreamType::BiDi).unwrap(); client.stream_send(stream_id, &[0x00]).unwrap(); let out = client.process(None, now()); - mem::drop(server.process(out.dgram(), now())); + mem::drop(server.process(out.as_dgram_ref(), now())); // We have a data_readable event. let stream_readable = |e| matches!(e, ConnectionEvent::RecvStreamReadable { .. }); @@ -842,7 +842,7 @@ fn no_dupdata_readable_events_empty_last_frame() { // the previous stream data has not been read yet. client.stream_close_send(stream_id).unwrap(); let out_second_data_frame = client.process(None, now()); - mem::drop(server.process(out_second_data_frame.dgram(), now())); + mem::drop(server.process(out_second_data_frame.as_dgram_ref(), now())); assert!(!server.events().any(stream_readable)); } @@ -864,14 +864,14 @@ fn change_flow_control(stream_type: StreamType, new_fc: u64) { // Send the stream to the client. let out = server.process(None, now()); - mem::drop(client.process(out.dgram(), now())); + mem::drop(client.process(out.as_dgram_ref(), now())); // change max_stream_data for stream_id. client.set_stream_max_data(stream_id, new_fc).unwrap(); // 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 out3 = server.process(out2.as_dgram_ref(), now()); let expected = usize::from(RECV_BUFFER_START < new_fc); assert_eq!(server.stats().frame_rx.max_stream_data, expected); @@ -884,9 +884,9 @@ fn change_flow_control(stream_type: StreamType, new_fc: u64) { } // Exchange packets so that client gets all data. - let out4 = client.process(out3.dgram(), now()); - let out5 = server.process(out4.dgram(), now()); - mem::drop(client.process(out5.dgram(), now())); + let out4 = client.process(out3.as_dgram_ref(), now()); + let out5 = server.process(out4.as_dgram_ref(), now()); + mem::drop(client.process(out5.as_dgram_ref(), now())); // read all data by client let mut buf = [0x0; 10000]; @@ -894,7 +894,7 @@ fn change_flow_control(stream_type: StreamType, new_fc: u64) { assert_eq!(u64::try_from(read).unwrap(), max(RECV_BUFFER_START, new_fc)); let out4 = client.process(None, now()); - mem::drop(server.process(out4.dgram(), now())); + mem::drop(server.process(out4.as_dgram_ref(), now())); let written3 = server.stream_send(stream_id, &[0x0; 10000]).unwrap(); assert_eq!(u64::try_from(written3).unwrap(), new_fc); @@ -949,12 +949,12 @@ fn session_flow_control_stop_sending_state_recv() { // The server sends STOP_SENDING -> the client sends RESET -> the server // sends MAX_DATA. let out = server.process(None, now()).dgram(); - let out = client.process(out, now()).dgram(); + let out = client.process(out.as_ref(), now()).dgram(); // the client is still limited. let stream_id2 = client.stream_create(StreamType::UniDi).unwrap(); assert_eq!(client.stream_avail_send_space(stream_id2).unwrap(), 0); - let out = server.process(out, now()).dgram(); - client.process_input(out.unwrap(), now()); + let out = server.process(out.as_ref(), now()).dgram(); + client.process_input(&out.unwrap(), now()); assert_eq!( client.stream_avail_send_space(stream_id2).unwrap(), SMALL_MAX_DATA @@ -991,7 +991,7 @@ fn session_flow_control_stop_sending_state_size_known() { client.stream_close_send(stream_id).unwrap(); let out2 = client.process(None, now()).dgram(); - server.process_input(out2.unwrap(), now()); + server.process_input(&out2.unwrap(), now()); server .stream_stop_sending(stream_id, Error::NoError.code()) @@ -1000,8 +1000,8 @@ fn session_flow_control_stop_sending_state_size_known() { // In this case the final size is known when stream_stop_sending is called // and the server releases flow control immediately and sends STOP_SENDING and // MAX_DATA in the same packet. - let out = server.process(out1, now()).dgram(); - client.process_input(out.unwrap(), now()); + let out = server.process(out1.as_ref(), now()).dgram(); + client.process_input(&out.unwrap(), now()); // The flow control should have been updated and the client can again send // SMALL_MAX_DATA. @@ -1123,10 +1123,10 @@ fn connect_w_different_limit(bidi_limit: u64, unidi_limit: u64) { .max_streams(StreamType::BiDi, bidi_limit) .max_streams(StreamType::UniDi, unidi_limit), ); - let out = server.process(out.dgram(), now()); + let out = server.process(out.as_dgram_ref(), now()); - let out = client.process(out.dgram(), now()); - mem::drop(server.process(out.dgram(), now())); + let out = client.process(out.as_dgram_ref(), now()); + mem::drop(server.process(out.as_dgram_ref(), now())); assert!(maybe_authenticate(&mut client)); diff --git a/third_party/rust/neqo-transport/src/connection/tests/vn.rs b/third_party/rust/neqo-transport/src/connection/tests/vn.rs index 416128f74e03..22f15c991c20 100644 --- a/third_party/rust/neqo-transport/src/connection/tests/vn.rs +++ b/third_party/rust/neqo-transport/src/connection/tests/vn.rs @@ -4,19 +4,21 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use super::super::{ConnectionError, ConnectionEvent, Output, State, ZeroRttState}; +use std::{mem, time::Duration}; + +use neqo_common::{event::Provider, Decoder, Encoder}; +use test_fixture::{self, assertions, datagram, now}; + use super::{ + super::{ConnectionError, ConnectionEvent, Output, State, ZeroRttState}, connect, connect_fail, default_client, default_server, exchange_ticket, new_client, new_server, send_something, }; -use crate::packet::PACKET_BIT_LONG; -use crate::tparams::{self, TransportParameter}; -use crate::{ConnectionParameters, Error, Version}; - -use neqo_common::{event::Provider, Datagram, Decoder, Encoder}; -use std::mem; -use std::time::Duration; -use test_fixture::{self, addr, assertions, now}; +use crate::{ + packet::PACKET_BIT_LONG, + tparams::{self, TransportParameter}, + ConnectionParameters, Error, Version, +}; // The expected PTO duration after the first Initial is sent. const INITIAL_PTO: Duration = Duration::from_millis(300); @@ -29,10 +31,7 @@ fn unknown_version() { let mut unknown_version_packet = vec![0x80, 0x1a, 0x1a, 0x1a, 0x1a]; unknown_version_packet.resize(1200, 0x0); - mem::drop(client.process( - Some(Datagram::new(addr(), addr(), unknown_version_packet)), - now(), - )); + mem::drop(client.process(Some(&datagram(unknown_version_packet)), now())); assert_eq!(1, client.stats().dropped_rx); } @@ -44,10 +43,7 @@ fn server_receive_unknown_first_packet() { unknown_version_packet.resize(1200, 0x0); assert_eq!( - server.process( - Some(Datagram::new(addr(), addr(), unknown_version_packet,)), - now(), - ), + server.process(Some(&datagram(unknown_version_packet,)), now(),), Output::None ); @@ -86,8 +82,8 @@ fn version_negotiation_current_version() { &[0x1a1a_1a1a, Version::default().wire_version()], ); - let dgram = Datagram::new(addr(), addr(), vn); - let delay = client.process(Some(dgram), now()).callback(); + let dgram = datagram(vn); + let delay = client.process(Some(&dgram), now()).callback(); assert_eq!(delay, INITIAL_PTO); assert_eq!(*client.state(), State::WaitInitial); assert_eq!(1, client.stats().dropped_rx); @@ -105,8 +101,8 @@ fn version_negotiation_version0() { let vn = create_vn(&initial_pkt, &[0, 0x1a1a_1a1a]); - let dgram = Datagram::new(addr(), addr(), vn); - let delay = client.process(Some(dgram), now()).callback(); + let dgram = datagram(vn); + let delay = client.process(Some(&dgram), now()).callback(); assert_eq!(delay, INITIAL_PTO); assert_eq!(*client.state(), State::WaitInitial); assert_eq!(1, client.stats().dropped_rx); @@ -124,8 +120,8 @@ fn version_negotiation_only_reserved() { let vn = create_vn(&initial_pkt, &[0x1a1a_1a1a, 0x2a2a_2a2a]); - let dgram = Datagram::new(addr(), addr(), vn); - assert_eq!(client.process(Some(dgram), now()), Output::None); + let dgram = datagram(vn); + assert_eq!(client.process(Some(&dgram), now()), Output::None); match client.state() { State::Closed(err) => { assert_eq!(*err, ConnectionError::Transport(Error::VersionNegotiation)); @@ -146,8 +142,8 @@ fn version_negotiation_corrupted() { let vn = create_vn(&initial_pkt, &[0x1a1a_1a1a, 0x2a2a_2a2a]); - let dgram = Datagram::new(addr(), addr(), &vn[..vn.len() - 1]); - let delay = client.process(Some(dgram), now()).callback(); + let dgram = datagram(vn[..vn.len() - 1].to_vec()); + let delay = client.process(Some(&dgram), now()).callback(); assert_eq!(delay, INITIAL_PTO); assert_eq!(*client.state(), State::WaitInitial); assert_eq!(1, client.stats().dropped_rx); @@ -165,8 +161,8 @@ fn version_negotiation_empty() { let vn = create_vn(&initial_pkt, &[]); - let dgram = Datagram::new(addr(), addr(), vn); - let delay = client.process(Some(dgram), now()).callback(); + let dgram = datagram(vn); + let delay = client.process(Some(&dgram), now()).callback(); assert_eq!(delay, INITIAL_PTO); assert_eq!(*client.state(), State::WaitInitial); assert_eq!(1, client.stats().dropped_rx); @@ -183,8 +179,8 @@ fn version_negotiation_not_supported() { .to_vec(); let vn = create_vn(&initial_pkt, &[0x1a1a_1a1a, 0x2a2a_2a2a, 0xff00_0001]); - let dgram = Datagram::new(addr(), addr(), vn); - assert_eq!(client.process(Some(dgram), now()), Output::None); + let dgram = datagram(vn); + assert_eq!(client.process(Some(&dgram), now()), Output::None); match client.state() { State::Closed(err) => { assert_eq!(*err, ConnectionError::Transport(Error::VersionNegotiation)); @@ -206,8 +202,8 @@ fn version_negotiation_bad_cid() { initial_pkt[6] ^= 0xc4; let vn = create_vn(&initial_pkt, &[0x1a1a_1a1a, 0x2a2a_2a2a, 0xff00_0001]); - let dgram = Datagram::new(addr(), addr(), vn); - let delay = client.process(Some(dgram), now()).callback(); + let dgram = datagram(vn); + let delay = client.process(Some(&dgram), now()).callback(); assert_eq!(delay, INITIAL_PTO); assert_eq!(*client.state(), State::WaitInitial); assert_eq!(1, client.stats().dropped_rx); @@ -223,8 +219,8 @@ fn compatible_upgrade() { assert_eq!(server.version(), Version::Version2); } -/// When the first packet from the client is gigantic, the server might generate acknowledgment packets in -/// version 1. Both client and server need to handle that gracefully. +/// When the first packet from the client is gigantic, the server might generate acknowledgment +/// packets in version 1. Both client and server need to handle that gracefully. #[test] fn compatible_upgrade_large_initial() { let params = ConnectionParameters::default().versions( @@ -244,11 +240,11 @@ fn compatible_upgrade_large_initial() { // Each should elicit a Version 1 ACK from the server. let dgram = client.process_output(now()).dgram(); assert!(dgram.is_some()); - let dgram = server.process(dgram, now()).dgram(); + let dgram = server.process(dgram.as_ref(), now()).dgram(); assert!(dgram.is_some()); // The following uses the Version from *outside* this crate. assertions::assert_version(dgram.as_ref().unwrap(), Version::Version1.wire_version()); - client.process_input(dgram.unwrap(), now()); + client.process_input(&dgram.unwrap(), now()); connect(&mut client, &mut server); assert_eq!(client.version(), Version::Version2); @@ -311,8 +307,8 @@ fn version_negotiation_downgrade() { // Start the handshake and spoof a VN packet. let initial = client.process_output(now()).dgram().unwrap(); let vn = create_vn(&initial, &[DOWNGRADE.wire_version()]); - let dgram = Datagram::new(addr(), addr(), vn); - client.process_input(dgram, now()); + let dgram = datagram(vn); + client.process_input(&dgram, now()); connect_fail( &mut client, @@ -332,7 +328,7 @@ fn invalid_server_version() { new_server(ConnectionParameters::default().versions(Version::Version2, Version::all())); let dgram = client.process_output(now()).dgram(); - server.process_input(dgram.unwrap(), now()); + server.process_input(&dgram.unwrap(), now()); // One packet received. assert_eq!(server.stats().packets_rx, 1); @@ -358,7 +354,7 @@ fn invalid_current_version_client() { assert_ne!(OTHER_VERSION, client.version()); client .set_local_tparam( - tparams::VERSION_NEGOTIATION, + tparams::VERSION_INFORMATION, TransportParameter::Versions { current: OTHER_VERSION.wire_version(), other: Version::all() @@ -394,7 +390,7 @@ fn invalid_current_version_server() { assert!(!Version::default().is_compatible(OTHER_VERSION)); server .set_local_tparam( - tparams::VERSION_NEGOTIATION, + tparams::VERSION_INFORMATION, TransportParameter::Versions { current: OTHER_VERSION.wire_version(), other: vec![OTHER_VERSION.wire_version()], @@ -420,7 +416,7 @@ fn no_compatible_version() { assert_ne!(OTHER_VERSION, client.version()); client .set_local_tparam( - tparams::VERSION_NEGOTIATION, + tparams::VERSION_INFORMATION, TransportParameter::Versions { current: Version::default().wire_version(), other: vec![OTHER_VERSION.wire_version()], @@ -463,7 +459,7 @@ fn compatible_upgrade_0rtt_rejected() { let initial = send_something(&mut client, now()); assertions::assert_version(&initial, Version::Version1.wire_version()); assertions::assert_coalesced_0rtt(&initial); - server.process_input(initial, now()); + server.process_input(&initial, now()); assert!(!server .events() .any(|e| matches!(e, ConnectionEvent::NewStream { .. }))); @@ -471,9 +467,9 @@ fn compatible_upgrade_0rtt_rejected() { // Finalize the connection. Don't use connect() because it uses // maybe_authenticate() too liberally and that eats the events we want to check. let dgram = server.process_output(now()).dgram(); // ServerHello flight - let dgram = client.process(dgram, now()).dgram(); // Client Finished (note: no authentication) - let dgram = server.process(dgram, now()).dgram(); // HANDSHAKE_DONE - client.process_input(dgram.unwrap(), now()); + let dgram = client.process(dgram.as_ref(), now()).dgram(); // Client Finished (note: no authentication) + let dgram = server.process(dgram.as_ref(), now()).dgram(); // HANDSHAKE_DONE + client.process_input(&dgram.unwrap(), now()); assert!(matches!(client.state(), State::Confirmed)); assert!(matches!(server.state(), State::Confirmed)); diff --git a/third_party/rust/neqo-transport/src/connection/tests/zerortt.rs b/third_party/rust/neqo-transport/src/connection/tests/zerortt.rs index 8c8a980c0c99..0aa5573c98bb 100644 --- a/third_party/rust/neqo-transport/src/connection/tests/zerortt.rs +++ b/third_party/rust/neqo-transport/src/connection/tests/zerortt.rs @@ -4,20 +4,18 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use super::super::Connection; -use super::{ - connect, default_client, default_server, exchange_ticket, new_server, resumed_server, - CountingConnectionIdGenerator, -}; -use crate::events::ConnectionEvent; -use crate::{ConnectionParameters, Error, StreamType, Version}; +use std::{cell::RefCell, rc::Rc}; use neqo_common::event::Provider; use neqo_crypto::{AllowZeroRtt, AntiReplay}; -use std::cell::RefCell; -use std::rc::Rc; use test_fixture::{self, assertions, now}; +use super::{ + super::Connection, connect, default_client, default_server, exchange_ticket, new_server, + resumed_server, CountingConnectionIdGenerator, +}; +use crate::{events::ConnectionEvent, ConnectionParameters, Error, StreamType, Version}; + #[test] fn zero_rtt_negotiate() { // Note that the two servers in this test will get different anti-replay filters. @@ -62,12 +60,12 @@ fn zero_rtt_send_recv() { // 0-RTT packets on their own shouldn't be padded to 1200. assert!(client_0rtt.as_dgram_ref().unwrap().len() < 1200); - let server_hs = server.process(client_hs.dgram(), now()); + let server_hs = server.process(client_hs.as_dgram_ref(), now()); assert!(server_hs.as_dgram_ref().is_some()); // ServerHello, etc... let all_frames = server.stats().frame_tx.all; let ack_frames = server.stats().frame_tx.ack; - let server_process_0rtt = server.process(client_0rtt.dgram(), now()); + let server_process_0rtt = server.process(client_0rtt.as_dgram_ref(), now()); assert!(server_process_0rtt.as_dgram_ref().is_some()); assert_eq!(server.stats().frame_tx.all, all_frames + 1); assert_eq!(server.stats().frame_tx.ack, ack_frames + 1); @@ -104,7 +102,7 @@ fn zero_rtt_send_coalesce() { assertions::assert_coalesced_0rtt(&client_0rtt.as_dgram_ref().unwrap()[..]); - let server_hs = server.process(client_0rtt.dgram(), now()); + let server_hs = server.process(client_0rtt.as_dgram_ref(), now()); assert!(server_hs.as_dgram_ref().is_some()); // Should produce ServerHello etc... let server_stream_id = server @@ -161,9 +159,9 @@ fn zero_rtt_send_reject() { let client_0rtt = client.process(None, now()); assert!(client_0rtt.as_dgram_ref().is_some()); - let server_hs = server.process(client_hs.dgram(), now()); + let server_hs = server.process(client_hs.as_dgram_ref(), now()); assert!(server_hs.as_dgram_ref().is_some()); // Should produce ServerHello etc... - let server_ignored = server.process(client_0rtt.dgram(), now()); + let server_ignored = server.process(client_0rtt.as_dgram_ref(), now()); assert!(server_ignored.as_dgram_ref().is_none()); // The server shouldn't receive that 0-RTT data. @@ -171,14 +169,14 @@ fn zero_rtt_send_reject() { assert!(!server.events().any(recvd_stream_evt)); // Client should get a rejection. - let client_fin = client.process(server_hs.dgram(), now()); + let client_fin = client.process(server_hs.as_dgram_ref(), now()); let recvd_0rtt_reject = |e| e == ConnectionEvent::ZeroRttRejected; assert!(client.events().any(recvd_0rtt_reject)); // Server consume client_fin - let server_ack = server.process(client_fin.dgram(), now()); + let server_ack = server.process(client_fin.as_dgram_ref(), now()); assert!(server_ack.as_dgram_ref().is_some()); - let client_out = client.process(server_ack.dgram(), now()); + let client_out = client.process(server_ack.as_dgram_ref(), now()); assert!(client_out.as_dgram_ref().is_none()); // ...and the client stream should be gone. @@ -194,7 +192,7 @@ fn zero_rtt_send_reject() { assert!(client_after_reject.is_some()); // The server should receive new stream - server.process_input(client_after_reject.unwrap(), now()); + server.process_input(&client_after_reject.unwrap(), now()); assert!(server.events().any(recvd_stream_evt)); } @@ -233,8 +231,8 @@ fn zero_rtt_update_flow_control() { assert!(!client.stream_send_atomic(bidi_stream, MESSAGE).unwrap()); // Now get the server transport parameters. - let server_hs = server.process(client_hs, now()).dgram(); - client.process_input(server_hs.unwrap(), now()); + let server_hs = server.process(client_hs.as_ref(), now()).dgram(); + client.process_input(&server_hs.unwrap(), now()); // The streams should report a writeable event. let mut uni_stream_event = false; diff --git a/third_party/rust/neqo-transport/src/crypto.rs b/third_party/rust/neqo-transport/src/crypto.rs index e97f8fc9b75e..f6cc7c0e2f6c 100644 --- a/third_party/rust/neqo-transport/src/crypto.rs +++ b/third_party/rust/neqo-transport/src/crypto.rs @@ -16,12 +16,12 @@ use std::{ }; use neqo_common::{hex, hex_snip_middle, qdebug, qinfo, qtrace, Encoder, Role}; - use neqo_crypto::{ hkdf, hp::HpKey, Aead, Agent, AntiReplay, Cipher, Epoch, Error as CryptoError, HandshakeState, PrivateKey, PublicKey, Record, RecordList, ResumptionToken, SymKey, ZeroRttChecker, TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384, TLS_CHACHA20_POLY1305_SHA256, TLS_CT_HANDSHAKE, TLS_EPOCH_APPLICATION_DATA, TLS_EPOCH_HANDSHAKE, TLS_EPOCH_INITIAL, TLS_EPOCH_ZERO_RTT, + TLS_GRP_EC_SECP256R1, TLS_GRP_EC_SECP384R1, TLS_GRP_EC_SECP521R1, TLS_GRP_EC_X25519, TLS_VERSION_1_3, }; @@ -78,6 +78,13 @@ impl Crypto { TLS_AES_256_GCM_SHA384, TLS_CHACHA20_POLY1305_SHA256, ])?; + agent.set_groups(&[ + TLS_GRP_EC_X25519, + TLS_GRP_EC_SECP256R1, + TLS_GRP_EC_SECP384R1, + TLS_GRP_EC_SECP521R1, + ])?; + agent.send_additional_key_shares(1)?; agent.set_alpn(&protocols)?; agent.disable_end_of_early_data()?; // Always enable 0-RTT on the client, but the server needs @@ -218,7 +225,7 @@ impl Crypto { self.tls.read_secret(TLS_EPOCH_ZERO_RTT), ), }; - let secret = secret.ok_or(Error::InternalError(1))?; + let secret = secret.ok_or(Error::InternalError)?; self.states .set_0rtt_keys(version, dir, &secret, cipher.unwrap()); Ok(true) @@ -245,21 +252,19 @@ impl Crypto { fn install_handshake_keys(&mut self) -> Res { qtrace!([self], "Attempt to install handshake keys"); - let write_secret = if let Some(secret) = self.tls.write_secret(TLS_EPOCH_HANDSHAKE) { - secret - } else { + let Some(write_secret) = self.tls.write_secret(TLS_EPOCH_HANDSHAKE) else { // No keys is fine. return Ok(false); }; let read_secret = self .tls .read_secret(TLS_EPOCH_HANDSHAKE) - .ok_or(Error::InternalError(2))?; + .ok_or(Error::InternalError)?; let cipher = match self.tls.info() { None => self.tls.preinfo()?.cipher_suite(), Some(info) => Some(info.cipher_suite()), } - .ok_or(Error::InternalError(3))?; + .ok_or(Error::InternalError)?; self.states .set_handshake_keys(self.version, &write_secret, &read_secret, cipher); qdebug!([self], "Handshake keys installed"); @@ -283,7 +288,7 @@ impl Crypto { let read_secret = self .tls .read_secret(TLS_EPOCH_APPLICATION_DATA) - .ok_or(Error::InternalError(4))?; + .ok_or(Error::InternalError)?; self.states .set_application_read_key(version, read_secret, expire_0rtt)?; qdebug!([self], "application read keys installed"); @@ -657,7 +662,7 @@ impl CryptoDxState { // The numbers in `Self::limit` assume a maximum packet size of 2^11. if body.len() > 2048 { debug_assert!(false); - return Err(Error::InternalError(12)); + return Err(Error::InternalError); } self.invoked()?; @@ -1400,6 +1405,9 @@ pub enum CryptoStreams { } impl CryptoStreams { + /// Keep around 64k if a server wants to push excess data at us. + const BUFFER_LIMIT: u64 = 65536; + pub fn discard(&mut self, space: PacketNumberSpace) { match space { PacketNumberSpace::Initial => { @@ -1434,8 +1442,14 @@ impl CryptoStreams { self.get_mut(space).unwrap().tx.send(data); } - pub fn inbound_frame(&mut self, space: PacketNumberSpace, offset: u64, data: &[u8]) { - self.get_mut(space).unwrap().rx.inbound_frame(offset, data); + pub fn inbound_frame(&mut self, space: PacketNumberSpace, offset: u64, data: &[u8]) -> Res<()> { + let rx = &mut self.get_mut(space).unwrap().rx; + rx.inbound_frame(offset, data); + if rx.received() - rx.retired() <= Self::BUFFER_LIMIT { + Ok(()) + } else { + Err(Error::CryptoBufferExceeded) + } } pub fn data_ready(&self, space: PacketNumberSpace) -> bool { @@ -1527,8 +1541,8 @@ impl CryptoStreams { } // Calculate length of data based on the minimum of: // - available data - // - remaining space, less the header, which counts only one byte - // for the length at first to avoid underestimating length + // - remaining space, less the header, which counts only one byte for the length at + // first to avoid underestimating length let length = min(data.len(), builder.remaining() - header_len); header_len += Encoder::varint_len(u64::try_from(length).unwrap()) - 1; let length = min(data.len(), builder.remaining() - header_len); @@ -1536,9 +1550,6 @@ impl CryptoStreams { builder.encode_varint(crate::frame::FRAME_TYPE_CRYPTO); builder.encode_varint(offset); builder.encode_vvec(&data[..length]); - if builder.len() > builder.limit() { - return Err(Error::InternalError(15)); - } cs.tx.mark_as_sent(offset, length); diff --git a/third_party/rust/neqo-transport/src/events.rs b/third_party/rust/neqo-transport/src/events.rs index 65b376eb0be4..88a85250eed5 100644 --- a/third_party/rust/neqo-transport/src/events.rs +++ b/third_party/rust/neqo-transport/src/events.rs @@ -6,17 +6,18 @@ // Collecting a list of events relevant to whoever is using the Connection. -use std::cell::RefCell; -use std::collections::VecDeque; -use std::rc::Rc; +use std::{cell::RefCell, collections::VecDeque, rc::Rc}; -use crate::connection::State; -use crate::quic_datagrams::DatagramTracking; -use crate::stream_id::{StreamId, StreamType}; -use crate::{AppError, Stats}; use neqo_common::event::Provider as EventProvider; use neqo_crypto::ResumptionToken; +use crate::{ + connection::State, + quic_datagrams::DatagramTracking, + stream_id::{StreamId, StreamType}, + AppError, Stats, +}; + #[derive(Debug, PartialOrd, Ord, PartialEq, Eq)] pub enum OutgoingDatagramOutcome { DroppedTooBig, @@ -235,7 +236,7 @@ impl ConnectionEvents { where F: Fn(&ConnectionEvent) -> bool, { - self.events.borrow_mut().retain(|evt| !f(evt)) + self.events.borrow_mut().retain(|evt| !f(evt)); } } diff --git a/third_party/rust/neqo-transport/src/fc.rs b/third_party/rust/neqo-transport/src/fc.rs index 090afdc53820..a219ca7e8d41 100644 --- a/third_party/rust/neqo-transport/src/fc.rs +++ b/third_party/rust/neqo-transport/src/fc.rs @@ -7,6 +7,14 @@ // Tracks possibly-redundant flow control signals from other code and converts // into flow control frames needing to be sent to the remote. +use std::{ + convert::TryFrom, + fmt::Debug, + ops::{Deref, DerefMut, Index, IndexMut}, +}; + +use neqo_common::{qtrace, Role}; + use crate::{ frame::{ FRAME_TYPE_DATA_BLOCKED, FRAME_TYPE_MAX_DATA, FRAME_TYPE_MAX_STREAMS_BIDI, @@ -19,13 +27,6 @@ use crate::{ stream_id::{StreamId, StreamType}, Error, Res, }; -use neqo_common::{qtrace, Role}; - -use std::{ - convert::TryFrom, - fmt::Debug, - ops::{Deref, DerefMut, Index, IndexMut}, -}; #[derive(Debug)] pub struct SenderFlowControl @@ -575,6 +576,8 @@ impl IndexMut for LocalStreamLimits { #[cfg(test)] mod test { + use neqo_common::{Encoder, Role}; + use super::{LocalStreamLimits, ReceiverFlowControl, RemoteStreamLimits, SenderFlowControl}; use crate::{ packet::PacketBuilder, @@ -582,7 +585,6 @@ mod test { stream_id::{StreamId, StreamType}, Error, }; - use neqo_common::{Encoder, Role}; #[test] fn blocked_at_zero() { diff --git a/third_party/rust/neqo-transport/src/frame.rs b/third_party/rust/neqo-transport/src/frame.rs index 8d56fd30001f..f3d567ac7c9c 100644 --- a/third_party/rust/neqo-transport/src/frame.rs +++ b/third_party/rust/neqo-transport/src/frame.rs @@ -6,15 +6,16 @@ // Directly relating to QUIC frames. +use std::{convert::TryFrom, ops::RangeInclusive}; + use neqo_common::{qtrace, Decoder}; -use crate::cid::MAX_CONNECTION_ID_LEN; -use crate::packet::PacketType; -use crate::stream_id::{StreamId, StreamType}; -use crate::{AppError, ConnectionError, Error, Res, TransportError}; - -use std::convert::TryFrom; -use std::ops::RangeInclusive; +use crate::{ + cid::MAX_CONNECTION_ID_LEN, + packet::PacketType, + stream_id::{StreamId, StreamType}, + AppError, ConnectionError, Error, Res, TransportError, +}; #[allow(clippy::module_name_repetitions)] pub type FrameType = u64; @@ -368,7 +369,7 @@ impl<'a> Frame<'a> { )), Self::Padding => None, Self::Datagram { data, .. } => Some(format!("Datagram {{ len: {} }}", data.len())), - _ => Some(format!("{:?}", self)), + _ => Some(format!("{self:?}")), } } @@ -387,6 +388,17 @@ impl<'a> Frame<'a> { } pub fn decode(dec: &mut Decoder<'a>) -> Res { + /// Maximum ACK Range Count in ACK Frame + /// + /// Given a max UDP datagram size of 64k bytes and a minimum ACK Range size of 2 + /// bytes (2 QUIC varints), a single datagram can at most contain 32k ACK + /// Ranges. + /// + /// Note that the maximum (jumbogram) Ethernet MTU of 9216 or on the + /// Internet the regular Ethernet MTU of 1518 are more realistically to + /// be the limiting factor. Though for simplicity the higher limit is chosen. + const MAX_ACK_RANGE_COUNT: u64 = 32 * 1024; + fn d(v: Option) -> Res { v.ok_or(Error::NoMoreData) } @@ -410,7 +422,13 @@ impl<'a> Frame<'a> { FRAME_TYPE_ACK | FRAME_TYPE_ACK_ECN => { let la = dv(dec)?; let ad = dv(dec)?; - let nr = dv(dec)?; + let nr = dv(dec).and_then(|nr| { + if nr < MAX_ACK_RANGE_COUNT { + Ok(nr) + } else { + Err(Error::TooMuchData) + } + })?; let fa = dv(dec)?; let mut arr: Vec = Vec::with_capacity(nr as usize); for _ in 0..nr { @@ -595,9 +613,10 @@ impl<'a> Frame<'a> { #[cfg(test)] mod tests { - use super::*; use neqo_common::{Decoder, Encoder}; + use super::*; + fn just_dec(f: &Frame, s: &str) { let encoded = Encoder::from_hex(s); let decoded = Frame::decode(&mut encoded.as_decoder()).unwrap(); @@ -658,7 +677,7 @@ mod tests { application_error_code: 0x77, }; - just_dec(&f, "053F4077") + just_dec(&f, "053F4077"); } #[test] @@ -943,4 +962,16 @@ mod tests { }; just_dec(&f, "403103010203"); } + + #[test] + fn frame_decode_enforces_bound_on_ack_range() { + let mut e = Encoder::new(); + + e.encode_varint(FRAME_TYPE_ACK); + e.encode_varint(0u64); // largest acknowledged + e.encode_varint(0u64); // ACK delay + e.encode_varint(u32::MAX); // ACK range count = huge, but maybe available for allocation + + assert_eq!(Err(Error::TooMuchData), Frame::decode(&mut e.as_decoder())); + } } diff --git a/third_party/rust/neqo-transport/src/lib.rs b/third_party/rust/neqo-transport/src/lib.rs index daff7e73c240..ecf7ee2f7321 100644 --- a/third_party/rust/neqo-transport/src/lib.rs +++ b/third_party/rust/neqo-transport/src/lib.rs @@ -16,7 +16,6 @@ mod cc; mod cid; mod connection; mod crypto; -mod dump; mod events; mod fc; mod frame; @@ -26,6 +25,9 @@ mod path; mod qlog; mod quic_datagrams; mod recovery; +#[cfg(feature = "bench")] +pub mod recv_stream; +#[cfg(not(feature = "bench"))] mod recv_stream; mod rtt; mod send_stream; @@ -38,26 +40,29 @@ pub mod tparams; mod tracking; pub mod version; -pub use self::cc::CongestionControlAlgorithm; -pub use self::cid::{ - ConnectionId, ConnectionIdDecoder, ConnectionIdGenerator, ConnectionIdRef, - EmptyConnectionIdGenerator, RandomConnectionIdGenerator, +pub use self::{ + cc::CongestionControlAlgorithm, + cid::{ + ConnectionId, ConnectionIdDecoder, ConnectionIdGenerator, ConnectionIdRef, + EmptyConnectionIdGenerator, RandomConnectionIdGenerator, + }, + connection::{ + params::{ConnectionParameters, ACK_RATIO_SCALE}, + Connection, Output, State, ZeroRttState, + }, + events::{ConnectionEvent, ConnectionEvents}, + frame::CloseError, + quic_datagrams::DatagramTracking, + recv_stream::{RecvStreamStats, RECV_BUFFER_SIZE}, + send_stream::{SendStreamStats, SEND_BUFFER_SIZE}, + stats::Stats, + stream_id::{StreamId, StreamType}, + version::Version, }; -pub use self::connection::{ - params::ConnectionParameters, params::ACK_RATIO_SCALE, Connection, Output, State, ZeroRttState, -}; -pub use self::events::{ConnectionEvent, ConnectionEvents}; -pub use self::frame::CloseError; -pub use self::quic_datagrams::DatagramTracking; -pub use self::stats::Stats; -pub use self::stream_id::{StreamId, StreamType}; -pub use self::version::Version; - -pub use self::recv_stream::{RecvStreamStats, RECV_BUFFER_SIZE}; -pub use self::send_stream::{SendStreamStats, SEND_BUFFER_SIZE}; pub type TransportError = u64; const ERROR_APPLICATION_CLOSE: TransportError = 12; +const ERROR_CRYPTO_BUFFER_EXCEEDED: TransportError = 13; const ERROR_AEAD_LIMIT_REACHED: TransportError = 15; #[derive(Clone, Debug, PartialEq, PartialOrd, Ord, Eq)] @@ -65,7 +70,7 @@ pub enum Error { NoError, // Each time tihe error is return a different parameter is supply. // This will be use to distinguish each occurance of this error. - InternalError(u16), + InternalError, ConnectionRefused, FlowControlError, StreamLimitError, @@ -76,6 +81,7 @@ pub enum Error { ProtocolViolation, InvalidToken, ApplicationError, + CryptoBufferExceeded, CryptoError(CryptoError), QlogError, CryptoAlert(u8), @@ -142,6 +148,7 @@ impl Error { Self::KeysExhausted => ERROR_AEAD_LIMIT_REACHED, Self::ApplicationError => ERROR_APPLICATION_CLOSE, Self::NoAvailablePath => 16, + Self::CryptoBufferExceeded => ERROR_CRYPTO_BUFFER_EXCEEDED, Self::CryptoAlert(a) => 0x100 + u64::from(*a), // As we have a special error code for ECH fallbacks, we lose the alert. // Send the server "ech_required" directly. @@ -176,7 +183,7 @@ impl From for Error { } impl ::std::error::Error for Error { - fn source(&self) -> Option<&(dyn ::std::error::Error + 'static)> { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { Self::CryptoError(e) => Some(e), _ => None, @@ -186,7 +193,7 @@ impl ::std::error::Error for Error { impl ::std::fmt::Display for Error { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - write!(f, "Transport error: {:?}", self) + write!(f, "Transport error: {self:?}") } } @@ -202,7 +209,7 @@ impl ConnectionError { pub fn app_code(&self) -> Option { match self { Self::Application(e) => Some(*e), - _ => None, + Self::Transport(_) => None, } } } diff --git a/third_party/rust/neqo-transport/src/pace.rs b/third_party/rust/neqo-transport/src/pace.rs index 84a60bcd3ee7..e5214c1bc8f4 100644 --- a/third_party/rust/neqo-transport/src/pace.rs +++ b/third_party/rust/neqo-transport/src/pace.rs @@ -7,12 +7,14 @@ // Pacer #![deny(clippy::pedantic)] -use neqo_common::qtrace; +use std::{ + cmp::min, + convert::TryFrom, + fmt::{Debug, Display}, + time::{Duration, Instant}, +}; -use std::cmp::min; -use std::convert::TryFrom; -use std::fmt::{Debug, Display}; -use std::time::{Duration, Instant}; +use neqo_common::qtrace; /// This value determines how much faster the pacer operates than the /// congestion window. @@ -26,6 +28,8 @@ const PACER_SPEEDUP: usize = 2; /// A pacer that uses a leaky bucket. pub struct Pacer { + /// Whether pacing is enabled. + enabled: bool, /// The last update time. t: Instant, /// The maximum capacity, or burst size, in bytes. @@ -47,9 +51,15 @@ impl Pacer { /// The value of `p` is the packet size in bytes, which determines the minimum /// credit needed before a packet is sent. This should be a substantial /// fraction of the maximum packet size, if not the packet size. - pub fn new(now: Instant, m: usize, p: usize) -> Self { + pub fn new(enabled: bool, now: Instant, m: usize, p: usize) -> Self { assert!(m >= p, "maximum capacity has to be at least one packet"); - Self { t: now, m, c: m, p } + Self { + enabled, + t: now, + m, + c: m, + p, + } } /// Determine when the next packet will be available based on the provided RTT @@ -74,10 +84,15 @@ impl Pacer { } /// Spend credit. This cannot fail; users of this API are expected to call - /// next() to determine when to spend. This takes the current time (`now`), + /// `next()` to determine when to spend. This takes the current time (`now`), /// an estimate of the round trip time (`rtt`), the estimated congestion /// window (`cwnd`), and the number of bytes that were sent (`count`). pub fn spend(&mut self, now: Instant, rtt: Duration, cwnd: usize, count: usize) { + if !self.enabled { + self.t = now; + return; + } + qtrace!([self], "spend {} over {}, {:?}", count, cwnd, rtt); // Increase the capacity by: // `(now - self.t) * PACER_SPEEDUP * cwnd / rtt` @@ -108,31 +123,43 @@ impl Debug for Pacer { } } -#[cfg(tests)] +#[cfg(test)] mod tests { - use super::Pacer; + use std::time::Duration; + use test_fixture::now; + use super::Pacer; + const RTT: Duration = Duration::from_millis(1000); const PACKET: usize = 1000; const CWND: usize = PACKET * 10; #[test] fn even() { - let mut n = now(); - let p = Pacer::new(n, PACKET, PACKET); - assert_eq!(p.next(RTT, CWND), None); + let n = now(); + let mut p = Pacer::new(true, n, PACKET, PACKET); + assert_eq!(p.next(RTT, CWND), n); p.spend(n, RTT, CWND, PACKET); - assert_eq!(p.next(RTT, CWND), Some(n + (RTT / 10))); + assert_eq!(p.next(RTT, CWND), n + (RTT / 20)); } #[test] fn backwards_in_time() { - let mut n = now(); - let p = Pacer::new(n + RTT, PACKET, PACKET); - assert_eq!(p.next(RTT, CWND), None); + let n = now(); + let mut p = Pacer::new(true, n + RTT, PACKET, PACKET); + assert_eq!(p.next(RTT, CWND), n + RTT); // Now spend some credit in the past using a time machine. p.spend(n, RTT, CWND, PACKET); - assert_eq!(p.next(RTT, CWND), Some(n + (RTT / 10))); + assert_eq!(p.next(RTT, CWND), n + (RTT / 20)); + } + + #[test] + fn pacing_disabled() { + let n = now(); + let mut p = Pacer::new(false, n, PACKET, PACKET); + assert_eq!(p.next(RTT, CWND), n); + p.spend(n, RTT, CWND, PACKET); + assert_eq!(p.next(RTT, CWND), n); } } diff --git a/third_party/rust/neqo-transport/src/packet/mod.rs b/third_party/rust/neqo-transport/src/packet/mod.rs index 631bf8479583..ccfd212d5f82 100644 --- a/third_party/rust/neqo-transport/src/packet/mod.rs +++ b/third_party/rust/neqo-transport/src/packet/mod.rs @@ -5,20 +5,24 @@ // except according to those terms. // Encoding and decoding packets off the wire. -use crate::cid::{ConnectionId, ConnectionIdDecoder, ConnectionIdRef, MAX_CONNECTION_ID_LEN}; -use crate::crypto::{CryptoDxState, CryptoSpace, CryptoStates}; -use crate::version::{Version, WireVersion}; -use crate::{Error, Res}; +use std::{ + cmp::min, + convert::TryFrom, + fmt, + iter::ExactSizeIterator, + ops::{Deref, DerefMut, Range}, + time::Instant, +}; use neqo_common::{hex, hex_with_len, qtrace, qwarn, Decoder, Encoder}; use neqo_crypto::random; -use std::cmp::min; -use std::convert::TryFrom; -use std::fmt; -use std::iter::ExactSizeIterator; -use std::ops::{Deref, DerefMut, Range}; -use std::time::Instant; +use crate::{ + cid::{ConnectionId, ConnectionIdDecoder, ConnectionIdRef, MAX_CONNECTION_ID_LEN}, + crypto::{CryptoDxState, CryptoSpace, CryptoStates}, + version::{Version, WireVersion}, + Error, Res, +}; pub const PACKET_BIT_LONG: u8 = 0x80; const PACKET_BIT_SHORT: u8 = 0x00; @@ -241,7 +245,7 @@ impl PacketBuilder { /// Adjust the limit to ensure that no more data is added. pub fn mark_full(&mut self) { - self.limit = self.encoder.len() + self.limit = self.encoder.len(); } /// Mark the packet as needing padding (or not). @@ -352,7 +356,7 @@ impl PacketBuilder { if self.len() > self.limit { qwarn!("Packet contents are more than the limit"); debug_assert!(false); - return Err(Error::InternalError(5)); + return Err(Error::InternalError); } self.pad_for_crypto(crypto); @@ -405,7 +409,7 @@ impl PacketBuilder { /// Make a retry packet. /// As this is a simple packet, this is just an associated function. /// As Retry is odd (it has to be constructed with leading bytes), - /// this returns a Vec rather than building on an encoder. + /// this returns a [`Vec`] rather than building on an encoder. pub fn retry( version: Version, dcid: &[u8], @@ -497,8 +501,8 @@ pub struct PublicPacket<'a> { dcid: ConnectionIdRef<'a>, /// The source connection ID, if this is a long header packet. scid: Option>, - /// Any token that is included in the packet (Retry always has a token; Initial sometimes does). - /// This is empty when there is no token. + /// Any token that is included in the packet (Retry always has a token; Initial sometimes + /// does). This is empty when there is no token. token: &'a [u8], /// The size of the header, not including the packet number. header_len: usize, @@ -599,9 +603,7 @@ impl<'a> PublicPacket<'a> { } // Check that this is a long header from a supported version. - let version = if let Ok(v) = Version::try_from(version) { - v - } else { + let Ok(version) = Version::try_from(version) else { return Ok(( Self { packet_type: PacketType::OtherVersion, @@ -671,13 +673,12 @@ impl<'a> PublicPacket<'a> { self.packet_type } - pub fn dcid(&self) -> &ConnectionIdRef<'a> { - &self.dcid + pub fn dcid(&self) -> ConnectionIdRef<'a> { + self.dcid } - pub fn scid(&self) -> &ConnectionIdRef<'a> { + pub fn scid(&self) -> ConnectionIdRef<'a> { self.scid - .as_ref() .expect("should only be called for long header packets") } @@ -864,12 +865,15 @@ impl Deref for DecryptedPacket { #[cfg(all(test, not(feature = "fuzzing")))] mod tests { - use super::*; - use crate::crypto::{CryptoDxState, CryptoStates}; - use crate::{EmptyConnectionIdGenerator, RandomConnectionIdGenerator, Version}; use neqo_common::Encoder; use test_fixture::{fixture_init, now}; + use super::*; + use crate::{ + crypto::{CryptoDxState, CryptoStates}, + EmptyConnectionIdGenerator, RandomConnectionIdGenerator, Version, + }; + const CLIENT_CID: &[u8] = &[0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08]; const SERVER_CID: &[u8] = &[0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, 0xb5]; @@ -1020,7 +1024,8 @@ mod tests { assert_eq!(&decrypted[..], SAMPLE_SHORT_PAYLOAD); } - /// By telling the decoder that the connection ID is shorter than it really is, we get a decryption error. + /// By telling the decoder that the connection ID is shorter than it really is, we get a + /// decryption error. #[test] fn decode_short_bad_cid() { fixture_init(); @@ -1170,9 +1175,9 @@ mod tests { } const SAMPLE_RETRY_V2: &[u8] = &[ - 0xcf, 0x70, 0x9a, 0x50, 0xc4, 0x00, 0x08, 0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, 0xb5, - 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x1d, 0xc7, 0x11, 0x30, 0xcd, 0x1e, 0xd3, 0x9d, 0x6e, 0xfc, - 0xee, 0x5c, 0x85, 0x80, 0x65, 0x01, + 0xcf, 0x6b, 0x33, 0x43, 0xcf, 0x00, 0x08, 0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, 0xb5, + 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0xc8, 0x64, 0x6c, 0xe8, 0xbf, 0xe3, 0x39, 0x52, 0xd9, 0x55, + 0x54, 0x36, 0x65, 0xdc, 0xc7, 0xb6, ]; const SAMPLE_RETRY_V1: &[u8] = &[ @@ -1353,7 +1358,7 @@ mod tests { const SAMPLE_VN: &[u8] = &[ 0x80, 0x00, 0x00, 0x00, 0x00, 0x08, 0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, 0xb5, 0x08, - 0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08, 0x70, 0x9a, 0x50, 0xc4, 0x00, 0x00, 0x00, + 0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08, 0x6b, 0x33, 0x43, 0xcf, 0x00, 0x00, 0x00, 0x01, 0xff, 0x00, 0x00, 0x20, 0xff, 0x00, 0x00, 0x1f, 0xff, 0x00, 0x00, 0x1e, 0xff, 0x00, 0x00, 0x1d, 0x0a, 0x0a, 0x0a, 0x0a, ]; diff --git a/third_party/rust/neqo-transport/src/packet/retry.rs b/third_party/rust/neqo-transport/src/packet/retry.rs index e9a7e90ab99b..004e9de6e71e 100644 --- a/third_party/rust/neqo-transport/src/packet/retry.rs +++ b/third_party/rust/neqo-transport/src/packet/retry.rs @@ -6,13 +6,12 @@ #![deny(clippy::pedantic)] -use crate::version::Version; -use crate::{Error, Res}; +use std::cell::RefCell; use neqo_common::qerror; use neqo_crypto::{hkdf, Aead, TLS_AES_128_GCM_SHA256, TLS_VERSION_1_3}; -use std::cell::RefCell; +use crate::{version::Version, Error, Res}; /// The AEAD used for Retry is fixed, so use thread local storage. fn make_aead(version: Version) -> Aead { @@ -46,7 +45,7 @@ where .try_with(|aead| f(&aead.borrow())) .map_err(|e| { qerror!("Unable to access Retry AEAD: {:?}", e); - Error::InternalError(6) + Error::InternalError })? } diff --git a/third_party/rust/neqo-transport/src/path.rs b/third_party/rust/neqo-transport/src/path.rs index 3a25a1bea9ae..d6920c8d94ae 100644 --- a/third_party/rust/neqo-transport/src/path.rs +++ b/third_party/rust/neqo-transport/src/path.rs @@ -7,31 +7,33 @@ #![deny(clippy::pedantic)] #![allow(clippy::module_name_repetitions)] -use std::cell::RefCell; -use std::convert::TryFrom; -use std::fmt::{self, Display}; -use std::mem; -use std::net::{IpAddr, SocketAddr}; -use std::rc::Rc; -use std::time::{Duration, Instant}; - -use crate::ackrate::{AckRate, PeerAckDelay}; -use crate::cc::CongestionControlAlgorithm; -use crate::cid::{ConnectionId, ConnectionIdRef, ConnectionIdStore, RemoteConnectionIdEntry}; -use crate::frame::{ - FRAME_TYPE_PATH_CHALLENGE, FRAME_TYPE_PATH_RESPONSE, FRAME_TYPE_RETIRE_CONNECTION_ID, +use std::{ + cell::RefCell, + convert::TryFrom, + fmt::{self, Display}, + mem, + net::{IpAddr, SocketAddr}, + rc::Rc, + time::{Duration, Instant}, }; -use crate::packet::PacketBuilder; -use crate::recovery::RecoveryToken; -use crate::rtt::RttEstimate; -use crate::sender::PacketSender; -use crate::stats::FrameStats; -use crate::tracking::{PacketNumberSpace, SentPacket}; -use crate::{Error, Res}; -use neqo_common::{hex, qdebug, qinfo, qlog::NeqoQlog, qtrace, Datagram, Encoder}; +use neqo_common::{hex, qdebug, qinfo, qlog::NeqoQlog, qtrace, Datagram, Encoder, IpTos}; use neqo_crypto::random; +use crate::{ + ackrate::{AckRate, PeerAckDelay}, + cc::CongestionControlAlgorithm, + cid::{ConnectionId, ConnectionIdRef, ConnectionIdStore, RemoteConnectionIdEntry}, + frame::{FRAME_TYPE_PATH_CHALLENGE, FRAME_TYPE_PATH_RESPONSE, FRAME_TYPE_RETIRE_CONNECTION_ID}, + packet::PacketBuilder, + recovery::RecoveryToken, + rtt::RttEstimate, + sender::PacketSender, + stats::FrameStats, + tracking::{PacketNumberSpace, SentPacket}, + Stats, +}; + /// This is the MTU that we assume when using IPv6. /// We use this size for Initial packets, so we don't need to worry about probing for support. /// If the path doesn't support this MTU, we will assume that it doesn't support QUIC. @@ -56,6 +58,8 @@ pub type PathRef = Rc>; #[derive(Debug, Default)] pub struct Paths { /// All of the paths. All of these paths will be permanent. + #[allow(unknown_lints)] // available with Rust v1.75 + #[allow(clippy::struct_field_names)] paths: Vec, /// This is the primary path. This will only be `None` initially, so /// care needs to be taken regarding that only during the handshake. @@ -80,6 +84,7 @@ impl Paths { local: SocketAddr, remote: SocketAddr, cc: CongestionControlAlgorithm, + pacing: bool, now: Instant, ) -> PathRef { self.paths @@ -92,7 +97,7 @@ impl Paths { } }) .unwrap_or_else(|| { - let mut p = Path::temporary(local, remote, cc, self.qlog.clone(), now); + let mut p = Path::temporary(local, remote, cc, pacing, self.qlog.clone(), now); if let Some(primary) = self.primary.as_ref() { p.prime_rtt(primary.borrow().rtt()); } @@ -109,6 +114,7 @@ impl Paths { local: SocketAddr, remote: SocketAddr, cc: CongestionControlAlgorithm, + pacing: bool, now: Instant, ) -> PathRef { self.paths @@ -134,6 +140,7 @@ impl Paths { local, remote, cc, + pacing, self.qlog.clone(), now, ))) @@ -408,7 +415,7 @@ impl Paths { builder: &mut PacketBuilder, tokens: &mut Vec, stats: &mut FrameStats, - ) -> Res<()> { + ) { while let Some(seqno) = self.to_retire.pop() { if builder.remaining() < 1 + Encoder::varint_len(seqno) { self.to_retire.push(seqno); @@ -416,9 +423,6 @@ impl Paths { } builder.encode_varint(FRAME_TYPE_RETIRE_CONNECTION_ID); builder.encode_varint(seqno); - if builder.len() > builder.limit() { - return Err(Error::InternalError(20)); - } tokens.push(RecoveryToken::RetireConnectionId(seqno)); stats.retire_connection_id += 1; } @@ -427,8 +431,6 @@ impl Paths { self.primary() .borrow_mut() .write_cc_frames(builder, tokens, stats); - - Ok(()) } pub fn lost_retire_cid(&mut self, lost: u64) { @@ -491,7 +493,7 @@ enum ProbeState { } impl ProbeState { - /// Determine whether the current state requires probing. + /// Determine whether the current state requires probing. fn probe_needed(&self) -> bool { matches!(self, Self::ProbeNeeded { .. }) } @@ -532,6 +534,10 @@ pub struct Path { rtt: RttEstimate, /// A packet sender for the path, which includes congestion control and a pacer. sender: PacketSender, + /// The DSCP/ECN marking to use for outgoing packets on this path. + tos: IpTos, + /// The IP TTL to use for outgoing packets on this path. + ttl: u8, /// The number of bytes received on this path. /// Note that this value might saturate on a long-lived connection, @@ -551,10 +557,11 @@ impl Path { local: SocketAddr, remote: SocketAddr, cc: CongestionControlAlgorithm, + pacing: bool, qlog: NeqoQlog, now: Instant, ) -> Self { - let mut sender = PacketSender::new(cc, Self::mtu_by_addr(remote.ip()), now); + let mut sender = PacketSender::new(cc, pacing, Self::mtu_by_addr(remote.ip()), now); sender.set_qlog(qlog.clone()); Self { local, @@ -567,6 +574,8 @@ impl Path { challenge: None, rtt: RttEstimate::default(), sender, + tos: IpTos::default(), // TODO: Default to Ect0 when ECN is supported. + ttl: 64, // This is the default TTL on many OSes. received_bytes: 0, sent_bytes: 0, qlog, @@ -658,7 +667,7 @@ impl Path { /// Set the remote connection ID based on the peer's choice. /// This is only valid during the handshake. - pub fn set_remote_cid(&mut self, cid: &ConnectionIdRef) { + pub fn set_remote_cid(&mut self, cid: ConnectionIdRef) { self.remote_cid .as_mut() .unwrap() @@ -689,7 +698,7 @@ impl Path { /// Make a datagram. pub fn datagram>>(&self, payload: V) -> Datagram { - Datagram::new(self.local, self.remote, payload) + Datagram::new(self.local, self.remote, self.tos, Some(self.ttl), payload) } /// Get local address as `SocketAddr` @@ -760,9 +769,9 @@ impl Path { stats: &mut FrameStats, mtu: bool, // Whether the packet we're writing into will be a full MTU. now: Instant, - ) -> Res { + ) -> bool { if builder.remaining() < 9 { - return Ok(false); + return false; } // Send PATH_RESPONSE. @@ -770,9 +779,6 @@ impl Path { qtrace!([self], "Responding to path challenge {}", hex(challenge)); builder.encode_varint(FRAME_TYPE_PATH_RESPONSE); builder.encode(&challenge[..]); - if builder.len() > builder.limit() { - return Err(Error::InternalError(21)); - } // These frames are not retransmitted in the usual fashion. // There is no token, therefore we need to count `all` specially. @@ -780,7 +786,7 @@ impl Path { stats.all += 1; if builder.remaining() < 9 { - return Ok(true); + return true; } true } else { @@ -793,9 +799,6 @@ impl Path { let data = <[u8; 8]>::try_from(&random(8)[..]).unwrap(); builder.encode_varint(FRAME_TYPE_PATH_CHALLENGE); builder.encode(&data); - if builder.len() > builder.limit() { - return Err(Error::InternalError(22)); - } // As above, no recovery token. stats.path_challenge += 1; @@ -807,9 +810,9 @@ impl Path { mtu, sent: now, }; - Ok(true) + true } else { - Ok(resp_sent) + resp_sent } } @@ -932,7 +935,7 @@ impl Path { } /// Discard a packet that previously might have been in-flight. - pub fn discard_packet(&mut self, sent: &SentPacket, now: Instant) { + pub fn discard_packet(&mut self, sent: &SentPacket, now: Instant, stats: &mut Stats) { if self.rtt.first_sample_time().is_none() { // When discarding a packet there might not be a good RTT estimate. // But discards only occur after receiving something, so that means @@ -944,6 +947,7 @@ impl Path { "discarding a packet without an RTT estimate; guessing RTT={:?}", now - sent.time_sent ); + stats.rtt_init_guess = true; self.rtt.update( &mut self.qlog, now - sent.time_sent, @@ -959,8 +963,7 @@ impl Path { /// Record packets as acknowledged with the sender. pub fn on_packets_acked(&mut self, acked_pkts: &[SentPacket], now: Instant) { debug_assert!(self.is_primary()); - self.sender - .on_packets_acked(acked_pkts, self.rtt.minimum(), now); + self.sender.on_packets_acked(acked_pkts, &self.rtt, now); } /// Record packets as lost with the sender. @@ -994,7 +997,8 @@ impl Path { .map_or(usize::MAX, |limit| { let budget = if limit == 0 { // If we have received absolutely nothing thus far, then this endpoint - // is the one initiating communication on this path. Allow enough space for probing. + // is the one initiating communication on this path. Allow enough space for + // probing. self.mtu() * 5 } else { limit diff --git a/third_party/rust/neqo-transport/src/qlog.rs b/third_party/rust/neqo-transport/src/qlog.rs index dce27732b094..434395fd23f8 100644 --- a/third_party/rust/neqo-transport/src/qlog.rs +++ b/third_party/rust/neqo-transport/src/qlog.rs @@ -13,16 +13,15 @@ use std::{ time::Duration, }; +use neqo_common::{hex, qinfo, qlog::NeqoQlog, Decoder}; use qlog::events::{ connectivity::{ConnectionStarted, ConnectionState, ConnectionStateUpdated}, quic::{ AckedRanges, ErrorSpace, MetricsUpdated, PacketDropped, PacketHeader, PacketLost, - PacketReceived, PacketSent, QuicFrame, StreamType, + PacketReceived, PacketSent, QuicFrame, StreamType, VersionInformation, }, - Event, EventData, RawInfo, + EventData, RawInfo, }; - -use neqo_common::{hex, qinfo, qlog::NeqoQlog, Decoder}; use smallvec::SmallVec; use crate::{ @@ -33,10 +32,11 @@ use crate::{ stream_id::StreamType as NeqoStreamType, tparams::{self, TransportParametersHandler}, tracking::SentPacket, + version::{Version, VersionConfig, WireVersion}, }; pub fn connection_tparams_set(qlog: &mut NeqoQlog, tph: &TransportParametersHandler) { - qlog.add_event(|| { + qlog.add_event_data(|| { let remote = tph.remote(); let ev_data = EventData::TransportParametersSet( qlog::events::quic::TransportParametersSet { @@ -60,29 +60,35 @@ pub fn connection_tparams_set(qlog: &mut NeqoQlog, tph: &TransportParametersHand max_udp_payload_size: Some(remote.get_integer(tparams::MAX_UDP_PAYLOAD_SIZE) as u32), ack_delay_exponent: Some(remote.get_integer(tparams::ACK_DELAY_EXPONENT) as u16), max_ack_delay: Some(remote.get_integer(tparams::MAX_ACK_DELAY) as u16), - // TODO(hawkinsw@obs.cr): We do not yet handle ACTIVE_CONNECTION_ID_LIMIT in tparams yet. - active_connection_id_limit: None, + active_connection_id_limit: Some(remote.get_integer(tparams::ACTIVE_CONNECTION_ID_LIMIT) as u32), initial_max_data: Some(remote.get_integer(tparams::INITIAL_MAX_DATA)), initial_max_stream_data_bidi_local: Some(remote.get_integer(tparams::INITIAL_MAX_STREAM_DATA_BIDI_LOCAL)), initial_max_stream_data_bidi_remote: Some(remote.get_integer(tparams::INITIAL_MAX_STREAM_DATA_BIDI_REMOTE)), initial_max_stream_data_uni: Some(remote.get_integer(tparams::INITIAL_MAX_STREAM_DATA_UNI)), initial_max_streams_bidi: Some(remote.get_integer(tparams::INITIAL_MAX_STREAMS_BIDI)), initial_max_streams_uni: Some(remote.get_integer(tparams::INITIAL_MAX_STREAMS_UNI)), - // TODO(hawkinsw@obs.cr): We do not yet handle PREFERRED_ADDRESS in tparams yet. - preferred_address: None, + preferred_address: remote.get_preferred_address().and_then(|(paddr, cid)| { + Some(qlog::events::quic::PreferredAddress { + ip_v4: paddr.ipv4()?.ip().to_string(), + ip_v6: paddr.ipv6()?.ip().to_string(), + port_v4: paddr.ipv4()?.port(), + port_v6: paddr.ipv6()?.port(), + connection_id: cid.connection_id().to_string(), + stateless_reset_token: hex(cid.reset_token()), + }) + }), }); - // This event occurs very early, so just mark the time as 0.0. - Some(Event::with_time(0.0, ev_data)) - }) + Some(ev_data) + }); } pub fn server_connection_started(qlog: &mut NeqoQlog, path: &PathRef) { - connection_started(qlog, path) + connection_started(qlog, path); } pub fn client_connection_started(qlog: &mut NeqoQlog, path: &PathRef) { - connection_started(qlog, path) + connection_started(qlog, path); } fn connection_started(qlog: &mut NeqoQlog, path: &PathRef) { @@ -104,7 +110,7 @@ fn connection_started(qlog: &mut NeqoQlog, path: &PathRef) { }); Some(ev_data) - }) + }); } pub fn connection_state_updated(qlog: &mut NeqoQlog, new: &State) { @@ -112,19 +118,73 @@ pub fn connection_state_updated(qlog: &mut NeqoQlog, new: &State) { let ev_data = EventData::ConnectionStateUpdated(ConnectionStateUpdated { old: None, new: match new { - State::Init => ConnectionState::Attempted, - State::WaitInitial => ConnectionState::Attempted, + State::Init | State::WaitInitial => ConnectionState::Attempted, State::WaitVersion | State::Handshaking => ConnectionState::HandshakeStarted, State::Connected => ConnectionState::HandshakeCompleted, State::Confirmed => ConnectionState::HandshakeConfirmed, - State::Closing { .. } => ConnectionState::Draining, + State::Closing { .. } => ConnectionState::Closing, State::Draining { .. } => ConnectionState::Draining, State::Closed { .. } => ConnectionState::Closed, }, }); Some(ev_data) - }) + }); +} + +pub fn client_version_information_initiated(qlog: &mut NeqoQlog, version_config: &VersionConfig) { + qlog.add_event_data(|| { + Some(EventData::VersionInformation(VersionInformation { + client_versions: Some( + version_config + .all() + .iter() + .map(|v| format!("{:02x}", v.wire_version())) + .collect(), + ), + server_versions: None, + chosen_version: Some(format!("{:02x}", version_config.initial().wire_version())), + })) + }); +} + +pub fn client_version_information_negotiated( + qlog: &mut NeqoQlog, + client: &[Version], + server: &[WireVersion], + chosen: Version, +) { + qlog.add_event_data(|| { + Some(EventData::VersionInformation(VersionInformation { + client_versions: Some( + client + .iter() + .map(|v| format!("{:02x}", v.wire_version())) + .collect(), + ), + server_versions: Some(server.iter().map(|v| format!("{v:02x}")).collect()), + chosen_version: Some(format!("{:02x}", chosen.wire_version())), + })) + }); +} + +pub fn server_version_information_failed( + qlog: &mut NeqoQlog, + server: &[Version], + client: WireVersion, +) { + qlog.add_event_data(|| { + Some(EventData::VersionInformation(VersionInformation { + client_versions: Some(vec![format!("{client:02x}")]), + server_versions: Some( + server + .iter() + .map(|v| format!("{:02x}", v.wire_version())) + .collect(), + ), + chosen_version: None, + })) + }); } pub fn packet_sent( @@ -136,23 +196,20 @@ pub fn packet_sent( ) { qlog.add_event_with_stream(|stream| { let mut d = Decoder::from(body); - let header = PacketHeader::with_type(to_qlog_pkt_type(pt), pn, None, None, None); + let header = PacketHeader::with_type(to_qlog_pkt_type(pt), Some(pn), None, None, None); let raw = RawInfo { - length: None, - payload_length: Some(plen as u64), + length: Some(plen as u64), + payload_length: None, data: None, }; let mut frames = SmallVec::new(); while d.remaining() > 0 { - match Frame::decode(&mut d) { - Ok(f) => { - frames.push(frame_to_qlogframe(&f)); - } - Err(_) => { - qinfo!("qlog: invalid frame"); - break; - } + if let Ok(f) = Frame::decode(&mut d) { + frames.push(frame_to_qlogframe(&f)) + } else { + qinfo!("qlog: invalid frame"); + break; } } @@ -170,17 +227,21 @@ pub fn packet_sent( }); stream.add_event_data_now(ev_data) - }) + }); } -pub fn packet_dropped(qlog: &mut NeqoQlog, payload: &PublicPacket) { +pub fn packet_dropped(qlog: &mut NeqoQlog, public_packet: &PublicPacket) { qlog.add_event_data(|| { - // TODO: packet number is optional in the spec but qlog crate doesn't support that, so use a placeholder value of 0 - let header = - PacketHeader::with_type(to_qlog_pkt_type(payload.packet_type()), 0, None, None, None); + let header = PacketHeader::with_type( + to_qlog_pkt_type(public_packet.packet_type()), + None, + None, + None, + None, + ); let raw = RawInfo { - length: None, - payload_length: Some(payload.len() as u64), + length: Some(public_packet.len() as u64), + payload_length: None, data: None, }; @@ -193,14 +254,14 @@ pub fn packet_dropped(qlog: &mut NeqoQlog, payload: &PublicPacket) { }); Some(ev_data) - }) + }); } pub fn packets_lost(qlog: &mut NeqoQlog, pkts: &[SentPacket]) { qlog.add_event_with_stream(|stream| { for pkt in pkts { let header = - PacketHeader::with_type(to_qlog_pkt_type(pkt.pt), pkt.pn, None, None, None); + PacketHeader::with_type(to_qlog_pkt_type(pkt.pt), Some(pkt.pn), None, None, None); let ev_data = EventData::PacketLost(PacketLost { header: Some(header), @@ -211,7 +272,7 @@ pub fn packets_lost(qlog: &mut NeqoQlog, pkts: &[SentPacket]) { stream.add_event_data_now(ev_data)?; } Ok(()) - }) + }); } pub fn packet_received( @@ -224,26 +285,25 @@ pub fn packet_received( let header = PacketHeader::with_type( to_qlog_pkt_type(public_packet.packet_type()), - payload.pn(), + Some(payload.pn()), None, None, None, ); let raw = RawInfo { - length: None, - payload_length: Some(public_packet.len() as u64), + length: Some(public_packet.len() as u64), + payload_length: None, data: None, }; let mut frames = Vec::new(); while d.remaining() > 0 { - match Frame::decode(&mut d) { - Ok(f) => frames.push(frame_to_qlogframe(&f)), - Err(_) => { - qinfo!("qlog: invalid frame"); - break; - } + if let Ok(f) = Frame::decode(&mut d) { + frames.push(frame_to_qlogframe(&f)) + } else { + qinfo!("qlog: invalid frame"); + break; } } @@ -260,7 +320,7 @@ pub fn packet_received( }); stream.add_event_data_now(ev_data) - }) + }); } #[allow(dead_code)] @@ -302,7 +362,7 @@ pub fn metrics_updated(qlog: &mut NeqoQlog, updated_metrics: &[QlogMetric]) { QlogMetric::RttVariance(v) => rtt_variance = Some(*v as f32), QlogMetric::PtoCount(v) => pto_count = Some(u16::try_from(*v).unwrap()), QlogMetric::CongestionWindow(v) => { - congestion_window = Some(u64::try_from(*v).unwrap()) + congestion_window = Some(u64::try_from(*v).unwrap()); } QlogMetric::BytesInFlight(v) => bytes_in_flight = Some(u64::try_from(*v).unwrap()), QlogMetric::SsThresh(v) => ssthresh = Some(u64::try_from(*v).unwrap()), @@ -326,7 +386,7 @@ pub fn metrics_updated(qlog: &mut NeqoQlog, updated_metrics: &[QlogMetric]) { }); Some(ev_data) - }) + }); } // Helper functions diff --git a/third_party/rust/neqo-transport/src/quic_datagrams.rs b/third_party/rust/neqo-transport/src/quic_datagrams.rs index e9c4497cde7c..07f3594768db 100644 --- a/third_party/rust/neqo-transport/src/quic_datagrams.rs +++ b/third_party/rust/neqo-transport/src/quic_datagrams.rs @@ -6,14 +6,17 @@ // https://datatracker.ietf.org/doc/html/draft-ietf-quic-datagram -use crate::frame::{FRAME_TYPE_DATAGRAM, FRAME_TYPE_DATAGRAM_WITH_LEN}; -use crate::packet::PacketBuilder; -use crate::recovery::RecoveryToken; -use crate::{events::OutgoingDatagramOutcome, ConnectionEvents, Error, Res, Stats}; +use std::{cmp::min, collections::VecDeque, convert::TryFrom}; + use neqo_common::Encoder; -use std::cmp::min; -use std::collections::VecDeque; -use std::convert::TryFrom; + +use crate::{ + events::OutgoingDatagramOutcome, + frame::{FRAME_TYPE_DATAGRAM, FRAME_TYPE_DATAGRAM_WITH_LEN}, + packet::PacketBuilder, + recovery::RecoveryToken, + ConnectionEvents, Error, Res, Stats, +}; pub const MAX_QUIC_DATAGRAM: u64 = 65535; @@ -140,7 +143,9 @@ impl QuicDatagrams { } /// Returns true if there was an unsent datagram that has been dismissed. + /// /// # Error + /// /// The function returns `TooMuchData` if the supply buffer is bigger than /// the allowed remote datagram size. The funcion does not check if the /// datagram can fit into a packet (i.e. MTU limit). This is checked during diff --git a/third_party/rust/neqo-transport/src/recovery.rs b/third_party/rust/neqo-transport/src/recovery.rs index 9256a0727cd2..d90989b486ad 100644 --- a/third_party/rust/neqo-transport/src/recovery.rs +++ b/third_party/rust/neqo-transport/src/recovery.rs @@ -8,37 +8,40 @@ #![deny(clippy::pedantic)] -use std::cmp::{max, min}; -use std::collections::BTreeMap; -use std::convert::TryFrom; -use std::mem; -use std::ops::RangeInclusive; -use std::time::{Duration, Instant}; - -use smallvec::{smallvec, SmallVec}; +use std::{ + cmp::{max, min}, + collections::BTreeMap, + convert::TryFrom, + mem, + ops::RangeInclusive, + time::{Duration, Instant}, +}; use neqo_common::{qdebug, qinfo, qlog::NeqoQlog, qtrace, qwarn}; +use smallvec::{smallvec, SmallVec}; -use crate::ackrate::AckRate; -use crate::cid::ConnectionIdEntry; -use crate::crypto::CryptoRecoveryToken; -use crate::packet::PacketNumber; -use crate::path::{Path, PathRef}; -use crate::qlog::{self, QlogMetric}; -use crate::quic_datagrams::DatagramTracking; -use crate::rtt::RttEstimate; -use crate::send_stream::SendStreamRecoveryToken; -use crate::stats::{Stats, StatsCell}; -use crate::stream_id::{StreamId, StreamType}; -use crate::tracking::{AckToken, PacketNumberSpace, PacketNumberSpaceSet, SentPacket}; +use crate::{ + ackrate::AckRate, + cid::ConnectionIdEntry, + crypto::CryptoRecoveryToken, + packet::PacketNumber, + path::{Path, PathRef}, + qlog::{self, QlogMetric}, + quic_datagrams::DatagramTracking, + rtt::RttEstimate, + send_stream::SendStreamRecoveryToken, + stats::{Stats, StatsCell}, + stream_id::{StreamId, StreamType}, + tracking::{AckToken, PacketNumberSpace, PacketNumberSpaceSet, SentPacket}, +}; pub(crate) const PACKET_THRESHOLD: u64 = 3; /// `ACK_ONLY_SIZE_LIMIT` is the minimum size of the congestion window. /// If the congestion window is this small, we will only send ACK frames. pub(crate) const ACK_ONLY_SIZE_LIMIT: usize = 256; -/// The number of packets we send on a PTO. -/// And the number to declare lost when the PTO timer is hit. -pub const PTO_PACKET_COUNT: usize = 2; +/// The maximum number of packets we send on a PTO. +/// And the maximum number to declare lost when the PTO timer is hit. +pub const MAX_PTO_PACKET_COUNT: usize = 2; /// The preferred limit on the number of packets that are tracked. /// If we exceed this number, we start sending `PING` frames sooner to /// force the peer to acknowledge some of them. @@ -408,7 +411,7 @@ impl LossRecoverySpace { .sent_packets .iter_mut() // BTreeMap iterates in order of ascending PN - .take_while(|(&k, _)| Some(k) < largest_acked) + .take_while(|(&k, _)| k < largest_acked.unwrap_or(PacketNumber::MAX)) { // Packets sent before now - loss_delay are deemed lost. if packet.time_sent + loss_delay <= now { @@ -426,7 +429,9 @@ impl LossRecoverySpace { largest_acked ); } else { - self.first_ooo_time = Some(packet.time_sent); + if largest_acked.is_some() { + self.first_ooo_time = Some(packet.time_sent); + } // No more packets can be declared lost after this one. break; }; @@ -458,7 +463,9 @@ impl LossRecoverySpaces { /// Drop a packet number space and return all the packets that were /// outstanding, so that those can be marked as lost. + /// /// # Panics + /// /// If the space has already been removed. pub fn drop_space(&mut self, space: PacketNumberSpace) -> impl IntoIterator { let sp = match space { @@ -516,21 +523,34 @@ struct PtoState { } impl PtoState { - pub fn new(space: PacketNumberSpace, probe: PacketNumberSpaceSet) -> Self { + /// The number of packets we send on a PTO. + /// And the number to declare lost when the PTO timer is hit. + fn pto_packet_count(space: PacketNumberSpace, rx_count: usize) -> usize { + if space == PacketNumberSpace::Initial && rx_count == 0 { + // For the Initial space, we only send one packet on PTO if we have not received any + // packets from the peer yet. This avoids sending useless PING-only packets + // when the Client Initial is deemed lost. + 1 + } else { + MAX_PTO_PACKET_COUNT + } + } + + pub fn new(space: PacketNumberSpace, probe: PacketNumberSpaceSet, rx_count: usize) -> Self { debug_assert!(probe[space]); Self { space, count: 1, - packets: PTO_PACKET_COUNT, + packets: Self::pto_packet_count(space, rx_count), probe, } } - pub fn pto(&mut self, space: PacketNumberSpace, probe: PacketNumberSpaceSet) { + pub fn pto(&mut self, space: PacketNumberSpace, probe: PacketNumberSpaceSet, rx_count: usize) { debug_assert!(probe[space]); self.space = space; self.count += 1; - self.packets = PTO_PACKET_COUNT; + self.packets = Self::pto_packet_count(space, rx_count); self.probe = probe; } @@ -605,7 +625,7 @@ impl LossRecovery { .collect::>(); let mut path = primary_path.borrow_mut(); for p in &mut dropped { - path.discard_packet(p, now); + path.discard_packet(p, now, &mut self.stats.borrow_mut()); } dropped } @@ -745,7 +765,7 @@ impl LossRecovery { .collect::>(); let mut path = primary_path.borrow_mut(); for p in &mut dropped { - path.discard_packet(p, now); + path.discard_packet(p, now, &mut self.stats.borrow_mut()); } dropped } @@ -778,7 +798,7 @@ impl LossRecovery { qdebug!([self], "Reset loss recovery state for {}", space); let mut path = primary_path.borrow_mut(); for p in self.spaces.drop_space(space) { - path.discard_packet(&p, now); + path.discard_packet(&p, now, &mut self.stats.borrow_mut()); } // We just made progress, so discard PTO count. @@ -806,7 +826,7 @@ impl LossRecovery { (Some(loss_time), Some(pto_time)) => Some(min(loss_time, pto_time)), (Some(loss_time), None) => Some(loss_time), (None, Some(pto_time)) => Some(pto_time), - _ => None, + (None, None) => None, } } @@ -831,11 +851,7 @@ impl LossRecovery { // where F = fast_pto / FAST_PTO_SCALE (== 1 by default) let pto_count = pto_state.map_or(0, |p| u32::try_from(p.count).unwrap_or(0)); rtt.pto(pn_space) - .checked_mul( - u32::from(fast_pto) - .checked_shl(pto_count) - .unwrap_or(u32::MAX), - ) + .checked_mul(u32::from(fast_pto) << min(pto_count, u32::BITS - u8::BITS)) .map_or(Duration::from_secs(3600), |p| p / u32::from(FAST_PTO_SCALE)) } @@ -873,10 +889,11 @@ impl LossRecovery { } fn fire_pto(&mut self, pn_space: PacketNumberSpace, allow_probes: PacketNumberSpaceSet) { + let rx_count = self.stats.borrow().packets_rx; if let Some(st) = &mut self.pto_state { - st.pto(pn_space, allow_probes); + st.pto(pn_space, allow_probes, rx_count); } else { - self.pto_state = Some(PtoState::new(pn_space, allow_probes)); + self.pto_state = Some(PtoState::new(pn_space, allow_probes, rx_count)); } self.pto_state @@ -906,7 +923,14 @@ impl LossRecovery { if t <= now { qdebug!([self], "PTO timer fired for {}", pn_space); let space = self.spaces.get_mut(*pn_space).unwrap(); - lost.extend(space.pto_packets(PTO_PACKET_COUNT).cloned()); + lost.extend( + space + .pto_packets(PtoState::pto_packet_count( + *pn_space, + self.stats.borrow().packets_rx, + )) + .cloned(), + ); pto_space = pto_space.or(Some(*pn_space)); } @@ -994,22 +1018,28 @@ impl ::std::fmt::Display for LossRecovery { #[cfg(test)] mod tests { + use std::{ + cell::RefCell, + convert::TryInto, + ops::{Deref, DerefMut, RangeInclusive}, + rc::Rc, + time::{Duration, Instant}, + }; + + use neqo_common::qlog::NeqoQlog; + use test_fixture::{addr, now}; + use super::{ LossRecovery, LossRecoverySpace, PacketNumberSpace, SendProfile, SentPacket, FAST_PTO_SCALE, }; - use crate::cc::CongestionControlAlgorithm; - use crate::cid::{ConnectionId, ConnectionIdEntry}; - use crate::packet::PacketType; - use crate::path::{Path, PathRef}; - use crate::rtt::RttEstimate; - use crate::stats::{Stats, StatsCell}; - use neqo_common::qlog::NeqoQlog; - use std::cell::RefCell; - use std::convert::TryInto; - use std::ops::{Deref, DerefMut, RangeInclusive}; - use std::rc::Rc; - use std::time::{Duration, Instant}; - use test_fixture::{addr, now}; + use crate::{ + cc::CongestionControlAlgorithm, + cid::{ConnectionId, ConnectionIdEntry}, + packet::PacketType, + path::{Path, PathRef}, + rtt::RttEstimate, + stats::{Stats, StatsCell}, + }; // Shorthand for a time in milliseconds. const fn ms(t: u64) -> Duration { @@ -1075,7 +1105,7 @@ mod tests { impl Default for Fixture { fn default() -> Self { const CC: CongestionControlAlgorithm = CongestionControlAlgorithm::NewReno; - let mut path = Path::temporary(addr(), addr(), CC, NeqoQlog::default(), now()); + let mut path = Path::temporary(addr(), addr(), CC, true, NeqoQlog::default(), now()); path.make_permanent( None, ConnectionIdEntry::new(0, ConnectionId::from(&[1, 2, 3]), [0; 16]), diff --git a/third_party/rust/neqo-transport/src/recv_stream.rs b/third_party/rust/neqo-transport/src/recv_stream.rs index ff7b497a5ab9..06ca59685d6b 100644 --- a/third_party/rust/neqo-transport/src/recv_stream.rs +++ b/third_party/rust/neqo-transport/src/recv_stream.rs @@ -7,25 +7,29 @@ // Building a stream of ordered bytes to give the application from a series of // incoming STREAM frames. -use std::cmp::max; -use std::collections::BTreeMap; -use std::convert::TryFrom; -use std::mem; -use std::rc::{Rc, Weak}; +use std::{ + cell::RefCell, + cmp::max, + collections::BTreeMap, + convert::TryFrom, + mem, + rc::{Rc, Weak}, +}; +use neqo_common::{qtrace, Role}; use smallvec::SmallVec; -use crate::events::ConnectionEvents; -use crate::fc::ReceiverFlowControl; -use crate::frame::FRAME_TYPE_STOP_SENDING; -use crate::packet::PacketBuilder; -use crate::recovery::{RecoveryToken, StreamRecoveryToken}; -use crate::send_stream::SendStreams; -use crate::stats::FrameStats; -use crate::stream_id::StreamId; -use crate::{AppError, Error, Res}; -use neqo_common::{qtrace, Role}; -use std::cell::RefCell; +use crate::{ + events::ConnectionEvents, + fc::ReceiverFlowControl, + frame::FRAME_TYPE_STOP_SENDING, + packet::PacketBuilder, + recovery::{RecoveryToken, StreamRecoveryToken}, + send_stream::SendStreams, + stats::FrameStats, + stream_id::StreamId, + AppError, Error, Res, +}; const RX_STREAM_DATA_WINDOW: u64 = 0x10_0000; // 1MiB @@ -196,26 +200,49 @@ impl RxStreamOrderer { false }; - // Now handle possible overlap with next entries - let mut to_remove = SmallVec::<[_; 8]>::new(); let mut to_add = new_data; + if self + .data_ranges + .last_entry() + .map_or(false, |e| *e.key() >= new_start) + { + // Is this at the end (common case)? If so, nothing to do in this block + // Common case: + // PPPPPP -> PPPPPP + // NNNNNNN NNNNNNN + // or + // PPPPPP -> PPPPPP + // NNNNNNN NNNNNNN + // + // Not the common case, handle possible overlap with next entries + // PPPPPP AAA -> PPPPPP + // NNNNNNN NNNNNNN + // or + // PPPPPP AAAA -> PPPPPP AAAA + // NNNNNNN NNNNN + // or (this is where to_remove is used) + // PPPPPP AA -> PPPPPP + // NNNNNNN NNNNNNN - for (&next_start, next_data) in self.data_ranges.range_mut(new_start..) { - let next_end = next_start + u64::try_from(next_data.len()).unwrap(); - let overlap = new_end.saturating_sub(next_start); - if overlap == 0 { - break; - } else if next_end >= new_end { - qtrace!( - "New frame {}-{} overlaps with next frame by {}, truncating", - new_start, - new_end, - overlap - ); - let truncate_to = new_data.len() - usize::try_from(overlap).unwrap(); - to_add = &new_data[..truncate_to]; - break; - } else { + let mut to_remove = SmallVec::<[_; 8]>::new(); + + for (&next_start, next_data) in self.data_ranges.range_mut(new_start..) { + let next_end = next_start + u64::try_from(next_data.len()).unwrap(); + let overlap = new_end.saturating_sub(next_start); + if overlap == 0 { + // Fills in the hole, exactly (probably common) + break; + } else if next_end >= new_end { + qtrace!( + "New frame {}-{} overlaps with next frame by {}, truncating", + new_start, + new_end, + overlap + ); + let truncate_to = new_data.len() - usize::try_from(overlap).unwrap(); + to_add = &new_data[..truncate_to]; + break; + } qtrace!( "New frame {}-{} spans entire next frame {}-{}, replacing", new_start, @@ -224,11 +251,12 @@ impl RxStreamOrderer { next_end ); to_remove.push(next_start); + // Continue, since we may have more overlaps } - } - for start in to_remove { - self.data_ranges.remove(&start); + for start in to_remove { + self.data_ranges.remove(&start); + } } if !to_add.is_empty() { @@ -278,11 +306,11 @@ impl RxStreamOrderer { } /// Bytes read by the application. - fn retired(&self) -> u64 { + pub fn retired(&self) -> u64 { self.retired } - fn received(&self) -> u64 { + pub fn received(&self) -> u64 { self.received } @@ -652,12 +680,12 @@ impl RecvStream { | RecvStreamState::AbortReading { .. } | RecvStreamState::WaitForReset { .. } | RecvStreamState::ResetRecvd { .. } => { - qtrace!("data received when we are in state {}", self.state.name()) + qtrace!("data received when we are in state {}", self.state.name()); } } if !already_data_ready && (self.data_ready() || self.needs_to_inform_app_about_fin()) { - self.conn_events.recv_stream_readable(self.stream_id) + self.conn_events.recv_stream_readable(self.stream_id); } Ok(()) @@ -764,6 +792,7 @@ impl RecvStream { } /// # Errors + /// /// `NoMoreData` if data and fin bit were previously read by the application. pub fn read(&mut self, buf: &mut [u8]) -> Res<(usize, bool)> { let data_recvd_state = matches!(self.state, RecvStreamState::DataRecvd { .. }); @@ -837,7 +866,7 @@ impl RecvStream { err, final_received: received, final_read: read, - }) + }); } RecvStreamState::DataRecvd { fc, @@ -961,10 +990,12 @@ impl RecvStream { #[cfg(test)] mod tests { - use super::*; - use neqo_common::Encoder; use std::ops::Range; + use neqo_common::Encoder; + + use super::*; + const SESSION_WINDOW: usize = 1024; fn recv_ranges(ranges: &[Range], available: usize) { diff --git a/third_party/rust/neqo-transport/src/rtt.rs b/third_party/rust/neqo-transport/src/rtt.rs index 3d6d0e70f821..4b05198bc9fd 100644 --- a/third_party/rust/neqo-transport/src/rtt.rs +++ b/third_party/rust/neqo-transport/src/rtt.rs @@ -8,17 +8,21 @@ #![deny(clippy::pedantic)] -use std::cmp::{max, min}; -use std::time::{Duration, Instant}; +use std::{ + cmp::{max, min}, + time::{Duration, Instant}, +}; use neqo_common::{qlog::NeqoQlog, qtrace}; -use crate::ackrate::{AckRate, PeerAckDelay}; -use crate::packet::PacketBuilder; -use crate::qlog::{self, QlogMetric}; -use crate::recovery::RecoveryToken; -use crate::stats::FrameStats; -use crate::tracking::PacketNumberSpace; +use crate::{ + ackrate::{AckRate, PeerAckDelay}, + packet::PacketBuilder, + qlog::{self, QlogMetric}, + recovery::RecoveryToken, + stats::FrameStats, + tracking::PacketNumberSpace, +}; /// The smallest time that the system timer (via `sleep()`, `nanosleep()`, /// `select()`, or similar) can reliably deliver; see `neqo_common::hrtime`. @@ -47,6 +51,18 @@ impl RttEstimate { self.rttvar = rtt / 2; } + #[cfg(test)] + pub const fn from_duration(rtt: Duration) -> Self { + Self { + first_sample_time: None, + latest_rtt: rtt, + smoothed_rtt: rtt, + rttvar: Duration::from_millis(0), + min_rtt: rtt, + ack_delay: PeerAckDelay::Fixed(Duration::from_millis(25)), + } + } + pub fn set_initial(&mut self, rtt: Duration) { qtrace!("initial RTT={:?}", rtt); if rtt >= GRANULARITY { diff --git a/third_party/rust/neqo-transport/src/send_stream.rs b/third_party/rust/neqo-transport/src/send_stream.rs index 21877ab23d3c..5feb785ac624 100644 --- a/third_party/rust/neqo-transport/src/send_stream.rs +++ b/third_party/rust/neqo-transport/src/send_stream.rs @@ -11,16 +11,15 @@ use std::{ cmp::{max, min, Ordering}, collections::{BTreeMap, VecDeque}, convert::TryFrom, + hash::{Hash, Hasher}, mem, ops::Add, rc::Rc, }; use indexmap::IndexMap; -use smallvec::SmallVec; -use std::hash::{Hash, Hasher}; - use neqo_common::{qdebug, qerror, qinfo, qtrace, Encoder, Role}; +use smallvec::SmallVec; use crate::{ events::ConnectionEvents, @@ -260,7 +259,7 @@ impl RangeTracker { // Create final chunk if anything remains of the new range if tmp_len > 0 { - v.push((tmp_off, tmp_len, new_state)) + v.push((tmp_off, tmp_len, new_state)); } v @@ -276,8 +275,6 @@ impl RangeTracker { .map(|(len, _)| *len); if let Some(len_from_zero) = acked_range_from_zero { - let mut to_remove = SmallVec::<[_; 8]>::new(); - let mut new_len_from_zero = len_from_zero; // See if there's another Acked range entry contiguous to this one @@ -286,17 +283,14 @@ impl RangeTracker { .get(&new_len_from_zero) .filter(|(_, state)| *state == RangeState::Acked) { - to_remove.push(new_len_from_zero); + let to_remove = new_len_from_zero; new_len_from_zero += *next_len; + self.used.remove(&to_remove); } if len_from_zero != new_len_from_zero { self.used.get_mut(&0).expect("must be there").0 = new_len_from_zero; } - - for val in to_remove { - self.used.remove(&val); - } } } @@ -312,7 +306,7 @@ impl RangeTracker { self.used.insert(sub_off, (sub_len, sub_state)); } - self.coalesce_acked_from_zero() + self.coalesce_acked_from_zero(); } fn unmark_range(&mut self, off: u64, len: usize) { @@ -443,7 +437,7 @@ impl TxBuffer { } pub fn mark_as_sent(&mut self, offset: u64, len: usize) { - self.ranges.mark_range(offset, len, RangeState::Sent) + self.ranges.mark_range(offset, len, RangeState::Sent); } pub fn mark_as_acked(&mut self, offset: u64, len: usize) { @@ -463,7 +457,7 @@ impl TxBuffer { } pub fn mark_as_lost(&mut self, offset: u64, len: usize) { - self.ranges.unmark_range(offset, len) + self.ranges.unmark_range(offset, len); } /// Forget about anything that was marked as sent. @@ -622,7 +616,7 @@ pub struct SendStream { impl Hash for SendStream { fn hash(&self, state: &mut H) { - self.stream_id.hash(state) + self.stream_id.hash(state); } } @@ -751,7 +745,7 @@ impl SendStream { final_written, .. } => *final_retired + *final_written, - _ => 0, + SendStreamState::Ready { .. } => 0, } } @@ -763,7 +757,7 @@ impl SendStream { SendStreamState::DataRecvd { retired, .. } => *retired, SendStreamState::ResetSent { final_retired, .. } | SendStreamState::ResetRecvd { final_retired, .. } => *final_retired, - _ => 0, + SendStreamState::Ready { .. } => 0, } } @@ -909,7 +903,7 @@ impl SendStream { | SendStreamState::Send { .. } | SendStreamState::DataSent { .. } | SendStreamState::DataRecvd { .. } => { - qtrace!([self], "Reset acked while in {} state?", self.state.name()) + qtrace!([self], "Reset acked while in {} state?", self.state.name()); } SendStreamState::ResetSent { final_retired, @@ -1023,7 +1017,7 @@ impl SendStream { } => { send_buf.mark_as_acked(offset, len); if self.avail() > 0 { - self.conn_events.send_stream_writable(self.stream_id) + self.conn_events.send_stream_writable(self.stream_id); } } SendStreamState::DataSent { @@ -1101,7 +1095,7 @@ impl SendStream { let stream_was_blocked = fc.available() == 0; fc.update(limit); if stream_was_blocked && self.avail() > 0 { - self.conn_events.send_stream_writable(self.stream_id) + self.conn_events.send_stream_writable(self.stream_id); } } } @@ -1285,7 +1279,8 @@ pub struct OrderGroupIter<'a> { // We store the next position in the OrderGroup. // Otherwise we'd need an explicit "done iterating" call to be made, or implement Drop to // copy the value back. - // This is where next was when we iterated for the first time; when we get back to that we stop. + // This is where next was when we iterated for the first time; when we get back to that we + // stop. started_at: Option, } @@ -1326,7 +1321,10 @@ impl OrderGroup { pub fn insert(&mut self, stream_id: StreamId) { match self.vec.binary_search(&stream_id) { - Ok(_) => panic!("Duplicate stream_id {}", stream_id), // element already in vector @ `pos` + Ok(_) => { + // element already in vector @ `pos` + panic!("Duplicate stream_id {}", stream_id) + } Err(pos) => self.vec.insert(pos, stream_id), } } @@ -1336,7 +1334,10 @@ impl OrderGroup { Ok(pos) => { self.vec.remove(pos); } - Err(_) => panic!("Missing stream_id {}", stream_id), // element already in vector @ `pos` + Err(_) => { + // element already in vector @ `pos` + panic!("Missing stream_id {}", stream_id) + } } } } @@ -1484,7 +1485,7 @@ impl SendStreams { pub fn reset_acked(&mut self, id: StreamId) { if let Some(ss) = self.map.get_mut(&id) { - ss.reset_acked() + ss.reset_acked(); } } @@ -1526,7 +1527,7 @@ impl SendStreams { match stream.sendorder() { None => regular.remove(*stream_id), Some(sendorder) => { - sendordered.get_mut(&sendorder).unwrap().remove(*stream_id) + sendordered.get_mut(&sendorder).unwrap().remove(*stream_id); } }; } @@ -1595,16 +1596,13 @@ impl SendStreams { .flat_map(|group| group.iter()), ); for stream_id in stream_ids { - match self.map.get_mut(&stream_id).unwrap().sendorder() { - Some(order) => qdebug!(" {} ({})", stream_id, order), - None => qdebug!(" None"), + let stream = self.map.get_mut(&stream_id).unwrap(); + if let Some(order) = stream.sendorder() { + qdebug!(" {} ({})", stream_id, order) + } else { + qdebug!(" None") } - if !self - .map - .get_mut(&stream_id) - .unwrap() - .write_frames_with_early_return(priority, builder, tokens, stats) - { + if !stream.write_frames_with_early_return(priority, builder, tokens, stats) { break; } } @@ -1642,11 +1640,11 @@ pub struct SendStreamRecoveryToken { #[cfg(test)] mod tests { - use super::*; - - use crate::events::ConnectionEvent; use neqo_common::{event::Provider, hex_with_len, qtrace}; + use super::*; + use crate::events::ConnectionEvent; + fn connection_fc(limit: u64) -> Rc>> { Rc::new(RefCell::new(SenderFlowControl::new((), limit))) } diff --git a/third_party/rust/neqo-transport/src/sender.rs b/third_party/rust/neqo-transport/src/sender.rs index 05cf9740bb51..9a00dfc7a768 100644 --- a/third_party/rust/neqo-transport/src/sender.rs +++ b/third_party/rust/neqo-transport/src/sender.rs @@ -8,15 +8,19 @@ #![deny(clippy::pedantic)] #![allow(clippy::module_name_repetitions)] -use crate::cc::{ - ClassicCongestionControl, CongestionControl, CongestionControlAlgorithm, Cubic, NewReno, +use std::{ + fmt::{self, Debug, Display}, + time::{Duration, Instant}, }; -use crate::pace::Pacer; -use crate::tracking::SentPacket; + use neqo_common::qlog::NeqoQlog; -use std::fmt::{self, Debug, Display}; -use std::time::{Duration, Instant}; +use crate::{ + cc::{ClassicCongestionControl, CongestionControl, CongestionControlAlgorithm, Cubic, NewReno}, + pace::Pacer, + rtt::RttEstimate, + tracking::SentPacket, +}; /// The number of packets we allow to burst from the pacer. pub const PACING_BURST_SIZE: usize = 2; @@ -35,7 +39,12 @@ impl Display for PacketSender { impl PacketSender { #[must_use] - pub fn new(alg: CongestionControlAlgorithm, mtu: usize, now: Instant) -> Self { + pub fn new( + alg: CongestionControlAlgorithm, + pacing_enabled: bool, + mtu: usize, + now: Instant, + ) -> Self { Self { cc: match alg { CongestionControlAlgorithm::NewReno => { @@ -45,7 +54,7 @@ impl PacketSender { Box::new(ClassicCongestionControl::new(Cubic::default())) } }, - pacer: Pacer::new(now, mtu * PACING_BURST_SIZE, mtu), + pacer: Pacer::new(pacing_enabled, now, mtu * PACING_BURST_SIZE, mtu), } } @@ -63,8 +72,13 @@ impl PacketSender { self.cc.cwnd_avail() } - pub fn on_packets_acked(&mut self, acked_pkts: &[SentPacket], min_rtt: Duration, now: Instant) { - self.cc.on_packets_acked(acked_pkts, min_rtt, now); + pub fn on_packets_acked( + &mut self, + acked_pkts: &[SentPacket], + rtt_est: &RttEstimate, + now: Instant, + ) { + self.cc.on_packets_acked(acked_pkts, rtt_est, now); } /// Called when packets are lost. Returns true if the congestion window was reduced. diff --git a/third_party/rust/neqo-transport/src/server.rs b/third_party/rust/neqo-transport/src/server.rs index 75cc6d42d83b..12a7d2f9e0d1 100644 --- a/third_party/rust/neqo-transport/src/server.rs +++ b/third_party/rust/neqo-transport/src/server.rs @@ -6,6 +6,18 @@ // This file implements a server that can handle multiple connections. +use std::{ + cell::RefCell, + collections::{HashMap, HashSet, VecDeque}, + fs::OpenOptions, + mem, + net::SocketAddr, + ops::{Deref, DerefMut}, + path::PathBuf, + rc::{Rc, Weak}, + time::{Duration, Instant}, +}; + use neqo_common::{ self as common, event::Provider, hex, qdebug, qerror, qinfo, qlog::NeqoQlog, qtrace, qwarn, timer::Timer, Datagram, Decoder, Role, @@ -17,21 +29,13 @@ use neqo_crypto::{ use qlog::streamer::QlogStreamer; pub use crate::addr_valid::ValidateAddress; -use crate::addr_valid::{AddressValidation, AddressValidationResult}; -use crate::cid::{ConnectionId, ConnectionIdDecoder, ConnectionIdGenerator, ConnectionIdRef}; -use crate::connection::{Connection, Output, State}; -use crate::packet::{PacketBuilder, PacketType, PublicPacket}; -use crate::{ConnectionParameters, Res, Version}; - -use std::cell::RefCell; -use std::collections::{HashMap, HashSet, VecDeque}; -use std::fs::OpenOptions; -use std::mem; -use std::net::SocketAddr; -use std::ops::{Deref, DerefMut}; -use std::path::PathBuf; -use std::rc::{Rc, Weak}; -use std::time::{Duration, Instant}; +use crate::{ + addr_valid::{AddressValidation, AddressValidationResult}, + cid::{ConnectionId, ConnectionIdDecoder, ConnectionIdGenerator, ConnectionIdRef}, + connection::{Connection, Output, State}, + packet::{PacketBuilder, PacketType, PublicPacket}, + ConnectionParameters, Res, Version, +}; pub enum InitialResult { Accept, @@ -186,11 +190,11 @@ impl Server { /// * `certs` is a list of the certificates that should be configured. /// * `protocols` is the preference list of ALPN values. /// * `anti_replay` is an anti-replay context. - /// * `zero_rtt_checker` determines whether 0-RTT should be accepted. This - /// will be passed the value of the `extra` argument that was passed to - /// `Connection::send_ticket` to see if it is OK. - /// * `cid_generator` is responsible for generating connection IDs and parsing them; - /// connection IDs produced by the manager cannot be zero-length. + /// * `zero_rtt_checker` determines whether 0-RTT should be accepted. This will be passed the + /// value of the `extra` argument that was passed to `Connection::send_ticket` to see if it is + /// OK. + /// * `cid_generator` is responsible for generating connection IDs and parsing them; connection + /// IDs produced by the manager cannot be zero-length. pub fn new( now: Instant, certs: &[impl AsRef], @@ -259,7 +263,7 @@ impl Server { fn process_connection( &mut self, c: StateRef, - dgram: Option, + dgram: Option<&Datagram>, now: Instant, ) -> Option { qtrace!([self], "Process connection {:?}", c); @@ -278,7 +282,7 @@ impl Server { self.timers.add(next, Rc::clone(&c)); } } - _ => { + Output::None => { self.remove_timer(&c); } } @@ -303,14 +307,14 @@ impl Server { out.dgram() } - fn connection(&self, cid: &ConnectionIdRef) -> Option { + fn connection(&self, cid: ConnectionIdRef) -> Option { self.connections.borrow().get(&cid[..]).map(Rc::clone) } fn handle_initial( &mut self, initial: InitialDetails, - dgram: Datagram, + dgram: &Datagram, now: Instant, ) -> Option { qdebug!([self], "Handle initial"); @@ -332,9 +336,7 @@ impl Server { dgram.source(), now, ); - let token = if let Ok(t) = res { - t - } else { + let Ok(token) = res else { qerror!([self], "unable to generate token, dropping packet"); return None; }; @@ -347,7 +349,13 @@ impl Server { &initial.dst_cid, ); if let Ok(p) = packet { - let retry = Datagram::new(dgram.destination(), dgram.source(), p); + let retry = Datagram::new( + dgram.destination(), + dgram.source(), + dgram.tos(), + dgram.ttl(), + p, + ); Some(retry) } else { qerror!([self], "unable to encode retry, dropping packet"); @@ -364,7 +372,7 @@ impl Server { fn connection_attempt( &mut self, initial: InitialDetails, - dgram: Datagram, + dgram: &Datagram, orig_dcid: Option, now: Instant, ) -> Option { @@ -385,11 +393,11 @@ impl Server { } } - fn create_qlog_trace(&self, attempt_key: &AttemptKey) -> NeqoQlog { + fn create_qlog_trace(&self, odcid: ConnectionIdRef<'_>) -> NeqoQlog { if let Some(qlog_dir) = &self.qlog_dir { let mut qlog_path = qlog_dir.to_path_buf(); - qlog_path.push(format!("{}.qlog", attempt_key.odcid)); + qlog_path.push(format!("{}.qlog", odcid)); // The original DCID is chosen by the client. Using create_new() // prevents attackers from overwriting existing logs. @@ -451,7 +459,7 @@ impl Server { c.set_retry_cids(odcid, initial.src_cid, initial.dst_cid); } c.set_validation(Rc::clone(&self.address_validation)); - c.set_qlog(self.create_qlog_trace(attempt_key)); + c.set_qlog(self.create_qlog_trace(attempt_key.odcid.as_cid_ref())); if let Some(cfg) = &self.ech_config { if c.server_enable_ech(cfg.config, &cfg.public_name, &cfg.sk, &cfg.pk) .is_err() @@ -465,7 +473,7 @@ impl Server { &mut self, attempt_key: AttemptKey, initial: InitialDetails, - dgram: Datagram, + dgram: &Datagram, orig_dcid: Option, now: Instant, ) -> Option { @@ -489,20 +497,30 @@ impl Server { params, ); - if let Ok(mut c) = sconn { - self.setup_connection(&mut c, &attempt_key, initial, orig_dcid); - let c = Rc::new(RefCell::new(ServerConnectionState { - c, - last_timer: now, - active_attempt: Some(attempt_key.clone()), - })); - cid_mgr.borrow_mut().set_connection(Rc::clone(&c)); - let previous_attempt = self.active_attempts.insert(attempt_key, Rc::clone(&c)); - debug_assert!(previous_attempt.is_none()); - self.process_connection(c, Some(dgram), now) - } else { - qwarn!([self], "Unable to create connection"); - None + match sconn { + Ok(mut c) => { + self.setup_connection(&mut c, &attempt_key, initial, orig_dcid); + let c = Rc::new(RefCell::new(ServerConnectionState { + c, + last_timer: now, + active_attempt: Some(attempt_key.clone()), + })); + cid_mgr.borrow_mut().set_connection(Rc::clone(&c)); + let previous_attempt = self.active_attempts.insert(attempt_key, Rc::clone(&c)); + debug_assert!(previous_attempt.is_none()); + self.process_connection(c, Some(dgram), now) + } + Err(e) => { + qwarn!([self], "Unable to create connection"); + if e == crate::Error::VersionNegotiation { + crate::qlog::server_version_information_failed( + &mut self.create_qlog_trace(attempt_key.odcid.as_cid_ref()), + self.conn_params.get_versions().all(), + initial.version.wire_version(), + ) + } + None + } } } @@ -511,7 +529,7 @@ impl Server { /// receives a connection ID from the server. fn handle_0rtt( &mut self, - dgram: Datagram, + dgram: &Datagram, dcid: ConnectionId, now: Instant, ) -> Option { @@ -533,18 +551,15 @@ impl Server { } } - fn process_input(&mut self, dgram: Datagram, now: Instant) -> Option { + fn process_input(&mut self, dgram: &Datagram, now: Instant) -> Option { qtrace!("Process datagram: {}", hex(&dgram[..])); // This is only looking at the first packet header in the datagram. // All packets in the datagram are routed to the same connection. let res = PublicPacket::decode(&dgram[..], self.cid_generator.borrow().as_decoder()); - let (packet, _remainder) = match res { - Ok(res) => res, - _ => { - qtrace!([self], "Discarding {:?}", dgram); - return None; - } + let Ok((packet, _remainder)) = res else { + qtrace!([self], "Discarding {:?}", dgram); + return None; }; // Finding an existing connection. Should be the most common case. @@ -573,12 +588,25 @@ impl Server { qdebug!([self], "Unsupported version: {:x}", packet.wire_version()); let vn = PacketBuilder::version_negotiation( - packet.scid(), - packet.dcid(), + &packet.scid()[..], + &packet.dcid()[..], packet.wire_version(), self.conn_params.get_versions().all(), ); - return Some(Datagram::new(dgram.destination(), dgram.source(), vn)); + + crate::qlog::server_version_information_failed( + &mut self.create_qlog_trace(packet.dcid()), + self.conn_params.get_versions().all(), + packet.wire_version(), + ); + + return Some(Datagram::new( + dgram.destination(), + dgram.source(), + dgram.tos(), + dgram.ttl(), + vn, + )); } match packet.packet_type() { @@ -587,7 +615,8 @@ impl Server { qdebug!([self], "Drop initial: too short"); return None; } - // Copy values from `packet` because they are currently still borrowing from `dgram`. + // Copy values from `packet` because they are currently still borrowing from + // `dgram`. let initial = InitialDetails::new(&packet); self.handle_initial(initial, dgram, now) } @@ -629,29 +658,24 @@ impl Server { } } - pub fn process(&mut self, dgram: Option, now: Instant) -> Output { - let out = if let Some(d) = dgram { - self.process_input(d, now) - } else { - None - }; - let out = out.or_else(|| self.process_next_output(now)); - match out { - Some(d) => { + pub fn process(&mut self, dgram: Option<&Datagram>, now: Instant) -> Output { + dgram + .and_then(|d| self.process_input(d, now)) + .or_else(|| self.process_next_output(now)) + .map(|d| { qtrace!([self], "Send packet: {:?}", d); Output::Datagram(d) - } - _ => match self.next_time(now) { - Some(delay) => { + }) + .or_else(|| { + self.next_time(now).map(|delay| { qtrace!([self], "Wait: {:?}", delay); Output::Callback(delay) - } - _ => { - qtrace!([self], "Go dormant"); - Output::None - } - }, - } + }) + }) + .unwrap_or_else(|| { + qtrace!([self], "Go dormant"); + Output::None + }) } /// This lists the connections that have received new events @@ -687,7 +711,7 @@ impl ActiveConnectionRef { impl std::hash::Hash for ActiveConnectionRef { fn hash(&self, state: &mut H) { let ptr: *const _ = self.c.as_ref(); - ptr.hash(state) + ptr.hash(state); } } diff --git a/third_party/rust/neqo-transport/src/stats.rs b/third_party/rust/neqo-transport/src/stats.rs index 9428b61949aa..d6c7a911f9b5 100644 --- a/third_party/rust/neqo-transport/src/stats.rs +++ b/third_party/rust/neqo-transport/src/stats.rs @@ -7,8 +7,6 @@ // Tracking of some useful statistics. #![deny(clippy::pedantic)] -use crate::packet::PacketNumber; -use neqo_common::qinfo; use std::{ cell::RefCell, fmt::{self, Debug}, @@ -17,6 +15,10 @@ use std::{ time::Duration, }; +use neqo_common::qinfo; + +use crate::packet::PacketNumber; + pub(crate) const MAX_PTO_COUNTS: usize = 16; #[derive(Default, Clone)] @@ -141,6 +143,8 @@ pub struct Stats { pub rtt: Duration, /// The current, estimated round-trip time variation on the primary path. pub rttvar: Duration, + /// Whether the first RTT sample was guessed from a discarded packet. + pub rtt_init_guess: bool, /// Count PTOs. Single PTOs, 2 PTOs in a row, 3 PTOs in row, etc. are counted /// separately. @@ -174,6 +178,7 @@ impl Stats { } /// # Panics + /// /// When preconditions are violated. pub fn add_pto_count(&mut self, count: usize) { debug_assert!(count > 0); diff --git a/third_party/rust/neqo-transport/src/stream_id.rs b/third_party/rust/neqo-transport/src/stream_id.rs index 51df2ca9fb01..f3b07b86a81b 100644 --- a/third_party/rust/neqo-transport/src/stream_id.rs +++ b/third_party/rust/neqo-transport/src/stream_id.rs @@ -133,9 +133,10 @@ impl ::std::fmt::Display for StreamId { #[cfg(test)] mod test { - use super::StreamId; use neqo_common::Role; + use super::StreamId; + #[test] fn bidi_stream_properties() { let id1 = StreamId::from(16); diff --git a/third_party/rust/neqo-transport/src/streams.rs b/third_party/rust/neqo-transport/src/streams.rs index 735e602feb21..7cbb29ce0253 100644 --- a/third_party/rust/neqo-transport/src/streams.rs +++ b/third_party/rust/neqo-transport/src/streams.rs @@ -5,6 +5,10 @@ // except according to those terms. // Stream management for a connection. +use std::{cell::RefCell, cmp::Ordering, rc::Rc}; + +use neqo_common::{qtrace, qwarn, Role}; + use crate::{ fc::{LocalStreamLimits, ReceiverFlowControl, RemoteStreamLimits, SenderFlowControl}, frame::Frame, @@ -17,9 +21,6 @@ use crate::{ tparams::{self, TransportParametersHandler}, ConnectionEvents, Error, Res, }; -use neqo_common::{qtrace, qwarn, Role}; -use std::cmp::Ordering; -use std::{cell::RefCell, rc::Rc}; pub type SendOrder = i64; @@ -269,7 +270,7 @@ impl Streams { StreamRecoveryToken::Stream(st) => self.send.lost(st), StreamRecoveryToken::ResetStream { stream_id } => self.send.reset_lost(*stream_id), StreamRecoveryToken::StreamDataBlocked { stream_id, limit } => { - self.send.blocked_lost(*stream_id, *limit) + self.send.blocked_lost(*stream_id, *limit); } StreamRecoveryToken::MaxStreamData { stream_id, @@ -294,10 +295,10 @@ impl Streams { self.remote_stream_limits[*stream_type].frame_lost(*max_streams); } StreamRecoveryToken::DataBlocked(limit) => { - self.sender_fc.borrow_mut().frame_lost(*limit) + self.sender_fc.borrow_mut().frame_lost(*limit); } StreamRecoveryToken::MaxData(maximum_data) => { - self.receiver_fc.borrow_mut().frame_lost(*maximum_data) + self.receiver_fc.borrow_mut().frame_lost(*maximum_data); } } } @@ -438,9 +439,10 @@ impl Streams { if st == StreamType::BiDi { // From the local perspective, this is a local- originated BiDi stream. From the - // remote perspective, this is a remote-originated BiDi stream. Therefore, look at - // the local transport parameters for the INITIAL_MAX_STREAM_DATA_BIDI_LOCAL value - // to decide how much this endpoint will allow its peer to send. + // remote perspective, this is a remote-originated BiDi stream. Therefore, look + // at the local transport parameters for the + // INITIAL_MAX_STREAM_DATA_BIDI_LOCAL value to decide how + // much this endpoint will allow its peer to send. let recv_initial_max_stream_data = self .tps .borrow() diff --git a/third_party/rust/neqo-transport/src/tparams.rs b/third_party/rust/neqo-transport/src/tparams.rs index 82efa8db5527..129782909470 100644 --- a/third_party/rust/neqo-transport/src/tparams.rs +++ b/third_party/rust/neqo-transport/src/tparams.rs @@ -6,10 +6,12 @@ // Transport parameters. See -transport section 7.3. -use crate::{ - cid::{ConnectionId, ConnectionIdEntry, CONNECTION_ID_SEQNO_PREFERRED, MAX_CONNECTION_ID_LEN}, - version::{Version, VersionConfig, WireVersion}, - Error, Res, +use std::{ + cell::RefCell, + collections::HashMap, + convert::TryFrom, + net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6}, + rc::Rc, }; use neqo_common::{hex, qdebug, qinfo, qtrace, Decoder, Encoder, Role}; @@ -19,12 +21,10 @@ use neqo_crypto::{ random, HandshakeMessage, ZeroRttCheckResult, ZeroRttChecker, }; -use std::{ - cell::RefCell, - collections::HashMap, - convert::TryFrom, - net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6}, - rc::Rc, +use crate::{ + cid::{ConnectionId, ConnectionIdEntry, CONNECTION_ID_SEQNO_PREFERRED, MAX_CONNECTION_ID_LEN}, + version::{Version, VersionConfig, WireVersion}, + Error, Res, }; pub type TransportParameterId = u64; @@ -55,10 +55,10 @@ tpids! { ACTIVE_CONNECTION_ID_LIMIT = 0x0e, INITIAL_SOURCE_CONNECTION_ID = 0x0f, RETRY_SOURCE_CONNECTION_ID = 0x10, + VERSION_INFORMATION = 0x11, GREASE_QUIC_BIT = 0x2ab2, MIN_ACK_DELAY = 0xff02_de1a, MAX_DATAGRAM_FRAME_SIZE = 0x0020, - VERSION_NEGOTIATION = 0xff73db, } #[derive(Clone, Debug)] @@ -71,6 +71,7 @@ impl PreferredAddress { /// Make a new preferred address configuration. /// /// # Panics + /// /// If neither address is provided, or if either address is of the wrong type. #[must_use] pub fn new(v4: Option, v6: Option) -> Self { @@ -298,7 +299,7 @@ impl TransportParameter { _ => return Err(Error::TransportParameterError), }, - VERSION_NEGOTIATION => Self::decode_versions(&mut d)?, + VERSION_INFORMATION => Self::decode_versions(&mut d)?, // Skip. _ => return Ok(None), @@ -440,7 +441,7 @@ impl TransportParameters { let rbuf = random(4); let mut other = Vec::with_capacity(versions.all().len() + 1); let mut dec = Decoder::new(&rbuf); - let grease = (dec.decode_uint(4).unwrap() as u32) & 0xf0f0_f0f0 | 0x0a0a0a0a; + let grease = (dec.decode_uint(4).unwrap() as u32) & 0xf0f0_f0f0 | 0x0a0a_0a0a; other.push(grease); for &v in versions.all() { if role == Role::Client && !versions.initial().is_compatible(v) { @@ -450,7 +451,7 @@ impl TransportParameters { } let current = versions.initial().wire_version(); self.set( - VERSION_NEGOTIATION, + VERSION_INFORMATION, TransportParameter::Versions { current, other }, ); } @@ -458,7 +459,7 @@ impl TransportParameters { fn compatible_upgrade(&mut self, v: Version) { if let Some(TransportParameter::Versions { ref mut current, .. - }) = self.params.get_mut(&VERSION_NEGOTIATION) + }) = self.params.get_mut(&VERSION_INFORMATION) { *current = v.wire_version(); } else { @@ -543,7 +544,7 @@ impl TransportParameters { #[must_use] pub fn get_versions(&self) -> Option<(WireVersion, &[WireVersion])> { if let Some(TransportParameter::Versions { current, other }) = - self.params.get(&VERSION_NEGOTIATION) + self.params.get(&VERSION_INFORMATION) { Some((*current, other)) } else { @@ -584,7 +585,7 @@ impl TransportParametersHandler { pub fn set_version(&mut self, version: Version) { debug_assert_eq!(self.role, Role::Client); self.versions.set_initial(version); - self.local.set_versions(self.role, &self.versions) + self.local.set_versions(self.role, &self.versions); } pub fn remote(&self) -> &TransportParameters { @@ -726,16 +727,12 @@ where return ZeroRttCheckResult::Reject; } let mut dec = Decoder::from(token); - let tpslice = if let Some(v) = dec.decode_vvec() { - v - } else { + let Some(tpslice) = dec.decode_vvec() else { qinfo!("0-RTT: token code error"); return ZeroRttCheckResult::Fail; }; let mut dec_tp = Decoder::from(tpslice); - let remembered = if let Ok(v) = TransportParameters::decode(&mut dec_tp) { - v - } else { + let Ok(remembered) = TransportParameters::decode(&mut dec_tp) else { qinfo!("0-RTT: transport parameter decode error"); return ZeroRttCheckResult::Fail; }; @@ -771,7 +768,7 @@ mod tests { let tps2 = TransportParameters::decode(&mut enc.as_decoder()).expect("Couldn't decode"); assert_eq!(tps, tps2); - println!("TPS = {:?}", tps); + println!("TPS = {tps:?}"); assert_eq!(tps2.get_integer(IDLE_TIMEOUT), 0); // Default assert_eq!(tps2.get_integer(MAX_ACK_DELAY), 25); // Default assert_eq!(tps2.get_integer(ACTIVE_CONNECTION_ID_LIMIT), 2); // Default @@ -1027,7 +1024,8 @@ mod tests { fn active_connection_id_limit_min_2() { let mut tps = TransportParameters::default(); - // Intentionally set an invalid value for the ACTIVE_CONNECTION_ID_LIMIT transport parameter. + // Intentionally set an invalid value for the ACTIVE_CONNECTION_ID_LIMIT transport + // parameter. tps.params .insert(ACTIVE_CONNECTION_ID_LIMIT, TransportParameter::Integer(1)); @@ -1043,8 +1041,7 @@ mod tests { #[test] fn versions_encode_decode() { const ENCODED: &[u8] = &[ - 0x80, 0xff, 0x73, 0xdb, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x1a, 0x2a, 0x3a, 0x4a, 0x5a, - 0x6a, 0x7a, 0x8a, + 0x11, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x1a, 0x2a, 0x3a, 0x4a, 0x5a, 0x6a, 0x7a, 0x8a, ]; let vn = TransportParameter::Versions { current: Version::Version1.wire_version(), @@ -1052,12 +1049,12 @@ mod tests { }; let mut enc = Encoder::new(); - vn.encode(&mut enc, VERSION_NEGOTIATION); + vn.encode(&mut enc, VERSION_INFORMATION); assert_eq!(enc.as_ref(), ENCODED); let mut dec = enc.as_decoder(); let (id, decoded) = TransportParameter::decode(&mut dec).unwrap().unwrap(); - assert_eq!(id, VERSION_NEGOTIATION); + assert_eq!(id, VERSION_INFORMATION); assert_eq!(decoded, vn); } @@ -1076,10 +1073,8 @@ mod tests { #[test] fn versions_zero() { - const ZERO1: &[u8] = &[0x80, 0xff, 0x73, 0xdb, 0x04, 0x00, 0x00, 0x00, 0x00]; - const ZERO2: &[u8] = &[ - 0x80, 0xff, 0x73, 0xdb, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - ]; + const ZERO1: &[u8] = &[0x11, 0x04, 0x00, 0x00, 0x00, 0x00]; + const ZERO2: &[u8] = &[0x11, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00]; let mut dec = Decoder::from(&ZERO1); assert_eq!( @@ -1097,7 +1092,7 @@ mod tests { fn versions_equal_0rtt() { let mut current = TransportParameters::default(); current.set( - VERSION_NEGOTIATION, + VERSION_INFORMATION, TransportParameter::Versions { current: Version::Version1.wire_version(), other: vec![0x1a2a_3a4a], @@ -1112,7 +1107,7 @@ mod tests { // If the version matches, it's OK to use 0-RTT. remembered.set( - VERSION_NEGOTIATION, + VERSION_INFORMATION, TransportParameter::Versions { current: Version::Version1.wire_version(), other: vec![0x5a6a_7a8a, 0x9aaa_baca], @@ -1123,7 +1118,7 @@ mod tests { // An apparent "upgrade" is still cause to reject 0-RTT. remembered.set( - VERSION_NEGOTIATION, + VERSION_INFORMATION, TransportParameter::Versions { current: Version::Version1.wire_version() + 1, other: vec![], diff --git a/third_party/rust/neqo-transport/src/tracking.rs b/third_party/rust/neqo-transport/src/tracking.rs index 32f1c8d1b72b..64d00257d34a 100644 --- a/third_party/rust/neqo-transport/src/tracking.rs +++ b/third_party/rust/neqo-transport/src/tracking.rs @@ -18,16 +18,14 @@ use std::{ use neqo_common::{qdebug, qinfo, qtrace, qwarn}; use neqo_crypto::{Epoch, TLS_EPOCH_HANDSHAKE, TLS_EPOCH_INITIAL}; +use smallvec::{smallvec, SmallVec}; use crate::{ packet::{PacketBuilder, PacketNumber, PacketType}, recovery::RecoveryToken, stats::FrameStats, - Error, Res, }; -use smallvec::{smallvec, SmallVec}; - // TODO(mt) look at enabling EnumMap for this: https://stackoverflow.com/a/44905797/1375574 #[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Ord, Eq)] pub enum PacketNumberSpace { @@ -725,14 +723,10 @@ impl AckTracker { builder: &mut PacketBuilder, tokens: &mut Vec, stats: &mut FrameStats, - ) -> Res<()> { + ) { if let Some(space) = self.get_mut(pn_space) { space.write_frame(now, rtt, builder, tokens, stats); - if builder.len() > builder.limit() { - return Err(Error::InternalError(24)); - } } - Ok(()) } } @@ -750,6 +744,11 @@ impl Default for AckTracker { #[cfg(test)] mod tests { + use std::collections::HashSet; + + use lazy_static::lazy_static; + use neqo_common::Encoder; + use super::{ AckTracker, Duration, Instant, PacketNumberSpace, PacketNumberSpaceSet, RecoveryToken, RecvdPackets, MAX_TRACKED_RANGES, @@ -759,9 +758,6 @@ mod tests { packet::{PacketBuilder, PacketNumber}, stats::FrameStats, }; - use lazy_static::lazy_static; - use neqo_common::Encoder; - use std::collections::HashSet; const RTT: Duration = Duration::from_millis(100); lazy_static! { @@ -1059,16 +1055,14 @@ mod tests { let mut tokens = Vec::new(); let mut stats = FrameStats::default(); - tracker - .write_frame( - PacketNumberSpace::Initial, - *NOW, - RTT, - &mut builder, - &mut tokens, - &mut stats, - ) - .unwrap(); + tracker.write_frame( + PacketNumberSpace::Initial, + *NOW, + RTT, + &mut builder, + &mut tokens, + &mut stats, + ); assert_eq!(stats.ack, 1); // Mark another packet as received so we have cause to send another ACK in that space. @@ -1087,16 +1081,14 @@ mod tests { assert!(tracker .ack_time(NOW.checked_sub(Duration::from_millis(1)).unwrap()) .is_none()); - tracker - .write_frame( - PacketNumberSpace::Initial, - *NOW, - RTT, - &mut builder, - &mut tokens, - &mut stats, - ) - .unwrap(); + tracker.write_frame( + PacketNumberSpace::Initial, + *NOW, + RTT, + &mut builder, + &mut tokens, + &mut stats, + ); assert_eq!(stats.ack, 1); if let RecoveryToken::Ack(tok) = &tokens[0] { tracker.acked(tok); // Should be a noop. @@ -1120,16 +1112,14 @@ mod tests { builder.set_limit(10); let mut stats = FrameStats::default(); - tracker - .write_frame( - PacketNumberSpace::Initial, - *NOW, - RTT, - &mut builder, - &mut Vec::new(), - &mut stats, - ) - .unwrap(); + tracker.write_frame( + PacketNumberSpace::Initial, + *NOW, + RTT, + &mut builder, + &mut Vec::new(), + &mut stats, + ); assert_eq!(stats.ack, 0); assert_eq!(builder.len(), 1); // Only the short packet header has been added. } @@ -1153,16 +1143,14 @@ mod tests { builder.set_limit(32); let mut stats = FrameStats::default(); - tracker - .write_frame( - PacketNumberSpace::Initial, - *NOW, - RTT, - &mut builder, - &mut Vec::new(), - &mut stats, - ) - .unwrap(); + tracker.write_frame( + PacketNumberSpace::Initial, + *NOW, + RTT, + &mut builder, + &mut Vec::new(), + &mut stats, + ); assert_eq!(stats.ack, 1); let mut dec = builder.as_decoder(); diff --git a/third_party/rust/neqo-transport/src/version.rs b/third_party/rust/neqo-transport/src/version.rs index 71a1d7a8e679..13db0bf024ae 100644 --- a/third_party/rust/neqo-transport/src/version.rs +++ b/third_party/rust/neqo-transport/src/version.rs @@ -4,10 +4,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use crate::{Error, Res}; -use neqo_common::qdebug; use std::convert::TryFrom; +use neqo_common::qdebug; + +use crate::{Error, Res}; + pub type WireVersion = u32; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -23,7 +25,7 @@ pub enum Version { impl Version { pub const fn wire_version(self) -> WireVersion { match self { - Self::Version2 => 0x709a50c4, + Self::Version2 => 0x6b33_43cf, Self::Version1 => 1, Self::Draft29 => 0xff00_0000 + 29, Self::Draft30 => 0xff00_0000 + 30, @@ -34,8 +36,8 @@ impl Version { pub(crate) fn initial_salt(self) -> &'static [u8] { const INITIAL_SALT_V2: &[u8] = &[ - 0xa7, 0x07, 0xc2, 0x03, 0xa5, 0x9b, 0x47, 0x18, 0x4a, 0x1d, 0x62, 0xca, 0x57, 0x04, - 0x06, 0xea, 0x7a, 0xe3, 0xe5, 0xd3, + 0x0d, 0xed, 0xe3, 0xde, 0xf7, 0x00, 0xa6, 0xdb, 0x81, 0x93, 0x81, 0xbe, 0x6e, 0x26, + 0x9d, 0xcb, 0xf9, 0xbd, 0x2e, 0xd9, ]; const INITIAL_SALT_V1: &[u8] = &[ 0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17, 0x9a, 0xe6, 0xa4, 0xc8, @@ -62,20 +64,20 @@ impl Version { } pub(crate) fn retry_secret(self) -> &'static [u8] { - const RETRY_SECRET_29: &[u8] = &[ - 0x8b, 0x0d, 0x37, 0xeb, 0x85, 0x35, 0x02, 0x2e, 0xbc, 0x8d, 0x76, 0xa2, 0x07, 0xd8, - 0x0d, 0xf2, 0x26, 0x46, 0xec, 0x06, 0xdc, 0x80, 0x96, 0x42, 0xc3, 0x0a, 0x8b, 0xaa, - 0x2b, 0xaa, 0xff, 0x4c, + const RETRY_SECRET_V2: &[u8] = &[ + 0xc4, 0xdd, 0x24, 0x84, 0xd6, 0x81, 0xae, 0xfa, 0x4f, 0xf4, 0xd6, 0x9c, 0x2c, 0x20, + 0x29, 0x99, 0x84, 0xa7, 0x65, 0xa5, 0xd3, 0xc3, 0x19, 0x82, 0xf3, 0x8f, 0xc7, 0x41, + 0x62, 0x15, 0x5e, 0x9f, ]; const RETRY_SECRET_V1: &[u8] = &[ 0xd9, 0xc9, 0x94, 0x3e, 0x61, 0x01, 0xfd, 0x20, 0x00, 0x21, 0x50, 0x6b, 0xcc, 0x02, 0x81, 0x4c, 0x73, 0x03, 0x0f, 0x25, 0xc7, 0x9d, 0x71, 0xce, 0x87, 0x6e, 0xca, 0x87, 0x6e, 0x6f, 0xca, 0x8e, ]; - const RETRY_SECRET_V2: &[u8] = &[ - 0x34, 0x25, 0xc2, 0x0c, 0xf8, 0x87, 0x79, 0xdf, 0x2f, 0xf7, 0x1e, 0x8a, 0xbf, 0xa7, - 0x82, 0x49, 0x89, 0x1e, 0x76, 0x3b, 0xbe, 0xd2, 0xf1, 0x3c, 0x04, 0x83, 0x43, 0xd3, - 0x48, 0xc0, 0x60, 0xe2, + const RETRY_SECRET_29: &[u8] = &[ + 0x8b, 0x0d, 0x37, 0xeb, 0x85, 0x35, 0x02, 0x2e, 0xbc, 0x8d, 0x76, 0xa2, 0x07, 0xd8, + 0x0d, 0xf2, 0x26, 0x46, 0xec, 0x06, 0xdc, 0x80, 0x96, 0x42, 0xc3, 0x0a, 0x8b, 0xaa, + 0x2b, 0xaa, 0xff, 0x4c, ]; match self { Self::Version2 => RETRY_SECRET_V2, @@ -131,7 +133,7 @@ impl TryFrom for Version { fn try_from(wire: WireVersion) -> Res { if wire == 1 { Ok(Self::Version1) - } else if wire == 0x709a50c4 { + } else if wire == 0x6b33_43cf { Ok(Self::Version2) } else if wire == 0xff00_0000 + 29 { Ok(Self::Draft29) diff --git a/third_party/rust/neqo-transport/tests/common/mod.rs b/third_party/rust/neqo-transport/tests/common/mod.rs index 3bc97a05280f..a43f91e3fe6d 100644 --- a/third_party/rust/neqo-transport/tests/common/mod.rs +++ b/third_party/rust/neqo-transport/tests/common/mod.rs @@ -8,6 +8,8 @@ #![warn(clippy::pedantic)] #![allow(unused)] +use std::{cell::RefCell, convert::TryFrom, mem, ops::Range, rc::Rc}; + use neqo_common::{event::Provider, hex_with_len, qtrace, Datagram, Decoder, Role}; use neqo_crypto::{ constants::{TLS_AES_128_GCM_SHA256, TLS_VERSION_1_3}, @@ -21,12 +23,6 @@ use neqo_transport::{ }; use test_fixture::{self, default_client, now, CountingConnectionIdGenerator}; -use std::cell::RefCell; -use std::convert::TryFrom; -use std::mem; -use std::ops::Range; -use std::rc::Rc; - /// Create a server. This is different than the one in the fixture, which is a single connection. pub fn new_server(params: ConnectionParameters) -> Server { Server::new( @@ -63,28 +59,28 @@ pub fn connect(client: &mut Connection, server: &mut Server) -> ActiveConnection server.set_validation(ValidateAddress::Never); assert_eq!(*client.state(), State::Init); - let dgram = client.process(None, now()).dgram(); // ClientHello - assert!(dgram.is_some()); - let dgram = server.process(dgram, now()).dgram(); // ServerHello... - assert!(dgram.is_some()); + let out = client.process(None, now()); // ClientHello + assert!(out.as_dgram_ref().is_some()); + let out = server.process(out.as_dgram_ref(), now()); // ServerHello... + assert!(out.as_dgram_ref().is_some()); // Ingest the server Certificate. - let dgram = client.process(dgram, now()).dgram(); - assert!(dgram.is_some()); // This should just be an ACK. - let dgram = server.process(dgram, now()).dgram(); - assert!(dgram.is_none()); // So the server should have nothing to say. + let out = client.process(out.as_dgram_ref(), now()); + assert!(out.as_dgram_ref().is_some()); // This should just be an ACK. + let out = server.process(out.as_dgram_ref(), now()); + assert!(out.as_dgram_ref().is_none()); // So the server should have nothing to say. // Now mark the server as authenticated. client.authenticated(AuthenticationStatus::Ok, now()); - let dgram = client.process(None, now()).dgram(); - assert!(dgram.is_some()); + let out = client.process(None, now()); + assert!(out.as_dgram_ref().is_some()); assert_eq!(*client.state(), State::Connected); - let dgram = server.process(dgram, now()).dgram(); - assert!(dgram.is_some()); // ACK + HANDSHAKE_DONE + NST + let out = server.process(out.as_dgram_ref(), now()); + assert!(out.as_dgram_ref().is_some()); // ACK + HANDSHAKE_DONE + NST // Have the client process the HANDSHAKE_DONE. - let dgram = client.process(dgram, now()).dgram(); - assert!(dgram.is_none()); + let out = client.process(out.as_dgram_ref(), now()); + assert!(out.as_dgram_ref().is_none()); assert_eq!(*client.state(), State::Confirmed); connected_server(server) @@ -225,14 +221,14 @@ pub fn generate_ticket(server: &mut Server) -> ResumptionToken { let mut server_conn = connect(&mut client, server); server_conn.borrow_mut().send_ticket(now(), &[]).unwrap(); - let dgram = server.process(None, now()).dgram(); - client.process_input(dgram.unwrap(), now()); // Consume ticket, ignore output. + let out = server.process(None, now()); + client.process_input(out.as_dgram_ref().unwrap(), now()); // Consume ticket, ignore output. let ticket = find_ticket(&mut client); // Have the client close the connection and then let the server clean up. client.close(now(), 0, "got a ticket"); - let dgram = client.process_output(now()).dgram(); - mem::drop(server.process(dgram, now())); + let out = client.process_output(now()); + mem::drop(server.process(out.as_dgram_ref(), now())); // Calling active_connections clears the set of active connections. assert_eq!(server.active_connections().len(), 1); ticket diff --git a/third_party/rust/neqo-transport/tests/conn_vectors.rs b/third_party/rust/neqo-transport/tests/conn_vectors.rs index 83de136d917a..91dbbf31ccce 100644 --- a/third_party/rust/neqo-transport/tests/conn_vectors.rs +++ b/third_party/rust/neqo-transport/tests/conn_vectors.rs @@ -8,91 +8,89 @@ #![deny(clippy::pedantic)] #![cfg(not(feature = "fuzzing"))] -use neqo_common::Datagram; +use std::{cell::RefCell, rc::Rc}; + use neqo_transport::{ Connection, ConnectionParameters, RandomConnectionIdGenerator, State, Version, }; -use test_fixture::{self, addr, now}; - -use std::cell::RefCell; -use std::rc::Rc; +use test_fixture::{self, datagram, now}; const INITIAL_PACKET_V2: &[u8] = &[ - 0xdd, 0x70, 0x9a, 0x50, 0xc4, 0x08, 0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08, 0x00, 0x00, - 0x44, 0x9e, 0x43, 0x91, 0xd8, 0x48, 0x23, 0xb8, 0xe6, 0x10, 0x58, 0x9c, 0x83, 0xc9, 0x2d, 0x0e, - 0x97, 0xeb, 0x7a, 0x6e, 0x50, 0x03, 0xf5, 0x77, 0x64, 0xc5, 0xc7, 0xf0, 0x09, 0x5b, 0xa5, 0x4b, - 0x90, 0x81, 0x8f, 0x1b, 0xfe, 0xec, 0xc1, 0xc9, 0x7c, 0x54, 0xfc, 0x73, 0x1e, 0xdb, 0xd2, 0xa2, - 0x44, 0xe3, 0xb1, 0xe6, 0x39, 0xa9, 0xbc, 0x75, 0xed, 0x54, 0x5b, 0x98, 0x64, 0x93, 0x43, 0xb2, - 0x53, 0x61, 0x5e, 0xc6, 0xb3, 0xe4, 0xdf, 0x0f, 0xd2, 0xe7, 0xfe, 0x9d, 0x69, 0x1a, 0x09, 0xe6, - 0xa1, 0x44, 0xb4, 0x36, 0xd8, 0xa2, 0xc0, 0x88, 0xa4, 0x04, 0x26, 0x23, 0x40, 0xdf, 0xd9, 0x95, - 0xec, 0x38, 0x65, 0x69, 0x4e, 0x30, 0x26, 0xec, 0xd8, 0xc6, 0xd2, 0x56, 0x1a, 0x5a, 0x36, 0x67, - 0x2a, 0x10, 0x05, 0x01, 0x81, 0x68, 0xc0, 0xf0, 0x81, 0xc1, 0x0e, 0x2b, 0xf1, 0x4d, 0x55, 0x0c, - 0x97, 0x7e, 0x28, 0xbb, 0x9a, 0x75, 0x9c, 0x57, 0xd0, 0xf7, 0xff, 0xb1, 0xcd, 0xfb, 0x40, 0xbd, - 0x77, 0x4d, 0xec, 0x58, 0x96, 0x57, 0x54, 0x20, 0x47, 0xdf, 0xfe, 0xfa, 0x56, 0xfc, 0x80, 0x89, - 0xa4, 0xd1, 0xef, 0x37, 0x9c, 0x81, 0xba, 0x3d, 0xf7, 0x1a, 0x05, 0xdd, 0xc7, 0x92, 0x83, 0x40, - 0x77, 0x59, 0x10, 0xfe, 0xb3, 0xce, 0x4c, 0xbc, 0xfd, 0x8d, 0x25, 0x3e, 0xdd, 0x05, 0xf1, 0x61, - 0x45, 0x8f, 0x9d, 0xc4, 0x4b, 0xea, 0x01, 0x7c, 0x31, 0x17, 0xcc, 0xa7, 0x06, 0x5a, 0x31, 0x5d, - 0xed, 0xa9, 0x46, 0x4e, 0x67, 0x2e, 0xc8, 0x0c, 0x3f, 0x79, 0xac, 0x99, 0x34, 0x37, 0xb4, 0x41, - 0xef, 0x74, 0x22, 0x7e, 0xcc, 0x4d, 0xc9, 0xd5, 0x97, 0xf6, 0x6a, 0xb0, 0xab, 0x8d, 0x21, 0x4b, - 0x55, 0x84, 0x0c, 0x70, 0x34, 0x9d, 0x76, 0x16, 0xcb, 0xe3, 0x8e, 0x5e, 0x1d, 0x05, 0x2d, 0x07, - 0xf1, 0xfe, 0xdb, 0x3d, 0xd3, 0xc4, 0xd8, 0xce, 0x29, 0x57, 0x24, 0x94, 0x5e, 0x67, 0xed, 0x2e, - 0xef, 0xcd, 0x9f, 0xb5, 0x24, 0x72, 0x38, 0x7f, 0x31, 0x8e, 0x3d, 0x9d, 0x23, 0x3b, 0xe7, 0xdf, - 0xc7, 0x9d, 0x6b, 0xf6, 0x08, 0x0d, 0xcb, 0xbb, 0x41, 0xfe, 0xb1, 0x80, 0xd7, 0x85, 0x88, 0x49, - 0x7c, 0x3e, 0x43, 0x9d, 0x38, 0xc3, 0x34, 0x74, 0x8d, 0x2b, 0x56, 0xfd, 0x19, 0xab, 0x36, 0x4d, - 0x05, 0x7a, 0x9b, 0xd5, 0xa6, 0x99, 0xae, 0x14, 0x5d, 0x7f, 0xdb, 0xc8, 0xf5, 0x77, 0x75, 0x18, - 0x1b, 0x0a, 0x97, 0xc3, 0xbd, 0xed, 0xc9, 0x1a, 0x55, 0x5d, 0x6c, 0x9b, 0x86, 0x34, 0xe1, 0x06, - 0xd8, 0xc9, 0xca, 0x45, 0xa9, 0xd5, 0x45, 0x0a, 0x76, 0x79, 0xed, 0xc5, 0x45, 0xda, 0x91, 0x02, - 0x5b, 0xc9, 0x3a, 0x7c, 0xf9, 0xa0, 0x23, 0xa0, 0x66, 0xff, 0xad, 0xb9, 0x71, 0x7f, 0xfa, 0xf3, - 0x41, 0x4c, 0x3b, 0x64, 0x6b, 0x57, 0x38, 0xb3, 0xcc, 0x41, 0x16, 0x50, 0x2d, 0x18, 0xd7, 0x9d, - 0x82, 0x27, 0x43, 0x63, 0x06, 0xd9, 0xb2, 0xb3, 0xaf, 0xc6, 0xc7, 0x85, 0xce, 0x3c, 0x81, 0x7f, - 0xeb, 0x70, 0x3a, 0x42, 0xb9, 0xc8, 0x3b, 0x59, 0xf0, 0xdc, 0xef, 0x12, 0x45, 0xd0, 0xb3, 0xe4, - 0x02, 0x99, 0x82, 0x1e, 0xc1, 0x95, 0x49, 0xce, 0x48, 0x97, 0x14, 0xfe, 0x26, 0x11, 0xe7, 0x2c, - 0xd8, 0x82, 0xf4, 0xf7, 0x0d, 0xce, 0x7d, 0x36, 0x71, 0x29, 0x6f, 0xc0, 0x45, 0xaf, 0x5c, 0x9f, - 0x63, 0x0d, 0x7b, 0x49, 0xa3, 0xeb, 0x82, 0x1b, 0xbc, 0xa6, 0x0f, 0x19, 0x84, 0xdc, 0xe6, 0x64, - 0x91, 0x71, 0x3b, 0xfe, 0x06, 0x00, 0x1a, 0x56, 0xf5, 0x1b, 0xb3, 0xab, 0xe9, 0x2f, 0x79, 0x60, - 0x54, 0x7c, 0x4d, 0x0a, 0x70, 0xf4, 0xa9, 0x62, 0xb3, 0xf0, 0x5d, 0xc2, 0x5a, 0x34, 0xbb, 0xe8, - 0x30, 0xa7, 0xea, 0x47, 0x36, 0xd3, 0xb0, 0x16, 0x17, 0x23, 0x50, 0x0d, 0x82, 0xbe, 0xda, 0x9b, - 0xe3, 0x32, 0x7a, 0xf2, 0xaa, 0x41, 0x38, 0x21, 0xff, 0x67, 0x8b, 0x2a, 0x87, 0x6e, 0xc4, 0xb0, - 0x0b, 0xb6, 0x05, 0xff, 0xcc, 0x39, 0x17, 0xff, 0xdc, 0x27, 0x9f, 0x18, 0x7d, 0xaa, 0x2f, 0xce, - 0x8c, 0xde, 0x12, 0x19, 0x80, 0xbb, 0xa8, 0xec, 0x8f, 0x44, 0xca, 0x56, 0x2b, 0x0f, 0x13, 0x19, - 0x14, 0xc9, 0x01, 0xcf, 0xbd, 0x84, 0x74, 0x08, 0xb7, 0x78, 0xe6, 0x73, 0x8c, 0x7b, 0xb5, 0xb1, - 0xb3, 0xf9, 0x7d, 0x01, 0xb0, 0xa2, 0x4d, 0xcc, 0xa4, 0x0e, 0x3b, 0xed, 0x29, 0x41, 0x1b, 0x1b, - 0xa8, 0xf6, 0x08, 0x43, 0xc4, 0xa2, 0x41, 0x02, 0x1b, 0x23, 0x13, 0x2b, 0x95, 0x00, 0x50, 0x9b, - 0x9a, 0x35, 0x16, 0xd4, 0xa9, 0xdd, 0x41, 0xd3, 0xba, 0xcb, 0xcd, 0x42, 0x6b, 0x45, 0x13, 0x93, - 0x52, 0x18, 0x28, 0xaf, 0xed, 0xcf, 0x20, 0xfa, 0x46, 0xac, 0x24, 0xf4, 0x4a, 0x8e, 0x29, 0x73, - 0x30, 0xb1, 0x67, 0x05, 0xd5, 0xd5, 0xf7, 0x98, 0xef, 0xf9, 0xe9, 0x13, 0x4a, 0x06, 0x59, 0x79, - 0x87, 0xa1, 0xdb, 0x46, 0x17, 0xca, 0xa2, 0xd9, 0x38, 0x37, 0x73, 0x08, 0x29, 0xd4, 0xd8, 0x9e, - 0x16, 0x41, 0x3b, 0xe4, 0xd8, 0xa8, 0xa3, 0x8a, 0x7e, 0x62, 0x26, 0x62, 0x3b, 0x64, 0xa8, 0x20, - 0x17, 0x8e, 0xc3, 0xa6, 0x69, 0x54, 0xe1, 0x07, 0x10, 0xe0, 0x43, 0xae, 0x73, 0xdd, 0x3f, 0xb2, - 0x71, 0x5a, 0x05, 0x25, 0xa4, 0x63, 0x43, 0xfb, 0x75, 0x90, 0xe5, 0xea, 0xc7, 0xee, 0x55, 0xfc, - 0x81, 0x0e, 0x0d, 0x8b, 0x4b, 0x8f, 0x7b, 0xe8, 0x2c, 0xd5, 0xa2, 0x14, 0x57, 0x5a, 0x1b, 0x99, - 0x62, 0x9d, 0x47, 0xa9, 0xb2, 0x81, 0xb6, 0x13, 0x48, 0xc8, 0x62, 0x7c, 0xab, 0x38, 0xe2, 0xa6, - 0x4d, 0xb6, 0x62, 0x6e, 0x97, 0xbb, 0x8f, 0x77, 0xbd, 0xcb, 0x0f, 0xee, 0x47, 0x6a, 0xed, 0xd7, - 0xba, 0x8f, 0x54, 0x41, 0xac, 0xaa, 0xb0, 0x0f, 0x44, 0x32, 0xed, 0xab, 0x37, 0x91, 0x04, 0x7d, - 0x90, 0x91, 0xb2, 0xa7, 0x53, 0xf0, 0x35, 0x64, 0x84, 0x31, 0xf6, 0xd1, 0x2f, 0x7d, 0x6a, 0x68, - 0x1e, 0x64, 0xc8, 0x61, 0xf4, 0xac, 0x91, 0x1a, 0x0f, 0x7d, 0x6e, 0xc0, 0x49, 0x1a, 0x78, 0xc9, - 0xf1, 0x92, 0xf9, 0x6b, 0x3a, 0x5e, 0x75, 0x60, 0xa3, 0xf0, 0x56, 0xbc, 0x1c, 0xa8, 0x59, 0x83, - 0x67, 0xad, 0x6a, 0xcb, 0x6f, 0x2e, 0x03, 0x4c, 0x7f, 0x37, 0xbe, 0xeb, 0x9e, 0xd4, 0x70, 0xc4, - 0x30, 0x4a, 0xf0, 0x10, 0x7f, 0x0e, 0xb9, 0x19, 0xbe, 0x36, 0xa8, 0x6f, 0x68, 0xf3, 0x7f, 0xa6, - 0x1d, 0xae, 0x7a, 0xff, 0x14, 0xde, 0xcd, 0x67, 0xec, 0x31, 0x57, 0xa1, 0x14, 0x88, 0xa1, 0x4f, - 0xed, 0x01, 0x42, 0x82, 0x83, 0x48, 0xf5, 0xf6, 0x08, 0xb0, 0xfe, 0x03, 0xe1, 0xf3, 0xc0, 0xaf, - 0x3a, 0xcc, 0xa0, 0xce, 0x36, 0x85, 0x2e, 0xd4, 0x2e, 0x22, 0x0a, 0xe9, 0xab, 0xf8, 0xf8, 0x90, - 0x6f, 0x00, 0xf1, 0xb8, 0x6b, 0xff, 0x85, 0x04, 0xc8, 0xf1, 0x6c, 0x78, 0x4f, 0xd5, 0x2d, 0x25, - 0xe0, 0x13, 0xff, 0x4f, 0xda, 0x90, 0x3e, 0x9e, 0x1e, 0xb4, 0x53, 0xc1, 0x46, 0x4b, 0x11, 0x96, - 0x6d, 0xb9, 0xb2, 0x8e, 0x8f, 0x26, 0xa3, 0xfc, 0x41, 0x9e, 0x6a, 0x60, 0xa4, 0x8d, 0x4c, 0x72, - 0x14, 0xee, 0x9c, 0x6c, 0x6a, 0x12, 0xb6, 0x8a, 0x32, 0xca, 0xc8, 0xf6, 0x15, 0x80, 0xc6, 0x4f, - 0x29, 0xcb, 0x69, 0x22, 0x40, 0x87, 0x83, 0xc6, 0xd1, 0x2e, 0x72, 0x5b, 0x01, 0x4f, 0xe4, 0x85, - 0xcd, 0x17, 0xe4, 0x84, 0xc5, 0x95, 0x2b, 0xf9, 0x9b, 0xc9, 0x49, 0x41, 0xd4, 0xb1, 0x91, 0x9d, - 0x04, 0x31, 0x7b, 0x8a, 0xa1, 0xbd, 0x37, 0x54, 0xec, 0xba, 0xa1, 0x0e, 0xc2, 0x27, 0xde, 0x85, - 0x40, 0x69, 0x5b, 0xf2, 0xfb, 0x8e, 0xe5, 0x6f, 0x6d, 0xc5, 0x26, 0xef, 0x36, 0x66, 0x25, 0xb9, - 0x1a, 0xa4, 0x97, 0x0b, 0x6f, 0xfa, 0x5c, 0x82, 0x84, 0xb9, 0xb5, 0xab, 0x85, 0x2b, 0x90, 0x5f, - 0x9d, 0x83, 0xf5, 0x66, 0x9c, 0x05, 0x35, 0xbc, 0x37, 0x7b, 0xcc, 0x05, 0xad, 0x5e, 0x48, 0xe2, - 0x81, 0xec, 0x0e, 0x19, 0x17, 0xca, 0x3c, 0x6a, 0x47, 0x1f, 0x8d, 0xa0, 0x89, 0x4b, 0xc8, 0x2a, - 0xc2, 0xa8, 0x96, 0x54, 0x05, 0xd6, 0xee, 0xf3, 0xb5, 0xe2, 0x93, 0xa8, 0x8f, 0xda, 0x20, 0x3f, - 0x09, 0xbd, 0xc7, 0x27, 0x57, 0xb1, 0x07, 0xab, 0x14, 0x88, 0x0e, 0xaa, 0x3e, 0xf7, 0x04, 0x5b, - 0x58, 0x0f, 0x48, 0x21, 0xce, 0x6d, 0xd3, 0x25, 0xb5, 0xa9, 0x06, 0x55, 0xd8, 0xc5, 0xb5, 0x5f, - 0x76, 0xfb, 0x84, 0x62, 0x79, 0xa9, 0xb5, 0x18, 0xc5, 0xe9, 0xb9, 0xa2, 0x11, 0x65, 0xc5, 0x09, - 0x3e, 0xd4, 0x9b, 0xaa, 0xac, 0xad, 0xf1, 0xf2, 0x18, 0x73, 0x26, 0x6c, 0x76, 0x7f, 0x67, 0x69, + 0xd7, 0x6b, 0x33, 0x43, 0xcf, 0x08, 0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08, 0x00, 0x00, + 0x44, 0x9e, 0xa0, 0xc9, 0x5e, 0x82, 0xff, 0xe6, 0x7b, 0x6a, 0xbc, 0xdb, 0x42, 0x98, 0xb4, 0x85, + 0xdd, 0x04, 0xde, 0x80, 0x60, 0x71, 0xbf, 0x03, 0xdc, 0xee, 0xbf, 0xa1, 0x62, 0xe7, 0x5d, 0x6c, + 0x96, 0x05, 0x8b, 0xdb, 0xfb, 0x12, 0x7c, 0xdf, 0xcb, 0xf9, 0x03, 0x38, 0x8e, 0x99, 0xad, 0x04, + 0x9f, 0x9a, 0x3d, 0xd4, 0x42, 0x5a, 0xe4, 0xd0, 0x99, 0x2c, 0xff, 0xf1, 0x8e, 0xcf, 0x0f, 0xdb, + 0x5a, 0x84, 0x2d, 0x09, 0x74, 0x70, 0x52, 0xf1, 0x7a, 0xc2, 0x05, 0x3d, 0x21, 0xf5, 0x7c, 0x5d, + 0x25, 0x0f, 0x2c, 0x4f, 0x0e, 0x02, 0x02, 0xb7, 0x07, 0x85, 0xb7, 0x94, 0x6e, 0x99, 0x2e, 0x58, + 0xa5, 0x9a, 0xc5, 0x2d, 0xea, 0x67, 0x74, 0xd4, 0xf0, 0x3b, 0x55, 0x54, 0x52, 0x43, 0xcf, 0x1a, + 0x12, 0x83, 0x4e, 0x3f, 0x24, 0x9a, 0x78, 0xd3, 0x95, 0xe0, 0xd1, 0x8f, 0x4d, 0x76, 0x60, 0x04, + 0xf1, 0xa2, 0x67, 0x48, 0x02, 0xa7, 0x47, 0xea, 0xa9, 0x01, 0xc3, 0xf1, 0x0c, 0xda, 0x55, 0x00, + 0xcb, 0x91, 0x22, 0xfa, 0xa9, 0xf1, 0xdf, 0x66, 0xc3, 0x92, 0x07, 0x9a, 0x1b, 0x40, 0xf0, 0xde, + 0x1c, 0x60, 0x54, 0x19, 0x6a, 0x11, 0xcb, 0xea, 0x40, 0xaf, 0xb6, 0xef, 0x52, 0x53, 0xcd, 0x68, + 0x18, 0xf6, 0x62, 0x5e, 0xfc, 0xe3, 0xb6, 0xde, 0xf6, 0xba, 0x7e, 0x4b, 0x37, 0xa4, 0x0f, 0x77, + 0x32, 0xe0, 0x93, 0xda, 0xa7, 0xd5, 0x21, 0x90, 0x93, 0x5b, 0x8d, 0xa5, 0x89, 0x76, 0xff, 0x33, + 0x12, 0xae, 0x50, 0xb1, 0x87, 0xc1, 0x43, 0x3c, 0x0f, 0x02, 0x8e, 0xdc, 0xc4, 0xc2, 0x83, 0x8b, + 0x6a, 0x9b, 0xfc, 0x22, 0x6c, 0xa4, 0xb4, 0x53, 0x0e, 0x7a, 0x4c, 0xce, 0xe1, 0xbf, 0xa2, 0xa3, + 0xd3, 0x96, 0xae, 0x5a, 0x3f, 0xb5, 0x12, 0x38, 0x4b, 0x2f, 0xdd, 0x85, 0x1f, 0x78, 0x4a, 0x65, + 0xe0, 0x3f, 0x2c, 0x4f, 0xbe, 0x11, 0xa5, 0x3c, 0x77, 0x77, 0xc0, 0x23, 0x46, 0x22, 0x39, 0xdd, + 0x6f, 0x75, 0x21, 0xa3, 0xf6, 0xc7, 0xd5, 0xdd, 0x3e, 0xc9, 0xb3, 0xf2, 0x33, 0x77, 0x3d, 0x4b, + 0x46, 0xd2, 0x3c, 0xc3, 0x75, 0xeb, 0x19, 0x8c, 0x63, 0x30, 0x1c, 0x21, 0x80, 0x1f, 0x65, 0x20, + 0xbc, 0xfb, 0x79, 0x66, 0xfc, 0x49, 0xb3, 0x93, 0xf0, 0x06, 0x1d, 0x97, 0x4a, 0x27, 0x06, 0xdf, + 0x8c, 0x4a, 0x94, 0x49, 0xf1, 0x1d, 0x7f, 0x3d, 0x2d, 0xcb, 0xb9, 0x0c, 0x6b, 0x87, 0x70, 0x45, + 0x63, 0x6e, 0x7c, 0x0c, 0x0f, 0xe4, 0xeb, 0x0f, 0x69, 0x75, 0x45, 0x46, 0x0c, 0x80, 0x69, 0x10, + 0xd2, 0xc3, 0x55, 0xf1, 0xd2, 0x53, 0xbc, 0x9d, 0x24, 0x52, 0xaa, 0xa5, 0x49, 0xe2, 0x7a, 0x1f, + 0xac, 0x7c, 0xf4, 0xed, 0x77, 0xf3, 0x22, 0xe8, 0xfa, 0x89, 0x4b, 0x6a, 0x83, 0x81, 0x0a, 0x34, + 0xb3, 0x61, 0x90, 0x17, 0x51, 0xa6, 0xf5, 0xeb, 0x65, 0xa0, 0x32, 0x6e, 0x07, 0xde, 0x7c, 0x12, + 0x16, 0xcc, 0xce, 0x2d, 0x01, 0x93, 0xf9, 0x58, 0xbb, 0x38, 0x50, 0xa8, 0x33, 0xf7, 0xae, 0x43, + 0x2b, 0x65, 0xbc, 0x5a, 0x53, 0x97, 0x5c, 0x15, 0x5a, 0xa4, 0xbc, 0xb4, 0xf7, 0xb2, 0xc4, 0xe5, + 0x4d, 0xf1, 0x6e, 0xfa, 0xf6, 0xdd, 0xea, 0x94, 0xe2, 0xc5, 0x0b, 0x4c, 0xd1, 0xdf, 0xe0, 0x60, + 0x17, 0xe0, 0xe9, 0xd0, 0x29, 0x00, 0xcf, 0xfe, 0x19, 0x35, 0xe0, 0x49, 0x1d, 0x77, 0xff, 0xb4, + 0xfd, 0xf8, 0x52, 0x90, 0xfd, 0xd8, 0x93, 0xd5, 0x77, 0xb1, 0x13, 0x1a, 0x61, 0x0e, 0xf6, 0xa5, + 0xc3, 0x2b, 0x2e, 0xe0, 0x29, 0x36, 0x17, 0xa3, 0x7c, 0xbb, 0x08, 0xb8, 0x47, 0x74, 0x1c, 0x3b, + 0x80, 0x17, 0xc2, 0x5c, 0xa9, 0x05, 0x2c, 0xa1, 0x07, 0x9d, 0x8b, 0x78, 0xae, 0xbd, 0x47, 0x87, + 0x6d, 0x33, 0x0a, 0x30, 0xf6, 0xa8, 0xc6, 0xd6, 0x1d, 0xd1, 0xab, 0x55, 0x89, 0x32, 0x9d, 0xe7, + 0x14, 0xd1, 0x9d, 0x61, 0x37, 0x0f, 0x81, 0x49, 0x74, 0x8c, 0x72, 0xf1, 0x32, 0xf0, 0xfc, 0x99, + 0xf3, 0x4d, 0x76, 0x6c, 0x69, 0x38, 0x59, 0x70, 0x40, 0xd8, 0xf9, 0xe2, 0xbb, 0x52, 0x2f, 0xf9, + 0x9c, 0x63, 0xa3, 0x44, 0xd6, 0xa2, 0xae, 0x8a, 0xa8, 0xe5, 0x1b, 0x7b, 0x90, 0xa4, 0xa8, 0x06, + 0x10, 0x5f, 0xcb, 0xca, 0x31, 0x50, 0x6c, 0x44, 0x61, 0x51, 0xad, 0xfe, 0xce, 0xb5, 0x1b, 0x91, + 0xab, 0xfe, 0x43, 0x96, 0x09, 0x77, 0xc8, 0x74, 0x71, 0xcf, 0x9a, 0xd4, 0x07, 0x4d, 0x30, 0xe1, + 0x0d, 0x6a, 0x7f, 0x03, 0xc6, 0x3b, 0xd5, 0xd4, 0x31, 0x7f, 0x68, 0xff, 0x32, 0x5b, 0xa3, 0xbd, + 0x80, 0xbf, 0x4d, 0xc8, 0xb5, 0x2a, 0x0b, 0xa0, 0x31, 0x75, 0x80, 0x22, 0xeb, 0x02, 0x5c, 0xdd, + 0x77, 0x0b, 0x44, 0xd6, 0xd6, 0xcf, 0x06, 0x70, 0xf4, 0xe9, 0x90, 0xb2, 0x23, 0x47, 0xa7, 0xdb, + 0x84, 0x82, 0x65, 0xe3, 0xe5, 0xeb, 0x72, 0xdf, 0xe8, 0x29, 0x9a, 0xd7, 0x48, 0x1a, 0x40, 0x83, + 0x22, 0xca, 0xc5, 0x57, 0x86, 0xe5, 0x2f, 0x63, 0x3b, 0x2f, 0xb6, 0xb6, 0x14, 0xea, 0xed, 0x18, + 0xd7, 0x03, 0xdd, 0x84, 0x04, 0x5a, 0x27, 0x4a, 0xe8, 0xbf, 0xa7, 0x33, 0x79, 0x66, 0x13, 0x88, + 0xd6, 0x99, 0x1f, 0xe3, 0x9b, 0x0d, 0x93, 0xde, 0xbb, 0x41, 0x70, 0x0b, 0x41, 0xf9, 0x0a, 0x15, + 0xc4, 0xd5, 0x26, 0x25, 0x02, 0x35, 0xdd, 0xcd, 0x67, 0x76, 0xfc, 0x77, 0xbc, 0x97, 0xe7, 0xa4, + 0x17, 0xeb, 0xcb, 0x31, 0x60, 0x0d, 0x01, 0xe5, 0x7f, 0x32, 0x16, 0x2a, 0x85, 0x60, 0xca, 0xcc, + 0x7e, 0x27, 0xa0, 0x96, 0xd3, 0x7a, 0x1a, 0x86, 0x95, 0x2e, 0xc7, 0x1b, 0xd8, 0x9a, 0x3e, 0x9a, + 0x30, 0xa2, 0xa2, 0x61, 0x62, 0x98, 0x4d, 0x77, 0x40, 0xf8, 0x11, 0x93, 0xe8, 0x23, 0x8e, 0x61, + 0xf6, 0xb5, 0xb9, 0x84, 0xd4, 0xd3, 0xdf, 0xa0, 0x33, 0xc1, 0xbb, 0x7e, 0x4f, 0x00, 0x37, 0xfe, + 0xbf, 0x40, 0x6d, 0x91, 0xc0, 0xdc, 0xcf, 0x32, 0xac, 0xf4, 0x23, 0xcf, 0xa1, 0xe7, 0x07, 0x10, + 0x10, 0xd3, 0xf2, 0x70, 0x12, 0x1b, 0x49, 0x3c, 0xe8, 0x50, 0x54, 0xef, 0x58, 0xba, 0xda, 0x42, + 0x31, 0x01, 0x38, 0xfe, 0x08, 0x1a, 0xdb, 0x04, 0xe2, 0xbd, 0x90, 0x1f, 0x2f, 0x13, 0x45, 0x8b, + 0x3d, 0x67, 0x58, 0x15, 0x81, 0x97, 0x10, 0x7c, 0x14, 0xeb, 0xb1, 0x93, 0x23, 0x0c, 0xd1, 0x15, + 0x73, 0x80, 0xaa, 0x79, 0xca, 0xe1, 0x37, 0x4a, 0x7c, 0x1e, 0x5b, 0xbc, 0xb8, 0x0e, 0xe2, 0x3e, + 0x06, 0xeb, 0xfd, 0xe2, 0x06, 0xbf, 0xb0, 0xfc, 0xbc, 0x0e, 0xdc, 0x4e, 0xbe, 0xc3, 0x09, 0x66, + 0x1b, 0xdd, 0x90, 0x8d, 0x53, 0x2e, 0xb0, 0xc6, 0xad, 0xc3, 0x8b, 0x7c, 0xa7, 0x33, 0x1d, 0xce, + 0x8d, 0xfc, 0xe3, 0x9a, 0xb7, 0x1e, 0x7c, 0x32, 0xd3, 0x18, 0xd1, 0x36, 0xb6, 0x10, 0x06, 0x71, + 0xa1, 0xae, 0x6a, 0x66, 0x00, 0xe3, 0x89, 0x9f, 0x31, 0xf0, 0xee, 0xd1, 0x9e, 0x34, 0x17, 0xd1, + 0x34, 0xb9, 0x0c, 0x90, 0x58, 0xf8, 0x63, 0x2c, 0x79, 0x8d, 0x44, 0x90, 0xda, 0x49, 0x87, 0x30, + 0x7c, 0xba, 0x92, 0x2d, 0x61, 0xc3, 0x98, 0x05, 0xd0, 0x72, 0xb5, 0x89, 0xbd, 0x52, 0xfd, 0xf1, + 0xe8, 0x62, 0x15, 0xc2, 0xd5, 0x4e, 0x66, 0x70, 0xe0, 0x73, 0x83, 0xa2, 0x7b, 0xbf, 0xfb, 0x5a, + 0xdd, 0xf4, 0x7d, 0x66, 0xaa, 0x85, 0xa0, 0xc6, 0xf9, 0xf3, 0x2e, 0x59, 0xd8, 0x5a, 0x44, 0xdd, + 0x5d, 0x3b, 0x22, 0xdc, 0x2b, 0xe8, 0x09, 0x19, 0xb4, 0x90, 0x43, 0x7a, 0xe4, 0xf3, 0x6a, 0x0a, + 0xe5, 0x5e, 0xdf, 0x1d, 0x0b, 0x5c, 0xb4, 0xe9, 0xa3, 0xec, 0xab, 0xee, 0x93, 0xdf, 0xc6, 0xe3, + 0x8d, 0x20, 0x9d, 0x0f, 0xa6, 0x53, 0x6d, 0x27, 0xa5, 0xd6, 0xfb, 0xb1, 0x76, 0x41, 0xcd, 0xe2, + 0x75, 0x25, 0xd6, 0x10, 0x93, 0xf1, 0xb2, 0x80, 0x72, 0xd1, 0x11, 0xb2, 0xb4, 0xae, 0x5f, 0x89, + 0xd5, 0x97, 0x4e, 0xe1, 0x2e, 0x5c, 0xf7, 0xd5, 0xda, 0x4d, 0x6a, 0x31, 0x12, 0x30, 0x41, 0xf3, + 0x3e, 0x61, 0x40, 0x7e, 0x76, 0xcf, 0xfc, 0xdc, 0xfd, 0x7e, 0x19, 0xba, 0x58, 0xcf, 0x4b, 0x53, + 0x6f, 0x4c, 0x49, 0x38, 0xae, 0x79, 0x32, 0x4d, 0xc4, 0x02, 0x89, 0x4b, 0x44, 0xfa, 0xf8, 0xaf, + 0xba, 0xb3, 0x52, 0x82, 0xab, 0x65, 0x9d, 0x13, 0xc9, 0x3f, 0x70, 0x41, 0x2e, 0x85, 0xcb, 0x19, + 0x9a, 0x37, 0xdd, 0xec, 0x60, 0x05, 0x45, 0x47, 0x3c, 0xfb, 0x5a, 0x05, 0xe0, 0x8d, 0x0b, 0x20, + 0x99, 0x73, 0xb2, 0x17, 0x2b, 0x4d, 0x21, 0xfb, 0x69, 0x74, 0x5a, 0x26, 0x2c, 0xcd, 0xe9, 0x6b, + 0xa1, 0x8b, 0x2f, 0xaa, 0x74, 0x5b, 0x6f, 0xe1, 0x89, 0xcf, 0x77, 0x2a, 0x9f, 0x84, 0xcb, 0xfc, ]; const INITIAL_PACKET_V1: &[u8] = &[ @@ -265,9 +263,9 @@ fn make_server(v: Version) -> Connection { fn process_client_initial(v: Version, packet: &[u8]) { let mut server = make_server(v); - let dgram = Datagram::new(addr(), addr(), packet); + let dgram = datagram(packet.to_vec()); assert_eq!(*server.state(), State::Init); - let out = server.process(Some(dgram), now()); + let out = server.process(Some(&dgram), now()); assert_eq!(*server.state(), State::Handshaking); assert!(out.dgram().is_some()); } diff --git a/third_party/rust/neqo-transport/tests/connection.rs b/third_party/rust/neqo-transport/tests/connection.rs index 6dd3d263cd0e..4cbf57f4051e 100644 --- a/third_party/rust/neqo-transport/tests/connection.rs +++ b/third_party/rust/neqo-transport/tests/connection.rs @@ -9,11 +9,13 @@ mod common; +use std::convert::TryFrom; + use common::{ apply_header_protection, decode_initial_header, initial_aead_and_hp, remove_header_protection, }; -use neqo_common::{Datagram, Decoder, Role}; -use neqo_transport::{ConnectionParameters, State, Version}; +use neqo_common::{Datagram, Decoder, Encoder, Role}; +use neqo_transport::{ConnectionError, ConnectionParameters, Error, State, Version}; use test_fixture::{self, default_client, default_server, new_client, now, split_datagram}; #[test] @@ -26,33 +28,35 @@ fn truncate_long_packet() { let mut client = default_client(); let mut server = default_server(); - let dgram = client.process(None, now()).dgram(); - assert!(dgram.is_some()); - let dgram = server.process(dgram, now()).dgram(); - assert!(dgram.is_some()); + let out = client.process(None, now()); + assert!(out.as_dgram_ref().is_some()); + let out = server.process(out.as_dgram_ref(), now()); + assert!(out.as_dgram_ref().is_some()); // This will truncate the Handshake packet from the server. - let dupe = dgram.as_ref().unwrap().clone(); + let dupe = out.as_dgram_ref().unwrap().clone(); // Count the padding in the packet, plus 1. let tail = dupe.iter().rev().take_while(|b| **b == 0).count() + 1; let truncated = Datagram::new( dupe.source(), dupe.destination(), + dupe.tos(), + dupe.ttl(), &dupe[..(dupe.len() - tail)], ); - let hs_probe = client.process(Some(truncated), now()).dgram(); + let hs_probe = client.process(Some(&truncated), now()).dgram(); assert!(hs_probe.is_some()); // Now feed in the untruncated packet. - let dgram = client.process(dgram, now()).dgram(); - assert!(dgram.is_some()); // Throw this ACK away. + let out = client.process(out.as_dgram_ref(), now()); + assert!(out.as_dgram_ref().is_some()); // Throw this ACK away. assert!(test_fixture::maybe_authenticate(&mut client)); - let dgram = client.process(None, now()).dgram(); - assert!(dgram.is_some()); + let out = client.process(None, now()); + assert!(out.as_dgram_ref().is_some()); assert!(client.state().connected()); - let dgram = server.process(dgram, now()).dgram(); - assert!(dgram.is_some()); + let out = server.process(out.as_dgram_ref(), now()); + assert!(out.as_dgram_ref().is_some()); assert!(server.state().connected()); } @@ -67,12 +71,12 @@ fn reorder_server_initial() { ); let mut server = default_server(); - let client_initial = client.process_output(now()).dgram(); + let client_initial = client.process_output(now()); let (_, client_dcid, _, _) = - decode_initial_header(client_initial.as_ref().unwrap(), Role::Client); + decode_initial_header(client_initial.as_dgram_ref().unwrap(), Role::Client); let client_dcid = client_dcid.to_owned(); - let server_packet = server.process(client_initial, now()).dgram(); + let server_packet = server.process(client_initial.as_dgram_ref(), now()).dgram(); let (server_initial, server_hs) = split_datagram(server_packet.as_ref().unwrap()); let (protected_header, _, _, payload) = decode_initial_header(&server_initial, Role::Server); @@ -107,21 +111,98 @@ fn reorder_server_initial() { let reordered = Datagram::new( server_initial.source(), server_initial.destination(), + server_initial.tos(), + server_initial.ttl(), packet, ); // Now a connection can be made successfully. // Though we modified the server's Initial packet, we get away with it. // TLS only authenticates the content of the CRYPTO frame, which was untouched. - client.process_input(reordered, now()); - client.process_input(server_hs.unwrap(), now()); + client.process_input(&reordered, now()); + client.process_input(&server_hs.unwrap(), now()); assert!(test_fixture::maybe_authenticate(&mut client)); - let finished = client.process_output(now()).dgram(); + let finished = client.process_output(now()); assert_eq!(*client.state(), State::Connected); - let done = server.process(finished, now()).dgram(); + let done = server.process(finished.as_dgram_ref(), now()); assert_eq!(*server.state(), State::Confirmed); - client.process_input(done.unwrap(), now()); + client.process_input(done.as_dgram_ref().unwrap(), now()); assert_eq!(*client.state(), State::Confirmed); } + +/// Overflow the crypto buffer. +#[test] +fn overflow_crypto() { + let mut client = new_client( + ConnectionParameters::default().versions(Version::Version1, vec![Version::Version1]), + ); + let mut server = default_server(); + + let client_initial = client.process_output(now()).dgram(); + let (_, client_dcid, _, _) = + decode_initial_header(client_initial.as_ref().unwrap(), Role::Client); + let client_dcid = client_dcid.to_owned(); + + let server_packet = server.process(client_initial.as_ref(), now()).dgram(); + let (server_initial, _) = split_datagram(server_packet.as_ref().unwrap()); + + // Now decrypt the server packet to get AEAD and HP instances. + // We won't be using the packet, but making new ones. + let (aead, hp) = initial_aead_and_hp(&client_dcid, Role::Server); + let (_, server_dcid, server_scid, _) = decode_initial_header(&server_initial, Role::Server); + + // Send in 100 packets, each with 1000 bytes of crypto frame data each, + // eventually this will overrun the buffer we keep for crypto data. + let mut payload = Encoder::with_capacity(1024); + for pn in 0..100_u64 { + payload.truncate(0); + payload + .encode_varint(0x06_u64) // CRYPTO frame type. + .encode_varint(pn * 1000 + 1) // offset + .encode_varint(1000_u64); // length + let plen = payload.len(); + payload.pad_to(plen + 1000, 44); + + let mut packet = Encoder::with_capacity(1200); + packet + .encode_byte(0xc1) // Initial with packet number length of 2. + .encode_uint(4, Version::Version1.wire_version()) + .encode_vec(1, server_dcid) + .encode_vec(1, server_scid) + .encode_vvec(&[]) // token + .encode_varint(u64::try_from(2 + payload.len() + aead.expansion()).unwrap()); // length + let pn_offset = packet.len(); + packet.encode_uint(2, pn); + + let mut packet = Vec::from(packet); + let header = packet.clone(); + packet.resize(header.len() + payload.len() + aead.expansion(), 0); + aead.encrypt(pn, &header, payload.as_ref(), &mut packet[header.len()..]) + .unwrap(); + apply_header_protection(&hp, &mut packet, pn_offset..(pn_offset + 2)); + packet.resize(1200, 0); // Initial has to be 1200 bytes! + + let dgram = Datagram::new( + server_initial.source(), + server_initial.destination(), + server_initial.tos(), + server_initial.ttl(), + packet, + ); + client.process_input(&dgram, now()); + if let State::Closing { error, .. } = client.state() { + assert!( + matches!( + error, + ConnectionError::Transport(Error::CryptoBufferExceeded), + ), + "the connection need to abort on crypto buffer" + ); + assert!(pn > 64, "at least 64000 bytes of data is buffered"); + return; + } + } + panic!("Was not able to overflow the crypto buffer"); +} diff --git a/third_party/rust/neqo-transport/tests/network.rs b/third_party/rust/neqo-transport/tests/network.rs index 3f9d2240a0d6..8c388457c566 100644 --- a/third_party/rust/neqo-transport/tests/network.rs +++ b/third_party/rust/neqo-transport/tests/network.rs @@ -9,14 +9,14 @@ mod sim; +use std::{ops::Range, time::Duration}; + use neqo_transport::{ConnectionError, ConnectionParameters, Error, State}; use sim::{ connection::{ConnectionNode, ReachState, ReceiveData, SendData}, network::{Delay, Drop, TailDrop}, Simulator, }; -use std::ops::Range; -use std::time::Duration; /// The amount of transfer. Much more than this takes a surprising amount of time. const TRANSFER_AMOUNT: usize = 1 << 20; // 1M @@ -67,7 +67,7 @@ simulate!( ))) ] ), - Delay::new(weeks(150)..weeks(150)), + Delay::new(weeks(6)..weeks(6)), Drop::percentage(10), ConnectionNode::new_server( ConnectionParameters::default().idle_timeout(weeks(1000)), @@ -78,7 +78,7 @@ simulate!( ))) ] ), - Delay::new(weeks(100)..weeks(100)), + Delay::new(weeks(8)..weeks(8)), Drop::percentage(10), ], ); diff --git a/third_party/rust/neqo-transport/tests/retry.rs b/third_party/rust/neqo-transport/tests/retry.rs index 51cc442ddd36..93759c7df9cc 100644 --- a/third_party/rust/neqo-transport/tests/retry.rs +++ b/third_party/rust/neqo-transport/tests/retry.rs @@ -10,6 +10,13 @@ mod common; +use std::{ + convert::TryFrom, + mem, + net::{IpAddr, Ipv4Addr, SocketAddr}, + time::Duration, +}; + use common::{ apply_header_protection, connected_server, decode_initial_header, default_server, generate_ticket, initial_aead_and_hp, remove_header_protection, @@ -17,11 +24,7 @@ use common::{ use neqo_common::{hex_with_len, qdebug, qtrace, Datagram, Encoder, Role}; use neqo_crypto::AuthenticationStatus; use neqo_transport::{server::ValidateAddress, ConnectionError, Error, State, StreamType}; -use std::convert::TryFrom; -use std::mem; -use std::net::{IpAddr, Ipv4Addr, SocketAddr}; -use std::time::Duration; -use test_fixture::{self, addr, assertions, default_client, now, split_datagram}; +use test_fixture::{self, assertions, datagram, default_client, now, split_datagram}; #[test] fn retry_basic() { @@ -31,21 +34,21 @@ fn retry_basic() { let dgram = client.process(None, now()).dgram(); // Initial assert!(dgram.is_some()); - let dgram = server.process(dgram, now()).dgram(); // Retry + let dgram = server.process(dgram.as_ref(), now()).dgram(); // Retry assert!(dgram.is_some()); assertions::assert_retry(dgram.as_ref().unwrap()); - let dgram = client.process(dgram, now()).dgram(); // Initial w/token + let dgram = client.process(dgram.as_ref(), now()).dgram(); // Initial w/token assert!(dgram.is_some()); - let dgram = server.process(dgram, now()).dgram(); // Initial, HS + let dgram = server.process(dgram.as_ref(), now()).dgram(); // Initial, HS assert!(dgram.is_some()); - mem::drop(client.process(dgram, now()).dgram()); // Ingest, drop any ACK. + mem::drop(client.process(dgram.as_ref(), now()).dgram()); // Ingest, drop any ACK. client.authenticated(AuthenticationStatus::Ok, now()); let dgram = client.process(None, now()).dgram(); // Send Finished assert!(dgram.is_some()); assert_eq!(*client.state(), State::Connected); - let dgram = server.process(dgram, now()).dgram(); // (done) + let dgram = server.process(dgram.as_ref(), now()).dgram(); // (done) assert!(dgram.is_some()); // Note that this packet will be dropped... connected_server(&mut server); } @@ -62,10 +65,10 @@ fn implicit_rtt_retry() { let dgram = client.process(None, now).dgram(); now += RTT / 2; - let dgram = server.process(dgram, now).dgram(); + let dgram = server.process(dgram.as_ref(), now).dgram(); assertions::assert_retry(dgram.as_ref().unwrap()); now += RTT / 2; - client.process_input(dgram.unwrap(), now); + client.process_input(&dgram.unwrap(), now); assert_eq!(client.stats().rtt, RTT); } @@ -79,16 +82,16 @@ fn retry_expired() { let dgram = client.process(None, now).dgram(); // Initial assert!(dgram.is_some()); - let dgram = server.process(dgram, now).dgram(); // Retry + let dgram = server.process(dgram.as_ref(), now).dgram(); // Retry assert!(dgram.is_some()); assertions::assert_retry(dgram.as_ref().unwrap()); - let dgram = client.process(dgram, now).dgram(); // Initial w/token + let dgram = client.process(dgram.as_ref(), now).dgram(); // Initial w/token assert!(dgram.is_some()); now += Duration::from_secs(60); // Too long for Retry. - let dgram = server.process(dgram, now).dgram(); // Initial, HS + let dgram = server.process(dgram.as_ref(), now).dgram(); // Initial, HS assert!(dgram.is_none()); } @@ -108,23 +111,23 @@ fn retry_0rtt() { let dgram = client.process(None, now()).dgram(); // Initial w/0-RTT assert!(dgram.is_some()); assertions::assert_coalesced_0rtt(dgram.as_ref().unwrap()); - let dgram = server.process(dgram, now()).dgram(); // Retry + let dgram = server.process(dgram.as_ref(), now()).dgram(); // Retry assert!(dgram.is_some()); assertions::assert_retry(dgram.as_ref().unwrap()); // After retry, there should be a token and still coalesced 0-RTT. - let dgram = client.process(dgram, now()).dgram(); + let dgram = client.process(dgram.as_ref(), now()).dgram(); assert!(dgram.is_some()); assertions::assert_coalesced_0rtt(dgram.as_ref().unwrap()); - let dgram = server.process(dgram, now()).dgram(); // Initial, HS + let dgram = server.process(dgram.as_ref(), now()).dgram(); // Initial, HS assert!(dgram.is_some()); - let dgram = client.process(dgram, now()).dgram(); + let dgram = client.process(dgram.as_ref(), now()).dgram(); // Note: the client doesn't need to authenticate the server here // as there is no certificate; authentication is based on the ticket. assert!(dgram.is_some()); assert_eq!(*client.state(), State::Connected); - let dgram = server.process(dgram, now()).dgram(); // (done) + let dgram = server.process(dgram.as_ref(), now()).dgram(); // (done) assert!(dgram.is_some()); connected_server(&mut server); assert!(client.tls_info().unwrap().resumed()); @@ -136,22 +139,28 @@ fn retry_different_ip() { server.set_validation(ValidateAddress::Always); let mut client = default_client(); - let dgram = client.process(None, now()).dgram(); // Initial + let dgram = client.process(None.as_ref(), now()).dgram(); // Initial assert!(dgram.is_some()); - let dgram = server.process(dgram, now()).dgram(); // Retry + let dgram = server.process(dgram.as_ref(), now()).dgram(); // Retry assert!(dgram.is_some()); assertions::assert_retry(dgram.as_ref().unwrap()); - let dgram = client.process(dgram, now()).dgram(); // Initial w/token + let dgram = client.process(dgram.as_ref(), now()).dgram(); // Initial w/token assert!(dgram.is_some()); // Change the source IP on the address from the client. let dgram = dgram.unwrap(); let other_v4 = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)); let other_addr = SocketAddr::new(other_v4, 443); - let from_other = Datagram::new(other_addr, dgram.destination(), &dgram[..]); - let dgram = server.process(Some(from_other), now()).dgram(); + let from_other = Datagram::new( + other_addr, + dgram.destination(), + dgram.tos(), + dgram.ttl(), + &dgram[..], + ); + let dgram = server.process(Some(&from_other), now()).dgram(); assert!(dgram.is_none()); } @@ -171,8 +180,14 @@ fn new_token_different_ip() { // Now rewrite the source address. let d = dgram.unwrap(); let src = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), d.source().port()); - let dgram = Some(Datagram::new(src, d.destination(), &d[..])); - let dgram = server.process(dgram, now()).dgram(); // Retry + let dgram = Some(Datagram::new( + src, + d.destination(), + d.tos(), + d.ttl(), + &d[..], + )); + let dgram = server.process(dgram.as_ref(), now()).dgram(); // Retry assert!(dgram.is_some()); assertions::assert_retry(dgram.as_ref().unwrap()); } @@ -196,8 +211,14 @@ fn new_token_expired() { let the_future = now() + Duration::from_secs(60 * 60 * 24 * 30); let d = dgram.unwrap(); let src = SocketAddr::new(d.source().ip(), d.source().port() + 1); - let dgram = Some(Datagram::new(src, d.destination(), &d[..])); - let dgram = server.process(dgram, the_future).dgram(); // Retry + let dgram = Some(Datagram::new( + src, + d.destination(), + d.tos(), + d.ttl(), + &d[..], + )); + let dgram = server.process(dgram.as_ref(), the_future).dgram(); // Retry assert!(dgram.is_some()); assertions::assert_retry(dgram.as_ref().unwrap()); } @@ -211,32 +232,32 @@ fn retry_after_initial() { let cinit = client.process(None, now()).dgram(); // Initial assert!(cinit.is_some()); - let server_flight = server.process(cinit.clone(), now()).dgram(); // Initial + let server_flight = server.process(cinit.as_ref(), now()).dgram(); // Initial assert!(server_flight.is_some()); // We need to have the client just process the Initial. let (server_initial, _other) = split_datagram(server_flight.as_ref().unwrap()); - let dgram = client.process(Some(server_initial), now()).dgram(); + let dgram = client.process(Some(&server_initial), now()).dgram(); assert!(dgram.is_some()); assert!(*client.state() != State::Connected); - let retry = retry_server.process(cinit, now()).dgram(); // Retry! + let retry = retry_server.process(cinit.as_ref(), now()).dgram(); // Retry! assert!(retry.is_some()); assertions::assert_retry(retry.as_ref().unwrap()); // The client should ignore the retry. - let junk = client.process(retry, now()).dgram(); + let junk = client.process(retry.as_ref(), now()).dgram(); assert!(junk.is_none()); // Either way, the client should still be able to process the server flight and connect. - let dgram = client.process(server_flight, now()).dgram(); + let dgram = client.process(server_flight.as_ref(), now()).dgram(); assert!(dgram.is_some()); // Drop this one. assert!(test_fixture::maybe_authenticate(&mut client)); let dgram = client.process(None, now()).dgram(); assert!(dgram.is_some()); assert_eq!(*client.state(), State::Connected); - let dgram = server.process(dgram, now()).dgram(); // (done) + let dgram = server.process(dgram.as_ref(), now()).dgram(); // (done) assert!(dgram.is_some()); connected_server(&mut server); } @@ -249,7 +270,7 @@ fn retry_bad_integrity() { let dgram = client.process(None, now()).dgram(); // Initial assert!(dgram.is_some()); - let dgram = server.process(dgram, now()).dgram(); // Retry + let dgram = server.process(dgram.as_ref(), now()).dgram(); // Retry assert!(dgram.is_some()); let retry = &dgram.as_ref().unwrap(); @@ -257,10 +278,16 @@ fn retry_bad_integrity() { let mut tweaked = retry.to_vec(); tweaked[retry.len() - 1] ^= 0x45; // damage the auth tag - let tweaked_packet = Datagram::new(retry.source(), retry.destination(), tweaked); + let tweaked_packet = Datagram::new( + retry.source(), + retry.destination(), + retry.tos(), + retry.ttl(), + tweaked, + ); // The client should ignore this packet. - let dgram = client.process(Some(tweaked_packet), now()).dgram(); + let dgram = client.process(Some(&tweaked_packet), now()).dgram(); assert!(dgram.is_none()); } @@ -274,12 +301,14 @@ fn retry_bad_token() { // Send a retry to one server, then replay it to the other. let client_initial1 = client.process(None, now()).dgram(); assert!(client_initial1.is_some()); - let retry = retry_server.process(client_initial1, now()).dgram(); + let retry = retry_server + .process(client_initial1.as_ref(), now()) + .dgram(); assert!(retry.is_some()); - let client_initial2 = client.process(retry, now()).dgram(); + let client_initial2 = client.process(retry.as_ref(), now()).dgram(); assert!(client_initial2.is_some()); - let dgram = server.process(client_initial2, now()).dgram(); + let dgram = server.process(client_initial2.as_ref(), now()).dgram(); assert!(dgram.is_none()); } @@ -300,17 +329,15 @@ fn retry_after_pto() { // Let PTO fire on the client and then let it exhaust its PTO packets. now += Duration::from_secs(1); - let pto1 = client.process(None, now).dgram(); - assert!(pto1.unwrap().len() >= 1200); - let pto2 = client.process(None, now).dgram(); - assert!(pto2.unwrap().len() >= 1200); + let pto = client.process(None, now).dgram(); + assert!(pto.unwrap().len() >= 1200); let cb = client.process(None, now).callback(); assert_ne!(cb, Duration::new(0, 0)); - let retry = server.process(ci, now).dgram(); + let retry = server.process(ci.as_ref(), now).dgram(); assertions::assert_retry(retry.as_ref().unwrap()); - let ci2 = client.process(retry, now).dgram(); + let ci2 = client.process(retry.as_ref(), now).dgram(); assert!(ci2.unwrap().len() >= 1200); } @@ -322,12 +349,12 @@ fn vn_after_retry() { let dgram = client.process(None, now()).dgram(); // Initial assert!(dgram.is_some()); - let dgram = server.process(dgram, now()).dgram(); // Retry + let dgram = server.process(dgram.as_ref(), now()).dgram(); // Retry assert!(dgram.is_some()); assertions::assert_retry(dgram.as_ref().unwrap()); - let dgram = client.process(dgram, now()).dgram(); // Initial w/token + let dgram = client.process(dgram.as_ref(), now()).dgram(); // Initial w/token assert!(dgram.is_some()); let mut encoder = Encoder::default(); @@ -336,10 +363,10 @@ fn vn_after_retry() { encoder.encode_vec(1, &client.odcid().unwrap()[..]); encoder.encode_vec(1, &[]); encoder.encode_uint(4, 0x5a5a_6a6a_u64); - let vn = Datagram::new(addr(), addr(), encoder); + let vn = datagram(encoder.into()); assert_ne!( - client.process(Some(vn), now()).callback(), + client.process(Some(&vn), now()).callback(), Duration::from_secs(0) ); } @@ -365,9 +392,11 @@ fn mitm_retry() { // Trigger initial and a second client Initial. let client_initial1 = client.process(None, now()).dgram(); assert!(client_initial1.is_some()); - let retry = retry_server.process(client_initial1, now()).dgram(); + let retry = retry_server + .process(client_initial1.as_ref(), now()) + .dgram(); assert!(retry.is_some()); - let client_initial2 = client.process(retry, now()).dgram(); + let client_initial2 = client.process(retry.as_ref(), now()).dgram(); assert!(client_initial2.is_some()); // Now to start the epic process of decrypting the packet, @@ -421,18 +450,20 @@ fn mitm_retry() { let new_datagram = Datagram::new( client_initial2.source(), client_initial2.destination(), + client_initial2.tos(), + client_initial2.ttl(), notoken_packet, ); qdebug!("passing modified Initial to the main server"); - let dgram = server.process(Some(new_datagram), now()).dgram(); + let dgram = server.process(Some(&new_datagram), now()).dgram(); assert!(dgram.is_some()); - let dgram = client.process(dgram, now()).dgram(); // Generate an ACK. + let dgram = client.process(dgram.as_ref(), now()).dgram(); // Generate an ACK. assert!(dgram.is_some()); - let dgram = server.process(dgram, now()).dgram(); + let dgram = server.process(dgram.as_ref(), now()).dgram(); assert!(dgram.is_none()); assert!(test_fixture::maybe_authenticate(&mut client)); - let dgram = client.process(dgram, now()).dgram(); + let dgram = client.process(dgram.as_ref(), now()).dgram(); assert!(dgram.is_some()); // Client sending CLOSE_CONNECTIONs assert!(matches!( *client.state(), diff --git a/third_party/rust/neqo-transport/tests/server.rs b/third_party/rust/neqo-transport/tests/server.rs index fe03b2df1d50..d6c9c2df95fc 100644 --- a/third_party/rust/neqo-transport/tests/server.rs +++ b/third_party/rust/neqo-transport/tests/server.rs @@ -9,11 +9,12 @@ mod common; +use std::{cell::RefCell, convert::TryFrom, mem, net::SocketAddr, rc::Rc, time::Duration}; + use common::{ apply_header_protection, connect, connected_server, decode_initial_header, default_server, find_ticket, generate_ticket, initial_aead_and_hp, new_server, remove_header_protection, }; - use neqo_common::{qtrace, Datagram, Decoder, Encoder, Role}; use neqo_crypto::{ generate_ech_keys, AllowZeroRtt, AuthenticationStatus, ZeroRttCheckResult, ZeroRttChecker, @@ -23,16 +24,16 @@ use neqo_transport::{ Connection, ConnectionError, ConnectionParameters, Error, Output, State, StreamType, Version, }; use test_fixture::{ - self, assertions, default_client, new_client, now, split_datagram, + self, assertions, datagram, default_client, new_client, now, split_datagram, CountingConnectionIdGenerator, }; -use std::{cell::RefCell, convert::TryFrom, mem, net::SocketAddr, rc::Rc, time::Duration}; - /// Take a pair of connections in any state and complete the handshake. /// The `datagram` argument is a packet that was received from the server. /// See `connect` for what this returns. +/// /// # Panics +/// /// Only when the connection fails. pub fn complete_connection( client: &mut Connection, @@ -47,8 +48,8 @@ pub fn complete_connection( }; while !is_done(client) { _ = test_fixture::maybe_authenticate(client); - let out = client.process(datagram, now()); - let out = server.process(out.dgram(), now()); + let out = client.process(datagram.as_ref(), now()); + let out = server.process(out.as_dgram_ref(), now()); datagram = out.dgram(); } @@ -109,11 +110,11 @@ fn connect_single_version_server() { if client.version() != version { // Run the version negotiation exchange if necessary. - let dgram = client.process_output(now()).dgram(); - assert!(dgram.is_some()); - let dgram = server.process(dgram, now()).dgram(); + let out = client.process_output(now()); + assert!(out.as_dgram_ref().is_some()); + let dgram = server.process(out.as_dgram_ref(), now()).dgram(); assertions::assert_vn(dgram.as_ref().unwrap()); - client.process_input(dgram.unwrap(), now()); + client.process_input(&dgram.unwrap(), now()); } let server_conn = connect(&mut client, &mut server); @@ -133,14 +134,14 @@ fn duplicate_initial() { let mut client = default_client(); assert_eq!(*client.state(), State::Init); - let initial = client.process(None, now()).dgram(); - assert!(initial.is_some()); + let initial = client.process(None, now()); + assert!(initial.as_dgram_ref().is_some()); // The server should ignore a packets with the same remote address and // destination connection ID as an existing connection attempt. - let server_initial = server.process(initial.clone(), now()).dgram(); + let server_initial = server.process(initial.as_dgram_ref(), now()).dgram(); assert!(server_initial.is_some()); - let dgram = server.process(initial, now()).dgram(); + let dgram = server.process(initial.as_dgram_ref(), now()).dgram(); assert!(dgram.is_none()); assert_eq!(server.active_connections().len(), 1); @@ -157,14 +158,16 @@ fn duplicate_initial_new_path() { let other = Datagram::new( SocketAddr::new(initial.source().ip(), initial.source().port() ^ 23), initial.destination(), + initial.tos(), + initial.ttl(), &initial[..], ); // The server should respond to both as these came from different addresses. - let dgram = server.process(Some(other), now()).dgram(); + let dgram = server.process(Some(&other), now()).dgram(); assert!(dgram.is_some()); - let server_initial = server.process(Some(initial), now()).dgram(); + let server_initial = server.process(Some(&initial), now()).dgram(); assert!(server_initial.is_some()); assert_eq!(server.active_connections().len(), 2); @@ -177,16 +180,20 @@ fn different_initials_same_path() { let mut client1 = default_client(); let mut client2 = default_client(); - let client_initial1 = client1.process(None, now()).dgram(); - assert!(client_initial1.is_some()); - let client_initial2 = client2.process(None, now()).dgram(); - assert!(client_initial2.is_some()); + let client_initial1 = client1.process(None, now()); + assert!(client_initial1.as_dgram_ref().is_some()); + let client_initial2 = client2.process(None, now()); + assert!(client_initial2.as_dgram_ref().is_some()); // The server should respond to both as these came from different addresses. - let server_initial1 = server.process(client_initial1, now()).dgram(); + let server_initial1 = server + .process(client_initial1.as_dgram_ref(), now()) + .dgram(); assert!(server_initial1.is_some()); - let server_initial2 = server.process(client_initial2, now()).dgram(); + let server_initial2 = server + .process(client_initial2.as_dgram_ref(), now()) + .dgram(); assert!(server_initial2.is_some()); assert_eq!(server.active_connections().len(), 2); @@ -199,10 +206,10 @@ fn same_initial_after_connected() { let mut server = default_server(); let mut client = default_client(); - let client_initial = client.process(None, now()).dgram(); - assert!(client_initial.is_some()); + let client_initial = client.process(None, now()); + assert!(client_initial.as_dgram_ref().is_some()); - let server_initial = server.process(client_initial.clone(), now()).dgram(); + let server_initial = server.process(client_initial.as_dgram_ref(), now()).dgram(); assert!(server_initial.is_some()); complete_connection(&mut client, &mut server, server_initial); // This removes the connection from the active set until something happens to it. @@ -210,7 +217,7 @@ fn same_initial_after_connected() { // Now make a new connection using the exact same initial as before. // The server should respond to an attempt to connect with the same Initial. - let dgram = server.process(client_initial, now()).dgram(); + let dgram = server.process(client_initial.as_dgram_ref(), now()).dgram(); assert!(dgram.is_some()); // The server should make a new connection object. assert_eq!(server.active_connections().len(), 1); @@ -231,8 +238,8 @@ fn drop_non_initial() { let mut bogus_data: Vec = header.into(); bogus_data.resize(1200, 66); - let bogus = Datagram::new(test_fixture::addr(), test_fixture::addr(), bogus_data); - assert!(server.process(Some(bogus), now()).dgram().is_none()); + let bogus = datagram(bogus_data); + assert!(server.process(Some(&bogus), now()).dgram().is_none()); } #[test] @@ -250,8 +257,8 @@ fn drop_short_initial() { let mut bogus_data: Vec = header.into(); bogus_data.resize(1199, 66); - let bogus = Datagram::new(test_fixture::addr(), test_fixture::addr(), bogus_data); - assert!(server.process(Some(bogus), now()).dgram().is_none()); + let bogus = datagram(bogus_data); + assert!(server.process(Some(&bogus), now()).dgram().is_none()); } /// Verify that the server can read 0-RTT properly. A more robust server would buffer @@ -296,12 +303,12 @@ fn zero_rtt() { let c4 = client_send(); // 0-RTT packets that arrive before the handshake get dropped. - mem::drop(server.process(Some(c2), now)); + mem::drop(server.process(Some(&c2), now)); assert!(server.active_connections().is_empty()); // Now handshake and let another 0-RTT packet in. - let shs = server.process(Some(c1), now).dgram(); - mem::drop(server.process(Some(c3), now)); + let shs = server.process(Some(&c1), now); + mem::drop(server.process(Some(&c3), now)); // The server will have received two STREAM frames now if it processed both packets. let active = server.active_connections(); assert_eq!(active.len(), 1); @@ -310,11 +317,11 @@ fn zero_rtt() { // Complete the handshake. As the client was pacing 0-RTT packets, extend the time // a little so that the pacer doesn't prevent the Finished from being sent. now += now - start_time; - let cfin = client.process(shs, now).dgram(); - mem::drop(server.process(cfin, now)); + let cfin = client.process(shs.as_dgram_ref(), now); + mem::drop(server.process(cfin.as_dgram_ref(), now)); // The server will drop this last 0-RTT packet. - mem::drop(server.process(Some(c4), now)); + mem::drop(server.process(Some(&c4), now)); let active = server.active_connections(); assert_eq!(active.len(), 1); assert_eq!(active[0].borrow().stats().frame_rx.stream, 2); @@ -332,21 +339,21 @@ fn new_token_0rtt() { let client_stream = client.stream_create(StreamType::UniDi).unwrap(); client.stream_send(client_stream, &[1, 2, 3]).unwrap(); - let dgram = client.process(None, now()).dgram(); // Initial w/0-RTT - assert!(dgram.is_some()); - assertions::assert_initial(dgram.as_ref().unwrap(), true); - assertions::assert_coalesced_0rtt(dgram.as_ref().unwrap()); - let dgram = server.process(dgram, now()).dgram(); // Initial - assert!(dgram.is_some()); - assertions::assert_initial(dgram.as_ref().unwrap(), false); + let out = client.process(None, now()); // Initial w/0-RTT + assert!(out.as_dgram_ref().is_some()); + assertions::assert_initial(out.as_dgram_ref().unwrap(), true); + assertions::assert_coalesced_0rtt(out.as_dgram_ref().unwrap()); + let out = server.process(out.as_dgram_ref(), now()); // Initial + assert!(out.as_dgram_ref().is_some()); + assertions::assert_initial(out.as_dgram_ref().unwrap(), false); - let dgram = client.process(dgram, now()).dgram(); + let dgram = client.process(out.as_dgram_ref(), now()); // Note: the client doesn't need to authenticate the server here // as there is no certificate; authentication is based on the ticket. - assert!(dgram.is_some()); + assert!(out.as_dgram_ref().is_some()); assert_eq!(*client.state(), State::Connected); - let dgram = server.process(dgram, now()).dgram(); // (done) - assert!(dgram.is_some()); + let dgram = server.process(dgram.as_dgram_ref(), now()); // (done) + assert!(dgram.as_dgram_ref().is_some()); connected_server(&mut server); assert!(client.tls_info().unwrap().resumed()); } @@ -367,8 +374,14 @@ fn new_token_different_port() { // Now rewrite the source port, which should not change that the token is OK. let d = dgram.unwrap(); let src = SocketAddr::new(d.source().ip(), d.source().port() + 1); - let dgram = Some(Datagram::new(src, d.destination(), &d[..])); - let dgram = server.process(dgram, now()).dgram(); // Retry + let dgram = Some(Datagram::new( + src, + d.destination(), + d.tos(), + d.ttl(), + &d[..], + )); + let dgram = server.process(dgram.as_ref(), now()).dgram(); // Retry assert!(dgram.is_some()); assertions::assert_initial(dgram.as_ref().unwrap(), false); } @@ -422,10 +435,16 @@ fn bad_client_initial() { &mut ciphertext, (header_enc.len() - 1)..header_enc.len(), ); - let bad_dgram = Datagram::new(dgram.source(), dgram.destination(), ciphertext); + let bad_dgram = Datagram::new( + dgram.source(), + dgram.destination(), + dgram.tos(), + dgram.ttl(), + ciphertext, + ); // The server should reject this. - let response = server.process(Some(bad_dgram), now()); + let response = server.process(Some(&bad_dgram), now()); let close_dgram = response.dgram().unwrap(); // The resulting datagram might contain multiple packets, but each is small. let (initial_close, rest) = split_datagram(&close_dgram); @@ -439,7 +458,7 @@ fn bad_client_initial() { // The client should accept this new and stop trying to connect. // It will generate a CONNECTION_CLOSE first though. - let response = client.process(Some(close_dgram), now()).dgram(); + let response = client.process(Some(&close_dgram), now()).dgram(); assert!(response.is_some()); // The client will now wait out its closing period. let delay = client.process(None, now()).callback(); @@ -470,8 +489,14 @@ fn version_negotiation_ignored() { let dgram = client.process(None, now()).dgram().expect("a datagram"); let mut input = dgram.to_vec(); input[1] ^= 0x12; - let damaged = Datagram::new(dgram.source(), dgram.destination(), input.clone()); - let vn = server.process(Some(damaged), now()).dgram(); + let damaged = Datagram::new( + dgram.source(), + dgram.destination(), + dgram.tos(), + dgram.ttl(), + input.clone(), + ); + let vn = server.process(Some(&damaged), now()).dgram(); let mut dec = Decoder::from(&input[5..]); // Skip past version. let d_cid = dec.decode_vec(1).expect("client DCID").to_vec(); @@ -492,7 +517,7 @@ fn version_negotiation_ignored() { assert!(found, "valid version not found"); // Client ignores VN packet that contain negotiated version. - let res = client.process(Some(vn), now()); + let res = client.process(Some(&vn), now()); assert!(res.callback() > Duration::new(0, 120)); assert_eq!(client.state(), &State::WaitInitial); } @@ -512,9 +537,9 @@ fn version_negotiation() { // `connect()` runs a fixed exchange, so manually run the Version Negotiation. let dgram = client.process_output(now()).dgram(); assert!(dgram.is_some()); - let dgram = server.process(dgram, now()).dgram(); + let dgram = server.process(dgram.as_ref(), now()).dgram(); assertions::assert_vn(dgram.as_ref().unwrap()); - client.process_input(dgram.unwrap(), now()); + client.process_input(&dgram.unwrap(), now()); let sconn = connect(&mut client, &mut server); assert_eq!(client.version(), VN_VERSION); @@ -548,22 +573,22 @@ fn version_negotiation_and_compatible() { let dgram = client.process_output(now()).dgram(); assert!(dgram.is_some()); assertions::assert_version(dgram.as_ref().unwrap(), ORIG_VERSION.wire_version()); - let dgram = server.process(dgram, now()).dgram(); + let dgram = server.process(dgram.as_ref(), now()).dgram(); assertions::assert_vn(dgram.as_ref().unwrap()); - client.process_input(dgram.unwrap(), now()); + client.process_input(&dgram.unwrap(), now()); let dgram = client.process(None, now()).dgram(); // ClientHello assertions::assert_version(dgram.as_ref().unwrap(), VN_VERSION.wire_version()); - let dgram = server.process(dgram, now()).dgram(); // ServerHello... + let dgram = server.process(dgram.as_ref(), now()).dgram(); // ServerHello... assertions::assert_version(dgram.as_ref().unwrap(), COMPAT_VERSION.wire_version()); - client.process_input(dgram.unwrap(), now()); + client.process_input(&dgram.unwrap(), now()); client.authenticated(AuthenticationStatus::Ok, now()); let dgram = client.process_output(now()).dgram(); assertions::assert_version(dgram.as_ref().unwrap(), COMPAT_VERSION.wire_version()); assert_eq!(*client.state(), State::Connected); - let dgram = server.process(dgram, now()).dgram(); // ACK + HANDSHAKE_DONE + NST - client.process_input(dgram.unwrap(), now()); + let dgram = server.process(dgram.as_ref(), now()).dgram(); // ACK + HANDSHAKE_DONE + NST + client.process_input(&dgram.unwrap(), now()); assert_eq!(*client.state(), State::Confirmed); let sconn = connected_server(&mut server); @@ -596,7 +621,7 @@ fn compatible_upgrade_resumption_and_vn() { server_conn.borrow_mut().send_ticket(now(), &[]).unwrap(); let dgram = server.process(None, now()).dgram(); - client.process_input(dgram.unwrap(), now()); // Consume ticket, ignore output. + client.process_input(&dgram.unwrap(), now()); // Consume ticket, ignore output. let ticket = find_ticket(&mut client); // This new server will reject the ticket, but it will also generate a VN packet. @@ -610,9 +635,9 @@ fn compatible_upgrade_resumption_and_vn() { let dgram = client.process_output(now()).dgram(); assert!(dgram.is_some()); assertions::assert_version(dgram.as_ref().unwrap(), COMPAT_VERSION.wire_version()); - let dgram = server.process(dgram, now()).dgram(); + let dgram = server.process(dgram.as_ref(), now()).dgram(); assertions::assert_vn(dgram.as_ref().unwrap()); - client.process_input(dgram.unwrap(), now()); + client.process_input(&dgram.unwrap(), now()); let server_conn = connect(&mut client, &mut server); assert_eq!(client.version(), RESUMPTION_VERSION); @@ -722,8 +747,8 @@ fn max_streams_after_0rtt_rejection() { client.enable_resumption(now(), &token).unwrap(); _ = client.stream_create(StreamType::BiDi).unwrap(); let dgram = client.process_output(now()).dgram(); - let dgram = server.process(dgram, now()).dgram(); - let dgram = client.process(dgram, now()).dgram(); + let dgram = server.process(dgram.as_ref(), now()).dgram(); + let dgram = client.process(dgram.as_ref(), now()).dgram(); assert!(dgram.is_some()); // We're far enough along to complete the test now. // Make sure that we can create MAX_STREAMS uni- and bidirectional streams. diff --git a/third_party/rust/neqo-transport/tests/sim/connection.rs b/third_party/rust/neqo-transport/tests/sim/connection.rs index 5768941e4ae8..45a5234512d3 100644 --- a/third_party/rust/neqo-transport/tests/sim/connection.rs +++ b/third_party/rust/neqo-transport/tests/sim/connection.rs @@ -6,18 +6,20 @@ #![allow(clippy::module_name_repetitions)] -use super::{Node, Rng}; -use neqo_common::{event::Provider, qdebug, qtrace, Datagram}; -use neqo_crypto::AuthenticationStatus; -use neqo_transport::{ - Connection, ConnectionEvent, ConnectionParameters, Output, State, StreamId, StreamType, -}; use std::{ cmp::min, fmt::{self, Debug}, time::Instant, }; +use neqo_common::{event::Provider, qdebug, qtrace, Datagram}; +use neqo_crypto::AuthenticationStatus; +use neqo_transport::{ + Connection, ConnectionEvent, ConnectionParameters, Output, State, StreamId, StreamType, +}; + +use super::{Node, Rng}; + /// The status of the processing of an event. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum GoalStatus { @@ -120,7 +122,7 @@ impl Node for ConnectionNode { fn process(&mut self, mut d: Option, now: Instant) -> Output { _ = self.process_goals(|goal, c| goal.process(c, now)); loop { - let res = self.c.process(d.take(), now); + let res = self.c.process(d.take().as_ref(), now); let mut active = false; while let Some(e) = self.c.next_event() { diff --git a/third_party/rust/neqo-transport/tests/sim/delay.rs b/third_party/rust/neqo-transport/tests/sim/delay.rs index 95188c0562a5..34cb923084c2 100644 --- a/third_party/rust/neqo-transport/tests/sim/delay.rs +++ b/third_party/rust/neqo-transport/tests/sim/delay.rs @@ -6,14 +6,18 @@ #![allow(clippy::module_name_repetitions)] -use super::{Node, Rng}; +use std::{ + collections::BTreeMap, + convert::TryFrom, + fmt::{self, Debug}, + ops::Range, + time::{Duration, Instant}, +}; + use neqo_common::Datagram; use neqo_transport::Output; -use std::collections::BTreeMap; -use std::convert::TryFrom; -use std::fmt::{self, Debug}; -use std::ops::Range; -use std::time::{Duration, Instant}; + +use super::{Node, Rng}; /// An iterator that shares a `Random` instance and produces uniformly /// random `Duration`s within a specified range. diff --git a/third_party/rust/neqo-transport/tests/sim/drop.rs b/third_party/rust/neqo-transport/tests/sim/drop.rs index d42913d99d62..629fbf48d362 100644 --- a/third_party/rust/neqo-transport/tests/sim/drop.rs +++ b/third_party/rust/neqo-transport/tests/sim/drop.rs @@ -6,11 +6,15 @@ #![allow(clippy::module_name_repetitions)] -use super::{Node, Rng}; +use std::{ + fmt::{self, Debug}, + time::Instant, +}; + use neqo_common::{qtrace, Datagram}; use neqo_transport::Output; -use std::fmt::{self, Debug}; -use std::time::Instant; + +use super::{Node, Rng}; /// A random dropper. pub struct Drop { diff --git a/third_party/rust/neqo-transport/tests/sim/mod.rs b/third_party/rust/neqo-transport/tests/sim/mod.rs index f7646aac5646..9ab9d57a4a4a 100644 --- a/third_party/rust/neqo-transport/tests/sim/mod.rs +++ b/third_party/rust/neqo-transport/tests/sim/mod.rs @@ -14,23 +14,23 @@ mod drop; pub mod rng; mod taildrop; +use std::{ + cell::RefCell, + cmp::min, + convert::TryFrom, + fmt::Debug, + rc::Rc, + time::{Duration, Instant}, +}; + use neqo_common::{qdebug, qinfo, qtrace, Datagram, Encoder}; use neqo_transport::Output; use rng::Random; -use std::cell::RefCell; -use std::cmp::min; -use std::convert::TryFrom; -use std::fmt::Debug; -use std::rc::Rc; -use std::time::{Duration, Instant}; use test_fixture::{self, now}; - use NodeState::{Active, Idle, Waiting}; pub mod network { - pub use super::delay::Delay; - pub use super::drop::Drop; - pub use super::taildrop::TailDrop; + pub use super::{delay::Delay, drop::Drop, taildrop::TailDrop}; } type Rng = Rc>; diff --git a/third_party/rust/neqo-transport/tests/sim/rng.rs b/third_party/rust/neqo-transport/tests/sim/rng.rs index d314e8b36fee..af4f70eb5f2b 100644 --- a/third_party/rust/neqo-transport/tests/sim/rng.rs +++ b/third_party/rust/neqo-transport/tests/sim/rng.rs @@ -4,9 +4,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::{convert::TryFrom, ops::Range}; + use neqo_common::Decoder; -use std::convert::TryFrom; -use std::ops::Range; /// An implementation of a xoshiro256** pseudorandom generator. pub struct Random { diff --git a/third_party/rust/neqo-transport/tests/sim/taildrop.rs b/third_party/rust/neqo-transport/tests/sim/taildrop.rs index 7346b2717827..26813800c9e5 100644 --- a/third_party/rust/neqo-transport/tests/sim/taildrop.rs +++ b/third_party/rust/neqo-transport/tests/sim/taildrop.rs @@ -6,14 +6,18 @@ #![allow(clippy::module_name_repetitions)] -use super::Node; +use std::{ + cmp::max, + collections::VecDeque, + convert::TryFrom, + fmt::{self, Debug}, + time::{Duration, Instant}, +}; + use neqo_common::{qtrace, Datagram}; use neqo_transport::Output; -use std::cmp::max; -use std::collections::VecDeque; -use std::convert::TryFrom; -use std::fmt::{self, Debug}; -use std::time::{Duration, Instant}; + +use super::Node; /// One second in nanoseconds. const ONE_SECOND_NS: u128 = 1_000_000_000; diff --git a/third_party/rust/qlog/.cargo-checksum.json b/third_party/rust/qlog/.cargo-checksum.json index 8a63fcb8ad65..17ad1f89784c 100644 --- a/third_party/rust/qlog/.cargo-checksum.json +++ b/third_party/rust/qlog/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"386d587126e071397a00f6264b97407048adb1a7a1f3c65197ba4c6d25f572af","README.md":"597691eb766c2cbd7a6591bda56d3e70e6836b62b6327fb73497523eabd5b53d","src/events/connectivity.rs":"fcf92e5f0ef5e1bc54df4d4da896b544e6c351a31b238af46ec51bf893ef9df1","src/events/h3.rs":"45dfa1dea722f3c8adb989f04ff24e8c39550a65a35325885b3a915cafd3a550","src/events/mod.rs":"1954a0dabc30b820f76718d0e34c6cb1201e6d30defa5736a05590fb9e6f69e6","src/events/qpack.rs":"5c7267c45e3fb947cdfa946f9f9692d3e3e36a166f70124ba293dc27534267d0","src/events/quic.rs":"861a784bb9572ec45ff9013c361bc164336758cce3af45a012180e294f56d9f9","src/events/security.rs":"e9852d7de16851b62c3e0a886a2c1a31d237e62574ef88428ef62dd179b0b008","src/lib.rs":"b9f60aa28f95d51cd6138d5c97d05747ffa16d88d1ce53a4212fd61e94bcf553","src/streamer.rs":"050fca0430011a5427bf109a22e7c73d5a5b02b2e7a4000f2ae00d0bbe35fd53"},"package":"321df7a3199d152be256a416096136191e88b7716f1e2e4c8c05b9f77ffb648b"} \ No newline at end of file +{"files":{"Cargo.toml":"15c2606defff66515f2ded1ace2aeb729229a3558d9a026c058e51a7518e6859","README.md":"597691eb766c2cbd7a6591bda56d3e70e6836b62b6327fb73497523eabd5b53d","src/events/connectivity.rs":"116993412e200e375c97762980ffb638d2244197fd752b9569b5b20baf574308","src/events/h3.rs":"45dfa1dea722f3c8adb989f04ff24e8c39550a65a35325885b3a915cafd3a550","src/events/mod.rs":"75f57b4717fa9777e19d61b99b6a79164f0e8bca9b4681c3ab11b204320c8c55","src/events/qpack.rs":"5c7267c45e3fb947cdfa946f9f9692d3e3e36a166f70124ba293dc27534267d0","src/events/quic.rs":"88b884f5788c671ffee79a3448f367c18f95ee30531262fcc14310d80e662f4a","src/events/security.rs":"e9852d7de16851b62c3e0a886a2c1a31d237e62574ef88428ef62dd179b0b008","src/lib.rs":"bbc190a6d0f484fd723f9df6c1b2a4596f826e0282ad40ee17a0822ea28a5626","src/reader.rs":"4e0069c24aca9cb99d75075c9b784fa02855ea449d2f1528bea944a4e02a9af5","src/streamer.rs":"4774c2abde1a5b0f4448aac06c62c7927208c12f338c46981f80c98703b54074"},"package":null} \ No newline at end of file diff --git a/third_party/rust/qlog/Cargo.toml b/third_party/rust/qlog/Cargo.toml index 172ef5b46243..665559312f4a 100644 --- a/third_party/rust/qlog/Cargo.toml +++ b/third_party/rust/qlog/Cargo.toml @@ -12,7 +12,7 @@ [package] edition = "2018" name = "qlog" -version = "0.9.0" +version = "0.11.0" authors = ["Lucas Pardue "] description = "qlog data model for QUIC and HTTP/3" readme = "README.md" @@ -25,19 +25,21 @@ categories = ["network-programming"] license = "BSD-2-Clause" repository = "https://github.com/cloudflare/quiche" -[dependencies.serde] -version = "1" -features = ["derive"] +[dependencies] +serde_derive = "1.0" -[dependencies.serde_derive] -version = "1.0" +[dependencies.serde] +version = "1.0.139" +features = ["derive"] [dependencies.serde_json] version = "1.0" features = ["preserve_order"] [dependencies.serde_with] -version = "1.3.1" +version = "3.0.0" +features = ["macros"] +default-features = false [dependencies.smallvec] version = "1.10" diff --git a/third_party/rust/qlog/src/events/connectivity.rs b/third_party/rust/qlog/src/events/connectivity.rs index 19a442cc7210..1b579232b102 100644 --- a/third_party/rust/qlog/src/events/connectivity.rs +++ b/third_party/rust/qlog/src/events/connectivity.rs @@ -61,6 +61,7 @@ pub enum ConnectivityEventType { ConnectionIdUpdated, SpinBitUpdated, ConnectionStateUpdated, + MtuUpdated, } #[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Debug)] @@ -136,3 +137,11 @@ pub struct ConnectionStateUpdated { pub old: Option, pub new: ConnectionState, } + +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)] +pub struct MtuUpdated { + pub old: Option, + pub new: u16, + pub done: Option, +} diff --git a/third_party/rust/qlog/src/events/mod.rs b/third_party/rust/qlog/src/events/mod.rs index 026869c0fd2d..ac18276fd0c0 100644 --- a/third_party/rust/qlog/src/events/mod.rs +++ b/third_party/rust/qlog/src/events/mod.rs @@ -35,6 +35,10 @@ use connectivity::ConnectivityEventType; use serde::Deserialize; use serde::Serialize; +use std::collections::BTreeMap; + +pub type ExData = BTreeMap; + #[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Debug, Default)] #[serde(untagged)] pub enum EventType { @@ -87,6 +91,9 @@ pub struct Event { #[serde(flatten)] pub data: EventData, + #[serde(flatten)] + pub ex_data: ExData, + pub protocol_type: Option, pub group_id: Option, @@ -99,20 +106,32 @@ pub struct Event { impl Event { /// Returns a new `Event` object with the provided time and data. pub fn with_time(time: f32, data: EventData) -> Self { + Self::with_time_ex(time, data, Default::default()) + } + + /// Returns a new `Event` object with the provided time, data and ex_data. + pub fn with_time_ex(time: f32, data: EventData, ex_data: ExData) -> Self { let ty = EventType::from(&data); Event { time, data, + ex_data, protocol_type: Default::default(), group_id: Default::default(), time_format: Default::default(), ty, } } +} - pub fn importance(&self) -> EventImportance { +impl Eventable for Event { + fn importance(&self) -> EventImportance { self.ty.into() } + + fn set_time(&mut self, time: f32) { + self.time = time; + } } impl PartialEq for Event { @@ -120,14 +139,37 @@ impl PartialEq for Event { fn eq(&self, other: &Event) -> bool { self.time == other.time && self.data == other.data && + self.ex_data == other.ex_data && self.protocol_type == other.protocol_type && self.group_id == other.group_id && self.time_format == other.time_format } } -#[derive(Clone)] +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct JsonEvent { + pub time: f32, + + #[serde(skip)] + pub importance: EventImportance, + + pub name: String, + pub data: serde_json::Value, +} + +impl Eventable for JsonEvent { + fn importance(&self) -> EventImportance { + self.importance + } + + fn set_time(&mut self, time: f32) { + self.time = time; + } +} + +#[derive(Clone, Copy, Debug, Default)] pub enum EventImportance { + #[default] Core, Base, Extra, @@ -169,14 +211,26 @@ impl From for EventImportance { EventType::ConnectivityEventType( ConnectivityEventType::ConnectionStateUpdated, ) => EventImportance::Base, + EventType::ConnectivityEventType( + ConnectivityEventType::MtuUpdated, + ) => EventImportance::Extra, EventType::SecurityEventType(SecurityEventType::KeyUpdated) => EventImportance::Base, EventType::SecurityEventType(SecurityEventType::KeyDiscarded) => EventImportance::Base, + EventType::TransportEventType( + TransportEventType::VersionInformation, + ) => EventImportance::Core, + EventType::TransportEventType( + TransportEventType::AlpnInformation, + ) => EventImportance::Core, EventType::TransportEventType(TransportEventType::ParametersSet) => EventImportance::Core, + EventType::TransportEventType( + TransportEventType::ParametersRestored, + ) => EventImportance::Base, EventType::TransportEventType( TransportEventType::DatagramsReceived, ) => EventImportance::Extra, @@ -193,6 +247,8 @@ impl From for EventImportance { EventImportance::Base, EventType::TransportEventType(TransportEventType::PacketBuffered) => EventImportance::Base, + EventType::TransportEventType(TransportEventType::PacketsAcked) => + EventImportance::Extra, EventType::TransportEventType( TransportEventType::StreamStateUpdated, ) => EventImportance::Base, @@ -248,6 +304,12 @@ impl From for EventImportance { } } +pub trait Eventable { + fn importance(&self) -> EventImportance; + + fn set_time(&mut self, time: f32); +} + #[derive(Serialize, Deserialize, Clone, Debug)] #[serde(rename_all = "snake_case")] pub enum EventCategory { @@ -328,6 +390,9 @@ impl From<&EventData> for EventType { EventType::ConnectivityEventType( ConnectivityEventType::ConnectionStateUpdated, ), + EventData::MtuUpdated { .. } => EventType::ConnectivityEventType( + ConnectivityEventType::MtuUpdated, + ), EventData::KeyUpdated { .. } => EventType::SecurityEventType(SecurityEventType::KeyUpdated), @@ -476,6 +541,9 @@ pub enum EventData { #[serde(rename = "connectivity:connection_state_updated")] ConnectionStateUpdated(connectivity::ConnectionStateUpdated), + #[serde(rename = "connectivity:mtu_updated")] + MtuUpdated(connectivity::MtuUpdated), + // Security #[serde(rename = "security:key_updated")] KeyUpdated(security::KeyUpdated), @@ -517,7 +585,7 @@ pub enum EventData { #[serde(rename = "transport:packet_buffered")] PacketBuffered(quic::PacketBuffered), - #[serde(rename = "transport:version_information")] + #[serde(rename = "transport:packets_acked")] PacketsAcked(quic::PacketsAcked), #[serde(rename = "transport:stream_state_updated")] diff --git a/third_party/rust/qlog/src/events/quic.rs b/third_party/rust/qlog/src/events/quic.rs index bb849019bb03..a7c1fa322509 100644 --- a/third_party/rust/qlog/src/events/quic.rs +++ b/third_party/rust/qlog/src/events/quic.rs @@ -66,7 +66,7 @@ pub enum PacketNumberSpace { #[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Debug)] pub struct PacketHeader { pub packet_type: PacketType, - pub packet_number: u64, + pub packet_number: Option, pub flags: Option, pub token: Option, @@ -85,7 +85,7 @@ impl PacketHeader { #[allow(clippy::too_many_arguments)] /// Creates a new PacketHeader. pub fn new( - packet_type: PacketType, packet_number: u64, flags: Option, + packet_type: PacketType, packet_number: Option, flags: Option, token: Option, length: Option, version: Option, scid: Option<&[u8]>, dcid: Option<&[u8]>, ) -> Self { @@ -129,7 +129,7 @@ impl PacketHeader { /// there are space benefits to not logging them in every packet, especially /// PacketType::OneRtt. pub fn with_type( - ty: PacketType, packet_number: u64, version: Option, + ty: PacketType, packet_number: Option, version: Option, scid: Option<&[u8]>, dcid: Option<&[u8]>, ) -> Self { match ty { diff --git a/third_party/rust/qlog/src/lib.rs b/third_party/rust/qlog/src/lib.rs index d4e745561861..68ff278fcd06 100644 --- a/third_party/rust/qlog/src/lib.rs +++ b/third_party/rust/qlog/src/lib.rs @@ -111,7 +111,7 @@ //! //! let pkt_hdr = qlog::events::quic::PacketHeader::new( //! qlog::events::quic::PacketType::Initial, -//! 0, // packet_number +//! Some(0), // packet_number //! None, // flags //! None, // token //! None, // length @@ -326,7 +326,7 @@ //! //! let pkt_hdr = qlog::events::quic::PacketHeader::with_type( //! qlog::events::quic::PacketType::OneRtt, -//! 0, +//! Some(0), //! Some(0x00000001), //! Some(&scid), //! Some(&dcid), @@ -669,7 +669,7 @@ pub mod testing { PacketHeader::new( packet_type, - 0, + Some(0), None, None, None, @@ -808,16 +808,14 @@ mod tests { let pkt_hdr = make_pkt_hdr(PacketType::Initial); - let mut frames = Vec::new(); - frames.push(QuicFrame::Padding); - frames.push(QuicFrame::Ping); - frames.push(QuicFrame::Stream { - stream_id: 0, - offset: 0, - length: 100, - fin: Some(true), - raw: None, - }); + let frames = + vec![QuicFrame::Padding, QuicFrame::Ping, QuicFrame::Stream { + stream_id: 0, + offset: 0, + length: 100, + fin: Some(true), + raw: None, + }]; let ev_data = EventData::PacketSent(PacketSent { header: pkt_hdr, @@ -969,4 +967,5 @@ mod tests { } pub mod events; +pub mod reader; pub mod streamer; diff --git a/third_party/rust/qlog/src/reader.rs b/third_party/rust/qlog/src/reader.rs new file mode 100644 index 000000000000..89a8e12a4420 --- /dev/null +++ b/third_party/rust/qlog/src/reader.rs @@ -0,0 +1,111 @@ +// Copyright (C) 2023, Cloudflare, Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::QlogSeq; + +/// Represents the format of the read event. +#[allow(clippy::large_enum_variant)] +pub enum Event { + /// A native qlog event type. + Qlog(crate::events::Event), + + // An extended JSON event type. + Json(crate::events::JsonEvent), +} + +/// A helper object specialized for reading JSON-SEQ qlog from a [`BufRead`] +/// trait. +/// +/// [`BufRead`]: https://doc.rust-lang.org/std/io/trait.BufRead.html +pub struct QlogSeqReader { + pub qlog: QlogSeq, + reader: Box, +} + +impl QlogSeqReader { + pub fn new( + mut reader: Box, + ) -> Result> { + // "null record" skip it + Self::read_record(reader.as_mut()); + + let header = Self::read_record(reader.as_mut()).ok_or_else(|| { + std::io::Error::new( + std::io::ErrorKind::Other, + "error reading file header bytes", + ) + })?; + + let res: Result = + serde_json::from_slice(&header); + match res { + Ok(qlog) => Ok(Self { qlog, reader }), + + Err(e) => Err(e.into()), + } + } + + fn read_record( + reader: &mut (dyn std::io::BufRead + Send + Sync), + ) -> Option> { + let mut buf = Vec::::new(); + let size = reader.read_until(b'', &mut buf).unwrap(); + if size <= 1 { + return None; + } + + buf.truncate(buf.len() - 1); + + Some(buf) + } +} + +impl Iterator for QlogSeqReader { + type Item = Event; + + #[inline] + fn next(&mut self) -> Option { + // Attempt to deserialize events but skip them if that fails for any + // reason, ensuring we always read all bytes in the reader. + while let Some(bytes) = Self::read_record(&mut self.reader) { + let r: serde_json::Result = + serde_json::from_slice(&bytes); + + if let Ok(event) = r { + return Some(Event::Qlog(event)); + } + + let r: serde_json::Result = + serde_json::from_slice(&bytes); + + if let Ok(event) = r { + return Some(Event::Json(event)); + } + } + + None + } +} diff --git a/third_party/rust/qlog/src/streamer.rs b/third_party/rust/qlog/src/streamer.rs index a0abe8d46b70..16761b3a67ae 100644 --- a/third_party/rust/qlog/src/streamer.rs +++ b/third_party/rust/qlog/src/streamer.rs @@ -27,6 +27,8 @@ use crate::events::EventData; use crate::events::EventImportance; use crate::events::EventType; +use crate::events::Eventable; +use crate::events::ExData; /// A helper object specialized for streaming JSON-serialized qlog to a /// [`Write`] trait. @@ -134,17 +136,20 @@ impl QlogStreamer { Ok(()) } - /// Writes a JSON-SEQ-serialized [Event] using [std::time::Instant::now()]. - pub fn add_event_now(&mut self, event: Event) -> Result<()> { + /// Writes a serializable to a JSON-SEQ record using + /// [std::time::Instant::now()]. + pub fn add_event_now( + &mut self, event: E, + ) -> Result<()> { let now = std::time::Instant::now(); self.add_event_with_instant(event, now) } - /// Writes a JSON-SEQ-serialized [Event] using the provided + /// Writes a serializable to a JSON-SEQ record using the provided /// [std::time::Instant]. - pub fn add_event_with_instant( - &mut self, mut event: Event, now: std::time::Instant, + pub fn add_event_with_instant( + &mut self, mut event: E, now: std::time::Instant, ) -> Result<()> { if self.state != StreamerState::Ready { return Err(Error::InvalidState); @@ -161,23 +166,40 @@ impl QlogStreamer { }; let rel_time = dur.as_secs_f32() * 1000.0; - event.time = rel_time; + event.set_time(rel_time); self.add_event(event) } - /// Writes a JSON-SEQ-serialized [Event] based on the provided [EventData] + /// Writes an [Event] based on the provided [EventData] to a JSON-SEQ record /// at time [std::time::Instant::now()]. pub fn add_event_data_now(&mut self, event_data: EventData) -> Result<()> { - let now = std::time::Instant::now(); - - self.add_event_data_with_instant(event_data, now) + self.add_event_data_ex_now(event_data, Default::default()) } - /// Writes a JSON-SEQ-serialized [Event] based on the provided [EventData] - /// and [std::time::Instant]. + /// Writes an [Event] based on the provided [EventData] and [ExData] to a + /// JSON-SEQ record at time [std::time::Instant::now()]. + pub fn add_event_data_ex_now( + &mut self, event_data: EventData, ex_data: ExData, + ) -> Result<()> { + let now = std::time::Instant::now(); + + self.add_event_data_ex_with_instant(event_data, ex_data, now) + } + + /// Writes an [Event] based on the provided [EventData] and + /// [std::time::Instant] to a JSON-SEQ record. pub fn add_event_data_with_instant( &mut self, event_data: EventData, now: std::time::Instant, + ) -> Result<()> { + self.add_event_data_ex_with_instant(event_data, Default::default(), now) + } + + /// Writes an [Event] based on the provided [EventData], [ExData], and + /// [std::time::Instant] to a JSON-SEQ record. + pub fn add_event_data_ex_with_instant( + &mut self, event_data: EventData, ex_data: ExData, + now: std::time::Instant, ) -> Result<()> { if self.state != StreamerState::Ready { return Err(Error::InvalidState); @@ -195,13 +217,15 @@ impl QlogStreamer { }; let rel_time = dur.as_secs_f32() * 1000.0; - let event = Event::with_time(rel_time, event_data); + let event = Event::with_time_ex(rel_time, event_data, ex_data); self.add_event(event) } /// Writes a JSON-SEQ-serialized [Event] using the provided [Event]. - pub fn add_event(&mut self, event: Event) -> Result<()> { + pub fn add_event( + &mut self, event: E, + ) -> Result<()> { if self.state != StreamerState::Ready { return Err(Error::InvalidState); } @@ -229,8 +253,16 @@ impl QlogStreamer { } } +impl Drop for QlogStreamer { + fn drop(&mut self) { + let _ = self.finish_log(); + } +} + #[cfg(test)] mod tests { + use std::collections::BTreeMap; + use super::*; use crate::events::quic; use crate::events::quic::QuicFrame; @@ -238,6 +270,8 @@ mod tests { use smallvec::smallvec; use testing::*; + use serde_json::json; + #[test] fn serialization_states() { let v: Vec = Vec::new(); @@ -354,6 +388,7 @@ mod tests { assert!(matches!(s.finish_log(), Ok(()))); let r = s.writer(); + #[allow(clippy::borrowed_box)] let w: &Box>> = unsafe { std::mem::transmute(r) }; let log_string = r#"{"qlog_version":"version","qlog_format":"JSON-SEQ","title":"title","description":"description","trace":{"vantage_point":{"type":"server"},"title":"Quiche qlog trace","description":"Quiche qlog trace description","configuration":{"time_offset":0.0}}} @@ -367,4 +402,143 @@ mod tests { assert_eq!(log_string, written_string); } + + #[test] + fn stream_json_event() { + let data = json!({"foo": "Bar", "hello": 123}); + let ev = events::JsonEvent { + time: 0.0, + importance: events::EventImportance::Core, + name: "jsonevent:sample".into(), + data, + }; + + let v: Vec = Vec::new(); + let buff = std::io::Cursor::new(v); + let writer = Box::new(buff); + + let trace = make_trace_seq(); + + let mut s = streamer::QlogStreamer::new( + "version".to_string(), + Some("title".to_string()), + Some("description".to_string()), + None, + std::time::Instant::now(), + trace, + EventImportance::Base, + writer, + ); + + assert!(matches!(s.start_log(), Ok(()))); + assert!(matches!(s.add_event(ev), Ok(()))); + assert!(matches!(s.finish_log(), Ok(()))); + + let r = s.writer(); + #[allow(clippy::borrowed_box)] + let w: &Box>> = unsafe { std::mem::transmute(r) }; + + let log_string = r#"{"qlog_version":"version","qlog_format":"JSON-SEQ","title":"title","description":"description","trace":{"vantage_point":{"type":"server"},"title":"Quiche qlog trace","description":"Quiche qlog trace description","configuration":{"time_offset":0.0}}} +{"time":0.0,"name":"jsonevent:sample","data":{"foo":"Bar","hello":123}} +"#; + + let written_string = std::str::from_utf8(w.as_ref().get_ref()).unwrap(); + + assert_eq!(log_string, written_string); + } + + #[test] + fn stream_data_ex() { + let v: Vec = Vec::new(); + let buff = std::io::Cursor::new(v); + let writer = Box::new(buff); + + let trace = make_trace_seq(); + let pkt_hdr = make_pkt_hdr(quic::PacketType::Handshake); + let raw = Some(RawInfo { + length: Some(1251), + payload_length: Some(1224), + data: None, + }); + + let frame1 = QuicFrame::Stream { + stream_id: 40, + offset: 40, + length: 400, + fin: Some(true), + raw: None, + }; + + let event_data1 = EventData::PacketSent(quic::PacketSent { + header: pkt_hdr.clone(), + frames: Some(smallvec![frame1]), + is_coalesced: None, + retry_token: None, + stateless_reset_token: None, + supported_versions: None, + raw: raw.clone(), + datagram_id: None, + send_at_time: None, + trigger: None, + }); + let j1 = json!({"foo": "Bar", "hello": 123}); + let j2 = json!({"baz": [1,2,3,4]}); + let mut ex_data = BTreeMap::new(); + ex_data.insert("first".to_string(), j1); + ex_data.insert("second".to_string(), j2); + + let ev1 = Event::with_time_ex(0.0, event_data1, ex_data); + + let frame2 = QuicFrame::Stream { + stream_id: 1, + offset: 0, + length: 100, + fin: Some(true), + raw: None, + }; + + let event_data2 = EventData::PacketSent(quic::PacketSent { + header: pkt_hdr.clone(), + frames: Some(smallvec![frame2]), + is_coalesced: None, + retry_token: None, + stateless_reset_token: None, + supported_versions: None, + raw: raw.clone(), + datagram_id: None, + send_at_time: None, + trigger: None, + }); + + let ev2 = Event::with_time(0.0, event_data2); + + let mut s = streamer::QlogStreamer::new( + "version".to_string(), + Some("title".to_string()), + Some("description".to_string()), + None, + std::time::Instant::now(), + trace, + EventImportance::Base, + writer, + ); + + assert!(matches!(s.start_log(), Ok(()))); + assert!(matches!(s.add_event(ev1), Ok(()))); + assert!(matches!(s.add_event(ev2), Ok(()))); + assert!(matches!(s.finish_log(), Ok(()))); + + let r = s.writer(); + #[allow(clippy::borrowed_box)] + let w: &Box>> = unsafe { std::mem::transmute(r) }; + + let log_string = r#"{"qlog_version":"version","qlog_format":"JSON-SEQ","title":"title","description":"description","trace":{"vantage_point":{"type":"server"},"title":"Quiche qlog trace","description":"Quiche qlog trace description","configuration":{"time_offset":0.0}}} +{"time":0.0,"name":"transport:packet_sent","data":{"header":{"packet_type":"handshake","packet_number":0,"version":"1","scil":8,"dcil":8,"scid":"7e37e4dcc6682da8","dcid":"36ce104eee50101c"},"raw":{"length":1251,"payload_length":1224},"frames":[{"frame_type":"stream","stream_id":40,"offset":40,"length":400,"fin":true}]},"first":{"foo":"Bar","hello":123},"second":{"baz":[1,2,3,4]}} +{"time":0.0,"name":"transport:packet_sent","data":{"header":{"packet_type":"handshake","packet_number":0,"version":"1","scil":8,"dcil":8,"scid":"7e37e4dcc6682da8","dcid":"36ce104eee50101c"},"raw":{"length":1251,"payload_length":1224},"frames":[{"frame_type":"stream","stream_id":1,"offset":0,"length":100,"fin":true}]}} +"#; + + let written_string = std::str::from_utf8(w.as_ref().get_ref()).unwrap(); + + assert_eq!(log_string, written_string); + } }