Bug 1959128 - update neqo to v0.13 r=leggert,kershaw,necko-reviewers,supply-chain-reviewers

Differential Revision: https://phabricator.services.mozilla.com/D245341
This commit is contained in:
Max Leonard Inden
2025-04-14 14:56:15 +00:00
parent a148a1cdd9
commit d18a9f5c74
199 changed files with 6373 additions and 3638 deletions

View File

@@ -100,9 +100,9 @@ git = "https://github.com/mozilla/mp4parse-rust"
rev = "e64650a686e5c5732395cd059e17cfd3b1e5b63b" rev = "e64650a686e5c5732395cd059e17cfd3b1e5b63b"
replace-with = "vendored-sources" replace-with = "vendored-sources"
[source."git+https://github.com/mozilla/neqo?tag=v0.12.2"] [source."git+https://github.com/mozilla/neqo?tag=v0.13.1"]
git = "https://github.com/mozilla/neqo" git = "https://github.com/mozilla/neqo"
tag = "v0.12.2" tag = "v0.13.1"
replace-with = "vendored-sources" replace-with = "vendored-sources"
[source."git+https://github.com/servo/rust-cssparser?rev=958a3f098acb92ddacdce18a7ef2c4a87ac3326f"] [source."git+https://github.com/servo/rust-cssparser?rev=958a3f098acb92ddacdce18a7ef2c4a87ac3326f"]

43
Cargo.lock generated
View File

@@ -4522,8 +4522,8 @@ dependencies = [
[[package]] [[package]]
name = "neqo-bin" name = "neqo-bin"
version = "0.12.2" version = "0.13.1"
source = "git+https://github.com/mozilla/neqo?tag=v0.12.2#f8946d5187271b3e63e8d0209343510bdeac1451" source = "git+https://github.com/mozilla/neqo?tag=v0.13.1#0a356afeb45631036df6b3028951a02d16fa8d27"
dependencies = [ dependencies = [
"clap", "clap",
"clap-verbosity-flag", "clap-verbosity-flag",
@@ -4544,35 +4544,38 @@ dependencies = [
[[package]] [[package]]
name = "neqo-common" name = "neqo-common"
version = "0.12.2" version = "0.13.1"
source = "git+https://github.com/mozilla/neqo?tag=v0.12.2#f8946d5187271b3e63e8d0209343510bdeac1451" source = "git+https://github.com/mozilla/neqo?tag=v0.13.1#0a356afeb45631036df6b3028951a02d16fa8d27"
dependencies = [ dependencies = [
"enum-map", "enum-map",
"env_logger", "env_logger",
"log", "log",
"qlog", "qlog",
"strum",
"windows", "windows",
] ]
[[package]] [[package]]
name = "neqo-crypto" name = "neqo-crypto"
version = "0.12.2" version = "0.13.1"
source = "git+https://github.com/mozilla/neqo?tag=v0.12.2#f8946d5187271b3e63e8d0209343510bdeac1451" source = "git+https://github.com/mozilla/neqo?tag=v0.13.1#0a356afeb45631036df6b3028951a02d16fa8d27"
dependencies = [ dependencies = [
"bindgen 0.69.4", "bindgen 0.69.4",
"enum-map",
"log", "log",
"mozbuild", "mozbuild",
"neqo-common", "neqo-common",
"semver", "semver",
"serde", "serde",
"serde_derive", "serde_derive",
"strum",
"toml", "toml",
] ]
[[package]] [[package]]
name = "neqo-http3" name = "neqo-http3"
version = "0.12.2" version = "0.13.1"
source = "git+https://github.com/mozilla/neqo?tag=v0.12.2#f8946d5187271b3e63e8d0209343510bdeac1451" source = "git+https://github.com/mozilla/neqo?tag=v0.13.1#0a356afeb45631036df6b3028951a02d16fa8d27"
dependencies = [ dependencies = [
"enumset", "enumset",
"log", "log",
@@ -4582,13 +4585,14 @@ dependencies = [
"neqo-transport", "neqo-transport",
"qlog", "qlog",
"sfv", "sfv",
"strum",
"url", "url",
] ]
[[package]] [[package]]
name = "neqo-qpack" name = "neqo-qpack"
version = "0.12.2" version = "0.13.1"
source = "git+https://github.com/mozilla/neqo?tag=v0.12.2#f8946d5187271b3e63e8d0209343510bdeac1451" source = "git+https://github.com/mozilla/neqo?tag=v0.13.1#0a356afeb45631036df6b3028951a02d16fa8d27"
dependencies = [ dependencies = [
"log", "log",
"neqo-common", "neqo-common",
@@ -4599,10 +4603,11 @@ dependencies = [
[[package]] [[package]]
name = "neqo-transport" name = "neqo-transport"
version = "0.12.2" version = "0.13.1"
source = "git+https://github.com/mozilla/neqo?tag=v0.12.2#f8946d5187271b3e63e8d0209343510bdeac1451" source = "git+https://github.com/mozilla/neqo?tag=v0.13.1#0a356afeb45631036df6b3028951a02d16fa8d27"
dependencies = [ dependencies = [
"enum-map", "enum-map",
"enumset",
"indexmap", "indexmap",
"log", "log",
"mtu", "mtu",
@@ -4611,12 +4616,13 @@ dependencies = [
"qlog", "qlog",
"smallvec", "smallvec",
"static_assertions", "static_assertions",
"strum",
] ]
[[package]] [[package]]
name = "neqo-udp" name = "neqo-udp"
version = "0.12.2" version = "0.13.1"
source = "git+https://github.com/mozilla/neqo?tag=v0.12.2#f8946d5187271b3e63e8d0209343510bdeac1451" source = "git+https://github.com/mozilla/neqo?tag=v0.13.1#0a356afeb45631036df6b3028951a02d16fa8d27"
dependencies = [ dependencies = [
"cfg_aliases", "cfg_aliases",
"log", "log",
@@ -5284,12 +5290,11 @@ dependencies = [
[[package]] [[package]]
name = "qlog" name = "qlog"
version = "0.13.0" version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b5f65b920fa913ce92267bb3c4ed3b9c2f81d05f8e1376c3bbc95455eedb7df" checksum = "0f15b83c59e6b945f2261c95a1dd9faf239187f32ff0a96af1d1d28c4557f919"
dependencies = [ dependencies = [
"serde", "serde",
"serde_derive",
"serde_json", "serde_json",
"serde_with", "serde_with",
"smallvec", "smallvec",
@@ -5303,9 +5308,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]] [[package]]
name = "quinn-udp" name = "quinn-udp"
version = "0.5.10" version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e46f3055866785f6b92bc6164b76be02ca8f2eb4b002c0354b28cf4c119e5944" checksum = "541d0f57c6ec747a90738a52741d3221f7960e8ac2f0ff4b1a63680e033b4ab5"
dependencies = [ dependencies = [
"cfg_aliases", "cfg_aliases",
"libc", "libc",

View File

@@ -10,17 +10,17 @@ name = "neqo_glue"
[dependencies] [dependencies]
firefox-on-glean = { path = "../../../toolkit/components/glean/api" } firefox-on-glean = { path = "../../../toolkit/components/glean/api" }
neqo-udp = { tag = "v0.12.2", git = "https://github.com/mozilla/neqo" } neqo-udp = { tag = "v0.13.1", git = "https://github.com/mozilla/neqo" }
neqo-http3 = { tag = "v0.12.2", git = "https://github.com/mozilla/neqo" } neqo-http3 = { tag = "v0.13.1", git = "https://github.com/mozilla/neqo" }
neqo-transport = { tag = "v0.12.2", git = "https://github.com/mozilla/neqo", features = ["gecko"] } neqo-transport = { tag = "v0.13.1", git = "https://github.com/mozilla/neqo", features = ["gecko"] }
neqo-common = { tag = "v0.12.2", git = "https://github.com/mozilla/neqo" } neqo-common = { tag = "v0.13.1", git = "https://github.com/mozilla/neqo" }
neqo-qpack = { tag = "v0.12.2", git = "https://github.com/mozilla/neqo" } neqo-qpack = { tag = "v0.13.1", git = "https://github.com/mozilla/neqo" }
nserror = { path = "../../../xpcom/rust/nserror" } nserror = { path = "../../../xpcom/rust/nserror" }
nsstring = { path = "../../../xpcom/rust/nsstring" } nsstring = { path = "../../../xpcom/rust/nsstring" }
xpcom = { path = "../../../xpcom/rust/xpcom" } xpcom = { path = "../../../xpcom/rust/xpcom" }
thin-vec = { version = "0.2.1", features = ["gecko-ffi"] } thin-vec = { version = "0.2.1", features = ["gecko-ffi"] }
log = "0.4.0" log = "0.4.0"
qlog = "0.13" qlog = "0.15.1"
libc = "0.2.0" libc = "0.2.0"
static_assertions = "1.1" static_assertions = "1.1"
static_prefs = { path = "../../../modules/libpref/init/static_prefs"} static_prefs = { path = "../../../modules/libpref/init/static_prefs"}
@@ -30,7 +30,7 @@ uuid = { version = "1.0", features = ["v4"] }
winapi = {version = "0.3", features = ["ws2def"] } winapi = {version = "0.3", features = ["ws2def"] }
[dependencies.neqo-crypto] [dependencies.neqo-crypto]
tag = "v0.12.2" tag = "v0.13.1"
git = "https://github.com/mozilla/neqo" git = "https://github.com/mozilla/neqo"
default-features = false default-features = false
features = ["gecko"] features = ["gecko"]

View File

@@ -261,7 +261,10 @@ impl NeqoHttp3Conn {
.max_stream_data(StreamType::BiDi, false, max_stream_data) .max_stream_data(StreamType::BiDi, false, max_stream_data)
.grease(static_prefs::pref!("security.tls.grease_http3_enable")) .grease(static_prefs::pref!("security.tls.grease_http3_enable"))
.sni_slicing(static_prefs::pref!("network.http.http3.sni-slicing")) .sni_slicing(static_prefs::pref!("network.http.http3.sni-slicing"))
.idle_timeout(Duration::from_secs(idle_timeout.into())); .idle_timeout(Duration::from_secs(idle_timeout.into()))
// Disabled on OpenBSD. See <https://bugzilla.mozilla.org/show_bug.cgi?id=1952304>.
.pmtud_iface_mtu(cfg!(not(target_os = "openbsd")))
.mlkem(false);
// Set a short timeout when fuzzing. // Set a short timeout when fuzzing.
#[cfg(feature = "fuzzing")] #[cfg(feature = "fuzzing")]
@@ -417,10 +420,10 @@ impl NeqoHttp3Conn {
} }
if static_prefs::pref!("network.http.http3.ecn") && stats.frame_rx.handshake_done != 0 { if static_prefs::pref!("network.http.http3.ecn") && stats.frame_rx.handshake_done != 0 {
if stats.ecn_tx[IpTosEcn::Ect0] > 0 { let tx_ect0_sum: u64 = stats.ecn_tx.into_values().map(|v| v[IpTosEcn::Ect0]).sum();
if let Ok(ratio) = i64::try_from( let tx_ce_sum: u64 = stats.ecn_tx.into_values().map(|v| v[IpTosEcn::Ce]).sum();
(stats.ecn_tx[IpTosEcn::Ce] * PRECISION_FACTOR) / stats.ecn_tx[IpTosEcn::Ect0], if tx_ect0_sum > 0 {
) { if let Ok(ratio) = i64::try_from((tx_ce_sum * PRECISION_FACTOR) / tx_ect0_sum) {
glean::http_3_ecn_ce_ect0_ratio_sent.accumulate_single_sample_signed(ratio); glean::http_3_ecn_ce_ect0_ratio_sent.accumulate_single_sample_signed(ratio);
} else { } else {
let msg = "Failed to convert ratio to i64 for use with glean"; let msg = "Failed to convert ratio to i64 for use with glean";
@@ -428,10 +431,10 @@ impl NeqoHttp3Conn {
debug_assert!(false, "{msg}"); debug_assert!(false, "{msg}");
} }
} }
if stats.ecn_rx[IpTosEcn::Ect0] > 0 { let rx_ect0_sum: u64 = stats.ecn_rx.into_values().map(|v| v[IpTosEcn::Ect0]).sum();
if let Ok(ratio) = i64::try_from( let rx_ce_sum: u64 = stats.ecn_rx.into_values().map(|v| v[IpTosEcn::Ce]).sum();
(stats.ecn_rx[IpTosEcn::Ce] * PRECISION_FACTOR) / stats.ecn_rx[IpTosEcn::Ect0], if rx_ect0_sum > 0 {
) { if let Ok(ratio) = i64::try_from((rx_ce_sum * PRECISION_FACTOR) / rx_ect0_sum) {
glean::http_3_ecn_ce_ect0_ratio_received.accumulate_single_sample_signed(ratio); glean::http_3_ecn_ce_ect0_ratio_received.accumulate_single_sample_signed(ratio);
} else { } else {
let msg = "Failed to convert ratio to i64 for use with glean"; let msg = "Failed to convert ratio to i64 for use with glean";
@@ -1159,7 +1162,6 @@ impl From<TransportError> for CloseError {
TransportError::ConnectionIdLimitExceeded => Self::TransportInternalErrorOther(1), TransportError::ConnectionIdLimitExceeded => Self::TransportInternalErrorOther(1),
TransportError::ConnectionIdsExhausted => Self::TransportInternalErrorOther(2), TransportError::ConnectionIdsExhausted => Self::TransportInternalErrorOther(2),
TransportError::ConnectionState => Self::TransportInternalErrorOther(3), TransportError::ConnectionState => Self::TransportInternalErrorOther(3),
TransportError::DecodingFrame => Self::TransportInternalErrorOther(4),
TransportError::DecryptError => Self::TransportInternalErrorOther(5), TransportError::DecryptError => Self::TransportInternalErrorOther(5),
TransportError::IntegerOverflow => Self::TransportInternalErrorOther(7), TransportError::IntegerOverflow => Self::TransportInternalErrorOther(7),
TransportError::InvalidInput => Self::TransportInternalErrorOther(8), TransportError::InvalidInput => Self::TransportInternalErrorOther(8),
@@ -1184,6 +1186,7 @@ impl From<TransportError> for CloseError {
TransportError::QlogError => Self::TransportInternalErrorOther(27), TransportError::QlogError => Self::TransportInternalErrorOther(27),
TransportError::NotAvailable => Self::TransportInternalErrorOther(28), TransportError::NotAvailable => Self::TransportInternalErrorOther(28),
TransportError::DisabledVersion => Self::TransportInternalErrorOther(29), TransportError::DisabledVersion => Self::TransportInternalErrorOther(29),
TransportError::UnknownTransportParameter => Self::TransportInternalErrorOther(30),
} }
} }
} }
@@ -1213,7 +1216,6 @@ const fn transport_error_to_glean_label(error: &TransportError) -> &'static str
TransportError::ConnectionIdLimitExceeded => "ConnectionIdLimitExceeded", TransportError::ConnectionIdLimitExceeded => "ConnectionIdLimitExceeded",
TransportError::ConnectionIdsExhausted => "ConnectionIdsExhausted", TransportError::ConnectionIdsExhausted => "ConnectionIdsExhausted",
TransportError::ConnectionState => "ConnectionState", TransportError::ConnectionState => "ConnectionState",
TransportError::DecodingFrame => "DecodingFrame",
TransportError::DecryptError => "DecryptError", TransportError::DecryptError => "DecryptError",
TransportError::DisabledVersion => "DisabledVersion", TransportError::DisabledVersion => "DisabledVersion",
TransportError::IdleTimeout => "IdleTimeout", TransportError::IdleTimeout => "IdleTimeout",
@@ -1242,6 +1244,7 @@ const fn transport_error_to_glean_label(error: &TransportError) -> &'static str
TransportError::UnknownFrameType => "UnknownFrameType", TransportError::UnknownFrameType => "UnknownFrameType",
TransportError::VersionNegotiation => "VersionNegotiation", TransportError::VersionNegotiation => "VersionNegotiation",
TransportError::WrongRole => "WrongRole", TransportError::WrongRole => "WrongRole",
TransportError::UnknownTransportParameter => "UnknownTransportParameter",
} }
} }

View File

@@ -6,11 +6,11 @@ edition = "2021"
license = "MPL-2.0" license = "MPL-2.0"
[dependencies] [dependencies]
neqo-bin = { tag = "v0.12.2", git = "https://github.com/mozilla/neqo" } neqo-bin = { tag = "v0.13.1", git = "https://github.com/mozilla/neqo" }
neqo-transport = { tag = "v0.12.2", git = "https://github.com/mozilla/neqo", features = ["gecko"] } neqo-transport = { tag = "v0.13.1", git = "https://github.com/mozilla/neqo", features = ["gecko"] }
neqo-common = { tag = "v0.12.2", git = "https://github.com/mozilla/neqo" } neqo-common = { tag = "v0.13.1", git = "https://github.com/mozilla/neqo" }
neqo-http3 = { tag = "v0.12.2", git = "https://github.com/mozilla/neqo" } neqo-http3 = { tag = "v0.13.1", git = "https://github.com/mozilla/neqo" }
neqo-qpack = { tag = "v0.12.2", git = "https://github.com/mozilla/neqo" } neqo-qpack = { tag = "v0.13.1", git = "https://github.com/mozilla/neqo" }
log = "0.4.0" log = "0.4.0"
base64 = "0.22" base64 = "0.22"
cfg-if = "1.0" cfg-if = "1.0"
@@ -20,7 +20,7 @@ tokio = { version = "1", features = ["rt-multi-thread"] }
mozilla-central-workspace-hack = { version = "0.1", features = ["http3server"], optional = true } mozilla-central-workspace-hack = { version = "0.1", features = ["http3server"], optional = true }
[dependencies.neqo-crypto] [dependencies.neqo-crypto]
tag = "v0.12.2" tag = "v0.13.1"
git = "https://github.com/mozilla/neqo" git = "https://github.com/mozilla/neqo"
default-features = false default-features = false
features = ["gecko"] features = ["gecko"]

View File

@@ -170,7 +170,7 @@ impl Http3TestServer {
} }
impl HttpServer for Http3TestServer { impl HttpServer for Http3TestServer {
fn process(&mut self, dgram: Option<Datagram<&[u8]>>, now: Instant) -> Output { fn process(&mut self, dgram: Option<Datagram<&mut [u8]>>, now: Instant) -> Output {
let output = self.server.process(dgram, now); let output = self.server.process(dgram, now);
let output = if self.sessions_to_close.is_empty() { let output = if self.sessions_to_close.is_empty() {
@@ -662,7 +662,7 @@ impl ::std::fmt::Display for Server {
} }
impl HttpServer for Server { impl HttpServer for Server {
fn process(&mut self, dgram: Option<Datagram<&[u8]>>, now: Instant) -> Output { fn process(&mut self, dgram: Option<Datagram<&mut [u8]>>, now: Instant) -> Output {
self.0.process(dgram, now) self.0.process(dgram, now)
} }
@@ -898,7 +898,7 @@ impl Http3ProxyServer {
} }
impl HttpServer for Http3ProxyServer { impl HttpServer for Http3ProxyServer {
fn process(&mut self, dgram: Option<Datagram<&[u8]>>, now: Instant) -> Output { fn process(&mut self, dgram: Option<Datagram<&mut [u8]>>, now: Instant) -> Output {
let output = self.server.process(dgram, now); let output = self.server.process(dgram, now);
#[cfg(not(target_os = "android"))] #[cfg(not(target_os = "android"))]
@@ -1027,7 +1027,7 @@ impl ::std::fmt::Display for NonRespondingServer {
} }
impl HttpServer for NonRespondingServer { impl HttpServer for NonRespondingServer {
fn process(&mut self, _dgram: Option<Datagram<&[u8]>>, _now: Instant) -> Output { fn process(&mut self, _dgram: Option<Datagram<&mut [u8]>>, _now: Instant) -> Output {
Output::None Output::None
} }

View File

@@ -4278,6 +4278,11 @@ who = "Kershaw Chang <kershaw@mozilla.com>"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
delta = "0.12.0 -> 0.13.0" delta = "0.12.0 -> 0.13.0"
[[audits.qlog]]
who = "Max Leonard Inden <mail@max-inden.de>"
criteria = "safe-to-deploy"
delta = "0.13.0 -> 0.15.2"
[[audits.quinn-udp]] [[audits.quinn-udp]]
who = "Kershaw Chang <kershaw@mozilla.com>" who = "Kershaw Chang <kershaw@mozilla.com>"
criteria = "safe-to-run" criteria = "safe-to-run"
@@ -4309,6 +4314,11 @@ who = "Max Leonard Inden <mail@max-inden.de>"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
delta = "0.5.9 -> 0.5.10" delta = "0.5.9 -> 0.5.10"
[[audits.quinn-udp]]
who = "Max Leonard Inden <mail@max-inden.de>"
criteria = "safe-to-deploy"
delta = "0.5.10 -> 0.5.11"
[[audits.quote]] [[audits.quote]]
who = "Nika Layzell <nika@thelayzells.com>" who = "Nika Layzell <nika@thelayzells.com>"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"

View File

@@ -1 +1 @@
{"files":{"Cargo.toml":"699f5a52f9533f1d44ad6c43324ec957d7ede0299657690146b8854536ce41e7","benches/main.rs":"fcfd00665a1c4eb2118c74dd3e066e451bed655a42ae5c92daee38a31b69c2fb","src/bin/client.rs":"d7dc368d9b3a3a7a391fd982509ce66afa09ede9ca54dc1328bfc222e69379dc","src/bin/server.rs":"f297d7d00613291e5a058772c7e416b55ad8a25e6f0cf438c7b7f836fb470135","src/client/http09.rs":"86726bac98edbcab5a41ee5e4614637944730032fb31354cb68df917992da9bd","src/client/http3.rs":"820d359d83c6d6df1ce463cf2780b4541d0bd880338b8bf7840b6f01f14732ec","src/client/mod.rs":"3a8d426736c892a69acf543ab469540b631a43ab7c18e8f4e1f74aa0f56806e2","src/lib.rs":"68936ae531d5ab903b0063e3c8a7cd49511c2bf87c42706f23e8968e8a5c3d89","src/send_data.rs":"7537b7a0f67e9077cf121cc3d0fcd42edb085801dd4baf25b14c22a01f1bf04f","src/server/http09.rs":"d71cd79bba792799f303abdc15abe10e843bc0dd4de3efa27f3d11bf82798de2","src/server/http3.rs":"56f45922eebcfa30d7d9ceb6bac97d6f725d4c803c7aa0836c6e4d21b8d4588e","src/server/mod.rs":"6286666e692c43e52ddb3e837c9630694a446101c0c8cff520a816ce25c247df","src/udp.rs":"9fbd463a87c7a1f0873a003c36631b3b02af6e1a3d215b5a12a1e2822c207362"},"package":null} {"files":{"Cargo.toml":"632822ea728db24db8ccbba6a7b38431bba66b1c7f090e50c9bc254f16c7fa16","benches/main.rs":"7bd395227024b062e87eff0e7e2afa4a2dc70d7497c999aa665709577818f466","src/bin/client.rs":"71952208bcee80e8ff37a5dfe7cac19940274157e8ecf8acc6edc4d918c813b7","src/bin/server.rs":"88d3b2310f4a899053603cce4fd03703f26a8b91ff487bf5942b6e45c135a9fa","src/client/http09.rs":"b0b3ff496dbf35fe582a3f02780792bba34cb510d2acbcb43023180bec27cae1","src/client/http3.rs":"3f7bd425fa3bbdea005c91be6f25c861dc20819c2aeee42d4ffab7a7e3ef7282","src/client/mod.rs":"9185f1039d1e920a720330ec8b63f8b42d3924c26e95b6636436c9c5e1ae20a1","src/lib.rs":"ced2561bbebce6440b8ee902316afe7ca9df8dfeb632053587854838815e7c74","src/send_data.rs":"7537b7a0f67e9077cf121cc3d0fcd42edb085801dd4baf25b14c22a01f1bf04f","src/server/http09.rs":"b5236d9cc7b3c6c46cef9be521bc85e1b618960c0315465760a3d58e92c82c32","src/server/http3.rs":"2c826fb81a792221878a6d16bcaf6f1bed88fe0b424315c262eb08479ac5b4c4","src/server/mod.rs":"1c4c453452e5788ea3b3ab0c2d2193a291a39426466a28d8a0b384206392d485","src/udp.rs":"d68cd0fba6a0e679f79cce5c443bceba48c6ddbd2b5f492a075c9da8765b3047"},"package":null}

View File

@@ -11,9 +11,9 @@
[package] [package]
edition = "2021" edition = "2021"
rust-version = "1.76.0" rust-version = "1.82.0"
name = "neqo-bin" name = "neqo-bin"
version = "0.12.2" version = "0.13.1"
authors = ["The Neqo Authors <necko@mozilla.com>"] authors = ["The Neqo Authors <necko@mozilla.com>"]
build = false build = false
autolib = false autolib = false
@@ -72,7 +72,7 @@ harness = false
required-features = ["bench"] required-features = ["bench"]
[dependencies.clap] [dependencies.clap]
version = "4.4" version = "4.5"
features = [ features = [
"std", "std",
"help", "help",
@@ -118,11 +118,11 @@ path = "./../neqo-transport"
path = "./../neqo-udp" path = "./../neqo-udp"
[dependencies.qlog] [dependencies.qlog]
version = "0.13" version = "0.15.1"
default-features = false default-features = false
[dependencies.quinn-udp] [dependencies.quinn-udp]
version = "0.5.6" version = "0.5.11"
features = [ features = [
"direct-log", "direct-log",
"fast-apple-datapath", "fast-apple-datapath",
@@ -141,12 +141,11 @@ features = [
"time", "time",
"macros", "macros",
"rt", "rt",
"rt-multi-thread",
] ]
default-features = false default-features = false
[dependencies.url] [dependencies.url]
version = "2.5.3" version = "2.5"
features = ["std"] features = ["std"]
default-features = false default-features = false
@@ -173,22 +172,56 @@ features = ["sync"]
default-features = false default-features = false
[lints.clippy] [lints.clippy]
allow_attributes = "warn"
allow_attributes_without_reason = "warn"
cfg_not_test = "warn" cfg_not_test = "warn"
clone_on_ref_ptr = "warn" clone_on_ref_ptr = "warn"
create_dir = "warn" create_dir = "warn"
dbg_macro = "warn"
empty_drop = "warn"
empty_enum_variants_with_brackets = "warn"
filetype_is_file = "warn"
float_cmp_const = "warn"
fn_to_numeric_cast_any = "warn"
get_unwrap = "warn" get_unwrap = "warn"
if_then_some_else_none = "warn" if_then_some_else_none = "warn"
infinite_loop = "warn"
large_include_file = "warn"
let_underscore_must_use = "warn"
let_underscore_untyped = "warn"
literal_string_with_formatting_args = "allow"
lossy_float_literal = "warn"
mem_forget = "warn"
mixed_read_write_in_expression = "warn"
module_name_repetitions = "warn"
multiple_crate_versions = "allow" multiple_crate_versions = "allow"
multiple_inherent_impl = "warn" multiple_inherent_impl = "warn"
mutex_atomic = "warn"
mutex_integer = "warn"
needless_raw_strings = "warn"
pathbuf_init_then_push = "warn" pathbuf_init_then_push = "warn"
pub_without_shorthand = "warn"
rc_buffer = "warn"
rc_mutex = "warn"
redundant_type_annotations = "warn" redundant_type_annotations = "warn"
ref_patterns = "warn" ref_patterns = "warn"
renamed_function_params = "warn" renamed_function_params = "warn"
rest_pat_in_fully_bound_structs = "warn"
self_named_module_files = "warn"
semicolon_inside_block = "warn" semicolon_inside_block = "warn"
string_lit_chars_any = "warn"
string_to_string = "warn"
suspicious_xor_used_as_pow = "warn"
try_err = "warn" try_err = "warn"
unnecessary_safety_comment = "warn"
unnecessary_safety_doc = "warn"
unnecessary_self_imports = "warn"
unneeded_field_pattern = "warn" unneeded_field_pattern = "warn"
unused_result_ok = "warn" unused_result_ok = "warn"
unused_trait_names = "warn" unused_trait_names = "warn"
unwrap_in_result = "warn"
unwrap_used = "warn"
verbose_file_reads = "warn"
[lints.clippy.cargo] [lints.clippy.cargo]
level = "warn" level = "warn"
@@ -205,7 +238,6 @@ priority = -1
[lints.rust] [lints.rust]
absolute_paths_not_starting_with_crate = "warn" absolute_paths_not_starting_with_crate = "warn"
ambiguous_negative_literals = "warn" ambiguous_negative_literals = "warn"
closure_returning_async_block = "warn"
explicit_outlives_requirements = "warn" explicit_outlives_requirements = "warn"
macro_use_extern_crate = "warn" macro_use_extern_crate = "warn"
missing_abi = "warn" missing_abi = "warn"
@@ -217,3 +249,4 @@ unit_bindings = "warn"
unused_import_braces = "warn" unused_import_braces = "warn"
unused_lifetimes = "warn" unused_lifetimes = "warn"
unused_macro_rules = "warn" unused_macro_rules = "warn"
unused_qualifications = "warn"

View File

@@ -4,11 +4,13 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![expect(clippy::unwrap_used, reason = "OK in a bench.")]
use std::{env, path::PathBuf, str::FromStr as _}; use std::{env, path::PathBuf, str::FromStr as _};
use criterion::{criterion_group, criterion_main, BatchSize, Criterion, Throughput}; use criterion::{criterion_group, criterion_main, BatchSize, Criterion, Throughput};
use neqo_bin::{client, server}; use neqo_bin::{client, server};
use tokio::runtime::Runtime; use tokio::runtime::Builder;
struct Benchmark { struct Benchmark {
name: String, name: String,
@@ -42,7 +44,7 @@ fn transfer(c: &mut Criterion) {
upload: false, upload: false,
}, },
Benchmark { Benchmark {
name: format!("1-conn/1-100mb-resp{mtu} (aka. Upload)"), name: format!("1-conn/1-100mb-req{mtu} (aka. Upload)"),
requests: vec![100 * 1024 * 1024], requests: vec![100 * 1024 * 1024],
upload: true, upload: true,
}, },
@@ -55,13 +57,14 @@ fn transfer(c: &mut Criterion) {
Throughput::Elements(requests.len() as u64) Throughput::Elements(requests.len() as u64)
}); });
group.bench_function("client", |b| { group.bench_function("client", |b| {
b.to_async(Runtime::new().unwrap()).iter_batched( b.to_async(Builder::new_current_thread().enable_all().build().unwrap())
|| client::client(client::Args::new(&requests, upload)), .iter_batched(
|client| async move { || client::client(client::Args::new(&requests, upload)),
client.await.unwrap(); |client| async move {
}, client.await.unwrap();
BatchSize::PerIteration, },
); BatchSize::PerIteration,
);
}); });
group.finish(); group.finish();
} }
@@ -69,17 +72,25 @@ fn transfer(c: &mut Criterion) {
done_sender.send(()).unwrap(); done_sender.send(()).unwrap();
} }
#[allow(clippy::redundant_pub_crate)] // Bug in clippy nursery? Not sure how this lint could fire here. #[allow(
clippy::allow_attributes,
clippy::redundant_pub_crate,
reason = "TODO: Bug in clippy nursery?"
)]
fn spawn_server() -> tokio::sync::oneshot::Sender<()> { fn spawn_server() -> tokio::sync::oneshot::Sender<()> {
let (done_sender, mut done_receiver) = tokio::sync::oneshot::channel(); let (done_sender, mut done_receiver) = tokio::sync::oneshot::channel();
std::thread::spawn(move || { std::thread::spawn(move || {
Runtime::new().unwrap().block_on(async { Builder::new_current_thread()
let mut server = Box::pin(server::server(server::Args::default())); .enable_all()
tokio::select! { .build()
_ = &mut done_receiver => {} .unwrap()
res = &mut server => panic!("expect server not to terminate: {res:?}"), .block_on(async {
}; let mut server = Box::pin(server::server(server::Args::default()));
}); tokio::select! {
_ = &mut done_receiver => {}
res = &mut server => panic!("expect server not to terminate: {res:?}"),
};
});
}); });
done_sender done_sender
} }

View File

@@ -6,7 +6,7 @@
use clap::Parser as _; use clap::Parser as _;
#[tokio::main] #[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), neqo_bin::client::Error> { async fn main() -> Result<(), neqo_bin::client::Error> {
let args = neqo_bin::client::Args::parse(); let args = neqo_bin::client::Args::parse();

View File

@@ -6,7 +6,7 @@
use clap::Parser as _; use clap::Parser as _;
#[tokio::main] #[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), neqo_bin::server::Error> { async fn main() -> Result<(), neqo_bin::server::Error> {
let args = neqo_bin::server::Args::parse(); let args = neqo_bin::server::Args::parse();

View File

@@ -4,6 +4,8 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![expect(clippy::unwrap_used, reason = "This is example code.")]
//! An [HTTP 0.9](https://www.w3.org/Protocols/HTTP/AsImplemented.html) client implementation. //! An [HTTP 0.9](https://www.w3.org/Protocols/HTTP/AsImplemented.html) client implementation.
use std::{ use std::{
@@ -159,7 +161,11 @@ pub fn create_client(
client.set_ciphers(&ciphers)?; client.set_ciphers(&ciphers)?;
} }
client.set_qlog(qlog_new(args, hostname, client.odcid().unwrap())?); client.set_qlog(qlog_new(
args,
hostname,
client.odcid().ok_or(Error::InternalError)?,
)?);
Ok(client) Ok(client)
} }
@@ -189,7 +195,7 @@ impl super::Client for Connection {
fn process_multiple_input<'a>( fn process_multiple_input<'a>(
&mut self, &mut self,
dgrams: impl IntoIterator<Item = Datagram<&'a [u8]>>, dgrams: impl IntoIterator<Item = Datagram<&'a mut [u8]>>,
now: Instant, now: Instant,
) { ) {
self.process_multiple_input(dgrams, now); self.process_multiple_input(dgrams, now);
@@ -302,7 +308,7 @@ impl<'b> Handler<'b> {
} else { } else {
qdebug!( qdebug!(
"READ[{stream_id}]: {}", "READ[{stream_id}]: {}",
std::str::from_utf8(read_buffer).unwrap() std::str::from_utf8(read_buffer).map_err(|_| Error::InvalidInput)?
); );
} }
if fin { if fin {

View File

@@ -4,6 +4,8 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![expect(clippy::unwrap_used, reason = "This is example code.")]
//! An HTTP 3 client implementation. //! An HTTP 3 client implementation.
use std::{ use std::{
@@ -18,7 +20,7 @@ use std::{
time::Instant, time::Instant,
}; };
use neqo_common::{event::Provider, hex, qdebug, qinfo, qwarn, Datagram, Header}; use neqo_common::{event::Provider, hex, qdebug, qerror, qinfo, qwarn, Datagram, Header};
use neqo_crypto::{AuthenticationStatus, ResumptionToken}; use neqo_crypto::{AuthenticationStatus, ResumptionToken};
use neqo_http3::{Error, Http3Client, Http3ClientEvent, Http3Parameters, Http3State, Priority}; use neqo_http3::{Error, Http3Client, Http3ClientEvent, Http3Parameters, Http3State, Priority};
use neqo_transport::{ use neqo_transport::{
@@ -31,7 +33,7 @@ use super::{get_output_file, qlog_new, Args, CloseState, Res};
use crate::{send_data::SendData, STREAM_IO_BUFFER_SIZE}; use crate::{send_data::SendData, STREAM_IO_BUFFER_SIZE};
pub struct Handler<'a> { pub struct Handler<'a> {
#[allow(clippy::struct_field_names)] #[expect(clippy::struct_field_names, reason = "This name is more descriptive.")]
url_handler: UrlHandler<'a>, url_handler: UrlHandler<'a>,
token: Option<ResumptionToken>, token: Option<ResumptionToken>,
output_read_data: bool, output_read_data: bool,
@@ -137,7 +139,7 @@ impl super::Client for Http3Client {
fn process_multiple_input<'a>( fn process_multiple_input<'a>(
&mut self, &mut self,
dgrams: impl IntoIterator<Item = Datagram<&'a [u8]>>, dgrams: impl IntoIterator<Item = Datagram<&'a mut [u8]>>,
now: Instant, now: Instant,
) { ) {
self.process_multiple_input(dgrams, now); self.process_multiple_input(dgrams, now);
@@ -200,9 +202,11 @@ impl super::Handler for Handler<'_> {
qwarn!("Data on unexpected stream: {stream_id}"); qwarn!("Data on unexpected stream: {stream_id}");
} }
Some(handler) => loop { Some(handler) => loop {
let (sz, fin) = client let (sz, fin) = client.read_data(
.read_data(Instant::now(), stream_id, &mut self.read_buffer) Instant::now(),
.expect("Read should succeed"); stream_id,
&mut self.read_buffer,
)?;
handler.process_data_readable( handler.process_data_readable(
stream_id, stream_id,
@@ -270,7 +274,7 @@ trait StreamHandler {
fin: bool, fin: bool,
data: &[u8], data: &[u8],
output_read_data: bool, output_read_data: bool,
) -> Res<bool>; ) -> Res<()>;
fn process_data_writable(&mut self, client: &mut Http3Client, stream_id: StreamId); fn process_data_writable(&mut self, client: &mut Http3Client, stream_id: StreamId);
} }
@@ -291,12 +295,12 @@ impl StreamHandler for DownloadStreamHandler {
fin: bool, fin: bool,
data: &[u8], data: &[u8],
output_read_data: bool, output_read_data: bool,
) -> Res<bool> { ) -> Res<()> {
if let Some(out_file) = &mut self.out_file { if let Some(out_file) = &mut self.out_file {
if !data.is_empty() { if !data.is_empty() {
out_file.write_all(data)?; out_file.write_all(data)?;
} }
return Ok(true); return Ok(());
} else if !output_read_data { } else if !output_read_data {
qdebug!("READ[{stream_id}]: {} bytes", data.len()); qdebug!("READ[{stream_id}]: {} bytes", data.len());
} else if let Ok(txt) = std::str::from_utf8(data) { } else if let Ok(txt) = std::str::from_utf8(data) {
@@ -313,7 +317,7 @@ impl StreamHandler for DownloadStreamHandler {
} }
} }
Ok(true) Ok(())
} }
fn process_data_writable(&mut self, _client: &mut Http3Client, _stream_id: StreamId) {} fn process_data_writable(&mut self, _client: &mut Http3Client, _stream_id: StreamId) {}
@@ -335,18 +339,19 @@ impl StreamHandler for UploadStreamHandler {
_fin: bool, _fin: bool,
data: &[u8], data: &[u8],
_output_read_data: bool, _output_read_data: bool,
) -> Res<bool> { ) -> Res<()> {
if let Ok(txt) = std::str::from_utf8(data) { if let Ok(txt) = std::str::from_utf8(data) {
let trimmed_txt = txt.trim_end_matches(char::from(0)); let trimmed_txt = txt.trim_end_matches(char::from(0));
let parsed: usize = trimmed_txt.parse().unwrap(); let parsed: usize = trimmed_txt.parse().map_err(|_| Error::InvalidInput)?;
if parsed == self.data.len() { if parsed == self.data.len() {
let upload_time = Instant::now().duration_since(self.start); let upload_time = Instant::now().duration_since(self.start);
qinfo!("Stream ID: {stream_id:?}, Upload time: {upload_time:?}"); qinfo!("Stream ID: {stream_id:?}, Upload time: {upload_time:?}");
} }
Ok(())
} else { } else {
panic!("Unexpected data [{stream_id}]: 0x{}", hex(data)); qerror!("Unexpected data [{stream_id}]: 0x{}", hex(data));
Err(crate::client::Error::Http3Error(Error::InvalidInput))
} }
Ok(true)
} }
fn process_data_writable(&mut self, client: &mut Http3Client, stream_id: StreamId) { fn process_data_writable(&mut self, client: &mut Http3Client, stream_id: StreamId) {

View File

@@ -4,7 +4,7 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![allow(clippy::future_not_send)] #![expect(clippy::unwrap_used, reason = "This is example code.")]
use std::{ use std::{
collections::{HashMap, VecDeque}, collections::{HashMap, VecDeque},
@@ -104,7 +104,10 @@ type Res<T> = Result<T, Error>;
#[derive(Debug, Parser)] #[derive(Debug, Parser)]
#[command(author, version, about, long_about = None)] #[command(author, version, about, long_about = None)]
#[allow(clippy::struct_excessive_bools)] // Not a good use of that lint. #[expect(
clippy::struct_excessive_bools,
reason = "Not a good use of that lint."
)]
pub struct Args { pub struct Args {
#[command(flatten)] #[command(flatten)]
shared: SharedArgs, shared: SharedArgs,
@@ -180,7 +183,7 @@ pub struct Args {
impl Args { impl Args {
#[must_use] #[must_use]
#[cfg(any(test, feature = "bench"))] #[cfg(any(test, feature = "bench"))]
#[allow(clippy::missing_panics_doc)] #[expect(clippy::missing_panics_doc, reason = "This is example code.")]
pub fn new(requests: &[usize], upload: bool) -> Self { pub fn new(requests: &[usize], upload: bool) -> Self {
use std::str::FromStr as _; use std::str::FromStr as _;
Self { Self {
@@ -383,7 +386,7 @@ trait Client {
fn process_output(&mut self, now: Instant) -> Output; fn process_output(&mut self, now: Instant) -> Output;
fn process_multiple_input<'a>( fn process_multiple_input<'a>(
&mut self, &mut self,
dgrams: impl IntoIterator<Item = Datagram<&'a [u8]>>, dgrams: impl IntoIterator<Item = Datagram<&'a mut [u8]>>,
now: Instant, now: Instant,
); );
fn has_events(&self) -> bool; fn has_events(&self) -> bool;
@@ -431,17 +434,14 @@ impl<'a, H: Handler> Runner<'a, H> {
continue; continue;
} }
#[allow(clippy::match_same_arms)]
match (handler_done, self.client.is_closed()?) { match (handler_done, self.client.is_closed()?) {
// more work // more work; or no more work, already closing connection
(false, _) => {} (true, CloseState::Closing) | (false, _) => {}
// no more work, closing connection // no more work, closing connection
(true, CloseState::NotClosing) => { (true, CloseState::NotClosing) => {
self.client.close(Instant::now(), 0, "kthxbye!"); self.client.close(Instant::now(), 0, "kthxbye!");
continue; continue;
} }
// no more work, already closing connection
(true, CloseState::Closing) => {}
// no more work, connection closed, terminating // no more work, connection closed, terminating
(true, CloseState::Closed) => break, (true, CloseState::Closed) => break,
} }
@@ -464,10 +464,19 @@ impl<'a, H: Handler> Runner<'a, H> {
async fn process_output(&mut self) -> Result<(), io::Error> { async fn process_output(&mut self) -> Result<(), io::Error> {
loop { loop {
match self.client.process_output(Instant::now()) { match self.client.process_output(Instant::now()) {
Output::Datagram(dgram) => { Output::Datagram(dgram) => loop {
self.socket.writable().await?; // Optimistically attempt sending datagram. In case the OS
self.socket.send(&dgram)?; // buffer is full, wait till socket is writable then try
} // again.
match self.socket.send(&dgram) {
Ok(()) => break,
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
self.socket.writable().await?;
// Now try again.
}
e @ Err(_) => return e,
}
},
Output::Callback(new_timeout) => { Output::Callback(new_timeout) => {
qdebug!("Setting timeout of {new_timeout:?}"); qdebug!("Setting timeout of {new_timeout:?}");
self.timeout = Some(Box::pin(tokio::time::sleep(new_timeout))); self.timeout = Some(Box::pin(tokio::time::sleep(new_timeout)));
@@ -542,6 +551,12 @@ fn urls_by_origin(urls: &[Url]) -> impl Iterator<Item = ((Host, u16), VecDeque<U
}) })
} }
#[expect(
clippy::future_not_send,
clippy::missing_panics_doc,
clippy::missing_errors_doc,
reason = "This is example code."
)]
pub async fn client(mut args: Args) -> Res<()> { pub async fn client(mut args: Args) -> Res<()> {
neqo_common::log::init( neqo_common::log::init(
args.shared args.shared

View File

@@ -4,9 +4,6 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![allow(clippy::missing_panics_doc)]
#![allow(clippy::missing_errors_doc)]
use std::{ use std::{
fmt::{self, Display}, fmt::{self, Display},
net::{SocketAddr, ToSocketAddrs as _}, net::{SocketAddr, ToSocketAddrs as _},
@@ -168,8 +165,8 @@ impl QuicParameters {
assert_eq!( assert_eq!(
opt.is_some(), opt.is_some(),
addr.is_some(), addr.is_some(),
"unable to resolve '{}' to an {v} address", "unable to resolve '{:?}' to an {v} address",
opt.as_ref().unwrap(), opt.as_ref()?,
); );
addr addr
} }

View File

@@ -4,6 +4,8 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![expect(clippy::unwrap_used, reason = "This is example code.")]
use std::{borrow::Cow, cell::RefCell, collections::HashMap, fmt::Display, rc::Rc, time::Instant}; use std::{borrow::Cow, cell::RefCell, collections::HashMap, fmt::Display, rc::Rc, time::Instant};
use neqo_common::{event::Provider as _, hex, qdebug, qerror, qinfo, qwarn, Datagram}; use neqo_common::{event::Provider as _, hex, qdebug, qerror, qinfo, qwarn, Datagram};
@@ -55,10 +57,10 @@ impl HttpServer {
server.set_validation(ValidateAddress::Always); server.set_validation(ValidateAddress::Always);
} }
if args.ech { if args.ech {
let (sk, pk) = generate_ech_keys().expect("generate ECH keys"); let (sk, pk) = generate_ech_keys().map_err(|_| Error::Internal)?;
server server
.enable_ech(random::<1>()[0], "public.example", &sk, &pk) .enable_ech(random::<1>()[0], "public.example", &sk, &pk)
.expect("enable ECH"); .map_err(|_| Error::Internal)?;
let cfg = server.ech_config(); let cfg = server.ech_config();
qinfo!("ECHConfigList: {}", hex(cfg)); qinfo!("ECHConfigList: {}", hex(cfg));
} }
@@ -70,9 +72,9 @@ impl HttpServer {
read_state: HashMap::new(), read_state: HashMap::new(),
is_qns_test, is_qns_test,
regex: if is_qns_test { regex: if is_qns_test {
Regex::new(r"GET +/(\S+)(?:\r)?\n").unwrap() Regex::new(r"GET +/(\S+)(?:\r)?\n").map_err(|_| Error::Internal)?
} else { } else {
Regex::new(r"GET +/(\d+)(?:\r)?\n").unwrap() Regex::new(r"GET +/(\d+)(?:\r)?\n").map_err(|_| Error::Internal)?
}, },
read_buffer: vec![0; STREAM_IO_BUFFER_SIZE], read_buffer: vec![0; STREAM_IO_BUFFER_SIZE],
}) })
@@ -185,14 +187,15 @@ impl HttpServer {
} }
impl super::HttpServer for HttpServer { impl super::HttpServer for HttpServer {
fn process(&mut self, dgram: Option<Datagram<&[u8]>>, now: Instant) -> Output { fn process(&mut self, dgram: Option<Datagram<&mut [u8]>>, now: Instant) -> Output {
self.server.process(dgram, now) self.server.process(dgram, now)
} }
fn process_events(&mut self, now: Instant) { fn process_events(&mut self, now: Instant) {
// `ActiveConnectionRef` `Hash` implementation doesnt access any of the interior mutable #[expect(
// types. clippy::mutable_key_type,
#[allow(clippy::mutable_key_type)] reason = "ActiveConnectionRef::Hash doesn't access any of the interior mutable types"
)]
let active_conns = self.server.active_connections(); let active_conns = self.server.active_connections();
for acr in active_conns { for acr in active_conns {
loop { loop {

View File

@@ -4,6 +4,8 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![expect(clippy::unwrap_used, reason = "This is example code.")]
use std::{ use std::{
cell::RefCell, cell::RefCell,
collections::HashMap, collections::HashMap,
@@ -80,7 +82,7 @@ impl Display for HttpServer {
} }
impl super::HttpServer for HttpServer { impl super::HttpServer for HttpServer {
fn process(&mut self, dgram: Option<Datagram<&[u8]>>, now: Instant) -> neqo_http3::Output { fn process(&mut self, dgram: Option<Datagram<&mut [u8]>>, now: Instant) -> neqo_http3::Output {
self.server.process(dgram, now) self.server.process(dgram, now)
} }

View File

@@ -4,7 +4,17 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![allow(clippy::future_not_send)] #![allow(
clippy::module_name_repetitions,
reason = "<https://github.com/mozilla/neqo/issues/2284#issuecomment-2782711813>"
)]
#![expect(
clippy::unwrap_used,
clippy::future_not_send,
clippy::missing_errors_doc,
clippy::missing_panics_doc,
reason = "This is example code."
)]
use std::{ use std::{
cell::RefCell, cell::RefCell,
@@ -190,14 +200,12 @@ fn qns_read_response(filename: &str) -> Result<Vec<u8>, io::Error> {
fs::read(path) fs::read(path)
} }
#[allow(clippy::module_name_repetitions)]
pub trait HttpServer: Display { pub trait HttpServer: Display {
fn process(&mut self, dgram: Option<Datagram<&[u8]>>, now: Instant) -> Output; fn process(&mut self, dgram: Option<Datagram<&mut [u8]>>, now: Instant) -> Output;
fn process_events(&mut self, now: Instant); fn process_events(&mut self, now: Instant);
fn has_events(&self) -> bool; fn has_events(&self) -> bool;
} }
#[allow(clippy::module_name_repetitions)]
pub struct ServerRunner { pub struct ServerRunner {
now: Box<dyn Fn() -> Instant>, now: Box<dyn Fn() -> Instant>,
server: Box<dyn HttpServer>, server: Box<dyn HttpServer>,
@@ -242,14 +250,25 @@ impl ServerRunner {
timeout: &mut Option<Pin<Box<Sleep>>>, timeout: &mut Option<Pin<Box<Sleep>>>,
sockets: &mut [(SocketAddr, crate::udp::Socket)], sockets: &mut [(SocketAddr, crate::udp::Socket)],
now: &dyn Fn() -> Instant, now: &dyn Fn() -> Instant,
mut input_dgram: Option<Datagram<&[u8]>>, mut input_dgram: Option<Datagram<&mut [u8]>>,
) -> Result<(), io::Error> { ) -> Result<(), io::Error> {
loop { loop {
match server.process(input_dgram.take(), now()) { match server.process(input_dgram.take(), now()) {
Output::Datagram(dgram) => { Output::Datagram(dgram) => {
let socket = Self::find_socket(sockets, dgram.source()); let socket = Self::find_socket(sockets, dgram.source());
socket.writable().await?; loop {
socket.send(&dgram)?; // Optimistically attempt sending datagram. In case the
// OS buffer is full, wait till socket is writable then
// try again.
match socket.send(&dgram) {
Ok(()) => break,
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
socket.writable().await?;
// Now try again.
}
e @ Err(_) => return e,
}
}
} }
Output::Callback(new_timeout) => { Output::Callback(new_timeout) => {
qdebug!("Setting timeout of {new_timeout:?}"); qdebug!("Setting timeout of {new_timeout:?}");

View File

@@ -4,9 +4,11 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![expect(clippy::missing_errors_doc, reason = "Passing up tokio errors.")]
use std::{io, net::SocketAddr}; use std::{io, net::SocketAddr};
use neqo_common::Datagram; use neqo_common::{qdebug, Datagram};
use neqo_udp::{DatagramIter, RecvBuf}; use neqo_udp::{DatagramIter, RecvBuf};
/// Ideally this would live in [`neqo-udp`]. [`neqo-udp`] is used in Firefox. /// Ideally this would live in [`neqo-udp`]. [`neqo-udp`] is used in Firefox.
@@ -24,10 +26,36 @@ pub struct Socket {
impl Socket { impl Socket {
/// Create a new [`Socket`] bound to the provided address, not managed externally. /// Create a new [`Socket`] bound to the provided address, not managed externally.
pub fn bind<A: std::net::ToSocketAddrs>(addr: A) -> Result<Self, io::Error> { pub fn bind<A: std::net::ToSocketAddrs>(addr: A) -> Result<Self, io::Error> {
const ONE_MB: usize = 1 << 20;
let socket = std::net::UdpSocket::bind(addr)?; let socket = std::net::UdpSocket::bind(addr)?;
let state = quinn_udp::UdpSocketState::new((&socket).into())?;
let send_buf_before = state.send_buffer_size((&socket).into())?;
// FIXME: We need to experiment if increasing this actually improves performance.
// Also, on BSD and Apple targets, this seems to increase the `net.inet.udp.maxdgram`
// sysctl, which is not the same as the socket buffer.
// if send_buf_before < ONE_MB {
// state.set_send_buffer_size((&socket).into(), ONE_MB)?;
// let send_buf_after = state.send_buffer_size((&socket).into())?;
// qdebug!("Increasing socket send buffer size from {send_buf_before} to {ONE_MB}, now:
// {send_buf_after}"); } else {
// qdebug!("Default socket send buffer size is {send_buf_before}, not changing");
// }
qdebug!("Default socket send buffer size is {send_buf_before}");
let recv_buf_before = state.recv_buffer_size((&socket).into())?;
if recv_buf_before < ONE_MB {
// Same as Firefox.
// <https://searchfox.org/mozilla-central/rev/fa5b44a4ea5c98b6a15f39638ea4cd04dc271f3d/modules/libpref/init/StaticPrefList.yaml#13474-13477>
state.set_recv_buffer_size((&socket).into(), ONE_MB)?;
let recv_buf_after = state.recv_buffer_size((&socket).into())?;
qdebug!("Increasing socket recv buffer size from {recv_buf_before} to {ONE_MB}, now: {recv_buf_after}");
} else {
qdebug!("Default socket receive buffer size is {recv_buf_before}, not changing");
}
Ok(Self { Ok(Self {
state: quinn_udp::UdpSocketState::new((&socket).into())?, state,
inner: tokio::net::UdpSocket::from_std(socket)?, inner: tokio::net::UdpSocket::from_std(socket)?,
}) })
} }

View File

@@ -1 +1 @@
{"files":{"Cargo.toml":"34eca5cf0f6408d36c215d7ca439aad5a496fb154bd4d6cb3faf0ddbcf7d2c34","benches/decoder.rs":"e7fab506974d7b6b4f41fe51036151b15f0d693b7e45b80cf9e2141bb1f6298f","build.rs":"ee5dc521f3d8e18c2b617192d6b6e678f7f2f9886fdd34c0c1c5ef841419b248","src/codec.rs":"8aea4a5041d1b06e4928d29fefcc8166d515b72f9a744e572d3c92b90e9ea268","src/datagram.rs":"e8bf176d3b120028731388c17344d03b8195e5fd70f4d03e37144ac5ae5951f5","src/event.rs":"289cf8e265c33e7cded58820ac81e5b575e3f84dd52fa18b0761f4094fb361c0","src/fuzz.rs":"9e0f2dca1832ef49b93b214e8d5f1ca2f5f8cb84a856fead344f62a722c370db","src/header.rs":"a316f48735375da268142ad9ac3430d240162c9df67114546f4a094c25a10024","src/hrtime.rs":"754f90ae3f3839e59ccaa34a9f07a2742ec7080f2df539f4b7691c7771dd4856","src/incrdecoder.rs":"2128bb6aa737932b484cdff8213efe8eac46004ef2d9219b6f48ac4d664297ac","src/lib.rs":"002f6a740464b618036e699db4ebea1fc803b9c9c154c0478f6f8721932f4300","src/log.rs":"2ea3f00b1776e085cbf6b46dc7489259b4e58a2dada7edaf94e746de3defb222","src/qlog.rs":"f335b4f4340ae0bfaeb6742bd5a2cb8b6f16292a40b410ed3f72ce309b171106","src/tos.rs":"206410cf0d164a0c1a1c3de19ddcd224e311601707bedc7af100a30a950d12f9","tests/log.rs":"671a963f938599b76c0678f3931592e1782765ed4ea13e6cbfec2ab97c36e2c9"},"package":null} {"files":{"Cargo.toml":"eb681eeb4daecac76e7fc32d2ec1b1d28d37acbfbf4e800e28533a3f9426db58","benches/decoder.rs":"3b03abd08c064d11c07e6d4eaf041873c282adf93e9bc76e40f2f0137e342929","build.rs":"d9accad1f92a1d82aff73a588269342db882918173e8d9b2b914c514e42e2839","src/codec.rs":"b56abcf6fc76d7c24a6f25e0df556fa3c47a37249b43ae97451944082d344a30","src/datagram.rs":"88ef237b95b4ce10abe5e8e4740a4a8178f19146fc82c49053d3b62819ac428a","src/event.rs":"289cf8e265c33e7cded58820ac81e5b575e3f84dd52fa18b0761f4094fb361c0","src/fuzz.rs":"9e0f2dca1832ef49b93b214e8d5f1ca2f5f8cb84a856fead344f62a722c370db","src/header.rs":"b561dffd52ec8909297ec67ac5f75150dfca73399111e709cf6ea7a34998fca5","src/hrtime.rs":"7096fd12ccc33566aac58f19a4e7439690b2fa38c258584946f76a422ab98087","src/incrdecoder.rs":"029081f0ae19db49c3278c865f22584fc24315169e5ffc1bd5a0087e8c0d38b9","src/lib.rs":"5f15b8b96cd8935e4cc231bb37d45458794ffe23eea0db1b37e42ea7346ac10d","src/log.rs":"61a9b24bf6bf1493da67082bcf7fef8fe55f0a23d7f2a9ad13748982c54c85e2","src/qlog.rs":"3c6f403f0ad2b34fc7ba63b1338bc0855bc232a2b5dd5c1039846b10af2b1d35","src/tos.rs":"108ebfcadf172aae1ba1c722316080b70ec017bf9c866f1ab8d315231d04fd09","tests/log.rs":"671a963f938599b76c0678f3931592e1782765ed4ea13e6cbfec2ab97c36e2c9"},"package":null}

View File

@@ -11,9 +11,9 @@
[package] [package]
edition = "2021" edition = "2021"
rust-version = "1.76.0" rust-version = "1.82.0"
name = "neqo-common" name = "neqo-common"
version = "0.12.2" version = "0.13.1"
authors = ["The Neqo Authors <necko@mozilla.com>"] authors = ["The Neqo Authors <necko@mozilla.com>"]
build = "build.rs" build = "build.rs"
autolib = false autolib = false
@@ -44,7 +44,7 @@ bench = [
"neqo-crypto/bench", "neqo-crypto/bench",
"test-fixture/bench", "test-fixture/bench",
] ]
build-fuzzing-corpus = ["hex"] build-fuzzing-corpus = ["hex/alloc"]
ci = [] ci = []
test-fixture = [] test-fixture = []
@@ -73,7 +73,6 @@ default-features = false
[dependencies.hex] [dependencies.hex]
version = "0.4" version = "0.4"
features = ["alloc"]
optional = true optional = true
default-features = false default-features = false
@@ -82,7 +81,12 @@ version = "0.4"
default-features = false default-features = false
[dependencies.qlog] [dependencies.qlog]
version = "0.13" version = "0.15.1"
default-features = false
[dependencies.strum]
version = "0.26"
features = ["derive"]
default-features = false default-features = false
[dev-dependencies.criterion] [dev-dependencies.criterion]
@@ -94,7 +98,6 @@ path = "../neqo-crypto"
[dev-dependencies.regex] [dev-dependencies.regex]
version = "1.9" version = "1.9"
features = ["unicode-perl"]
default-features = false default-features = false
[dev-dependencies.test-fixture] [dev-dependencies.test-fixture]
@@ -106,22 +109,56 @@ features = ["Win32_Media"]
default-features = false default-features = false
[lints.clippy] [lints.clippy]
allow_attributes = "warn"
allow_attributes_without_reason = "warn"
cfg_not_test = "warn" cfg_not_test = "warn"
clone_on_ref_ptr = "warn" clone_on_ref_ptr = "warn"
create_dir = "warn" create_dir = "warn"
dbg_macro = "warn"
empty_drop = "warn"
empty_enum_variants_with_brackets = "warn"
filetype_is_file = "warn"
float_cmp_const = "warn"
fn_to_numeric_cast_any = "warn"
get_unwrap = "warn" get_unwrap = "warn"
if_then_some_else_none = "warn" if_then_some_else_none = "warn"
infinite_loop = "warn"
large_include_file = "warn"
let_underscore_must_use = "warn"
let_underscore_untyped = "warn"
literal_string_with_formatting_args = "allow"
lossy_float_literal = "warn"
mem_forget = "warn"
mixed_read_write_in_expression = "warn"
module_name_repetitions = "warn"
multiple_crate_versions = "allow" multiple_crate_versions = "allow"
multiple_inherent_impl = "warn" multiple_inherent_impl = "warn"
mutex_atomic = "warn"
mutex_integer = "warn"
needless_raw_strings = "warn"
pathbuf_init_then_push = "warn" pathbuf_init_then_push = "warn"
pub_without_shorthand = "warn"
rc_buffer = "warn"
rc_mutex = "warn"
redundant_type_annotations = "warn" redundant_type_annotations = "warn"
ref_patterns = "warn" ref_patterns = "warn"
renamed_function_params = "warn" renamed_function_params = "warn"
rest_pat_in_fully_bound_structs = "warn"
self_named_module_files = "warn"
semicolon_inside_block = "warn" semicolon_inside_block = "warn"
string_lit_chars_any = "warn"
string_to_string = "warn"
suspicious_xor_used_as_pow = "warn"
try_err = "warn" try_err = "warn"
unnecessary_safety_comment = "warn"
unnecessary_safety_doc = "warn"
unnecessary_self_imports = "warn"
unneeded_field_pattern = "warn" unneeded_field_pattern = "warn"
unused_result_ok = "warn" unused_result_ok = "warn"
unused_trait_names = "warn" unused_trait_names = "warn"
unwrap_in_result = "warn"
unwrap_used = "warn"
verbose_file_reads = "warn"
[lints.clippy.cargo] [lints.clippy.cargo]
level = "warn" level = "warn"
@@ -138,7 +175,6 @@ priority = -1
[lints.rust] [lints.rust]
absolute_paths_not_starting_with_crate = "warn" absolute_paths_not_starting_with_crate = "warn"
ambiguous_negative_literals = "warn" ambiguous_negative_literals = "warn"
closure_returning_async_block = "warn"
explicit_outlives_requirements = "warn" explicit_outlives_requirements = "warn"
macro_use_extern_crate = "warn" macro_use_extern_crate = "warn"
missing_abi = "warn" missing_abi = "warn"
@@ -150,3 +186,4 @@ unit_bindings = "warn"
unused_import_braces = "warn" unused_import_braces = "warn"
unused_lifetimes = "warn" unused_lifetimes = "warn"
unused_macro_rules = "warn" unused_macro_rules = "warn"
unused_qualifications = "warn"

View File

@@ -4,6 +4,8 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![expect(clippy::unwrap_used, reason = "OK in a bench.")]
use criterion::{black_box, criterion_group, criterion_main, Criterion}; use criterion::{black_box, criterion_group, criterion_main, Criterion};
use neqo_common::Decoder; use neqo_common::Decoder;
use neqo_crypto::{init, randomize}; use neqo_crypto::{init, randomize};

View File

@@ -4,6 +4,8 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![expect(clippy::unwrap_used, reason = "OK in a build script.")]
use std::env; use std::env;
fn main() { fn main() {

View File

@@ -47,6 +47,7 @@ impl<'a> Decoder<'a> {
/// Only use this for tests because we panic rather than reporting a result. /// Only use this for tests because we panic rather than reporting a result.
#[cfg(any(test, feature = "test-fixture"))] #[cfg(any(test, feature = "test-fixture"))]
fn skip_inner(&mut self, n: Option<u64>) { fn skip_inner(&mut self, n: Option<u64>) {
#[expect(clippy::unwrap_used, reason = "Only used in tests.")]
self.skip(usize::try_from(n.expect("invalid length")).unwrap()); self.skip(usize::try_from(n.expect("invalid length")).unwrap());
} }
@@ -111,7 +112,7 @@ impl<'a> Decoder<'a> {
/// unsigned integer types: `u8`, `u16`, `u32`, or `u64`. /// unsigned integer types: `u8`, `u16`, `u32`, or `u64`.
/// Signed types will fail if the high bit is set. /// Signed types will fail if the high bit is set.
pub fn decode_uint<T: TryFrom<u64>>(&mut self) -> Option<T> { pub fn decode_uint<T: TryFrom<u64>>(&mut self) -> Option<T> {
let v = self.decode_n(std::mem::size_of::<T>()); let v = self.decode_n(size_of::<T>());
v.and_then(|v| T::try_from(v).ok()) v.and_then(|v| T::try_from(v).ok())
} }
@@ -161,7 +162,6 @@ impl<'a> Decoder<'a> {
// Implement `AsRef` for `Decoder` so that values can be examined without // Implement `AsRef` for `Decoder` so that values can be examined without
// moving the cursor. // moving the cursor.
impl<'a> AsRef<[u8]> for Decoder<'a> { impl<'a> AsRef<[u8]> for Decoder<'a> {
#[must_use]
fn as_ref(&self) -> &'a [u8] { fn as_ref(&self) -> &'a [u8] {
&self.buf[self.offset..] &self.buf[self.offset..]
} }
@@ -174,7 +174,6 @@ impl Debug for Decoder<'_> {
} }
impl<'a> From<&'a [u8]> for Decoder<'a> { impl<'a> From<&'a [u8]> for Decoder<'a> {
#[must_use]
fn from(buf: &'a [u8]) -> Self { fn from(buf: &'a [u8]) -> Self {
Decoder::new(buf) Decoder::new(buf)
} }
@@ -184,14 +183,12 @@ impl<'a, T> From<&'a T> for Decoder<'a>
where where
T: AsRef<[u8]>, T: AsRef<[u8]>,
{ {
#[must_use]
fn from(buf: &'a T) -> Self { fn from(buf: &'a T) -> Self {
Decoder::new(buf.as_ref()) Decoder::new(buf.as_ref())
} }
} }
impl<'b> PartialEq<Decoder<'b>> for Decoder<'_> { impl<'b> PartialEq<Decoder<'b>> for Decoder<'_> {
#[must_use]
fn eq(&self, other: &Decoder<'b>) -> bool { fn eq(&self, other: &Decoder<'b>) -> bool {
self.buf == other.buf self.buf == other.buf
} }
@@ -227,7 +224,7 @@ impl Encoder {
/// When `len` doesn't fit in a `u64`. /// When `len` doesn't fit in a `u64`.
#[must_use] #[must_use]
pub fn vvec_len(len: usize) -> usize { pub fn vvec_len(len: usize) -> usize {
Self::varint_len(u64::try_from(len).unwrap()) + len Self::varint_len(u64::try_from(len).expect("usize should fit into u64")) + len
} }
/// Default construction of an empty buffer. /// Default construction of an empty buffer.
@@ -286,6 +283,7 @@ impl Encoder {
let mut enc = Self::with_capacity(cap); let mut enc = Self::with_capacity(cap);
for i in 0..cap { for i in 0..cap {
#[expect(clippy::unwrap_used, reason = "Only used in tests.")]
let v = u8::from_str_radix(&s[i * 2..i * 2 + 2], 16).unwrap(); let v = u8::from_str_radix(&s[i * 2..i * 2 + 2], 16).unwrap();
enc.encode_byte(v); enc.encode_byte(v);
} }
@@ -341,8 +339,11 @@ impl Encoder {
/// ///
/// When `v` is longer than 2^64. /// When `v` is longer than 2^64.
pub fn encode_vec(&mut self, n: usize, v: &[u8]) -> &mut Self { pub fn encode_vec(&mut self, n: usize, v: &[u8]) -> &mut Self {
self.encode_uint(n, u64::try_from(v.as_ref().len()).unwrap()) self.encode_uint(
.encode(v) n,
u64::try_from(v.as_ref().len()).expect("v is longer than 2^64"),
)
.encode(v)
} }
/// Encode a vector in TLS style using a closure for the contents. /// Encode a vector in TLS style using a closure for the contents.
@@ -350,7 +351,10 @@ impl Encoder {
/// # Panics /// # Panics
/// ///
/// When `f()` returns a length larger than `2^8n`. /// When `f()` returns a length larger than `2^8n`.
#[allow(clippy::cast_possible_truncation)] #[expect(
clippy::cast_possible_truncation,
reason = "AND'ing with 0xff makes this OK."
)]
pub fn encode_vec_with<F: FnOnce(&mut Self)>(&mut self, n: usize, f: F) -> &mut Self { pub fn encode_vec_with<F: FnOnce(&mut Self)>(&mut self, n: usize, f: F) -> &mut Self {
let start = self.buf.len(); let start = self.buf.len();
self.buf.resize(self.buf.len() + n, 0); self.buf.resize(self.buf.len() + n, 0);
@@ -369,7 +373,7 @@ impl Encoder {
/// ///
/// When `v` is longer than 2^64. /// When `v` is longer than 2^64.
pub fn encode_vvec(&mut self, v: &[u8]) -> &mut Self { pub fn encode_vvec(&mut self, v: &[u8]) -> &mut Self {
self.encode_varint(u64::try_from(v.as_ref().len()).unwrap()) self.encode_varint(u64::try_from(v.as_ref().len()).expect("v is longer than 2^64"))
.encode(v) .encode(v)
} }
@@ -447,14 +451,12 @@ impl AsMut<[u8]> for Encoder {
} }
impl<'a> From<Decoder<'a>> for Encoder { impl<'a> From<Decoder<'a>> for Encoder {
#[must_use]
fn from(dec: Decoder<'a>) -> Self { fn from(dec: Decoder<'a>) -> Self {
Self::from(&dec.buf[dec.offset..]) Self::from(&dec.buf[dec.offset..])
} }
} }
impl From<&[u8]> for Encoder { impl From<&[u8]> for Encoder {
#[must_use]
fn from(buf: &[u8]) -> Self { fn from(buf: &[u8]) -> Self {
Self { Self {
buf: Vec::from(buf), buf: Vec::from(buf),
@@ -463,7 +465,6 @@ impl From<&[u8]> for Encoder {
} }
impl From<Encoder> for Vec<u8> { impl From<Encoder> for Vec<u8> {
#[must_use]
fn from(buf: Encoder) -> Self { fn from(buf: Encoder) -> Self {
buf.buf buf.buf
} }
@@ -664,6 +665,7 @@ mod tests {
} }
#[test] #[test]
#[cfg(target_pointer_width = "64")] // Test does not compile on 32-bit targets.
#[should_panic(expected = "Varint value too large")] #[should_panic(expected = "Varint value too large")]
fn encoded_vvec_length_oob() { fn encoded_vvec_length_oob() {
_ = Encoder::vvec_len(1 << 62); _ = Encoder::vvec_len(1 << 62);

View File

@@ -4,7 +4,10 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
use std::{net::SocketAddr, ops::Deref}; use std::{
net::SocketAddr,
ops::{Deref, DerefMut},
};
use crate::{hex_with_len, IpTos}; use crate::{hex_with_len, IpTos};
@@ -47,7 +50,6 @@ impl<D: AsRef<[u8]>> Datagram<D> {
} }
} }
#[cfg(test)]
impl<D: AsMut<[u8]> + AsRef<[u8]>> AsMut<[u8]> for Datagram<D> { impl<D: AsMut<[u8]> + AsRef<[u8]>> AsMut<[u8]> for Datagram<D> {
fn as_mut(&mut self) -> &mut [u8] { fn as_mut(&mut self) -> &mut [u8] {
self.d.as_mut() self.d.as_mut()
@@ -65,9 +67,14 @@ impl Datagram<Vec<u8>> {
} }
} }
impl<D: AsRef<[u8]> + AsMut<[u8]>> DerefMut for Datagram<D> {
fn deref_mut(&mut self) -> &mut Self::Target {
AsMut::<[u8]>::as_mut(self)
}
}
impl<D: AsRef<[u8]>> Deref for Datagram<D> { impl<D: AsRef<[u8]>> Deref for Datagram<D> {
type Target = [u8]; type Target = [u8];
#[must_use]
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
AsRef::<[u8]>::as_ref(self) AsRef::<[u8]>::as_ref(self)
} }
@@ -86,9 +93,9 @@ impl<D: AsRef<[u8]>> std::fmt::Debug for Datagram<D> {
} }
} }
impl<'a> Datagram<&'a [u8]> { impl<'a> Datagram<&'a mut [u8]> {
#[must_use] #[must_use]
pub const fn from_slice(src: SocketAddr, dst: SocketAddr, tos: IpTos, d: &'a [u8]) -> Self { pub fn from_slice(src: SocketAddr, dst: SocketAddr, tos: IpTos, d: &'a mut [u8]) -> Self {
Self { src, dst, tos, d } Self { src, dst, tos, d }
} }

View File

@@ -36,11 +36,21 @@ impl Header {
) )
} }
#[allow(
clippy::allow_attributes,
clippy::missing_const_for_fn,
reason = "TODO: False positive on nightly."
)]
#[must_use] #[must_use]
pub fn name(&self) -> &str { pub fn name(&self) -> &str {
&self.name &self.name
} }
#[allow(
clippy::allow_attributes,
clippy::missing_const_for_fn,
reason = "TODO: False positive on nightly."
)]
#[must_use] #[must_use]
pub fn value(&self) -> &str { pub fn value(&self) -> &str {
&self.value &self.value

View File

@@ -70,7 +70,7 @@ impl PeriodSet {
fn min(&self) -> Option<Period> { fn min(&self) -> Option<Period> {
for (i, v) in self.counts.iter().enumerate() { for (i, v) in self.counts.iter().enumerate() {
if *v > 0 { if *v > 0 {
return Some(Period(u8::try_from(i).unwrap() + Period::MIN.0)); return Some(Period(u8::try_from(i).ok()? + Period::MIN.0));
} }
} }
None None
@@ -78,7 +78,7 @@ impl PeriodSet {
} }
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
#[allow(non_camel_case_types)] #[expect(non_camel_case_types, reason = "These are C types.")]
mod mac { mod mac {
use std::ptr::addr_of_mut; use std::ptr::addr_of_mut;
@@ -124,9 +124,9 @@ mod mac {
} }
const THREAD_TIME_CONSTRAINT_POLICY: thread_policy_flavor_t = 2; const THREAD_TIME_CONSTRAINT_POLICY: thread_policy_flavor_t = 2;
#[allow(clippy::cast_possible_truncation)] #[expect(clippy::cast_possible_truncation, reason = "These are C types.")]
const THREAD_TIME_CONSTRAINT_POLICY_COUNT: mach_msg_type_number_t = const THREAD_TIME_CONSTRAINT_POLICY_COUNT: mach_msg_type_number_t =
(std::mem::size_of::<thread_time_constraint_policy>() / std::mem::size_of::<integer_t>()) (size_of::<thread_time_constraint_policy>() / size_of::<integer_t>())
as mach_msg_type_number_t; as mach_msg_type_number_t;
// These function definitions are taken from a comment in <thread_policy.h>. // These function definitions are taken from a comment in <thread_policy.h>.
@@ -179,7 +179,11 @@ mod mac {
/// Create a realtime policy and set it. /// Create a realtime policy and set it.
pub fn set_realtime(base: f64) { pub fn set_realtime(base: f64) {
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] #[expect(
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
reason = "These are C types."
)]
let policy = thread_time_constraint_policy { let policy = thread_time_constraint_policy {
period: base as u32, // Base interval period: base as u32, // Base interval
computation: (base * 0.5) as u32, computation: (base * 0.5) as u32,
@@ -302,7 +306,10 @@ impl Time {
} }
#[cfg(all(not(target_os = "macos"), not(target_os = "windows")))] #[cfg(all(not(target_os = "macos"), not(target_os = "windows")))]
#[allow(clippy::unused_self)] #[expect(
clippy::unused_self,
reason = "Not used on platforms other than macOS and Windows."
)]
const fn start(&self) {} const fn start(&self) {}
#[cfg(windows)] #[cfg(windows)]
@@ -313,7 +320,10 @@ impl Time {
} }
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]
#[allow(clippy::unused_self)] #[expect(
clippy::unused_self,
reason = "Not used on platforms other than Windows."
)]
const fn stop(&self) {} const fn stop(&self) {}
fn update(&mut self) { fn update(&mut self) {
@@ -372,7 +382,8 @@ impl Drop for Time {
// Only run these tests in CI on Linux, where the timer accuracies are OK enough to pass the tests, // Only run these tests in CI on Linux, where the timer accuracies are OK enough to pass the tests,
// but only when not running sanitizers. // but only when not running sanitizers.
#[cfg(all(test, target_os = "linux", not(neqo_sanitize)))] #[cfg(all(target_os = "linux", not(neqo_sanitize)))]
#[cfg(test)]
mod test { mod test {
use std::{ use std::{
thread::{sleep, spawn}, thread::{sleep, spawn},

View File

@@ -31,7 +31,7 @@ impl IncrementalDecoderUint {
if amount < 8 { if amount < 8 {
self.v <<= amount * 8; self.v <<= amount * 8;
} }
self.v |= dv.decode_n(amount).unwrap(); self.v |= dv.decode_n(amount)?;
*r -= amount; *r -= amount;
(*r == 0).then_some(self.v) (*r == 0).then_some(self.v)
} else { } else {
@@ -89,7 +89,7 @@ impl IncrementalDecoderBuffer {
/// Never; but rust doesn't know that. /// Never; but rust doesn't know that.
pub fn consume(&mut self, dv: &mut Decoder) -> Option<Vec<u8>> { pub fn consume(&mut self, dv: &mut Decoder) -> Option<Vec<u8>> {
let amount = min(self.remaining, dv.remaining()); let amount = min(self.remaining, dv.remaining());
let b = dv.decode(amount).unwrap(); let b = dv.decode(amount)?;
self.v.extend_from_slice(b); self.v.extend_from_slice(b);
self.remaining -= amount; self.remaining -= amount;
(self.remaining == 0).then(|| mem::take(&mut self.v)) (self.remaining == 0).then(|| mem::take(&mut self.v))

View File

@@ -4,8 +4,6 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![allow(clippy::module_name_repetitions)] // This lint doesn't work here.
mod codec; mod codec;
mod datagram; mod datagram;
pub mod event; pub mod event;
@@ -21,6 +19,7 @@ pub mod tos;
use std::fmt::Write as _; use std::fmt::Write as _;
use enum_map::Enum; use enum_map::Enum;
use strum::Display;
#[cfg(feature = "build-fuzzing-corpus")] #[cfg(feature = "build-fuzzing-corpus")]
pub use self::fuzz::write_item_to_fuzzing_corpus; pub use self::fuzz::write_item_to_fuzzing_corpus;
@@ -81,7 +80,7 @@ pub const fn const_min(a: usize, b: usize) -> usize {
[a, b][(a >= b) as usize] [a, b][(a >= b) as usize]
} }
#[derive(Debug, PartialEq, Eq, Copy, Clone, Enum)] #[derive(Debug, PartialEq, Eq, Copy, Clone, Enum, Display)]
/// Client or Server. /// Client or Server.
pub enum Role { pub enum Role {
Client, Client,
@@ -98,12 +97,6 @@ impl Role {
} }
} }
impl ::std::fmt::Display for Role {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(f, "{self:?}")
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MessageType { pub enum MessageType {
Request, Request,

View File

@@ -4,8 +4,6 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![allow(clippy::module_name_repetitions)]
use std::{ use std::{
io::Write as _, io::Write as _,
sync::{Once, OnceLock}, sync::{Once, OnceLock},

View File

@@ -4,6 +4,11 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![allow(
clippy::module_name_repetitions,
reason = "<https://github.com/mozilla/neqo/issues/2284#issuecomment-2782711813>"
)]
use std::{ use std::{
cell::RefCell, cell::RefCell,
fmt::{self, Display}, fmt::{self, Display},
@@ -20,7 +25,6 @@ use qlog::{
use crate::Role; use crate::Role;
#[allow(clippy::module_name_repetitions)]
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct NeqoQlog { pub struct NeqoQlog {
inner: Rc<RefCell<Option<NeqoQlogShared>>>, inner: Rc<RefCell<Option<NeqoQlogShared>>>,

View File

@@ -4,26 +4,29 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![allow(
clippy::module_name_repetitions,
reason = "<https://github.com/mozilla/neqo/issues/2284#issuecomment-2782711813>"
)]
use std::fmt::Debug; use std::fmt::Debug;
use enum_map::Enum; use enum_map::Enum;
use strum::{EnumIter, FromRepr};
/// ECN (Explicit Congestion Notification) codepoints mapped to the /// ECN (Explicit Congestion Notification) codepoints mapped to the
/// lower 2 bits of the TOS field. /// lower 2 bits of the TOS field.
/// <https://www.iana.org/assignments/dscp-registry/dscp-registry.xhtml> /// <https://www.iana.org/assignments/dscp-registry/dscp-registry.xhtml>
#[derive(Copy, Clone, PartialEq, Eq, Enum, Default, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Enum, Default, Debug, FromRepr, EnumIter)]
#[repr(u8)] #[repr(u8)]
pub enum IpTosEcn { pub enum IpTosEcn {
#[default] #[default]
/// Not-ECT, Not ECN-Capable Transport, RFC3168 /// Not-ECT, Not ECN-Capable Transport, RFC3168
NotEct = 0b00, NotEct = 0b00,
/// ECT(1), ECN-Capable Transport(1), RFC8311 and RFC9331 /// ECT(1), ECN-Capable Transport(1), RFC8311 and RFC9331
Ect1 = 0b01, Ect1 = 0b01,
/// ECT(0), ECN-Capable Transport(0), RFC3168 /// ECT(0), ECN-Capable Transport(0), RFC3168
Ect0 = 0b10, Ect0 = 0b10,
/// CE, Congestion Experienced, RFC3168 /// CE, Congestion Experienced, RFC3168
Ce = 0b11, Ce = 0b11,
} }
@@ -36,13 +39,7 @@ impl From<IpTosEcn> for u8 {
impl From<u8> for IpTosEcn { impl From<u8> for IpTosEcn {
fn from(v: u8) -> Self { fn from(v: u8) -> Self {
match v & 0b0000_0011 { Self::from_repr(v & 0b0000_0011).expect("all ECN values are covered")
0b00 => Self::NotEct,
0b01 => Self::Ect1,
0b10 => Self::Ect0,
0b11 => Self::Ce,
_ => unreachable!(),
}
} }
} }
@@ -64,76 +61,54 @@ impl IpTosEcn {
/// Diffserv codepoints, mapped to the upper six bits of the TOS field. /// Diffserv codepoints, mapped to the upper six bits of the TOS field.
/// <https://www.iana.org/assignments/dscp-registry/dscp-registry.xhtml> /// <https://www.iana.org/assignments/dscp-registry/dscp-registry.xhtml>
#[derive(Copy, Clone, PartialEq, Eq, Enum, Default, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Enum, Default, Debug, FromRepr)]
#[repr(u8)] #[repr(u8)]
pub enum IpTosDscp { pub enum IpTosDscp {
#[default] #[default]
/// Class Selector 0, RFC2474 /// Class Selector 0, RFC2474
Cs0 = 0b0000_0000, Cs0 = 0b0000_0000,
/// Class Selector 1, RFC2474 /// Class Selector 1, RFC2474
Cs1 = 0b0010_0000, Cs1 = 0b0010_0000,
/// Class Selector 2, RFC2474 /// Class Selector 2, RFC2474
Cs2 = 0b0100_0000, Cs2 = 0b0100_0000,
/// Class Selector 3, RFC2474 /// Class Selector 3, RFC2474
Cs3 = 0b0110_0000, Cs3 = 0b0110_0000,
/// Class Selector 4, RFC2474 /// Class Selector 4, RFC2474
Cs4 = 0b1000_0000, Cs4 = 0b1000_0000,
/// Class Selector 5, RFC2474 /// Class Selector 5, RFC2474
Cs5 = 0b1010_0000, Cs5 = 0b1010_0000,
/// Class Selector 6, RFC2474 /// Class Selector 6, RFC2474
Cs6 = 0b1100_0000, Cs6 = 0b1100_0000,
/// Class Selector 7, RFC2474 /// Class Selector 7, RFC2474
Cs7 = 0b1110_0000, Cs7 = 0b1110_0000,
/// Assured Forwarding 11, RFC2597 /// Assured Forwarding 11, RFC2597
Af11 = 0b0010_1000, Af11 = 0b0010_1000,
/// Assured Forwarding 12, RFC2597 /// Assured Forwarding 12, RFC2597
Af12 = 0b0011_0000, Af12 = 0b0011_0000,
/// Assured Forwarding 13, RFC2597 /// Assured Forwarding 13, RFC2597
Af13 = 0b0011_1000, Af13 = 0b0011_1000,
/// Assured Forwarding 21, RFC2597 /// Assured Forwarding 21, RFC2597
Af21 = 0b0100_1000, Af21 = 0b0100_1000,
/// Assured Forwarding 22, RFC2597 /// Assured Forwarding 22, RFC2597
Af22 = 0b0101_0000, Af22 = 0b0101_0000,
/// Assured Forwarding 23, RFC2597 /// Assured Forwarding 23, RFC2597
Af23 = 0b0101_1000, Af23 = 0b0101_1000,
/// Assured Forwarding 31, RFC2597 /// Assured Forwarding 31, RFC2597
Af31 = 0b0110_1000, Af31 = 0b0110_1000,
/// Assured Forwarding 32, RFC2597 /// Assured Forwarding 32, RFC2597
Af32 = 0b0111_0000, Af32 = 0b0111_0000,
/// Assured Forwarding 33, RFC2597 /// Assured Forwarding 33, RFC2597
Af33 = 0b0111_1000, Af33 = 0b0111_1000,
/// Assured Forwarding 41, RFC2597 /// Assured Forwarding 41, RFC2597
Af41 = 0b1000_1000, Af41 = 0b1000_1000,
/// Assured Forwarding 42, RFC2597 /// Assured Forwarding 42, RFC2597
Af42 = 0b1001_0000, Af42 = 0b1001_0000,
/// Assured Forwarding 43, RFC2597 /// Assured Forwarding 43, RFC2597
Af43 = 0b1001_1000, Af43 = 0b1001_1000,
/// Expedited Forwarding, RFC3246 /// Expedited Forwarding, RFC3246
Ef = 0b1011_1000, Ef = 0b1011_1000,
/// Capacity-Admitted Traffic, RFC5865 /// Capacity-Admitted Traffic, RFC5865
VoiceAdmit = 0b1011_0000, VoiceAdmit = 0b1011_0000,
/// Lower-Effort, RFC8622 /// Lower-Effort, RFC8622
Le = 0b0000_0100, Le = 0b0000_0100,
} }
@@ -146,32 +121,7 @@ impl From<IpTosDscp> for u8 {
impl From<u8> for IpTosDscp { impl From<u8> for IpTosDscp {
fn from(v: u8) -> Self { fn from(v: u8) -> Self {
match v & 0b1111_1100 { Self::from_repr(v & 0b1111_1100).expect("all DCSP values are covered")
0b0000_0000 => Self::Cs0,
0b0010_0000 => Self::Cs1,
0b0100_0000 => Self::Cs2,
0b0110_0000 => Self::Cs3,
0b1000_0000 => Self::Cs4,
0b1010_0000 => Self::Cs5,
0b1100_0000 => Self::Cs6,
0b1110_0000 => Self::Cs7,
0b0010_1000 => Self::Af11,
0b0011_0000 => Self::Af12,
0b0011_1000 => Self::Af13,
0b0100_1000 => Self::Af21,
0b0101_0000 => Self::Af22,
0b0101_1000 => Self::Af23,
0b0110_1000 => Self::Af31,
0b0111_0000 => Self::Af32,
0b0111_1000 => Self::Af33,
0b1000_1000 => Self::Af41,
0b1001_0000 => Self::Af42,
0b1001_1000 => Self::Af43,
0b1011_1000 => Self::Ef,
0b1011_0000 => Self::VoiceAdmit,
0b0000_0100 => Self::Le,
_ => unreachable!(),
}
} }
} }

View File

@@ -1 +1 @@
{"files":{"Cargo.toml":"483cd040a7d91cc03ea3e497db33cc0fbafb7123520ea342a7825df079a6e72f","bindings/bindings.toml":"e7e4b75736cfcf4d52febacb99a6f6c6c7b1d648ed8bdc424648be876c850e91","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":"dfc3b2c8038c4b1c69d7a10bd06c4226e36935e7597225aba26039f700cc8f4f","min_version.txt":"04b271df436ebebd03df52ef009d6814f6a64e55203988790a6fcee7b2dc27af","src/aead.rs":"018b826284ea4ddba7d65ff56f3ce9744e24ac8d26b1364b08f6e8b12b259be5","src/aead_null.rs":"81163fafef59bd2800bd0a078d53d0f05ee114f0e22165717823a5ff1cb908af","src/agent.rs":"6b6bcd735cb12d3e3ddebbb04ebd705d192a9cbce6e2a967e86e7b41cccf0a8d","src/agentio.rs":"237f85faacdb6ee5b3a6efa8a620fcf109094f5fd9dfb69d2c74986f11ccdb0e","src/auth.rs":"ced1a18f691894984244088020ea25dc1ee678603317f0c7dfc8b8842fa750b4","src/cert.rs":"4fdaa3834d8a72f41198449010fd5c3f6be6a54e429427c37bde5aab9421585c","src/constants.rs":"58e296e314825753b2ab1d6effe9a1386421dc568f6ebfa8e95a95acb87205da","src/ech.rs":"869e36414418b8747e6ebb45af24159c1908fc829439c16d79cdd89e355d2111","src/err.rs":"2366501e0b48933a6a2e1c5b934aa55108c093729c84878b45e1e012e4e45d51","src/exp.rs":"d953873e87430b1c84d4a83c8eb3815041f5585b210bbaf59ae2c4d0057f5edd","src/ext.rs":"cbf7d9f5ecabf4b8c9efd6c334637ab1596ec5266d38ab8d2d6ceae305283deb","src/hkdf.rs":"8745ba761be821c1819cedf6dfd91f8b3148c6718053a4a74f33eb50c7d0cc40","src/hp.rs":"510a4a7f278203aa306ead05608f99397edc3806dc22b0af9e28c665b43ae56c","src/lib.rs":"cfbf92759c6d1b888d89b9c28adbd96839bff60fc8106169a043eafce0f8a21e","src/min_version.rs":"c6e1f98b9f56db0622ac38c1be131c55acf4a0f09ed0d6283f4d6308e2d1301a","src/p11.rs":"120c45dc037c243a1551b79c7a32613acf22e2961f2ea1e11c73bbce9d1f76dc","src/prio.rs":"5cf0105e78b1db43c65283208174abc3714a41dbb4d5cd80ac547a5a5a7c627c","src/replay.rs":"5cda39bc8fa8a07c493b761b8dfb5cbc9f669f97a2df7832a028ab366b3426be","src/result.rs":"5a76688787741de7a935dbbab4bcb917d481d1c9c50a34df7e510036feb3da17","src/secrets.rs":"c09caca2bf7b60fc7855fc2ad56094a291cb0596308eadf46ab51af0e271a9f0","src/selfencrypt.rs":"80efac0f116b22d86f88f07d694ff2f822bd87b792b3dff8d6376a251394de39","src/ssl.rs":"450cf9fb327515de612b29e3a0f6e15499e6c0b523790e5986fd71a9ea5e76af","src/time.rs":"27d0185c16e150dc381cd6c01ef85fdf7881e5349386466e175d641aab9ab30f","tests/aead.rs":"e36ae77802df1ea6d17cfd1bd2178a3706089577d6fd1554ca86e748b8b235b9","tests/agent.rs":"cbd0011f1d33281883a45d433228221062424c94e86decade5697731c08a1c52","tests/ext.rs":"57af4e2df211fa8afdb73125d4344ef5c70c1ea4579107c3e6f5746308ee3e7b","tests/handshake.rs":"efae67a40f87419aea30c7ab7eb1007faf7f56a83dbf1a18e24fbc0cb92ec901","tests/hkdf.rs":"1d2098dc8398395864baf13e4886cfd1da6d36118727c3b264f457ee3da6b048","tests/hp.rs":"c74070a8f877aaa6305b9411f6e60d8b509b0ac1bef2988cc44b8a48552c953c","tests/init.rs":"3e15150c4b324c06ca5e8935618e4008da53dc0ef4b69325d150831e87dc0b63","tests/selfencrypt.rs":"8d10840b41629bf449a6b3a551377315e8a05ca26c6b041548748196652c5909"},"package":null} {"files":{"Cargo.toml":"2dd521b9c64bcc1916751c27e89a542528652f742092feecb8866178cf9a8a72","bindings/bindings.toml":"e7e4b75736cfcf4d52febacb99a6f6c6c7b1d648ed8bdc424648be876c850e91","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":"97fb6b8f8bb76dc1ed47988025645da618f3301871827d2a98d57254f7c62d5b","min_version.txt":"0f9ddf9ddaeb5137a5ab3d238d06286822f9579b1f46ba76312a8c6d76176500","src/aead.rs":"08d7cad82e3bec32661cfd1689e6611b30ae328ec88481cb32201dd255777365","src/aead_null.rs":"a766e2f71fd8b77a8f81bc60aaaafcffb6aef1f0a1f39ea07fef45b3696718ce","src/agent.rs":"19d9ee826e30c29ca8680e9ec767d19eac2326323f61b2a19bb62874990bf349","src/agentio.rs":"82f0cdececcb475c7b8639c993d4479bce7ac5fdf64b0d560f4b2fd356d03b03","src/auth.rs":"7a1524bef0a0c71616f5ee8b3976d66201210b809271bcf5d06c0e560ae482af","src/cert.rs":"4fdaa3834d8a72f41198449010fd5c3f6be6a54e429427c37bde5aab9421585c","src/constants.rs":"83606aeb646b2833a8094f9d980c266ecc3e8cb40c93a4820da221988319dd1a","src/ech.rs":"19d16af5a30e2060a8942a72487bd820c0d9c62ff1d3c490871752c56781c44b","src/err.rs":"b1e76e595326598a9bac45859f337ad9fb9a92ba9461d8c87250556a2629e7b6","src/exp.rs":"30f91d321439edc75c7f9a595ef5a0064918826d51ab15642db211472694009c","src/ext.rs":"7486595a24d39bd6c573e2229be345d13dbcd6911755ab6ab96d89ce7e263dd3","src/hkdf.rs":"76c5abc8b2d6ee12d8a86cd730af2cf47a59b2fbfd3b8a635a1826636156794d","src/hp.rs":"6adf4ad78b5a065ab7310c69ad239eec156256043e2c185bf60b9d1f12ab1be4","src/lib.rs":"3ab979c264a909e663c5ef140cd57013180745b99937671c73a9003ca6347f41","src/min_version.rs":"c6e1f98b9f56db0622ac38c1be131c55acf4a0f09ed0d6283f4d6308e2d1301a","src/p11.rs":"5c0759bb8ea5faad1a3d06fa7afb3dba114c93ea21981c85824d707f654d7c3d","src/prio.rs":"1858088afd2668e8fbff56959765b7d4df09342371b9282ade27bb4d7bd6ce69","src/replay.rs":"594ce92f368cbc5fb71ebfb62214f07d1e86df8e5ce94255d5593ffabb91cd03","src/result.rs":"5a76688787741de7a935dbbab4bcb917d481d1c9c50a34df7e510036feb3da17","src/secrets.rs":"f01ac665e3c65674865d0645fb1bf3e4579891bf392661e3de3c9f4621e454f0","src/selfencrypt.rs":"4f106465f582c38d3bb04cb5cbcbf65a349e3186784726d9f2bf511a4a4a35ee","src/ssl.rs":"04950bb534b5304eb417909a3a39ebaa9be234c7c13eacdc41c00a8edab1b09f","src/time.rs":"22989caf3dab85cfe955cc279fcca98a6df02d14fcd0e93cac7b39374b8b5763","tests/aead.rs":"e36ae77802df1ea6d17cfd1bd2178a3706089577d6fd1554ca86e748b8b235b9","tests/agent.rs":"fb95a2d5c86ce3fafcb127cd0a2a163e5ee70baf09b2c8483e4d1fb25644cee2","tests/ext.rs":"57af4e2df211fa8afdb73125d4344ef5c70c1ea4579107c3e6f5746308ee3e7b","tests/handshake.rs":"c06ed3d27b0f84b03ff0d0356a16601768821d9860cab81a49fea3eebb3091f2","tests/hkdf.rs":"1d2098dc8398395864baf13e4886cfd1da6d36118727c3b264f457ee3da6b048","tests/hp.rs":"1ad88108b6d8c5d078fc7bb2a4337a54f637d4e149db750f4916032d90aa64a7","tests/init.rs":"3cfe8411ca31ad7dfb23822bb1570e1a5b2b334857173bdd7df086b65b81d95a","tests/selfencrypt.rs":"b65aed70e83dce660017159fc8a956d3b52e0807b590ad8d0a3a4265caa8c1fa"},"package":null}

View File

@@ -11,9 +11,9 @@
[package] [package]
edition = "2021" edition = "2021"
rust-version = "1.76.0" rust-version = "1.82.0"
name = "neqo-crypto" name = "neqo-crypto"
version = "0.12.2" version = "0.13.1"
authors = ["The Neqo Authors <necko@mozilla.com>"] authors = ["The Neqo Authors <necko@mozilla.com>"]
build = "build.rs" build = "build.rs"
autolib = false autolib = false
@@ -82,6 +82,10 @@ path = "tests/init.rs"
name = "selfencrypt" name = "selfencrypt"
path = "tests/selfencrypt.rs" path = "tests/selfencrypt.rs"
[dependencies.enum-map]
version = "2.7"
default-features = false
[dependencies.log] [dependencies.log]
version = "0.4" version = "0.4"
default-features = false default-features = false
@@ -89,6 +93,11 @@ default-features = false
[dependencies.neqo-common] [dependencies.neqo-common]
path = "../neqo-common" path = "../neqo-common"
[dependencies.strum]
version = "0.26"
features = ["derive"]
default-features = false
[dev-dependencies.test-fixture] [dev-dependencies.test-fixture]
path = "../test-fixture" path = "../test-fixture"
@@ -119,22 +128,56 @@ version = "0.5"
default-features = false default-features = false
[lints.clippy] [lints.clippy]
allow_attributes = "warn"
allow_attributes_without_reason = "warn"
cfg_not_test = "warn" cfg_not_test = "warn"
clone_on_ref_ptr = "warn" clone_on_ref_ptr = "warn"
create_dir = "warn" create_dir = "warn"
dbg_macro = "warn"
empty_drop = "warn"
empty_enum_variants_with_brackets = "warn"
filetype_is_file = "warn"
float_cmp_const = "warn"
fn_to_numeric_cast_any = "warn"
get_unwrap = "warn" get_unwrap = "warn"
if_then_some_else_none = "warn" if_then_some_else_none = "warn"
infinite_loop = "warn"
large_include_file = "warn"
let_underscore_must_use = "warn"
let_underscore_untyped = "warn"
literal_string_with_formatting_args = "allow"
lossy_float_literal = "warn"
mem_forget = "warn"
mixed_read_write_in_expression = "warn"
module_name_repetitions = "warn"
multiple_crate_versions = "allow" multiple_crate_versions = "allow"
multiple_inherent_impl = "warn" multiple_inherent_impl = "warn"
mutex_atomic = "warn"
mutex_integer = "warn"
needless_raw_strings = "warn"
pathbuf_init_then_push = "warn" pathbuf_init_then_push = "warn"
pub_without_shorthand = "warn"
rc_buffer = "warn"
rc_mutex = "warn"
redundant_type_annotations = "warn" redundant_type_annotations = "warn"
ref_patterns = "warn" ref_patterns = "warn"
renamed_function_params = "warn" renamed_function_params = "warn"
rest_pat_in_fully_bound_structs = "warn"
self_named_module_files = "warn"
semicolon_inside_block = "warn" semicolon_inside_block = "warn"
string_lit_chars_any = "warn"
string_to_string = "warn"
suspicious_xor_used_as_pow = "warn"
try_err = "warn" try_err = "warn"
unnecessary_safety_comment = "warn"
unnecessary_safety_doc = "warn"
unnecessary_self_imports = "warn"
unneeded_field_pattern = "warn" unneeded_field_pattern = "warn"
unused_result_ok = "warn" unused_result_ok = "warn"
unused_trait_names = "warn" unused_trait_names = "warn"
unwrap_in_result = "warn"
unwrap_used = "warn"
verbose_file_reads = "warn"
[lints.clippy.cargo] [lints.clippy.cargo]
level = "warn" level = "warn"
@@ -151,7 +194,6 @@ priority = -1
[lints.rust] [lints.rust]
absolute_paths_not_starting_with_crate = "warn" absolute_paths_not_starting_with_crate = "warn"
ambiguous_negative_literals = "warn" ambiguous_negative_literals = "warn"
closure_returning_async_block = "warn"
explicit_outlives_requirements = "warn" explicit_outlives_requirements = "warn"
macro_use_extern_crate = "warn" macro_use_extern_crate = "warn"
missing_abi = "warn" missing_abi = "warn"
@@ -163,3 +205,4 @@ unit_bindings = "warn"
unused_import_braces = "warn" unused_import_braces = "warn"
unused_lifetimes = "warn" unused_lifetimes = "warn"
unused_macro_rules = "warn" unused_macro_rules = "warn"
unused_qualifications = "warn"

View File

@@ -4,6 +4,8 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![expect(clippy::unwrap_used, reason = " OK in a build script.")]
use std::{ use std::{
collections::HashMap, collections::HashMap,
env, fs, env, fs,
@@ -54,13 +56,6 @@ struct Bindings {
cplusplus: bool, cplusplus: bool,
} }
fn is_debug() -> bool {
// Check the build profile and not whether debug symbols are enabled (i.e.,
// `env::var("DEBUG")`), because we enable those for benchmarking/profiling and still want
// to build NSS in release mode.
env::var("PROFILE").unwrap_or_default() == "debug"
}
// bindgen needs access to libclang. // bindgen needs access to libclang.
// On windows, this doesn't just work, you have to set LIBCLANG_PATH. // 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. // Rather than download the 400Mb+ files, like gecko does, let's just reuse their work.
@@ -136,21 +131,19 @@ fn build_nss(dir: PathBuf, nsstarget: &str) {
} }
fn dynamic_link() { fn dynamic_link() {
let libs = if env::consts::OS == "windows" { let dynamic_libs = if env::consts::OS == "windows" {
&["nssutil3.dll", "nss3.dll", "ssl3.dll"] [
"nssutil3.dll",
"nss3.dll",
"ssl3.dll",
"libplds4.dll",
"libplc4.dll",
"libnspr4.dll",
]
} else { } else {
&["nssutil3", "nss3", "ssl3"] ["nssutil3", "nss3", "ssl3", "plds4", "plc4", "nspr4"]
}; };
dynamic_link_both(libs); for lib in dynamic_libs {
}
fn dynamic_link_both(extra_libs: &[&str]) {
let nspr_libs = if env::consts::OS == "windows" {
&["libplds4", "libplc4", "libnspr4"]
} else {
&["plds4", "plc4", "nspr4"]
};
for lib in nspr_libs.iter().chain(extra_libs) {
println!("cargo:rustc-link-lib=dylib={lib}"); println!("cargo:rustc-link-lib=dylib={lib}");
} }
} }
@@ -160,35 +153,71 @@ fn static_link() {
"certdb", "certdb",
"certhi", "certhi",
"cryptohi", "cryptohi",
"freebl", "freebl_static",
if env::consts::OS == "windows" {
"libnspr4"
} else {
"nspr4"
},
"nss_static", "nss_static",
"nssb", "nssb",
"nssdev", "nssdev",
"nsspki", "nsspki",
"nssutil", "nssutil",
"pk11wrap", "pk11wrap_static",
"pkcs12", if env::consts::OS == "windows" {
"pkcs7", "libplc4"
"smime", } else {
"plc4"
},
if env::consts::OS == "windows" {
"libplds4"
} else {
"plds4"
},
"softokn_static", "softokn_static",
"ssl", "ssl",
]; ];
if env::consts::OS != "macos" { // macOS always dynamically links against the system sqlite library.
// See https://github.com/nss-dev/nss/blob/a8c22d8fc0458db3e261acc5e19b436ab573a961/coreconf/Darwin.mk#L130-L135
if env::consts::OS == "macos" {
println!("cargo:rustc-link-lib=dylib=sqlite3");
} else {
static_libs.push("sqlite"); static_libs.push("sqlite");
} }
// Hardware specific libs.
// See https://github.com/mozilla/application-services/blob/0a2dac76f979b8bcfb6bacb5424b50f58520b8fe/components/support/rc_crypto/nss/nss_build_common/src/lib.rs#L127-L157
let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
// https://searchfox.org/nss/rev/0d5696b3edce5124353f03159d2aa15549db8306/lib/freebl/freebl.gyp#508-542
if target_arch == "arm" || target_arch == "aarch64" {
static_libs.push("armv8_c_lib");
}
if target_arch == "x86_64" || target_arch == "x86" {
static_libs.push("gcm-aes-x86_c_lib");
static_libs.push("sha-x86_c_lib");
}
if target_arch == "arm" {
static_libs.push("gcm-aes-arm32-neon_c_lib");
}
if target_arch == "aarch64" {
static_libs.push("gcm-aes-aarch64_c_lib");
}
if target_arch == "x86_64" {
static_libs.push("hw-acc-crypto-avx");
static_libs.push("hw-acc-crypto-avx2");
}
// https://searchfox.org/nss/rev/08c4d05078d00089f8d7540651b0717a9d66f87e/lib/freebl/freebl.gyp#315-324
if (target_os == "android" || target_os == "linux") && target_arch == "x86_64" {
static_libs.push("intel-gcm-wrap_c_lib");
// https://searchfox.org/nss/rev/08c4d05078d00089f8d7540651b0717a9d66f87e/lib/freebl/freebl.gyp#43-47
if (target_os == "android" || target_os == "linux") && target_arch == "x86_64" {
static_libs.push("intel-gcm-s_lib");
}
}
for lib in static_libs { for lib in static_libs {
println!("cargo:rustc-link-lib=static={lib}"); println!("cargo:rustc-link-lib=static={lib}");
} }
// Dynamic libs that aren't transitively included by NSS libs.
let mut other_libs = Vec::new();
if env::consts::OS != "windows" {
other_libs.extend_from_slice(&["pthread", "dl", "c", "z"]);
}
if env::consts::OS == "macos" {
other_libs.push("sqlite3");
}
dynamic_link_both(&other_libs);
} }
fn get_includes(nsstarget: &Path, nssdist: &Path) -> Vec<PathBuf> { fn get_includes(nsstarget: &Path, nssdist: &Path) -> Vec<PathBuf> {
@@ -336,7 +365,11 @@ fn setup_standalone(nss: &str) -> Vec<String> {
"cargo:rustc-link-search=native={}", "cargo:rustc-link-search=native={}",
nsslibdir.to_str().unwrap() nsslibdir.to_str().unwrap()
); );
if is_debug() || env::consts::OS == "windows" { if env::var("CARGO_CFG_FUZZING").is_ok()
|| env::var("PROFILE").unwrap_or_default() == "debug"
// FIXME: NSPR doesn't build proper dynamic libraries on Windows.
|| env::consts::OS == "windows"
{
static_link(); static_link();
} else { } else {
dynamic_link(); dynamic_link();
@@ -352,7 +385,10 @@ fn setup_standalone(nss: &str) -> Vec<String> {
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
fn setup_for_gecko() -> Vec<String> { fn setup_for_gecko() -> Vec<String> {
use mozbuild::TOPOBJDIR; use mozbuild::{
config::{BINDGEN_SYSTEM_FLAGS, NSPR_CFLAGS, NSS_CFLAGS},
TOPOBJDIR,
};
let fold_libs = mozbuild::config::MOZ_FOLD_LIBS; let fold_libs = mozbuild::config::MOZ_FOLD_LIBS;
let libs = if fold_libs { let libs = if fold_libs {
@@ -396,13 +432,11 @@ fn setup_for_gecko() -> Vec<String> {
); );
} }
let flags_path = TOPOBJDIR.join("netwerk/socket/neqo/extra-bindgen-flags"); let mut flags = BINDGEN_SYSTEM_FLAGS
.iter()
println!("cargo:rerun-if-changed={}", flags_path.to_str().unwrap()); .chain(&NSPR_CFLAGS)
let mut flags = fs::read_to_string(flags_path) .chain(&NSS_CFLAGS)
.expect("Failed to read extra-bindgen-flags file") .map(|s| s.to_string())
.split_whitespace()
.map(String::from)
.collect::<Vec<_>>(); .collect::<Vec<_>>();
flags.push(String::from("-include")); flags.push(String::from("-include"));

View File

@@ -1 +1 @@
3.105 3.110

View File

@@ -123,15 +123,46 @@ impl RealAead {
c_uint::try_from(output.len())?, c_uint::try_from(output.len())?,
) )
}?; }?;
Ok(&output[0..(l.try_into()?)]) Ok(&output[..l.try_into()?])
}
/// Encrypt `data` consisting of `aad` and plaintext `data` in place.
///
/// The last `Aead::expansion` of `data` is overwritten by the AEAD tag by this function.
/// Therefore, a buffer should be provided that is that much larger than the plaintext.
///
/// # Panics
///
/// If `data` is shorter than `<self as Aead>::expansion()`.
///
/// # Errors
///
/// If the input can't be protected or any input is too large for NSS.
pub fn encrypt_in_place<'a>(
&self,
count: u64,
aad: &[u8],
data: &'a mut [u8],
) -> Res<&'a mut [u8]> {
let mut l: c_uint = 0;
unsafe {
SSL_AeadEncrypt(
*self.ctx,
count,
aad.as_ptr(),
c_uint::try_from(aad.len())?,
data.as_ptr(),
c_uint::try_from(data.len() - self.expansion())?,
data.as_ptr(),
&mut l,
c_uint::try_from(data.len())?,
)
}?;
Ok(&mut data[..l.try_into()?])
} }
/// Decrypt a ciphertext. /// Decrypt a ciphertext.
/// ///
/// Note that NSS insists upon having extra space available for decryption, so
/// the buffer for `output` should be the same length as `input`, even though
/// the final result will be shorter.
///
/// # Errors /// # Errors
/// ///
/// If the input isn't authenticated or any input is too large for NSS. /// If the input isn't authenticated or any input is too large for NSS.
@@ -144,6 +175,9 @@ impl RealAead {
) -> Res<&'a [u8]> { ) -> Res<&'a [u8]> {
let mut l: c_uint = 0; let mut l: c_uint = 0;
unsafe { unsafe {
// Note that NSS insists upon having extra space available for decryption, so
// the buffer for `output` should be the same length as `input`, even though
// the final result will be shorter.
SSL_AeadDecrypt( SSL_AeadDecrypt(
*self.ctx, *self.ctx,
count, count,
@@ -156,7 +190,41 @@ impl RealAead {
c_uint::try_from(output.len())?, c_uint::try_from(output.len())?,
) )
}?; }?;
Ok(&output[0..(l.try_into()?)]) Ok(&output[..l.try_into()?])
}
/// Decrypt a ciphertext in place.
/// Returns a subslice of `data` (without the last `<self as Aead>::expansion()` bytes),
/// that has been decrypted in place.
///
/// # Errors
///
/// If the input isn't authenticated or any input is too large for NSS.
pub fn decrypt_in_place<'a>(
&self,
count: u64,
aad: &[u8],
data: &'a mut [u8],
) -> Res<&'a mut [u8]> {
let mut l: c_uint = 0;
unsafe {
// Note that NSS insists upon having extra space available for decryption, so
// the buffer for `output` should be the same length as `input`, even though
// the final result will be shorter.
SSL_AeadDecrypt(
*self.ctx,
count,
aad.as_ptr(),
c_uint::try_from(aad.len())?,
data.as_ptr(),
c_uint::try_from(data.len())?,
data.as_ptr(),
&mut l,
c_uint::try_from(data.len())?,
)
}?;
debug_assert_eq!(usize::try_from(l)?, data.len() - self.expansion());
Ok(&mut data[..l.try_into()?])
} }
} }

View File

@@ -4,6 +4,8 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![expect(clippy::missing_errors_doc, reason = "OK here.")]
use std::fmt; use std::fmt;
use crate::{ use crate::{
@@ -17,7 +19,6 @@ pub const AEAD_NULL_TAG: &[u8] = &[0x0a; 16];
pub struct AeadNull {} pub struct AeadNull {}
impl AeadNull { impl AeadNull {
#[allow(clippy::missing_errors_doc)]
pub const fn new( pub const fn new(
_version: Version, _version: Version,
_cipher: Cipher, _cipher: Cipher,
@@ -32,7 +33,6 @@ impl AeadNull {
AEAD_NULL_TAG.len() AEAD_NULL_TAG.len()
} }
#[allow(clippy::missing_errors_doc)]
pub fn encrypt<'a>( pub fn encrypt<'a>(
&self, &self,
_count: u64, _count: u64,
@@ -42,23 +42,30 @@ impl AeadNull {
) -> Res<&'a [u8]> { ) -> Res<&'a [u8]> {
let l = input.len(); let l = input.len();
output[..l].copy_from_slice(input); output[..l].copy_from_slice(input);
output[l..l + 16].copy_from_slice(AEAD_NULL_TAG); output[l..l + self.expansion()].copy_from_slice(AEAD_NULL_TAG);
Ok(&output[..l + 16]) Ok(&output[..l + self.expansion()])
} }
#[allow(clippy::missing_errors_doc)] pub fn encrypt_in_place<'a>(
pub fn decrypt<'a>(
&self, &self,
_count: u64, _count: u64,
_aad: &[u8], _aad: &[u8],
input: &[u8], data: &'a mut [u8],
output: &'a mut [u8], ) -> Res<&'a mut [u8]> {
) -> Res<&'a [u8]> { let pos = data.len() - self.expansion();
if input.len() < AEAD_NULL_TAG.len() { data[pos..].copy_from_slice(AEAD_NULL_TAG);
Ok(data)
}
fn decrypt_check(&self, _count: u64, _aad: &[u8], input: &[u8]) -> Res<usize> {
if input.len() < self.expansion() {
return Err(Error::from(SEC_ERROR_BAD_DATA)); return Err(Error::from(SEC_ERROR_BAD_DATA));
} }
let len_encrypted = input.len() - AEAD_NULL_TAG.len(); let len_encrypted = input
.len()
.checked_sub(self.expansion())
.ok_or_else(|| Error::from(SEC_ERROR_BAD_DATA))?;
// Check that: // Check that:
// 1) expansion is all zeros and // 1) expansion is all zeros and
// 2) if the encrypted data is also supplied that at least some values are no zero // 2) if the encrypted data is also supplied that at least some values are no zero
@@ -66,12 +73,34 @@ impl AeadNull {
if &input[len_encrypted..] == AEAD_NULL_TAG if &input[len_encrypted..] == AEAD_NULL_TAG
&& (len_encrypted == 0 || input[..len_encrypted].iter().any(|x| *x != 0x0)) && (len_encrypted == 0 || input[..len_encrypted].iter().any(|x| *x != 0x0))
{ {
output[..len_encrypted].copy_from_slice(&input[..len_encrypted]); Ok(len_encrypted)
Ok(&output[..len_encrypted])
} else { } else {
Err(Error::from(SEC_ERROR_BAD_DATA)) Err(Error::from(SEC_ERROR_BAD_DATA))
} }
} }
pub fn decrypt<'a>(
&self,
count: u64,
aad: &[u8],
input: &[u8],
output: &'a mut [u8],
) -> Res<&'a [u8]> {
self.decrypt_check(count, aad, input).map(|len| {
output[..len].copy_from_slice(&input[..len]);
&output[..len]
})
}
pub fn decrypt_in_place<'a>(
&self,
count: u64,
aad: &[u8],
data: &'a mut [u8],
) -> Res<&'a mut [u8]> {
self.decrypt_check(count, aad, data)
.map(move |len| &mut data[..len])
}
} }
impl fmt::Debug for AeadNull { impl fmt::Debug for AeadNull {

View File

@@ -4,6 +4,15 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![allow(
clippy::module_name_repetitions,
reason = "<https://github.com/mozilla/neqo/issues/2284#issuecomment-2782711813>"
)]
#![expect(
clippy::unwrap_used,
reason = "Let's assume the use of `unwrap` was checked when the use of `unsafe` was reviewed."
)]
use std::{ use std::{
cell::RefCell, cell::RefCell,
ffi::{CStr, CString}, ffi::{CStr, CString},
@@ -122,7 +131,11 @@ macro_rules! preinfo_arg {
pub fn $v(&self) -> Option<$t> { pub fn $v(&self) -> Option<$t> {
match self.info.valuesSet & ssl::$m { match self.info.valuesSet & ssl::$m {
0 => None, 0 => None,
_ => Some(<$t>::try_from(self.info.$f).unwrap()), _ => Some(
<$t>::try_from(self.info.$f)
.inspect_err(|e| qdebug!("Invalid value in preinfo: {e:?}"))
.ok()?,
),
} }
} }
}; };
@@ -135,7 +148,7 @@ impl SecretAgentPreInfo {
ssl::SSL_GetPreliminaryChannelInfo( ssl::SSL_GetPreliminaryChannelInfo(
fd, fd,
info.as_mut_ptr(), info.as_mut_ptr(),
c_uint::try_from(std::mem::size_of::<ssl::SSLPreliminaryChannelInfo>())?, c_uint::try_from(size_of::<ssl::SSLPreliminaryChannelInfo>())?,
) )
})?; })?;
@@ -158,12 +171,11 @@ impl SecretAgentPreInfo {
self.info.canSendEarlyData != 0 self.info.canSendEarlyData != 0
} }
/// # Panics /// # Errors
/// ///
/// If `usize` is less than 32 bits and the value is too large. /// If `usize` is less than 32 bits and the value is too large.
#[must_use] pub fn max_early_data(&self) -> Res<usize> {
pub fn max_early_data(&self) -> usize { Ok(usize::try_from(self.info.maxEarlyDataSize)?)
usize::try_from(self.info.maxEarlyDataSize).unwrap()
} }
/// Was ECH accepted. /// Was ECH accepted.
@@ -222,7 +234,7 @@ impl SecretAgentInfo {
ssl::SSL_GetChannelInfo( ssl::SSL_GetChannelInfo(
fd, fd,
info.as_mut_ptr(), info.as_mut_ptr(),
c_uint::try_from(std::mem::size_of::<ssl::SSLChannelInfo>())?, c_uint::try_from(size_of::<ssl::SSLChannelInfo>())?,
) )
})?; })?;
let info = unsafe { info.assume_init() }; let info = unsafe { info.assume_init() };
@@ -273,7 +285,6 @@ impl SecretAgentInfo {
/// `SecretAgent` holds the common parts of client and server. /// `SecretAgent` holds the common parts of client and server.
#[derive(Debug)] #[derive(Debug)]
#[allow(clippy::module_name_repetitions)]
pub struct SecretAgent { pub struct SecretAgent {
fd: *mut ssl::PRFileDesc, fd: *mut ssl::PRFileDesc,
secrets: SecretHolder, secrets: SecretHolder,
@@ -345,7 +356,6 @@ impl SecretAgent {
Ok(fd) Ok(fd)
} }
#[allow(clippy::missing_const_for_fn)]
unsafe extern "C" fn auth_complete_hook( unsafe extern "C" fn auth_complete_hook(
arg: *mut c_void, arg: *mut c_void,
_fd: *mut ssl::PRFileDesc, _fd: *mut ssl::PRFileDesc,
@@ -544,9 +554,7 @@ impl SecretAgent {
// NSS inherited an idiosyncratic API as a result of having implemented NPN // NSS inherited an idiosyncratic API as a result of having implemented NPN
// before ALPN. For that reason, we need to put the "best" option last. // before ALPN. For that reason, we need to put the "best" option last.
let (first, rest) = protocols let (first, rest) = protocols.split_first().ok_or(Error::InternalError)?;
.split_first()
.expect("at least one ALPN value needed");
for v in rest { for v in rest {
add(v.as_ref()); add(v.as_ref());
} }
@@ -585,14 +593,16 @@ impl SecretAgent {
// This function tracks whether handshake() or handshake_raw() was used // This function tracks whether handshake() or handshake_raw() was used
// and prevents the other from being used. // and prevents the other from being used.
fn set_raw(&mut self, r: bool) -> Res<()> { fn set_raw(&mut self, r: bool) -> Res<()> {
if self.raw.is_none() { if let Some(raw) = self.raw {
if raw == r {
Ok(())
} else {
Err(Error::MixedHandshakeMethod)
}
} else {
self.secrets.register(self.fd)?; self.secrets.register(self.fd)?;
self.raw = Some(r); self.raw = Some(r);
Ok(()) Ok(())
} else if self.raw.unwrap() == r {
Ok(())
} else {
Err(Error::MixedHandshakeMethod)
} }
} }
@@ -627,6 +637,11 @@ impl SecretAgent {
} }
/// Return any fatal alert that the TLS stack might have sent. /// Return any fatal alert that the TLS stack might have sent.
#[allow(
clippy::allow_attributes,
clippy::missing_const_for_fn,
reason = "TODO: False positive on nightly."
)]
#[must_use] #[must_use]
pub fn alert(&self) -> Option<&Alert> { pub fn alert(&self) -> Option<&Alert> {
(*self.alert).as_ref() (*self.alert).as_ref()
@@ -751,12 +766,15 @@ impl SecretAgent {
/// # Panics /// # Panics
/// ///
/// If setup fails. /// If setup fails.
#[allow(clippy::branches_sharing_code)]
pub fn close(&mut self) { pub fn close(&mut self) {
// It should be safe to close multiple times. // It should be safe to close multiple times.
if self.fd.is_null() { if self.fd.is_null() {
return; return;
} }
#[expect(
clippy::branches_sharing_code,
reason = "Moving the PR_Close call after the conditional crashes things?!"
)]
if self.raw == Some(true) { if self.raw == Some(true) {
// Need to hold the record list in scope until the close is done. // Need to hold the record list in scope until the close is done.
let _records = self.setup_raw().expect("Can only close"); let _records = self.setup_raw().expect("Can only close");
@@ -769,7 +787,7 @@ impl SecretAgent {
unsafe { unsafe {
prio::PR_Close(self.fd.cast()); prio::PR_Close(self.fd.cast());
} }
}; }
let _output = self.io.take_output(); let _output = self.io.take_output();
self.fd = null_mut(); self.fd = null_mut();
} }
@@ -793,6 +811,11 @@ impl SecretAgent {
} }
/// Get the active ECH configuration, which is empty if ECH is disabled. /// Get the active ECH configuration, which is empty if ECH is disabled.
#[allow(
clippy::allow_attributes,
clippy::missing_const_for_fn,
reason = "TODO: False positive on nightly."
)]
#[must_use] #[must_use]
pub fn ech_config(&self) -> &[u8] { pub fn ech_config(&self) -> &[u8] {
&self.ech_config &self.ech_config
@@ -840,13 +863,13 @@ impl ResumptionToken {
/// A TLS Client. /// A TLS Client.
#[derive(Debug)] #[derive(Debug)]
#[allow(clippy::box_collection)] // We need the Box.
pub struct Client { pub struct Client {
agent: SecretAgent, agent: SecretAgent,
/// The name of the server we're attempting a connection to. /// The name of the server we're attempting a connection to.
server_name: String, server_name: String,
/// Records the resumption tokens we've received. /// Records the resumption tokens we've received.
#[expect(clippy::box_collection, reason = "We need the Box.")]
resumption: Pin<Box<Vec<ResumptionToken>>>, resumption: Pin<Box<Vec<ResumptionToken>>>,
} }
@@ -878,12 +901,10 @@ impl Client {
arg: *mut c_void, arg: *mut c_void,
) -> ssl::SECStatus { ) -> ssl::SECStatus {
let mut info: MaybeUninit<ssl::SSLResumptionTokenInfo> = MaybeUninit::uninit(); let mut info: MaybeUninit<ssl::SSLResumptionTokenInfo> = MaybeUninit::uninit();
let info_res = &ssl::SSL_GetResumptionTokenInfo( let Ok(info_len) = c_uint::try_from(size_of::<ssl::SSLResumptionTokenInfo>()) else {
token, return ssl::SECFailure;
len, };
info.as_mut_ptr(), let info_res = &ssl::SSL_GetResumptionTokenInfo(token, len, info.as_mut_ptr(), info_len);
c_uint::try_from(std::mem::size_of::<ssl::SSLResumptionTokenInfo>()).unwrap(),
);
if info_res.is_err() { if info_res.is_err() {
// Ignore the token. // Ignore the token.
return ssl::SECSuccess; return ssl::SECSuccess;
@@ -893,8 +914,12 @@ impl Client {
// Ignore the token. // Ignore the token.
return ssl::SECSuccess; return ssl::SECSuccess;
} }
let resumption = arg.cast::<Vec<ResumptionToken>>().as_mut().unwrap(); let Some(resumption) = arg.cast::<Vec<ResumptionToken>>().as_mut() else {
let len = usize::try_from(len).unwrap(); return ssl::SECFailure;
};
let Ok(len) = usize::try_from(len) else {
return ssl::SECFailure;
};
let mut v = Vec::with_capacity(len); let mut v = Vec::with_capacity(len);
v.extend_from_slice(null_safe_slice(token, len)); v.extend_from_slice(null_safe_slice(token, len));
qdebug!("[{fd:p}] Got resumption token {}", hex_snip_middle(&v)); qdebug!("[{fd:p}] Got resumption token {}", hex_snip_middle(&v));
@@ -908,6 +933,11 @@ impl Client {
ssl::SECSuccess ssl::SECSuccess
} }
#[allow(
clippy::allow_attributes,
clippy::missing_const_for_fn,
reason = "TODO: False positive on nightly."
)]
#[must_use] #[must_use]
pub fn server_name(&self) -> &str { pub fn server_name(&self) -> &str {
&self.server_name &self.server_name
@@ -985,7 +1015,6 @@ impl Client {
impl Deref for Client { impl Deref for Client {
type Target = SecretAgent; type Target = SecretAgent;
#[must_use]
fn deref(&self) -> &SecretAgent { fn deref(&self) -> &SecretAgent {
&self.agent &self.agent
} }
@@ -1194,7 +1223,6 @@ impl Server {
impl Deref for Server { impl Deref for Server {
type Target = SecretAgent; type Target = SecretAgent;
#[must_use]
fn deref(&self) -> &SecretAgent { fn deref(&self) -> &SecretAgent {
&self.agent &self.agent
} }
@@ -1221,7 +1249,6 @@ pub enum Agent {
impl Deref for Agent { impl Deref for Agent {
type Target = SecretAgent; type Target = SecretAgent;
#[must_use]
fn deref(&self) -> &SecretAgent { fn deref(&self) -> &SecretAgent {
match self { match self {
Self::Client(c) => c, Self::Client(c) => c,
@@ -1240,14 +1267,12 @@ impl DerefMut for Agent {
} }
impl From<Client> for Agent { impl From<Client> for Agent {
#[must_use]
fn from(c: Client) -> Self { fn from(c: Client) -> Self {
Self::Client(c) Self::Client(c)
} }
} }
impl From<Server> for Agent { impl From<Server> for Agent {
#[must_use]
fn from(s: Server) -> Self { fn from(s: Server) -> Self {
Self::Server(s) Self::Server(s)
} }

View File

@@ -4,6 +4,11 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![expect(
clippy::unwrap_used,
reason = "Let's assume the use of `unwrap` was checked when the use of `unsafe` was reviewed."
)]
use std::{ use std::{
cmp::min, cmp::min,
fmt, mem, fmt, mem,
@@ -95,10 +100,17 @@ impl RecordList {
len: c_uint, len: c_uint,
arg: *mut c_void, arg: *mut c_void,
) -> ssl::SECStatus { ) -> ssl::SECStatus {
let records = arg.cast::<Self>().as_mut().unwrap(); let Ok(epoch) = Epoch::try_from(epoch) else {
return ssl::SECFailure;
};
let Ok(ct) = ContentType::try_from(ct) else {
return ssl::SECFailure;
};
let Some(records) = arg.cast::<Self>().as_mut() else {
return ssl::SECFailure;
};
let slice = null_safe_slice(data, len); let slice = null_safe_slice(data, len);
records.append(epoch, ContentType::try_from(ct).unwrap(), slice); records.append(epoch, ct, slice);
ssl::SECSuccess ssl::SECSuccess
} }
@@ -114,7 +126,6 @@ impl RecordList {
impl Deref for RecordList { impl Deref for RecordList {
type Target = Vec<Record>; type Target = Vec<Record>;
#[must_use]
fn deref(&self) -> &Vec<Record> { fn deref(&self) -> &Vec<Record> {
&self.records &self.records
} }
@@ -132,7 +143,6 @@ impl Iterator for RecordListIter {
impl IntoIterator for RecordList { impl IntoIterator for RecordList {
type Item = Record; type Item = Record;
type IntoIter = RecordListIter; type IntoIter = RecordListIter;
#[must_use]
fn into_iter(self) -> Self::IntoIter { fn into_iter(self) -> Self::IntoIter {
RecordListIter(self.records.into_iter()) RecordListIter(self.records.into_iter())
} }
@@ -175,7 +185,10 @@ impl AgentIoInput {
return Err(Error::NoDataAvailable); return Err(Error::NoDataAvailable);
} }
#[allow(clippy::disallowed_methods)] // We just checked if this was empty. #[expect(
clippy::disallowed_methods,
reason = "We just checked if this was empty."
)]
let src = unsafe { std::slice::from_raw_parts(self.input, amount) }; let src = unsafe { std::slice::from_raw_parts(self.input, amount) };
qtrace!("[{self}] read {}", hex(src)); qtrace!("[{self}] read {}", hex(src));
let dst = unsafe { std::slice::from_raw_parts_mut(buf, amount) }; let dst = unsafe { std::slice::from_raw_parts_mut(buf, amount) };
@@ -329,10 +342,14 @@ unsafe extern "C" fn agent_available64(mut fd: PrFd) -> prio::PRInt64 {
.unwrap_or_else(|_| PR_FAILURE.into()) .unwrap_or_else(|_| PR_FAILURE.into())
} }
#[allow(clippy::cast_possible_truncation)] #[expect(
clippy::cast_possible_truncation,
reason = "Cast is safe because prio::PR_AF_INET is 2."
)]
unsafe extern "C" fn agent_getname(_fd: PrFd, addr: *mut prio::PRNetAddr) -> PrStatus { unsafe extern "C" fn agent_getname(_fd: PrFd, addr: *mut prio::PRNetAddr) -> PrStatus {
let a = addr.as_mut().unwrap(); let Some(a) = addr.as_mut() else {
// Cast is safe because prio::PR_AF_INET is 2 return PR_FAILURE;
};
a.inet.family = prio::PR_AF_INET as prio::PRUint16; a.inet.family = prio::PR_AF_INET as prio::PRUint16;
a.inet.port = 0; a.inet.port = 0;
a.inet.ip = 0; a.inet.ip = 0;
@@ -340,10 +357,11 @@ unsafe extern "C" fn agent_getname(_fd: PrFd, addr: *mut prio::PRNetAddr) -> PrS
} }
unsafe extern "C" fn agent_getsockopt(_fd: PrFd, opt: *mut prio::PRSocketOptionData) -> PrStatus { unsafe extern "C" fn agent_getsockopt(_fd: PrFd, opt: *mut prio::PRSocketOptionData) -> PrStatus {
let o = opt.as_mut().unwrap(); if let Some(o) = opt.as_mut() {
if o.option == prio::PRSockOption::PR_SockOpt_Nonblocking { if o.option == prio::PRSockOption::PR_SockOpt_Nonblocking {
o.value.non_blocking = 1; o.value.non_blocking = 1;
return PR_SUCCESS; return PR_SUCCESS;
}
} }
PR_FAILURE PR_FAILURE
} }

View File

@@ -4,105 +4,48 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
use strum::FromRepr;
use crate::err::{mozpkix, sec, ssl, PRErrorCode}; use crate::err::{mozpkix, sec, ssl, PRErrorCode};
/// The outcome of authentication. /// The outcome of authentication.
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq, FromRepr)]
#[repr(i32)]
pub enum AuthenticationStatus { pub enum AuthenticationStatus {
Ok, Ok,
CaInvalid, CaInvalid = sec::SEC_ERROR_CA_CERT_INVALID,
CaNotV3, CaNotV3 = mozpkix::MOZILLA_PKIX_ERROR_V1_CERT_USED_AS_CA,
CertAlgorithmDisabled, CertAlgorithmDisabled = sec::SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED,
CertExpired, CertExpired = sec::SEC_ERROR_EXPIRED_CERTIFICATE,
CertInvalidTime, CertInvalidTime = sec::SEC_ERROR_INVALID_TIME,
CertIsCa, CertIsCa = mozpkix::MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY,
CertKeyUsage, CertKeyUsage = sec::SEC_ERROR_INADEQUATE_KEY_USAGE,
CertMitm, CertMitm = mozpkix::MOZILLA_PKIX_ERROR_MITM_DETECTED,
CertNotYetValid, CertNotYetValid = mozpkix::MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE,
CertRevoked, CertRevoked = sec::SEC_ERROR_REVOKED_CERTIFICATE,
CertSelfSigned, CertSelfSigned = mozpkix::MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT,
CertSubjectInvalid, CertSubjectInvalid = ssl::SSL_ERROR_BAD_CERT_DOMAIN,
CertUntrusted, CertUntrusted = sec::SEC_ERROR_UNTRUSTED_CERT,
CertWeakKey, CertWeakKey = mozpkix::MOZILLA_PKIX_ERROR_INADEQUATE_KEY_SIZE,
IssuerEmptyName, IssuerEmptyName = mozpkix::MOZILLA_PKIX_ERROR_EMPTY_ISSUER_NAME,
IssuerExpired, IssuerExpired = sec::SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE,
IssuerNotYetValid, IssuerNotYetValid = mozpkix::MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE,
IssuerUnknown, IssuerUnknown = sec::SEC_ERROR_UNKNOWN_ISSUER,
IssuerUntrusted, IssuerUntrusted = sec::SEC_ERROR_UNTRUSTED_ISSUER,
PolicyRejection, PolicyRejection = mozpkix::MOZILLA_PKIX_ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED,
Unknown, Unknown = sec::SEC_ERROR_LIBRARY_FAILURE,
} }
impl From<AuthenticationStatus> for PRErrorCode { impl From<AuthenticationStatus> for PRErrorCode {
#[must_use]
fn from(v: AuthenticationStatus) -> Self { fn from(v: AuthenticationStatus) -> Self {
match v { v as Self
AuthenticationStatus::Ok => 0,
AuthenticationStatus::CaInvalid => sec::SEC_ERROR_CA_CERT_INVALID,
AuthenticationStatus::CaNotV3 => mozpkix::MOZILLA_PKIX_ERROR_V1_CERT_USED_AS_CA,
AuthenticationStatus::CertAlgorithmDisabled => {
sec::SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED
}
AuthenticationStatus::CertExpired => sec::SEC_ERROR_EXPIRED_CERTIFICATE,
AuthenticationStatus::CertInvalidTime => sec::SEC_ERROR_INVALID_TIME,
AuthenticationStatus::CertIsCa => {
mozpkix::MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY
}
AuthenticationStatus::CertKeyUsage => sec::SEC_ERROR_INADEQUATE_KEY_USAGE,
AuthenticationStatus::CertMitm => mozpkix::MOZILLA_PKIX_ERROR_MITM_DETECTED,
AuthenticationStatus::CertNotYetValid => {
mozpkix::MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE
}
AuthenticationStatus::CertRevoked => sec::SEC_ERROR_REVOKED_CERTIFICATE,
AuthenticationStatus::CertSelfSigned => mozpkix::MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT,
AuthenticationStatus::CertSubjectInvalid => ssl::SSL_ERROR_BAD_CERT_DOMAIN,
AuthenticationStatus::CertUntrusted => sec::SEC_ERROR_UNTRUSTED_CERT,
AuthenticationStatus::CertWeakKey => mozpkix::MOZILLA_PKIX_ERROR_INADEQUATE_KEY_SIZE,
AuthenticationStatus::IssuerEmptyName => mozpkix::MOZILLA_PKIX_ERROR_EMPTY_ISSUER_NAME,
AuthenticationStatus::IssuerExpired => sec::SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE,
AuthenticationStatus::IssuerNotYetValid => {
mozpkix::MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE
}
AuthenticationStatus::IssuerUnknown => sec::SEC_ERROR_UNKNOWN_ISSUER,
AuthenticationStatus::IssuerUntrusted => sec::SEC_ERROR_UNTRUSTED_ISSUER,
AuthenticationStatus::PolicyRejection => {
mozpkix::MOZILLA_PKIX_ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED
}
AuthenticationStatus::Unknown => sec::SEC_ERROR_LIBRARY_FAILURE,
}
} }
} }
// Note that this mapping should be removed after gecko eventually learns how to // Note that this mapping should be removed after gecko eventually learns how to
// map into the enumerated type. // map into the enumerated type.
impl From<PRErrorCode> for AuthenticationStatus { impl From<PRErrorCode> for AuthenticationStatus {
#[must_use]
fn from(v: PRErrorCode) -> Self { fn from(v: PRErrorCode) -> Self {
match v { Self::from_repr(v).unwrap_or(Self::Unknown)
0 => Self::Ok,
sec::SEC_ERROR_CA_CERT_INVALID => Self::CaInvalid,
mozpkix::MOZILLA_PKIX_ERROR_V1_CERT_USED_AS_CA => Self::CaNotV3,
sec::SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED => Self::CertAlgorithmDisabled,
sec::SEC_ERROR_EXPIRED_CERTIFICATE => Self::CertExpired,
sec::SEC_ERROR_INVALID_TIME => Self::CertInvalidTime,
mozpkix::MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY => Self::CertIsCa,
sec::SEC_ERROR_INADEQUATE_KEY_USAGE => Self::CertKeyUsage,
mozpkix::MOZILLA_PKIX_ERROR_MITM_DETECTED => Self::CertMitm,
mozpkix::MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE => Self::CertNotYetValid,
sec::SEC_ERROR_REVOKED_CERTIFICATE => Self::CertRevoked,
mozpkix::MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT => Self::CertSelfSigned,
ssl::SSL_ERROR_BAD_CERT_DOMAIN => Self::CertSubjectInvalid,
sec::SEC_ERROR_UNTRUSTED_CERT => Self::CertUntrusted,
mozpkix::MOZILLA_PKIX_ERROR_INADEQUATE_KEY_SIZE => Self::CertWeakKey,
mozpkix::MOZILLA_PKIX_ERROR_EMPTY_ISSUER_NAME => Self::IssuerEmptyName,
sec::SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE => Self::IssuerExpired,
mozpkix::MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE => Self::IssuerNotYetValid,
sec::SEC_ERROR_UNKNOWN_ISSUER => Self::IssuerUnknown,
sec::SEC_ERROR_UNTRUSTED_ISSUER => Self::IssuerUntrusted,
mozpkix::MOZILLA_PKIX_ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED => {
Self::PolicyRejection
}
_ => Self::Unknown,
}
} }
} }

View File

@@ -4,30 +4,49 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![allow(dead_code)] use enum_map::Enum;
use strum::FromRepr;
use crate::ssl; use crate::{ssl, Error};
// Ideally all of these would be enums, but size matters and we need to allow // Ideally all of these would be enums, but size matters and we need to allow
// for values outside of those that are defined here. // for values outside of those that are defined here.
pub type Alert = u8; pub type Alert = u8;
pub type Epoch = u16; #[derive(Default, Debug, Enum, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, FromRepr)]
// TLS doesn't really have an "initial" concept that maps to QUIC so directly, #[repr(u16)]
// but this should be clear enough. pub enum Epoch {
pub const TLS_EPOCH_INITIAL: Epoch = 0_u16; // TLS doesn't really have an "initial" concept that maps to QUIC so directly,
pub const TLS_EPOCH_ZERO_RTT: Epoch = 1_u16; // but this should be clear enough.
pub const TLS_EPOCH_HANDSHAKE: Epoch = 2_u16; #[default]
// Also, we don't use TLS epochs > 3. Initial = 0,
pub const TLS_EPOCH_APPLICATION_DATA: Epoch = 3_u16; ZeroRtt,
Handshake,
ApplicationData,
// Also, we don't use TLS epochs > 3.
}
impl TryFrom<u16> for Epoch {
type Error = Error;
fn try_from(value: u16) -> Result<Self, Self::Error> {
Self::from_repr(value).ok_or(Error::InvalidEpoch)
}
}
impl From<Epoch> for usize {
fn from(e: Epoch) -> Self {
e as Self
}
}
/// Rather than defining a type alias and a bunch of constants, which leads to a ton of repetition, /// Rather than defining a type alias and a bunch of constants, which leads to a ton of repetition,
/// use this macro. /// use this macro.
macro_rules! remap_enum { macro_rules! remap_enum {
{ $t:ident: $s:ty { $( $n:ident = $v:path ),+ $(,)? } } => { { $t:ident: $s:ty { $( $n:ident = $v:path ),+ $(,)? } } => {
pub type $t = $s; pub type $t = $s;
$(#[allow(clippy::cast_possible_truncation)] pub const $n: $t = $v as $t; )+ $(#[expect(clippy::cast_possible_truncation, reason = "Inherent in macro use.")] pub const $n: $t = $v as $t; )+
}; };
{ $t:ident: $s:ty => $e:ident { $( $n:ident = $v:ident ),+ $(,)? } } => { { $t:ident: $s:ty => $e:ident { $( $n:ident = $v:ident ),+ $(,)? } } => {
remap_enum!{ $t: $s { $( $n = $e::$v ),+ } } remap_enum!{ $t: $s { $( $n = $e::$v ),+ } }
@@ -44,6 +63,7 @@ remap_enum! {
} }
} }
#[expect(dead_code, reason = "Code is bindgen-generated.")]
mod ciphers { mod ciphers {
include!(concat!(env!("OUT_DIR"), "/nss_ciphers.rs")); include!(concat!(env!("OUT_DIR"), "/nss_ciphers.rs"));
} }

View File

@@ -107,7 +107,7 @@ pub fn generate_keys() -> Res<(PrivateKey, PublicKey)> {
params.extend_from_slice(oid_slc); params.extend_from_slice(oid_slc);
let mut public_ptr: *mut SECKEYPublicKey = null_mut(); let mut public_ptr: *mut SECKEYPublicKey = null_mut();
let mut param_item = Item::wrap(&params); let mut param_item = Item::wrap(&params)?;
// If we have tracing on, try to ensure that key data can be read. // 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) { let insensitive_secret_ptr = if log::log_enabled!(log::Level::Trace) {

View File

@@ -4,29 +4,29 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![allow(dead_code)]
use std::{os::raw::c_char, str::Utf8Error}; use std::{os::raw::c_char, str::Utf8Error};
use crate::ssl::{SECStatus, SECSuccess}; use crate::ssl::{SECStatus, SECSuccess};
include!(concat!(env!("OUT_DIR"), "/nspr_error.rs")); include!(concat!(env!("OUT_DIR"), "/nspr_error.rs"));
#[expect(non_snake_case, dead_code, reason = "Code is bindgen-generated.")]
mod codes { mod codes {
#![allow(non_snake_case)]
include!(concat!(env!("OUT_DIR"), "/nss_secerr.rs")); include!(concat!(env!("OUT_DIR"), "/nss_secerr.rs"));
include!(concat!(env!("OUT_DIR"), "/nss_sslerr.rs")); include!(concat!(env!("OUT_DIR"), "/nss_sslerr.rs"));
} }
pub use codes::{SECErrorCodes as sec, SSLErrorCodes as ssl}; pub use codes::{SECErrorCodes as sec, SSLErrorCodes as ssl};
#[expect(dead_code, reason = "Code is bindgen-generated.")]
pub mod nspr { pub mod nspr {
include!(concat!(env!("OUT_DIR"), "/nspr_err.rs")); include!(concat!(env!("OUT_DIR"), "/nspr_err.rs"));
} }
#[expect(dead_code, reason = "Some constants are not used.")]
pub mod mozpkix { pub mod mozpkix {
// These are manually extracted from the many bindings generated // These are manually extracted from the many bindings generated
// by bindgen when provided with the simple header: // by bindgen when provided with the simple header:
// #include "mozpkix/pkixnss.h" // #include "mozpkix/pkixnss.h"
#[allow(non_camel_case_types)] #[expect(non_camel_case_types, reason = "Code is bindgen-generated.")]
pub type mozilla_pkix_ErrorCode = ::std::os::raw::c_int; pub type mozilla_pkix_ErrorCode = ::std::os::raw::c_int;
pub const MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE: mozilla_pkix_ErrorCode = -16384; pub const MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE: mozilla_pkix_ErrorCode = -16384;
pub const MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY: mozilla_pkix_ErrorCode = -16383; pub const MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY: mozilla_pkix_ErrorCode = -16383;
@@ -83,11 +83,9 @@ impl Error {
} }
impl std::error::Error for Error { impl std::error::Error for Error {
#[must_use]
fn cause(&self) -> Option<&dyn std::error::Error> { fn cause(&self) -> Option<&dyn std::error::Error> {
None None
} }
#[must_use]
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
None None
} }
@@ -100,13 +98,11 @@ impl std::fmt::Display for Error {
} }
impl From<std::num::TryFromIntError> for Error { impl From<std::num::TryFromIntError> for Error {
#[must_use]
fn from(_: std::num::TryFromIntError) -> Self { fn from(_: std::num::TryFromIntError) -> Self {
Self::IntegerOverflow Self::IntegerOverflow
} }
} }
impl From<std::ffi::NulError> for Error { impl From<std::ffi::NulError> for Error {
#[must_use]
fn from(_: std::ffi::NulError) -> Self { fn from(_: std::ffi::NulError) -> Self {
Self::InternalError Self::InternalError
} }

View File

@@ -7,10 +7,10 @@
#[macro_export] #[macro_export]
macro_rules! experimental_api { macro_rules! experimental_api {
( $n:ident ( $( $a:ident : $t:ty ),* $(,)? ) ) => { ( $n:ident ( $( $a:ident : $t:ty ),* $(,)? ) ) => {
#[allow(non_snake_case)] #[expect(non_snake_case, reason = "Inherent in macro use.")]
#[allow(clippy::too_many_arguments)] #[allow(clippy::allow_attributes, clippy::too_many_arguments, reason = "Inherent in macro use.")]
#[allow(clippy::missing_safety_doc)] #[allow(clippy::allow_attributes, clippy::missing_safety_doc, reason = "Inherent in macro use.")]
#[allow(clippy::missing_errors_doc)] #[allow(clippy::allow_attributes, clippy::missing_errors_doc, reason = "Inherent in macro use.")]
pub unsafe fn $n ( $( $a : $t ),* ) -> Result<(), $crate::err::Error> { pub unsafe fn $n ( $( $a : $t ),* ) -> Result<(), $crate::err::Error> {
const EXP_FUNCTION: &str = stringify!($n); const EXP_FUNCTION: &str = stringify!($n);
let n = ::std::ffi::CString::new(EXP_FUNCTION)?; let n = ::std::ffi::CString::new(EXP_FUNCTION)?;

View File

@@ -4,6 +4,11 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![expect(
clippy::unwrap_used,
reason = "Let's assume the use of `unwrap` was checked when the use of `unsafe` was reviewed."
)]
use std::{ use std::{
cell::RefCell, cell::RefCell,
os::raw::{c_uint, c_void}, os::raw::{c_uint, c_void},
@@ -75,7 +80,6 @@ impl ExtensionTracker {
f(&mut *rc.borrow_mut()) f(&mut *rc.borrow_mut())
} }
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
unsafe extern "C" fn extension_writer( unsafe extern "C" fn extension_writer(
_fd: *mut PRFileDesc, _fd: *mut PRFileDesc,
message: SSLHandshakeType::Type, message: SSLHandshakeType::Type,
@@ -86,7 +90,12 @@ impl ExtensionTracker {
) -> PRBool { ) -> PRBool {
let d = std::slice::from_raw_parts_mut(data, max_len as usize); let d = std::slice::from_raw_parts_mut(data, max_len as usize);
Self::wrap_handler_call(arg, |handler| { Self::wrap_handler_call(arg, |handler| {
// Cast is safe here because the message type is always part of the enum #[allow(
clippy::allow_attributes,
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
reason = "Cast is safe here because the message type is always part of the enum."
)]
match handler.write(message as HandshakeMessage, d) { match handler.write(message as HandshakeMessage, d) {
ExtensionWriterResult::Write(sz) => { ExtensionWriterResult::Write(sz) => {
*len = c_uint::try_from(sz).expect("integer overflow from extension writer"); *len = c_uint::try_from(sz).expect("integer overflow from extension writer");
@@ -106,9 +115,14 @@ impl ExtensionTracker {
arg: *mut c_void, arg: *mut c_void,
) -> SECStatus { ) -> SECStatus {
let d = null_safe_slice(data, len); let d = null_safe_slice(data, len);
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
Self::wrap_handler_call(arg, |handler| { Self::wrap_handler_call(arg, |handler| {
// Cast is safe here because the message type is always part of the enum // Cast is safe here because the message type is always part of the enum
#[allow(
clippy::allow_attributes,
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
reason = "Cast is safe here because the message type is always part of the enum."
)]
match handler.handle(message as HandshakeMessage, d) { match handler.handle(message as HandshakeMessage, d) {
ExtensionHandlerResult::Ok => SECSuccess, ExtensionHandlerResult::Ok => SECSuccess,
ExtensionHandlerResult::Alert(a) => { ExtensionHandlerResult::Alert(a) => {

View File

@@ -84,7 +84,7 @@ pub fn import_key(version: Version, buf: &[u8]) -> Res<SymKey> {
CK_MECHANISM_TYPE::from(CKM_HKDF_DERIVE), CK_MECHANISM_TYPE::from(CKM_HKDF_DERIVE),
PK11Origin::PK11_OriginUnwrap, PK11Origin::PK11_OriginUnwrap,
CK_ATTRIBUTE_TYPE::from(CKA_DERIVE), CK_ATTRIBUTE_TYPE::from(CKA_DERIVE),
&mut Item::wrap(buf), &mut Item::wrap(buf)?,
null_mut(), null_mut(),
) )
}; };

View File

@@ -4,6 +4,11 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![allow(
clippy::module_name_repetitions,
reason = "<https://github.com/mozilla/neqo/issues/2284#issuecomment-2782711813>"
)]
use std::{ use std::{
cell::RefCell, cell::RefCell,
fmt::{self, Debug}, fmt::{self, Debug},
@@ -106,7 +111,7 @@ impl HpKey {
mech, mech,
CK_ATTRIBUTE_TYPE::from(CKA_ENCRYPT), CK_ATTRIBUTE_TYPE::from(CKA_ENCRYPT),
*key, *key,
&Item::wrap(&ZERO[..0]), // Borrow a zero-length slice of ZERO. &Item::wrap(&ZERO[..0])?, // Borrow a zero-length slice of ZERO.
) )
}; };
let context = Context::from_ptr(context_ptr).or(Err(Error::CipherInitFailure))?; let context = Context::from_ptr(context_ptr).or(Err(Error::CipherInitFailure))?;
@@ -169,7 +174,7 @@ impl HpKey {
ulNonceBits: 96, ulNonceBits: 96,
}; };
let mut output_len: c_uint = 0; let mut output_len: c_uint = 0;
let mut param_item = Item::wrap_struct(&params); let mut param_item = Item::wrap_struct(&params)?;
secstatus_to_res(unsafe { secstatus_to_res(unsafe {
PK11_Encrypt( PK11_Encrypt(
**key, **key,

View File

@@ -4,9 +4,6 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![allow(clippy::module_name_repetitions)] // This lint doesn't work here.
#![allow(clippy::unseparated_literal_suffix, clippy::used_underscore_binding)] // For bindgen code.
mod aead; mod aead;
#[cfg(feature = "disable-encryption")] #[cfg(feature = "disable-encryption")]
pub mod aead_null; pub mod aead_null;
@@ -31,7 +28,7 @@ pub mod selfencrypt;
mod ssl; mod ssl;
mod time; mod time;
use std::{ffi::CString, path::PathBuf, ptr::null, sync::OnceLock}; use std::{env, ffi::CString, path::PathBuf, ptr::null, sync::OnceLock};
#[cfg(not(feature = "disable-encryption"))] #[cfg(not(feature = "disable-encryption"))]
pub use self::aead::RealAead as Aead; pub use self::aead::RealAead as Aead;
@@ -63,7 +60,7 @@ mod min_version;
use min_version::MINIMUM_NSS_VERSION; use min_version::MINIMUM_NSS_VERSION;
use neqo_common::qerror; use neqo_common::qerror;
#[allow(non_upper_case_globals)] #[expect(non_upper_case_globals, reason = "Code is bindgen-generated.")]
mod nss { mod nss {
include!(concat!(env!("OUT_DIR"), "/nss_init.rs")); include!(concat!(env!("OUT_DIR"), "/nss_init.rs"));
} }
@@ -170,6 +167,9 @@ pub fn init() -> Res<()> {
/// ///
/// If NSS cannot be initialized. /// If NSS cannot be initialized.
pub fn init_db<P: Into<PathBuf>>(dir: P) -> Res<()> { pub fn init_db<P: Into<PathBuf>>(dir: P) -> Res<()> {
// Allow overriding the NSS database path with an environment variable.
let dir = env::var("NSS_DB_PATH")
.unwrap_or(dir.into().to_str().ok_or(Error::InternalError)?.to_string());
let res = INITIALIZED.get_or_init(|| init_once(Some(dir.into()))); let res = INITIALIZED.get_or_init(|| init_once(Some(dir.into())));
res.as_ref().map(|_| ()).map_err(Clone::clone) res.as_ref().map(|_| ()).map_err(Clone::clone)
} }
@@ -201,7 +201,7 @@ where
if data.is_null() || len == 0 { if data.is_null() || len == 0 {
&[] &[]
} else { } else {
#[allow(clippy::disallowed_methods)] #[expect(clippy::disallowed_methods, reason = "This is non-null.")]
std::slice::from_raw_parts(data, len) std::slice::from_raw_parts(data, len)
} }
} }

View File

@@ -4,11 +4,6 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![allow(dead_code)]
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
use std::{ use std::{
cell::RefCell, cell::RefCell,
ops::{Deref, DerefMut}, ops::{Deref, DerefMut},
@@ -24,7 +19,14 @@ use crate::{
null_safe_slice, null_safe_slice,
}; };
#[allow(clippy::unreadable_literal)] #[expect(
dead_code,
non_snake_case,
non_upper_case_globals,
non_camel_case_types,
clippy::unreadable_literal,
reason = "For included bindgen code."
)]
mod nss_p11 { mod nss_p11 {
include!(concat!(env!("OUT_DIR"), "/nss_p11.rs")); include!(concat!(env!("OUT_DIR"), "/nss_p11.rs"));
} }
@@ -44,6 +46,7 @@ macro_rules! scoped_ptr {
/// # Errors /// # Errors
/// ///
/// When passed a null pointer generates an error. /// When passed a null pointer generates an error.
#[allow(clippy::allow_attributes, dead_code, reason = "False positive.")]
pub fn from_ptr(ptr: *mut $target) -> Result<Self, $crate::err::Error> { pub fn from_ptr(ptr: *mut $target) -> Result<Self, $crate::err::Error> {
if ptr.is_null() { if ptr.is_null() {
Err($crate::err::Error::last_nss_error()) Err($crate::err::Error::last_nss_error())
@@ -55,7 +58,6 @@ macro_rules! scoped_ptr {
impl Deref for $scoped { impl Deref for $scoped {
type Target = *mut $target; type Target = *mut $target;
#[must_use]
fn deref(&self) -> &*mut $target { fn deref(&self) -> &*mut $target {
&self.ptr &self.ptr
} }
@@ -105,7 +107,6 @@ impl PublicKey {
} }
impl Clone for PublicKey { impl Clone for PublicKey {
#[must_use]
fn clone(&self) -> Self { fn clone(&self) -> Self {
let ptr = unsafe { SECKEY_CopyPublicKey(self.ptr) }; let ptr = unsafe { SECKEY_CopyPublicKey(self.ptr) };
assert!(!ptr.is_null()); assert!(!ptr.is_null());
@@ -160,7 +161,6 @@ impl PrivateKey {
unsafe impl Send for PrivateKey {} unsafe impl Send for PrivateKey {}
impl Clone for PrivateKey { impl Clone for PrivateKey {
#[must_use]
fn clone(&self) -> Self { fn clone(&self) -> Self {
let ptr = unsafe { SECKEY_CopyPrivateKey(self.ptr) }; let ptr = unsafe { SECKEY_CopyPrivateKey(self.ptr) };
assert!(!ptr.is_null()); assert!(!ptr.is_null());
@@ -208,7 +208,6 @@ impl SymKey {
} }
impl Clone for SymKey { impl Clone for SymKey {
#[must_use]
fn clone(&self) -> Self { fn clone(&self) -> Self {
let ptr = unsafe { PK11_ReferenceSymKey(self.ptr) }; let ptr = unsafe { PK11_ReferenceSymKey(self.ptr) };
assert!(!ptr.is_null()); assert!(!ptr.is_null());
@@ -226,6 +225,12 @@ impl std::fmt::Debug for SymKey {
} }
} }
impl Default for SymKey {
fn default() -> Self {
Self { ptr: null_mut() }
}
}
unsafe fn destroy_pk11_context(ctxt: *mut PK11Context) { unsafe fn destroy_pk11_context(ctxt: *mut PK11Context) {
PK11_DestroyContext(ctxt, PRBool::from(true)); PK11_DestroyContext(ctxt, PRBool::from(true));
} }
@@ -247,25 +252,25 @@ impl Item {
/// Creating this object is technically safe, but using it is extremely dangerous. /// Creating this object is technically safe, but using it is extremely dangerous.
/// Minimally, it can only be passed as a `const SECItem*` argument to functions, /// Minimally, it can only be passed as a `const SECItem*` argument to functions,
/// or those that treat their argument as `const`. /// or those that treat their argument as `const`.
pub fn wrap(buf: &[u8]) -> SECItem { pub fn wrap(buf: &[u8]) -> Res<SECItem> {
SECItem { Ok(SECItem {
type_: SECItemType::siBuffer, type_: SECItemType::siBuffer,
data: buf.as_ptr().cast_mut(), data: buf.as_ptr().cast_mut(),
len: c_uint::try_from(buf.len()).unwrap(), len: c_uint::try_from(buf.len())?,
} })
} }
/// Create a wrapper for a struct. /// Create a wrapper for a struct.
/// Creating this object is technically safe, but using it is extremely dangerous. /// Creating this object is technically safe, but using it is extremely dangerous.
/// Minimally, it can only be passed as a `const SECItem*` argument to functions, /// Minimally, it can only be passed as a `const SECItem*` argument to functions,
/// or those that treat their argument as `const`. /// or those that treat their argument as `const`.
pub fn wrap_struct<T>(v: &T) -> SECItem { pub fn wrap_struct<T>(v: &T) -> Res<SECItem> {
let data: *const T = v; let data: *const T = v;
SECItem { Ok(SECItem {
type_: SECItemType::siBuffer, type_: SECItemType::siBuffer,
data: data.cast_mut().cast(), data: data.cast_mut().cast(),
len: c_uint::try_from(std::mem::size_of::<T>()).unwrap(), len: c_uint::try_from(size_of::<T>())?,
} })
} }
/// Make an empty `SECItem` for passing as a mutable `SECItem*` argument. /// Make an empty `SECItem` for passing as a mutable `SECItem*` argument.
@@ -276,20 +281,6 @@ impl Item {
len: 0, len: 0,
} }
} }
/// This dereferences the pointer held by the item and makes a copy of the
/// content that is referenced there.
///
/// # Safety
///
/// This dereferences two pointers. It doesn't get much less safe.
pub unsafe fn into_vec(self) -> Vec<u8> {
let b = self.ptr.as_ref().unwrap();
// Sanity check the type, as some types don't count bytes in `Item::len`.
assert_eq!(b.type_, SECItemType::siBuffer);
let slc = null_safe_slice(b.data, b.len);
Vec::from(slc)
}
} }
unsafe fn destroy_secitem_array(array: *mut SECItemArray) { unsafe fn destroy_secitem_array(array: *mut SECItemArray) {
@@ -348,8 +339,8 @@ pub fn randomize<B: AsMut<[u8]>>(mut buf: B) -> B {
#[cfg(not(feature = "disable-random"))] #[cfg(not(feature = "disable-random"))]
pub fn randomize<B: AsMut<[u8]>>(mut buf: B) -> B { pub fn randomize<B: AsMut<[u8]>>(mut buf: B) -> B {
let m_buf = buf.as_mut(); let m_buf = buf.as_mut();
let len = std::os::raw::c_int::try_from(m_buf.len()).unwrap(); let len = std::os::raw::c_int::try_from(m_buf.len()).expect("usize fits into c_int");
secstatus_to_res(unsafe { PK11_GenerateRandom(m_buf.as_mut_ptr(), len) }).unwrap(); secstatus_to_res(unsafe { PK11_GenerateRandom(m_buf.as_mut_ptr(), len) }).expect("NSS failed");
buf buf
} }

View File

@@ -9,7 +9,9 @@
non_upper_case_globals, non_upper_case_globals,
non_snake_case, non_snake_case,
clippy::cognitive_complexity, clippy::cognitive_complexity,
clippy::too_many_lines clippy::too_many_lines,
clippy::used_underscore_binding,
reason = "For included bindgen code."
)] )]
include!(concat!(env!("OUT_DIR"), "/nspr_io.rs")); include!(concat!(env!("OUT_DIR"), "/nspr_io.rs"));

View File

@@ -44,7 +44,6 @@ scoped_ptr!(
/// It limits the exposure of servers to replay attack by rejecting 0-RTT /// It limits the exposure of servers to replay attack by rejecting 0-RTT
/// if it appears to be a replay. There is a false-positive rate that can be /// if it appears to be a replay. There is a false-positive rate that can be
/// managed by tuning the parameters used to create the context. /// managed by tuning the parameters used to create the context.
#[allow(clippy::module_name_repetitions)]
pub struct AntiReplay { pub struct AntiReplay {
ctx: AntiReplayContext, ctx: AntiReplayContext,
} }

View File

@@ -4,9 +4,11 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
use std::{os::raw::c_void, pin::Pin}; use std::{mem, os::raw::c_void, pin::Pin};
use enum_map::EnumMap;
use neqo_common::qdebug; use neqo_common::qdebug;
use strum::FromRepr;
use crate::{ use crate::{
agentio::as_c_void, agentio::as_c_void,
@@ -22,44 +24,37 @@ experimental_api!(SSL_SecretCallback(
arg: *mut c_void, arg: *mut c_void,
)); ));
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug, FromRepr)]
#[cfg_attr(windows, repr(i32))] // Windows has to be different, of course.
#[cfg_attr(not(windows), repr(u32))]
pub enum SecretDirection { pub enum SecretDirection {
Read, Read = SSLSecretDirection::ssl_secret_read,
Write, Write = SSLSecretDirection::ssl_secret_write,
} }
impl From<SSLSecretDirection::Type> for SecretDirection { impl From<SSLSecretDirection::Type> for SecretDirection {
#[must_use]
fn from(dir: SSLSecretDirection::Type) -> Self { fn from(dir: SSLSecretDirection::Type) -> Self {
match dir { Self::from_repr(dir).expect("Invalid secret direction")
SSLSecretDirection::ssl_secret_read => Self::Read,
SSLSecretDirection::ssl_secret_write => Self::Write,
_ => unreachable!(),
}
} }
} }
#[derive(Debug, Default)] #[derive(Debug, Default)]
#[allow(clippy::module_name_repetitions)]
pub struct DirectionalSecrets { pub struct DirectionalSecrets {
// We only need to maintain 3 secrets for the epochs used during the handshake. secrets: EnumMap<Epoch, SymKey>,
secrets: [Option<SymKey>; 3],
} }
impl DirectionalSecrets { impl DirectionalSecrets {
fn put(&mut self, epoch: Epoch, key: SymKey) { fn put(&mut self, epoch: Epoch, key: SymKey) {
assert!(epoch > 0); debug_assert!(epoch != Epoch::Initial);
let i = (epoch - 1) as usize; self.secrets[epoch] = key;
assert!(i < self.secrets.len());
// assert!(self.secrets[i].is_none());
self.secrets[i] = Some(key);
} }
pub fn take(&mut self, epoch: Epoch) -> Option<SymKey> { pub fn take(&mut self, epoch: Epoch) -> Option<SymKey> {
assert!(epoch > 0); if self.secrets[epoch].is_null() {
let i = (epoch - 1) as usize; None
assert!(i < self.secrets.len()); } else {
self.secrets[i].take() Some(mem::take(&mut self.secrets[epoch]))
}
} }
} }
@@ -77,7 +72,15 @@ impl Secrets {
secret: *mut PK11SymKey, secret: *mut PK11SymKey,
arg: *mut c_void, arg: *mut c_void,
) { ) {
let secrets = arg.cast::<Self>().as_mut().unwrap(); let Ok(epoch) = Epoch::try_from(epoch) else {
debug_assert!(false, "Invalid epoch");
// Don't touch secrets.
return;
};
let Some(secrets) = arg.cast::<Self>().as_mut() else {
debug_assert!(false, "No secrets");
return;
};
secrets.put_raw(epoch, dir, secret); secrets.put_raw(epoch, dir, secret);
} }

View File

@@ -126,7 +126,7 @@ impl SelfEncrypt {
/// ///
/// Returns an error when the self-encrypted object is invalid; /// Returns an error when the self-encrypted object is invalid;
/// when the keys have been rotated; or when NSS fails. /// when the keys have been rotated; or when NSS fails.
#[allow(clippy::similar_names)] // aad is similar to aead #[expect(clippy::similar_names, reason = "aad is similar to aead.")]
pub fn open(&self, aad: &[u8], ciphertext: &[u8]) -> Res<Vec<u8>> { pub fn open(&self, aad: &[u8], ciphertext: &[u8]) -> Res<Vec<u8>> {
if ciphertext[0] != Self::VERSION { if ciphertext[0] != Self::VERSION {
return Err(Error::SelfEncryptFailure); return Err(Error::SelfEncryptFailure);

View File

@@ -5,11 +5,13 @@
// except according to those terms. // except according to those terms.
#![allow( #![allow(
clippy::allow_attributes,
dead_code, dead_code,
non_upper_case_globals, non_upper_case_globals,
non_snake_case, non_snake_case,
clippy::cognitive_complexity,
clippy::too_many_lines, clippy::too_many_lines,
clippy::cognitive_complexity reason = "For included bindgen code."
)] )]
use std::os::raw::{c_uint, c_void}; use std::os::raw::{c_uint, c_void};
@@ -20,56 +22,41 @@ use crate::{
}; };
include!(concat!(env!("OUT_DIR"), "/nss_ssl.rs")); include!(concat!(env!("OUT_DIR"), "/nss_ssl.rs"));
#[expect(non_snake_case, reason = "OK here.")]
mod SSLOption { mod SSLOption {
include!(concat!(env!("OUT_DIR"), "/nss_sslopt.rs")); include!(concat!(env!("OUT_DIR"), "/nss_sslopt.rs"));
} }
// I clearly don't understand how bindgen operates.
pub enum PLArenaPool {}
pub enum PRFileDesc {} pub enum PRFileDesc {}
// Remap some constants. // Remap some constants.
#[expect(non_upper_case_globals, reason = "OK here.")]
pub const SECSuccess: SECStatus = _SECStatus_SECSuccess; pub const SECSuccess: SECStatus = _SECStatus_SECSuccess;
#[expect(non_upper_case_globals, reason = "OK here.")]
pub const SECFailure: SECStatus = _SECStatus_SECFailure; pub const SECFailure: SECStatus = _SECStatus_SECFailure;
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
#[repr(u32)]
pub enum Opt { pub enum Opt {
Locking, Locking = SSLOption::SSL_NO_LOCKS,
Tickets, Tickets = SSLOption::SSL_ENABLE_SESSION_TICKETS,
OcspStapling, OcspStapling = SSLOption::SSL_ENABLE_OCSP_STAPLING,
Alpn, Alpn = SSLOption::SSL_ENABLE_ALPN,
ExtendedMasterSecret, ExtendedMasterSecret = SSLOption::SSL_ENABLE_EXTENDED_MASTER_SECRET,
SignedCertificateTimestamps, SignedCertificateTimestamps = SSLOption::SSL_ENABLE_SIGNED_CERT_TIMESTAMPS,
EarlyData, EarlyData = SSLOption::SSL_ENABLE_0RTT_DATA,
RecordSizeLimit, RecordSizeLimit = SSLOption::SSL_RECORD_SIZE_LIMIT,
Tls13CompatMode, Tls13CompatMode = SSLOption::SSL_ENABLE_TLS13_COMPAT_MODE,
HelloDowngradeCheck, HelloDowngradeCheck = SSLOption::SSL_ENABLE_HELLO_DOWNGRADE_CHECK,
SuppressEndOfEarlyData, SuppressEndOfEarlyData = SSLOption::SSL_SUPPRESS_END_OF_EARLY_DATA,
Grease, Grease = SSLOption::SSL_ENABLE_GREASE,
EnableChExtensionPermutation, EnableChExtensionPermutation = SSLOption::SSL_ENABLE_CH_EXTENSION_PERMUTATION,
} }
impl Opt { impl Opt {
// Cast is safe here because SSLOptions are within the i32 range
#[allow(clippy::cast_possible_wrap)]
#[must_use] #[must_use]
pub const fn as_int(self) -> PRInt32 { pub const fn as_int(self) -> PRInt32 {
let i = match self { self as PRInt32
Self::Locking => SSLOption::SSL_NO_LOCKS,
Self::Tickets => SSLOption::SSL_ENABLE_SESSION_TICKETS,
Self::OcspStapling => SSLOption::SSL_ENABLE_OCSP_STAPLING,
Self::Alpn => SSLOption::SSL_ENABLE_ALPN,
Self::ExtendedMasterSecret => SSLOption::SSL_ENABLE_EXTENDED_MASTER_SECRET,
Self::SignedCertificateTimestamps => SSLOption::SSL_ENABLE_SIGNED_CERT_TIMESTAMPS,
Self::EarlyData => SSLOption::SSL_ENABLE_0RTT_DATA,
Self::RecordSizeLimit => SSLOption::SSL_RECORD_SIZE_LIMIT,
Self::Tls13CompatMode => SSLOption::SSL_ENABLE_TLS13_COMPAT_MODE,
Self::HelloDowngradeCheck => SSLOption::SSL_ENABLE_HELLO_DOWNGRADE_CHECK,
Self::SuppressEndOfEarlyData => SSLOption::SSL_SUPPRESS_END_OF_EARLY_DATA,
Self::Grease => SSLOption::SSL_ENABLE_GREASE,
Self::EnableChExtensionPermutation => SSLOption::SSL_ENABLE_CH_EXTENSION_PERMUTATION,
};
i as PRInt32
} }
// Some options are backwards, like SSL_NO_LOCKS, so use this to manage that. // Some options are backwards, like SSL_NO_LOCKS, so use this to manage that.
@@ -86,11 +73,6 @@ impl Opt {
} }
} }
experimental_api!(SSL_GetCurrentEpoch(
fd: *mut PRFileDesc,
read_epoch: *mut u16,
write_epoch: *mut u16,
));
experimental_api!(SSL_HelloRetryRequestCallback( experimental_api!(SSL_HelloRetryRequestCallback(
fd: *mut PRFileDesc, fd: *mut PRFileDesc,
cb: SSLHelloRetryRequestCallback, cb: SSLHelloRetryRequestCallback,

View File

@@ -4,6 +4,11 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![expect(
clippy::unwrap_used,
reason = "Let's assume the use of `unwrap` was checked when the use of `unsafe` was reviewed."
)]
use std::{ use std::{
ops::Deref, ops::Deref,
os::raw::c_void, os::raw::c_void,
@@ -139,7 +144,6 @@ impl TryInto<PRTime> for Time {
} }
impl From<Time> for Instant { impl From<Time> for Instant {
#[must_use]
fn from(t: Time) -> Self { fn from(t: Time) -> Self {
t.t t.t
} }

View File

@@ -78,7 +78,7 @@ fn check_client_preinfo(client_preinfo: &SecretAgentPreInfo) {
assert_eq!(client_preinfo.cipher_suite(), None); assert_eq!(client_preinfo.cipher_suite(), None);
assert!(!client_preinfo.early_data()); assert!(!client_preinfo.early_data());
assert_eq!(client_preinfo.early_data_cipher(), None); assert_eq!(client_preinfo.early_data_cipher(), None);
assert_eq!(client_preinfo.max_early_data(), 0); assert_eq!(client_preinfo.max_early_data(), Ok(0));
assert_eq!(client_preinfo.alpn(), None); assert_eq!(client_preinfo.alpn(), None);
} }
@@ -87,7 +87,7 @@ fn check_server_preinfo(server_preinfo: &SecretAgentPreInfo) {
assert_eq!(server_preinfo.cipher_suite(), Some(TLS_AES_128_GCM_SHA256)); assert_eq!(server_preinfo.cipher_suite(), Some(TLS_AES_128_GCM_SHA256));
assert!(!server_preinfo.early_data()); assert!(!server_preinfo.early_data());
assert_eq!(server_preinfo.early_data_cipher(), None); assert_eq!(server_preinfo.early_data_cipher(), None);
assert_eq!(server_preinfo.max_early_data(), 0); assert_eq!(server_preinfo.max_early_data(), Ok(0));
assert_eq!(server_preinfo.alpn(), None); assert_eq!(server_preinfo.alpn(), None);
} }

View File

@@ -4,7 +4,7 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![allow(dead_code)] #![expect(clippy::unwrap_used, reason = "OK for tests.")]
use std::{mem, time::Instant}; use std::{mem, time::Instant};
@@ -16,8 +16,16 @@ use neqo_crypto::{
use test_fixture::{anti_replay, fixture_init, now}; use test_fixture::{anti_replay, fixture_init, now};
/// Consume records until the handshake state changes. /// Consume records until the handshake state changes.
#[allow(clippy::missing_panics_doc)] #[allow(
#[allow(clippy::missing_errors_doc)] clippy::allow_attributes,
clippy::missing_panics_doc,
reason = "OK for tests."
)]
#[allow(
clippy::allow_attributes,
clippy::missing_errors_doc,
reason = "OK for tests."
)]
pub fn forward_records( pub fn forward_records(
now: Instant, now: Instant,
agent: &mut SecretAgent, agent: &mut SecretAgent,
@@ -65,7 +73,11 @@ fn handshake(now: Instant, client: &mut SecretAgent, server: &mut SecretAgent) {
} }
} }
#[allow(clippy::missing_panics_doc)] #[allow(
clippy::allow_attributes,
clippy::missing_panics_doc,
reason = "OK for tests."
)]
pub fn connect_at(now: Instant, client: &mut SecretAgent, server: &mut SecretAgent) { pub fn connect_at(now: Instant, client: &mut SecretAgent, server: &mut SecretAgent) {
handshake(now, client, server); handshake(now, client, server);
qinfo!("client: {:?}", client.state()); qinfo!("client: {:?}", client.state());
@@ -78,13 +90,19 @@ pub fn connect(client: &mut SecretAgent, server: &mut SecretAgent) {
connect_at(now(), client, server); connect_at(now(), client, server);
} }
#[allow(clippy::missing_panics_doc)] #[allow(
clippy::allow_attributes,
clippy::missing_panics_doc,
dead_code,
reason = "OK for tests."
)]
pub fn connect_fail(client: &mut SecretAgent, server: &mut SecretAgent) { pub fn connect_fail(client: &mut SecretAgent, server: &mut SecretAgent) {
handshake(now(), client, server); handshake(now(), client, server);
assert!(!client.state().is_connected()); assert!(!client.state().is_connected());
assert!(!server.state().is_connected()); assert!(!server.state().is_connected());
} }
#[allow(clippy::allow_attributes, dead_code, reason = "False positive.")]
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub enum Resumption { pub enum Resumption {
WithoutZeroRtt, WithoutZeroRtt,
@@ -115,6 +133,7 @@ impl ZeroRttChecker for PermissiveZeroRttChecker {
} }
} }
#[allow(clippy::allow_attributes, dead_code, reason = "False positive.")]
fn zero_rtt_setup(mode: Resumption, client: &Client, server: &mut Server) -> Option<AntiReplay> { fn zero_rtt_setup(mode: Resumption, client: &Client, server: &mut Server) -> Option<AntiReplay> {
matches!(mode, Resumption::WithZeroRtt).then(|| { matches!(mode, Resumption::WithZeroRtt).then(|| {
client.enable_0rtt().expect("should enable 0-RTT on client"); client.enable_0rtt().expect("should enable 0-RTT on client");
@@ -131,7 +150,12 @@ fn zero_rtt_setup(mode: Resumption, client: &Client, server: &mut Server) -> Opt
}) })
} }
#[allow(clippy::missing_panics_doc)] #[allow(
clippy::allow_attributes,
clippy::missing_panics_doc,
dead_code,
reason = "OK for tests."
)]
#[must_use] #[must_use]
pub fn resumption_setup(mode: Resumption) -> (Option<AntiReplay>, ResumptionToken) { pub fn resumption_setup(mode: Resumption) -> (Option<AntiReplay>, ResumptionToken) {
fixture_init(); fixture_init();

View File

@@ -26,7 +26,7 @@ fn hp_test(cipher: Cipher, expected: &[u8]) {
let mask = hp.mask(&[0; 16]).expect("should produce a mask"); let mask = hp.mask(&[0; 16]).expect("should produce a mask");
assert_eq!(mask, expected, "first invocation should be correct"); assert_eq!(mask, expected, "first invocation should be correct");
#[allow(clippy::redundant_clone)] // This is deliberate. #[expect(clippy::redundant_clone, reason = "This is deliberate.")]
let hp2 = hp.clone(); let hp2 = hp.clone();
let mask = hp2.mask(&[0; 16]).expect("clone produces mask"); let mask = hp2.mask(&[0; 16]).expect("clone produces mask");
assert_eq!(mask, expected, "clone should produce the same mask"); assert_eq!(mask, expected, "clone should produce the same mask");

View File

@@ -15,7 +15,11 @@ use neqo_crypto::{assert_initialized, init_db};
// Pull in the NSS internals so that we can ask NSS if it thinks that // Pull in the NSS internals so that we can ask NSS if it thinks that
// it is properly initialized. // it is properly initialized.
#[allow(dead_code, non_upper_case_globals)] #[expect(
non_upper_case_globals,
dead_code,
reason = "Code is bindgen-generated."
)]
mod nss { mod nss {
include!(concat!(env!("OUT_DIR"), "/nss_init.rs")); include!(concat!(env!("OUT_DIR"), "/nss_init.rs"));
} }

View File

@@ -5,6 +5,7 @@
// except according to those terms. // except according to those terms.
#![cfg(not(feature = "disable-encryption"))] #![cfg(not(feature = "disable-encryption"))]
#![cfg(test)]
use neqo_crypto::{ use neqo_crypto::{
constants::{TLS_AES_128_GCM_SHA256, TLS_VERSION_1_3}, constants::{TLS_AES_128_GCM_SHA256, TLS_VERSION_1_3},

View File

@@ -1 +1 @@
{"files":{"Cargo.toml":"c82172fc083fd474f3c412afba7882d6792e3ee868da8e84881bfa5dd4c1195c","src/buffered_send_stream.rs":"d74c520af97bcdaa48862a396e6510c9f95124358dcf46a7722e4daf821fff2c","src/client_events.rs":"96d38deba366edbbe5f6e73d56d74ae36e239e35393ae3ac094d73063b564f68","src/conn_params.rs":"7f0df52bceda1923aef2b7c5c64a532f49ea083ea45e3dcd5bd4b03031b89643","src/connection.rs":"d225b55f1229988568085f133dac1cfe5d4adfd91864a7e1fc344a784a4b5594","src/connection_client.rs":"9a48949fe885e1139ae2c056a5504a3e336c8d75aae4cdcc2ad92355ad5aa623","src/connection_server.rs":"d16f73685ae29492e2d32321158b6206349fe1b0016d89bc50c2727f47c64140","src/control_stream_local.rs":"9325e5c8857036c68f7e49b81c898d5de51d43cb262e523385e6b2b7366c2cea","src/control_stream_remote.rs":"e7d0b5a195e3ef482b36972eac979b7d761215fcb76699112f2d4919708fcf26","src/features/extended_connect/mod.rs":"f9a08a6ec1dde79133c18c21b85f6b9a01468d98bace838f559340383cc603e7","src/features/extended_connect/tests/mod.rs":"fd6aee37243713e80fc526552f21f0222338cec9890409b6575a2a637b17ec1f","src/features/extended_connect/tests/webtransport/datagrams.rs":"51d6f3828c44b438eb1776e8dcce531af520f28bc0d715807d3f53a0eaa071d1","src/features/extended_connect/tests/webtransport/mod.rs":"9c0de1da3cfe9fac75f2d2faf3e0e0500261519e7862c6b5fec5097ab47f72f1","src/features/extended_connect/tests/webtransport/negotiation.rs":"8b505384d5b8a9c8e17194360a33d9ecbdb350c3359245b9f2bed9dc8cec7423","src/features/extended_connect/tests/webtransport/sessions.rs":"64b29ec9ba34e636b1109d99ab588666d85c85442565faa9071b743f4f764d6e","src/features/extended_connect/tests/webtransport/streams.rs":"eb0e793dc43ca9296334d29ced70d5ab3d90fd0c4ca3a485abad06c14eec29a4","src/features/extended_connect/webtransport_session.rs":"09a06af84e101f628f1ff2e9be344463662212f2d8914fe84b9675d9f0488af5","src/features/extended_connect/webtransport_streams.rs":"9855d77705acb7d21566333c4b297816e363be2ade14b8685fd1df4a4861cf74","src/features/mod.rs":"6f192996e6e2231a97f3fcae2aa02d648e571a6ec5e14b4a23b24b87888d117c","src/frames/hframe.rs":"5fcd9145a88e8ebf9ec1832c8ab61357a9afd362690e26d5a32595b2ae135395","src/frames/mod.rs":"0e6d49888d723b2c2c73df11020ceb88d9f062e9d4dc436eb38173e0b772d905","src/frames/reader.rs":"fff71d4e5a75ac16ed455c5cb7a12f355d56fe8bf7b33d6fb160e4bc48ee94cd","src/frames/tests/hframe.rs":"43a7735fc859692633e7f3c031710a9fb635611756cb4b9f387bac0a38c0fa09","src/frames/tests/mod.rs":"ee2eb2d88132e500640b156d5ae6b6ef5051e86eae09464a9cec827142ef6b73","src/frames/tests/reader.rs":"eaff7faa62aec5caf2ed21c6bd9e869654433b030df7676cc4843e2b15d1a6c1","src/frames/tests/wtframe.rs":"c6598d24f5e12972f02de6e1394362671633982db637a07e1c0bb9b56d93ea2a","src/frames/wtframe.rs":"544bc8fa74e3b15cde6e45ba47bcc52f098658e78664abfda8249a89c88d51b9","src/headers_checks.rs":"42f5bb95c6af9d7e747b3b4e39a2b10c4a323abd7da30cd4db4ebbc47696e060","src/lib.rs":"1fd024d8ec7fed5c4766f8a817531b1a4363bd2eaa60f9a41e4c2fed84682308","src/priority.rs":"946307329f31819d969093406ae5448f7923343ccc112221ea6eedf86cf447dc","src/push_controller.rs":"a03aa8b1e2db588f4342337252e7b7d10f43447498ddbc95733b5304a8e717ab","src/push_id.rs":"cd4cea3102b59918668b66725bbd19b66dc389e35d986132c5ceb72bbc9f3222","src/qlog.rs":"85187b6eee73d0ce085bc42dfc30999f4a51f92daa02a1a1ac1228a490131082","src/qpack_decoder_receiver.rs":"eb06c4be59da567fef70c20daa2c0f165c768131165479a210e69659f168b88f","src/qpack_encoder_receiver.rs":"831f3da9ec17966286786ba3f2c723395a132e65d6a33b4ec341fe7640c1a53d","src/recv_message.rs":"279c1de34d13bc43cfd5c622956d9a1a7e24153918b029c45c0358f384fef8fd","src/request_target.rs":"9720b9f87d66a7c2301bba7de5a5a9300f547613a63153a4d35c7a7506a59b31","src/send_message.rs":"16e688bcffe7aefa43bb5876cdad1f79b9227acad8695afc64f47c1565933e69","src/server.rs":"86c0a012b5dcb4d7c9de69360bf004dd30d75c7827b4cd60c282ca89c7bb2ed3","src/server_connection_events.rs":"1396baab265a814045ccfe63d637a4fdc32a667b5eb2925fa4951f5c3078fb20","src/server_events.rs":"26f3d3585633296a62c3e1e8594e491f45e4c6d17c3cd75847c3cdec5778c2e1","src/settings.rs":"d0f8c546e70161422a029a40564b9e9b953fe671c60835196b16f3364779eaf9","src/stream_type_reader.rs":"5ba77600c5e8c4abdd9443aec20311cf8a812b97ab18a90408aa4e689f42bded","tests/httpconn.rs":"5e2318ac776ce8b4c0d3c44b42fc32e6609b2ef31853767312462eae9a7c659d","tests/priority.rs":"f3869e5fbf63483f04b589d3447e02a4f167b2f3c406028cb30a87ad9a22da53","tests/send_message.rs":"21de05a3c6d516d28df6d77d7e4fb1b05985fc2debde1e8c7fb0607164c74b75","tests/webtransport.rs":"4ae2531c8384120b541198757106954b1dac9f2dcb39ed2231a67a59c1fb94f8"},"package":null} {"files":{"Cargo.toml":"2ea9b93fd8d1cd8fed7eb33f1f5ab004f6896812d5b53903d15ad09982709943","benches/streams.rs":"00140c3f08abd66b5c785772d110a19b3e75882e6c6a4ea912565a1c18add57e","src/buffered_send_stream.rs":"39c10972e27d2c5c79703a13cf74bd6e5b013c29176f2b352e16cf4d9481d4de","src/client_events.rs":"38e48177d4bc301dce85e4953670e54a39b6bf1ce657412f9fb3497589d099e9","src/conn_params.rs":"7f0df52bceda1923aef2b7c5c64a532f49ea083ea45e3dcd5bd4b03031b89643","src/connection.rs":"e1aecf717d032c5228f0d8bee86d2d2960f471596676ba3fd61445625aa23e77","src/connection_client.rs":"48cd86a73960affe2cf101c8db6d12f5e6c7e8dab3412051482a691537ad56b4","src/connection_server.rs":"d16f73685ae29492e2d32321158b6206349fe1b0016d89bc50c2727f47c64140","src/control_stream_local.rs":"6b7774bc392292d66da4b4e915a477942cdcc0b287b38d77c24f1638f4b85755","src/control_stream_remote.rs":"e7d0b5a195e3ef482b36972eac979b7d761215fcb76699112f2d4919708fcf26","src/features/extended_connect/mod.rs":"f24b419d28eaffe47444120443f2b5613db9b5b5154f4b5725b35d8640c222e1","src/features/extended_connect/tests/mod.rs":"fd6aee37243713e80fc526552f21f0222338cec9890409b6575a2a637b17ec1f","src/features/extended_connect/tests/webtransport/datagrams.rs":"51d6f3828c44b438eb1776e8dcce531af520f28bc0d715807d3f53a0eaa071d1","src/features/extended_connect/tests/webtransport/mod.rs":"4e81ae458ecaa1d0f29b7565c19c9751658fe0c7496ce40d7c90a102d9f04a16","src/features/extended_connect/tests/webtransport/negotiation.rs":"b0083f8737bdea9bc0de1940c627d497fee8b79ebc218bbcea0a562ae530527f","src/features/extended_connect/tests/webtransport/sessions.rs":"64b29ec9ba34e636b1109d99ab588666d85c85442565faa9071b743f4f764d6e","src/features/extended_connect/tests/webtransport/streams.rs":"eb0e793dc43ca9296334d29ced70d5ab3d90fd0c4ca3a485abad06c14eec29a4","src/features/extended_connect/webtransport_session.rs":"5888cd09d204738b1ef4e53b83f6c5ccee33d5d3549502d39b5332db3c1c53bd","src/features/extended_connect/webtransport_streams.rs":"9855d77705acb7d21566333c4b297816e363be2ade14b8685fd1df4a4861cf74","src/features/mod.rs":"6f192996e6e2231a97f3fcae2aa02d648e571a6ec5e14b4a23b24b87888d117c","src/frames/hframe.rs":"8cf686390f2bfd28c92a0badcb647c6ea439eeee2a8392e088f6d9af6662854d","src/frames/mod.rs":"ec86e32919bd4cf0baa8686444cb6062d6eabea9b74ef58680f5da476fdcc77c","src/frames/reader.rs":"08df3ec01b29e7b1001160cf63b27f393eafdb1891888e9751d20dd36e903781","src/frames/tests/hframe.rs":"43a7735fc859692633e7f3c031710a9fb635611756cb4b9f387bac0a38c0fa09","src/frames/tests/mod.rs":"4bbdf566a7370605d49373027b38c4bcbd91113f7693efee29b9d91e3914ad98","src/frames/tests/reader.rs":"35f94d1ce1d088fca35592d8a2898c49bd6df0f3facfa12ef6b918124a1356bc","src/frames/tests/wtframe.rs":"c6598d24f5e12972f02de6e1394362671633982db637a07e1c0bb9b56d93ea2a","src/frames/wtframe.rs":"544bc8fa74e3b15cde6e45ba47bcc52f098658e78664abfda8249a89c88d51b9","src/headers_checks.rs":"93b69056321eefa8b0260bbfd8ba82064de2fcb915c7fd63e0f2855ea6187eba","src/lib.rs":"f085007ac4fe1f75ca6f66f55836c395d6c36ccd849b3dfa77958e8bfad2daa4","src/priority.rs":"a32de5a2e333d387e6c73ebe7f1cbd2b14e42724145aed5d6d6c1bd787ca7704","src/push_controller.rs":"ebd055be1be92f9709d57b4347a4f2ceb56b8d1e48e5df1dc7c8cb475529dae6","src/push_id.rs":"cd4cea3102b59918668b66725bbd19b66dc389e35d986132c5ceb72bbc9f3222","src/qlog.rs":"6c0b622cd3ec7d64cb920d38fca0b0e87a43759c6fd4c2d63c352aa9c7ad11bf","src/qpack_decoder_receiver.rs":"eb06c4be59da567fef70c20daa2c0f165c768131165479a210e69659f168b88f","src/qpack_encoder_receiver.rs":"831f3da9ec17966286786ba3f2c723395a132e65d6a33b4ec341fe7640c1a53d","src/recv_message.rs":"cfdc209630332c013b075ff13c28227ecde6d93358116eff82cba88ca37d1c61","src/request_target.rs":"8b8c2d1221e1956f13b66d18f8bc880062549ce5f204f15d39c7d69f727875c9","src/send_message.rs":"4eadd7d5bd8f33e0d2212472200eaacf9f848c795d940ec543247cf4e0f89dbf","src/server.rs":"58da712bfa9fd2901df72d17b1cc7afb2cb8e30af508e3b8239de8841284d36b","src/server_connection_events.rs":"9b429916357f967d697cd9806c720e3ca7cf152603b0ddb16a7f9992a8188ee2","src/server_events.rs":"5599f3c87dfe4ac168c1279967617f702722fac0cb6c9dacf2dfdd31388fb815","src/settings.rs":"ae0b57b462803b963a17616863054e5d6858a89ff1021b52b43de8f72365bdc8","src/stream_type_reader.rs":"5ba77600c5e8c4abdd9443aec20311cf8a812b97ab18a90408aa4e689f42bded","tests/httpconn.rs":"a4dbbfa8bed000ca983bf4f2a015ee49283603924ae5fca8293b0a38f8ee5c2b","tests/priority.rs":"31aede7d5b6c66c415cbf9f0298c8de6fd9591e3c2d455ce24c5a30d56922d09","tests/send_message.rs":"f07c152d97ad074caf6dd52ea26b5eeb57f60a850d109179bae376cd1dcd6dee","tests/webtransport.rs":"1634de76a3081877670d015cfd7b7f7225f8f1c7ddfba729bd5070a25ba10da0"},"package":null}

View File

@@ -11,9 +11,9 @@
[package] [package]
edition = "2021" edition = "2021"
rust-version = "1.76.0" rust-version = "1.82.0"
name = "neqo-http3" name = "neqo-http3"
version = "0.12.2" version = "0.13.1"
authors = ["The Neqo Authors <necko@mozilla.com>"] authors = ["The Neqo Authors <necko@mozilla.com>"]
build = false build = false
autolib = false autolib = false
@@ -76,6 +76,12 @@ path = "tests/send_message.rs"
name = "webtransport" name = "webtransport"
path = "tests/webtransport.rs" path = "tests/webtransport.rs"
[[bench]]
name = "streams"
path = "benches/streams.rs"
harness = false
required-features = ["bench"]
[dependencies.enumset] [dependencies.enumset]
version = "1.1" version = "1.1"
default-features = false default-features = false
@@ -97,18 +103,27 @@ path = "./../neqo-qpack"
path = "./../neqo-transport" path = "./../neqo-transport"
[dependencies.qlog] [dependencies.qlog]
version = "0.13" version = "0.15.1"
default-features = false default-features = false
[dependencies.sfv] [dependencies.sfv]
version = "0.9" version = "0.9"
default-features = false default-features = false
[dependencies.strum]
version = "0.26"
features = ["derive"]
default-features = false
[dependencies.url] [dependencies.url]
version = "2.5.3" version = "2.5"
features = ["std"] features = ["std"]
default-features = false default-features = false
[dev-dependencies.criterion]
version = "0.5"
default-features = false
[dev-dependencies.neqo-http3] [dev-dependencies.neqo-http3]
path = "." path = "."
features = ["draft-29"] features = ["draft-29"]
@@ -121,22 +136,56 @@ features = ["draft-29"]
path = "../test-fixture" path = "../test-fixture"
[lints.clippy] [lints.clippy]
allow_attributes = "warn"
allow_attributes_without_reason = "warn"
cfg_not_test = "warn" cfg_not_test = "warn"
clone_on_ref_ptr = "warn" clone_on_ref_ptr = "warn"
create_dir = "warn" create_dir = "warn"
dbg_macro = "warn"
empty_drop = "warn"
empty_enum_variants_with_brackets = "warn"
filetype_is_file = "warn"
float_cmp_const = "warn"
fn_to_numeric_cast_any = "warn"
get_unwrap = "warn" get_unwrap = "warn"
if_then_some_else_none = "warn" if_then_some_else_none = "warn"
infinite_loop = "warn"
large_include_file = "warn"
let_underscore_must_use = "warn"
let_underscore_untyped = "warn"
literal_string_with_formatting_args = "allow"
lossy_float_literal = "warn"
mem_forget = "warn"
mixed_read_write_in_expression = "warn"
module_name_repetitions = "warn"
multiple_crate_versions = "allow" multiple_crate_versions = "allow"
multiple_inherent_impl = "warn" multiple_inherent_impl = "warn"
mutex_atomic = "warn"
mutex_integer = "warn"
needless_raw_strings = "warn"
pathbuf_init_then_push = "warn" pathbuf_init_then_push = "warn"
pub_without_shorthand = "warn"
rc_buffer = "warn"
rc_mutex = "warn"
redundant_type_annotations = "warn" redundant_type_annotations = "warn"
ref_patterns = "warn" ref_patterns = "warn"
renamed_function_params = "warn" renamed_function_params = "warn"
rest_pat_in_fully_bound_structs = "warn"
self_named_module_files = "warn"
semicolon_inside_block = "warn" semicolon_inside_block = "warn"
string_lit_chars_any = "warn"
string_to_string = "warn"
suspicious_xor_used_as_pow = "warn"
try_err = "warn" try_err = "warn"
unnecessary_safety_comment = "warn"
unnecessary_safety_doc = "warn"
unnecessary_self_imports = "warn"
unneeded_field_pattern = "warn" unneeded_field_pattern = "warn"
unused_result_ok = "warn" unused_result_ok = "warn"
unused_trait_names = "warn" unused_trait_names = "warn"
unwrap_in_result = "warn"
unwrap_used = "warn"
verbose_file_reads = "warn"
[lints.clippy.cargo] [lints.clippy.cargo]
level = "warn" level = "warn"
@@ -153,7 +202,6 @@ priority = -1
[lints.rust] [lints.rust]
absolute_paths_not_starting_with_crate = "warn" absolute_paths_not_starting_with_crate = "warn"
ambiguous_negative_literals = "warn" ambiguous_negative_literals = "warn"
closure_returning_async_block = "warn"
explicit_outlives_requirements = "warn" explicit_outlives_requirements = "warn"
macro_use_extern_crate = "warn" macro_use_extern_crate = "warn"
missing_abi = "warn" missing_abi = "warn"
@@ -165,3 +213,4 @@ unit_bindings = "warn"
unused_import_braces = "warn" unused_import_braces = "warn"
unused_lifetimes = "warn" unused_lifetimes = "warn"
unused_macro_rules = "warn" unused_macro_rules = "warn"
unused_qualifications = "warn"

View File

@@ -0,0 +1,101 @@
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![expect(clippy::unwrap_used, reason = "OK in a bench.")]
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
use neqo_crypto::AuthenticationStatus;
use neqo_http3::{Http3Client, Http3Parameters, Http3Server, Priority};
use neqo_transport::{ConnectionParameters, StreamType};
use test_fixture::{
fixture_init, http3_client_with_params, http3_server_with_params, now, DEFAULT_SERVER_NAME,
};
const STREAM_TYPE: StreamType = StreamType::BiDi;
const STREAMS_MAX: u64 = 1 << 60;
fn exchange_packets(client: &mut Http3Client, server: &mut Http3Server, is_handshake: bool) {
let mut out = None;
let mut auth_needed = is_handshake;
loop {
out = client.process(out, now()).dgram();
let client_out_is_none = out.is_none();
if auth_needed && client.peer_certificate().is_some() {
client.authenticated(AuthenticationStatus::Ok, now());
auth_needed = false;
}
out = server.process(out, now()).dgram();
if client_out_is_none && out.is_none() {
break;
}
}
}
fn use_streams(client: &mut Http3Client, server: &mut Http3Server, streams: usize, data: &[u8]) {
let stream_ids = (0..streams)
.map(|_| {
client
.fetch(
now(),
"GET",
&("https", DEFAULT_SERVER_NAME, "/"),
&[],
Priority::default(),
)
.unwrap()
})
.collect::<Vec<_>>();
exchange_packets(client, server, false);
for stream_id in &stream_ids {
client.send_data(*stream_id, data).unwrap();
}
exchange_packets(client, server, false);
for stream_id in &stream_ids {
client.stream_close_send(*stream_id).unwrap();
}
exchange_packets(client, server, false);
}
fn connect() -> (Http3Client, Http3Server) {
let cp = ConnectionParameters::default()
.max_streams(STREAM_TYPE, STREAMS_MAX)
.pmtud(false)
.pacing(false)
.mlkem(false)
.sni_slicing(false);
let h3p = Http3Parameters::default().connection_parameters(cp);
let mut client = http3_client_with_params(h3p.clone());
let mut server = http3_server_with_params(h3p);
exchange_packets(&mut client, &mut server, true);
(client, server)
}
fn criterion_benchmark(c: &mut Criterion) {
fixture_init();
for (streams, data_size) in [
(1, 1),
(1000, 1),
(10000, 1),
(1, 1000),
(100, 1000),
(1000, 1000),
] {
let mut group = c.benchmark_group(format!("{streams} streams of {data_size} bytes"));
group.bench_function("multistream", |b| {
let data = vec![0; data_size];
b.iter_batched_ref(
connect,
|(client, server)| use_streams(client, server, streams, &data),
BatchSize::PerIteration,
);
});
group.finish();
}
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);

View File

@@ -9,16 +9,14 @@ use neqo_transport::{Connection, StreamId};
use crate::{qlog, Res}; use crate::{qlog, Res};
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq, Default)]
pub enum BufferedStream { pub enum BufferedStream {
#[default]
Uninitialized, Uninitialized,
Initialized { stream_id: StreamId, buf: Vec<u8> }, Initialized {
} stream_id: StreamId,
buf: Vec<u8>,
impl Default for BufferedStream { },
fn default() -> Self {
Self::Uninitialized
}
} }
impl ::std::fmt::Display for BufferedStream { impl ::std::fmt::Display for BufferedStream {

View File

@@ -4,8 +4,6 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![allow(clippy::module_name_repetitions)]
use std::{cell::RefCell, collections::VecDeque, rc::Rc}; use std::{cell::RefCell, collections::VecDeque, rc::Rc};
use neqo_common::{event::Provider as EventProvider, Header}; use neqo_common::{event::Provider as EventProvider, Header};
@@ -16,7 +14,8 @@ use crate::{
connection::Http3State, connection::Http3State,
features::extended_connect::{ExtendedConnectEvents, ExtendedConnectType, SessionCloseReason}, features::extended_connect::{ExtendedConnectEvents, ExtendedConnectType, SessionCloseReason},
settings::HSettingType, settings::HSettingType,
CloseType, Http3StreamInfo, HttpRecvStreamEvents, PushId, RecvStreamEvents, SendStreamEvents, CloseType, Error, Http3StreamInfo, HttpRecvStreamEvents, PushId, RecvStreamEvents, Res,
SendStreamEvents,
}; };
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Eq, Clone)]
@@ -220,13 +219,14 @@ impl ExtendedConnectEvents for Http3ClientEvents {
} }
} }
fn extended_connect_new_stream(&self, stream_info: Http3StreamInfo) { fn extended_connect_new_stream(&self, stream_info: Http3StreamInfo) -> Res<()> {
self.insert(Http3ClientEvent::WebTransport( self.insert(Http3ClientEvent::WebTransport(
WebTransportEvent::NewStream { WebTransportEvent::NewStream {
stream_id: stream_info.stream_id(), stream_id: stream_info.stream_id(),
session_id: stream_info.session_id().unwrap(), session_id: stream_info.session_id().ok_or(Error::Internal)?,
}, },
)); ));
Ok(())
} }
fn new_datagram(&self, session_id: StreamId, datagram: Vec<u8>) { fn new_datagram(&self, session_id: StreamId, datagram: Vec<u8>) {

View File

@@ -4,8 +4,6 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![allow(clippy::module_name_repetitions)]
use std::{ use std::{
cell::RefCell, cell::RefCell,
collections::{BTreeSet, HashMap}, collections::{BTreeSet, HashMap},
@@ -20,6 +18,7 @@ use neqo_transport::{
streams::SendOrder, AppError, CloseReason, Connection, DatagramTracking, State, StreamId, streams::SendOrder, AppError, CloseReason, Connection, DatagramTracking, State, StreamId,
StreamType, ZeroRttState, StreamType, ZeroRttState,
}; };
use strum::Display;
use crate::{ use crate::{
client_events::Http3ClientEvents, client_events::Http3ClientEvents,
@@ -55,20 +54,12 @@ where
pub priority: Priority, pub priority: Priority,
} }
#[derive(Display)]
pub enum WebTransportSessionAcceptAction { pub enum WebTransportSessionAcceptAction {
Accept, Accept,
Reject(Vec<Header>), Reject(Vec<Header>),
} }
impl ::std::fmt::Display for WebTransportSessionAcceptAction {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
match self {
Self::Accept => f.write_str("Accept"),
Self::Reject(_) => f.write_str("Reject"),
}
}
}
#[derive(Debug)] #[derive(Debug)]
enum Http3RemoteSettingsState { enum Http3RemoteSettingsState {
NotReceived, NotReceived,
@@ -127,13 +118,13 @@ The API consists of:
- `webtransport_session_accept` - only used by the server-side implementation - `webtransport_session_accept` - only used by the server-side implementation
- `webtransport_close_session` - `webtransport_close_session`
- `webtransport_create_stream_local` - this function is called when an application wants to open - `webtransport_create_stream_local` - this function is called when an application wants to open
a new `WebTransport` stream. For example `Http3Client::webtransport_create_stream` will call a new `WebTransport` stream. For example `Http3Client::webtransport_create_stream` will call
this function. this function.
- `webtransport_create_stream_remote` - this is called when a `WebTransport` stream has been - `webtransport_create_stream_remote` - this is called when a `WebTransport` stream has been
opened by the peer and this function sets up the appropriate handler for the stream. opened by the peer and this function sets up the appropriate handler for the stream.
- functions that are called by `process_http3` - functions that are called by `process_http3`
- `process_sending` - some send-streams are buffered streams(see the Streams section) and this - `process_sending` - some send-streams are buffered streams(see the Streams section) and this
function is called to trigger sending of the buffer data. function is called to trigger sending of the buffer data.
- functions that are called to handle `ConnectionEvent`s: - functions that are called to handle `ConnectionEvent`s:
- `add_new_stream` - `add_new_stream`
- `handle_stream_readable` - `handle_stream_readable`
@@ -173,15 +164,15 @@ There are the following types of streams:
- `Decoder`: there is only a receiver stream of this type and the handler is `DecoderRecvStream`. - `Decoder`: there is only a receiver stream of this type and the handler is `DecoderRecvStream`.
- `Encoder`: there is only a receiver stream of this type and the handler is `EncoderRecvStream`. - `Encoder`: there is only a receiver stream of this type and the handler is `EncoderRecvStream`.
- `NewStream`: there is only a receiver stream of this type and the handler is - `NewStream`: there is only a receiver stream of this type and the handler is
`NewStreamHeadReader`. `NewStreamHeadReader`.
- `Http`: `SendMessage` and `RecvMessage` handlers are responsible for this type of streams. - `Http`: `SendMessage` and `RecvMessage` handlers are responsible for this type of streams.
- `Push`: `RecvMessage` is responsible for this type of streams. - `Push`: `RecvMessage` is responsible for this type of streams.
- `ExtendedConnect`: `WebTransportSession` is responsible sender and receiver handler. - `ExtendedConnect`: `WebTransportSession` is responsible sender and receiver handler.
- `WebTransport(StreamId)`: `WebTransportSendStream` and `WebTransportRecvStream` are responsible - `WebTransport(StreamId)`: `WebTransportSendStream` and `WebTransportRecvStream` are responsible
sender and receiver handler. sender and receiver handler.
- `Unknown`: These are all other stream types that are not unknown to the current implementation - `Unknown`: These are all other stream types that are not unknown to the current implementation
and should be handled properly by the spec, e.g., in our implementation the streams are and should be handled properly by the spec, e.g., in our implementation the streams are
reset. reset.
The streams are registered in `send_streams` and `recv_streams` in following ways depending if they The streams are registered in `send_streams` and `recv_streams` in following ways depending if they
are local or remote: are local or remote:
@@ -517,7 +508,6 @@ impl Http3Connection {
output = self.handle_new_stream(conn, stream_type, stream_id)?; output = self.handle_new_stream(conn, stream_type, stream_id)?;
} }
#[allow(clippy::match_same_arms)] // clippy is being stupid here
match output { match output {
ReceiveOutput::UnblockedStreams(unblocked_streams) => { ReceiveOutput::UnblockedStreams(unblocked_streams) => {
self.handle_unblocked_streams(unblocked_streams, conn)?; self.handle_unblocked_streams(unblocked_streams, conn)?;
@@ -536,11 +526,11 @@ impl Http3Connection {
NewStreamType::Push(_) NewStreamType::Push(_)
| NewStreamType::Http(_) | NewStreamType::Http(_)
| NewStreamType::WebTransportStream(_), | NewStreamType::WebTransportStream(_),
) => Ok(output), )
| ReceiveOutput::NoOutput => Ok(output),
ReceiveOutput::NewStream(_) => { ReceiveOutput::NewStream(_) => {
unreachable!("NewStream should have been handled already") unreachable!("NewStream should have been handled already")
} }
ReceiveOutput::NoOutput => Ok(output),
} }
} }
@@ -727,13 +717,13 @@ impl Http3Connection {
match conn.stream_fairness(stream_id, true) { match conn.stream_fairness(stream_id, true) {
Ok(()) | Err(neqo_transport::Error::InvalidStreamId) => (), Ok(()) | Err(neqo_transport::Error::InvalidStreamId) => (),
Err(e) => return Err(Error::from(e)), Err(e) => return Err(Error::from(e)),
}; }
qinfo!("[{self}] A new WebTransport stream {stream_id} for session {session_id}"); qinfo!("[{self}] A new WebTransport stream {stream_id} for session {session_id}");
} }
NewStreamType::Unknown => { NewStreamType::Unknown => {
conn.stream_stop_sending(stream_id, Error::HttpStreamCreation.code())?; conn.stream_stop_sending(stream_id, Error::HttpStreamCreation.code())?;
} }
}; }
match stream_type { match stream_type {
NewStreamType::Control | NewStreamType::Decoder | NewStreamType::Encoder => { NewStreamType::Control | NewStreamType::Decoder | NewStreamType::Encoder => {
@@ -891,7 +881,7 @@ impl Http3Connection {
send_message send_message
.http_stream() .http_stream()
.unwrap() .ok_or(Error::Internal)?
.send_headers(&final_headers, conn)?; .send_headers(&final_headers, conn)?;
self.add_streams( self.add_streams(
@@ -1182,9 +1172,13 @@ impl Http3Connection {
stream_id, stream_id,
events, events,
self.role, self.role,
self.recv_streams.remove(&stream_id).unwrap(), self.recv_streams
self.send_streams.remove(&stream_id).unwrap(), .remove(&stream_id)
))); .ok_or(Error::Internal)?,
self.send_streams
.remove(&stream_id)
.ok_or(Error::Internal)?,
)?));
self.add_streams( self.add_streams(
stream_id, stream_id,
Box::new(Rc::clone(&extended_conn)), Box::new(Rc::clone(&extended_conn)),
@@ -1258,7 +1252,7 @@ impl Http3Connection {
send_events, send_events,
recv_events, recv_events,
true, true,
); )?;
Ok(stream_id) Ok(stream_id)
} }
@@ -1285,7 +1279,7 @@ impl Http3Connection {
send_events, send_events,
recv_events, recv_events,
false, false,
); )?;
Ok(()) Ok(())
} }
@@ -1297,8 +1291,8 @@ impl Http3Connection {
send_events: Box<dyn SendStreamEvents>, send_events: Box<dyn SendStreamEvents>,
recv_events: Box<dyn RecvStreamEvents>, recv_events: Box<dyn RecvStreamEvents>,
local: bool, local: bool,
) { ) -> Res<()> {
webtransport_session.borrow_mut().add_stream(stream_id); webtransport_session.borrow_mut().add_stream(stream_id)?;
if stream_id.stream_type() == StreamType::UniDi { if stream_id.stream_type() == StreamType::UniDi {
if local { if local {
self.send_streams.insert( self.send_streams.insert(
@@ -1340,6 +1334,7 @@ impl Http3Connection {
)), )),
); );
} }
Ok(())
} }
pub fn webtransport_send_datagram( pub fn webtransport_send_datagram(
@@ -1481,7 +1476,7 @@ impl Http3Connection {
.http_stream() .http_stream()
.ok_or(Error::InvalidStreamId)?; .ok_or(Error::InvalidStreamId)?;
if stream.maybe_update_priority(priority) { if stream.maybe_update_priority(priority)? {
self.control_stream_local.queue_update_priority(stream_id); self.control_stream_local.queue_update_priority(stream_id);
Ok(true) Ok(true)
} else { } else {
@@ -1559,7 +1554,7 @@ impl Http3Connection {
let stream = self.recv_streams.remove(&stream_id); let stream = self.recv_streams.remove(&stream_id);
if let Some(s) = &stream { if let Some(s) = &stream {
if s.stream_type() == Http3StreamType::ExtendedConnect { if s.stream_type() == Http3StreamType::ExtendedConnect {
self.send_streams.remove(&stream_id).unwrap(); self.send_streams.remove(&stream_id)?;
if let Some(wt) = s.webtransport() { if let Some(wt) = s.webtransport() {
self.remove_extended_connect(&wt, conn); self.remove_extended_connect(&wt, conn);
} }
@@ -1576,7 +1571,7 @@ impl Http3Connection {
let stream = self.send_streams.remove(&stream_id); let stream = self.send_streams.remove(&stream_id);
if let Some(s) = &stream { if let Some(s) = &stream {
if s.stream_type() == Http3StreamType::ExtendedConnect { if s.stream_type() == Http3StreamType::ExtendedConnect {
if let Some(wt) = self.recv_streams.remove(&stream_id).unwrap().webtransport() { if let Some(wt) = self.recv_streams.remove(&stream_id)?.webtransport() {
self.remove_extended_connect(&wt, conn); self.remove_extended_connect(&wt, conn);
} }
} }

View File

@@ -785,7 +785,8 @@ impl Http3Client {
/// This cannot panic. The max varint length is 8. /// This cannot panic. The max varint length is 8.
pub fn webtransport_max_datagram_size(&self, session_id: StreamId) -> Res<u64> { pub fn webtransport_max_datagram_size(&self, session_id: StreamId) -> Res<u64> {
Ok(self.conn.max_datagram_size()? Ok(self.conn.max_datagram_size()?
- u64::try_from(Encoder::varint_len(session_id.as_u64())).unwrap()) - u64::try_from(Encoder::varint_len(session_id.as_u64()))
.map_err(|_| Error::Internal)?)
} }
/// Sets the `SendOrder` for a given stream /// Sets the `SendOrder` for a given stream
@@ -841,7 +842,11 @@ impl Http3Client {
} }
/// This function combines `process_input` and `process_output` function. /// This function combines `process_input` and `process_output` function.
pub fn process(&mut self, dgram: Option<Datagram<impl AsRef<[u8]>>>, now: Instant) -> Output { pub fn process(
&mut self,
dgram: Option<Datagram<impl AsRef<[u8]> + AsMut<[u8]>>>,
now: Instant,
) -> Output {
qtrace!("[{self}] Process"); qtrace!("[{self}] Process");
if let Some(d) = dgram { if let Some(d) = dgram {
self.process_input(d, now); self.process_input(d, now);
@@ -859,13 +864,13 @@ impl Http3Client {
/// packets need to be sent or if a timer needs to be updated. /// packets need to be sent or if a timer needs to be updated.
/// ///
/// [1]: ../neqo_transport/enum.ConnectionEvent.html /// [1]: ../neqo_transport/enum.ConnectionEvent.html
pub fn process_input(&mut self, dgram: Datagram<impl AsRef<[u8]>>, now: Instant) { pub fn process_input(&mut self, dgram: Datagram<impl AsRef<[u8]> + AsMut<[u8]>>, now: Instant) {
self.process_multiple_input(iter::once(dgram), now); self.process_multiple_input(iter::once(dgram), now);
} }
pub fn process_multiple_input( pub fn process_multiple_input(
&mut self, &mut self,
dgrams: impl IntoIterator<Item = Datagram<impl AsRef<[u8]>>>, dgrams: impl IntoIterator<Item = Datagram<impl AsRef<[u8]> + AsMut<[u8]>>>,
now: Instant, now: Instant,
) { ) {
let mut dgrams = dgrams.into_iter().peekable(); let mut dgrams = dgrams.into_iter().peekable();
@@ -1279,7 +1284,7 @@ mod tests {
use neqo_qpack::{encoder::QPackEncoder, QpackSettings}; use neqo_qpack::{encoder::QPackEncoder, QpackSettings};
use neqo_transport::{ use neqo_transport::{
CloseReason, ConnectionEvent, ConnectionParameters, Output, State, StreamId, StreamType, CloseReason, ConnectionEvent, ConnectionParameters, Output, State, StreamId, StreamType,
Version, MIN_INITIAL_PACKET_SIZE, RECV_BUFFER_SIZE, SEND_BUFFER_SIZE, Version, INITIAL_RECV_WINDOW_SIZE, MIN_INITIAL_PACKET_SIZE,
}; };
use test_fixture::{ use test_fixture::{
anti_replay, default_server_h3, fixture_init, new_server, now, anti_replay, default_server_h3, fixture_init, new_server, now,
@@ -1304,7 +1309,7 @@ mod tests {
assert_eq!(err, CloseReason::Application(expected.code())); assert_eq!(err, CloseReason::Application(expected.code()));
} }
_ => panic!("Wrong state {:?}", client.state()), _ => panic!("Wrong state {:?}", client.state()),
}; }
} }
/// Create a http3 client with default configuration. /// Create a http3 client with default configuration.
@@ -1625,12 +1630,16 @@ mod tests {
fn handshake_only(client: &mut Http3Client, server: &mut TestServer) -> Output { fn handshake_only(client: &mut Http3Client, server: &mut TestServer) -> Output {
assert_eq!(client.state(), Http3State::Initializing); assert_eq!(client.state(), Http3State::Initializing);
let out = client.process_output(now()); let out = client.process_output(now());
let out2 = client.process_output(now());
assert_eq!(client.state(), Http3State::Initializing); assert_eq!(client.state(), Http3State::Initializing);
assert_eq!(*server.conn.state(), State::Init); assert_eq!(*server.conn.state(), State::Init);
let out = server.conn.process(out.dgram(), now()); server.conn.process_input(out.dgram().unwrap(), now());
let out = server.conn.process(out2.dgram(), now());
assert_eq!(*server.conn.state(), State::Handshaking); 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.dgram(), now()); let out = client.process(out.dgram(), now());
let out = server.conn.process(out.dgram(), now()); let out = server.conn.process(out.dgram(), now());
assert!(out.as_dgram_ref().is_none()); assert!(out.as_dgram_ref().is_none());
@@ -2730,7 +2739,7 @@ mod tests {
if let ConnectionEvent::RecvStreamReadable { stream_id } = e { if let ConnectionEvent::RecvStreamReadable { stream_id } = e {
if stream_id == request_stream_id { if stream_id == request_stream_id {
// Read the DATA frame. // Read the DATA frame.
let mut buf = vec![1_u8; RECV_BUFFER_SIZE]; let mut buf = vec![1_u8; INITIAL_RECV_WINDOW_SIZE];
let (amount, fin) = server.conn.stream_recv(stream_id, &mut buf).unwrap(); let (amount, fin) = server.conn.stream_recv(stream_id, &mut buf).unwrap();
assert!(fin); assert!(fin);
assert_eq!( assert_eq!(
@@ -2803,7 +2812,7 @@ mod tests {
assert_eq!(sent, Ok(first_frame.len())); assert_eq!(sent, Ok(first_frame.len()));
// The second frame cannot fit. // The second frame cannot fit.
let sent = client.send_data(request_stream_id, &vec![0_u8; SEND_BUFFER_SIZE]); let sent = client.send_data(request_stream_id, &vec![0_u8; INITIAL_RECV_WINDOW_SIZE]);
assert_eq!(sent, Ok(expected_second_data_frame.len())); assert_eq!(sent, Ok(expected_second_data_frame.len()));
// Close stream. // Close stream.
@@ -2812,7 +2821,7 @@ mod tests {
let mut out = client.process_output(now()); let mut out = client.process_output(now());
// We need to loop a bit until all data has been sent. Once for every 1K // We need to loop a bit until all data has been sent. Once for every 1K
// of data. // of data.
for _i in 0..SEND_BUFFER_SIZE / 1000 { for _i in 0..INITIAL_RECV_WINDOW_SIZE / 1000 {
out = server.conn.process(out.dgram(), now()); out = server.conn.process(out.dgram(), now());
out = client.process(out.dgram(), now()); out = client.process(out.dgram(), now());
} }
@@ -2822,7 +2831,7 @@ mod tests {
if let ConnectionEvent::RecvStreamReadable { stream_id } = e { if let ConnectionEvent::RecvStreamReadable { stream_id } = e {
if stream_id == request_stream_id { if stream_id == request_stream_id {
// Read DATA frames. // Read DATA frames.
let mut buf = vec![1_u8; RECV_BUFFER_SIZE]; let mut buf = vec![1_u8; INITIAL_RECV_WINDOW_SIZE];
let (amount, fin) = server.conn.stream_recv(stream_id, &mut buf).unwrap(); let (amount, fin) = server.conn.stream_recv(stream_id, &mut buf).unwrap();
assert!(fin); assert!(fin);
assert_eq!( assert_eq!(
@@ -2875,7 +2884,7 @@ mod tests {
// After the first frame there is exactly 63+2 bytes left in the send buffer. // After the first frame there is exactly 63+2 bytes left in the send buffer.
#[test] #[test]
fn fetch_two_data_frame_second_63bytes() { fn fetch_two_data_frame_second_63bytes() {
let (buf, hdr) = alloc_buffer(SEND_BUFFER_SIZE - 88); let (buf, hdr) = alloc_buffer(INITIAL_RECV_WINDOW_SIZE - 88);
fetch_with_two_data_frames(&buf, &hdr, &[0x0, 0x3f], &[0_u8; 63]); fetch_with_two_data_frames(&buf, &hdr, &[0x0, 0x3f], &[0_u8; 63]);
} }
@@ -2884,7 +2893,7 @@ mod tests {
// but we can only send 63 bytes. // but we can only send 63 bytes.
#[test] #[test]
fn fetch_two_data_frame_second_63bytes_place_for_66() { fn fetch_two_data_frame_second_63bytes_place_for_66() {
let (buf, hdr) = alloc_buffer(SEND_BUFFER_SIZE - 89); let (buf, hdr) = alloc_buffer(INITIAL_RECV_WINDOW_SIZE - 89);
fetch_with_two_data_frames(&buf, &hdr, &[0x0, 0x3f], &[0_u8; 63]); fetch_with_two_data_frames(&buf, &hdr, &[0x0, 0x3f], &[0_u8; 63]);
} }
@@ -2893,7 +2902,7 @@ mod tests {
// but we can only send 64 bytes. // but we can only send 64 bytes.
#[test] #[test]
fn fetch_two_data_frame_second_64bytes_place_for_67() { fn fetch_two_data_frame_second_64bytes_place_for_67() {
let (buf, hdr) = alloc_buffer(SEND_BUFFER_SIZE - 90); let (buf, hdr) = alloc_buffer(INITIAL_RECV_WINDOW_SIZE - 90);
fetch_with_two_data_frames(&buf, &hdr, &[0x0, 0x40, 0x40], &[0_u8; 64]); fetch_with_two_data_frames(&buf, &hdr, &[0x0, 0x40, 0x40], &[0_u8; 64]);
} }
@@ -2901,7 +2910,7 @@ mod tests {
// After the first frame there is exactly 16383+3 bytes left in the send buffer. // After the first frame there is exactly 16383+3 bytes left in the send buffer.
#[test] #[test]
fn fetch_two_data_frame_second_16383bytes() { fn fetch_two_data_frame_second_16383bytes() {
let (buf, hdr) = alloc_buffer(SEND_BUFFER_SIZE - 16409); let (buf, hdr) = alloc_buffer(INITIAL_RECV_WINDOW_SIZE - 16409);
fetch_with_two_data_frames(&buf, &hdr, &[0x0, 0x7f, 0xff], &[0_u8; 16383]); fetch_with_two_data_frames(&buf, &hdr, &[0x0, 0x7f, 0xff], &[0_u8; 16383]);
} }
@@ -2910,7 +2919,7 @@ mod tests {
// send 16383 bytes. // send 16383 bytes.
#[test] #[test]
fn fetch_two_data_frame_second_16383bytes_place_for_16387() { fn fetch_two_data_frame_second_16383bytes_place_for_16387() {
let (buf, hdr) = alloc_buffer(SEND_BUFFER_SIZE - 16410); let (buf, hdr) = alloc_buffer(INITIAL_RECV_WINDOW_SIZE - 16410);
fetch_with_two_data_frames(&buf, &hdr, &[0x0, 0x7f, 0xff], &[0_u8; 16383]); fetch_with_two_data_frames(&buf, &hdr, &[0x0, 0x7f, 0xff], &[0_u8; 16383]);
} }
@@ -2919,7 +2928,7 @@ mod tests {
// send 16383 bytes. // send 16383 bytes.
#[test] #[test]
fn fetch_two_data_frame_second_16383bytes_place_for_16388() { fn fetch_two_data_frame_second_16383bytes_place_for_16388() {
let (buf, hdr) = alloc_buffer(SEND_BUFFER_SIZE - 16411); let (buf, hdr) = alloc_buffer(INITIAL_RECV_WINDOW_SIZE - 16411);
fetch_with_two_data_frames(&buf, &hdr, &[0x0, 0x7f, 0xff], &[0_u8; 16383]); fetch_with_two_data_frames(&buf, &hdr, &[0x0, 0x7f, 0xff], &[0_u8; 16383]);
} }
@@ -2928,7 +2937,7 @@ mod tests {
// 16384 bytes. // 16384 bytes.
#[test] #[test]
fn fetch_two_data_frame_second_16384bytes_place_for_16389() { fn fetch_two_data_frame_second_16384bytes_place_for_16389() {
let (buf, hdr) = alloc_buffer(SEND_BUFFER_SIZE - 16412); let (buf, hdr) = alloc_buffer(INITIAL_RECV_WINDOW_SIZE - 16412);
fetch_with_two_data_frames(&buf, &hdr, &[0x0, 0x80, 0x0, 0x40, 0x0], &[0_u8; 16384]); fetch_with_two_data_frames(&buf, &hdr, &[0x0, 0x80, 0x0, 0x40, 0x0], &[0_u8; 16384]);
} }
@@ -3675,7 +3684,7 @@ mod tests {
panic!("We should not receive a DataGeadable event!"); panic!("We should not receive a DataGeadable event!");
} }
_ => {} _ => {}
}; }
} }
// ok NOW send fin // ok NOW send fin
@@ -3699,7 +3708,7 @@ mod tests {
assert!(fin); assert!(fin);
} }
_ => {} _ => {}
}; }
} }
// Stream should now be closed and gone // Stream should now be closed and gone
@@ -3751,7 +3760,7 @@ mod tests {
assert_eq!(Ok((0, true)), client.read_data(now(), stream_id, &mut buf)); assert_eq!(Ok((0, true)), client.read_data(now(), stream_id, &mut buf));
} }
_ => {} _ => {}
}; }
} }
// Stream should now be closed and gone // Stream should now be closed and gone
@@ -3800,7 +3809,7 @@ mod tests {
panic!("We should not receive a DataGeadable event!"); panic!("We should not receive a DataGeadable event!");
} }
_ => {} _ => {}
}; }
} }
// ok NOW send fin // ok NOW send fin
@@ -3824,7 +3833,7 @@ mod tests {
assert!(fin); assert!(fin);
} }
_ => {} _ => {}
}; }
} }
// Stream should now be closed and gone // Stream should now be closed and gone
@@ -3871,7 +3880,7 @@ mod tests {
assert!(!fin); assert!(!fin);
} }
_ => {} _ => {}
}; }
} }
// ok NOW send fin // ok NOW send fin
@@ -4158,10 +4167,12 @@ mod tests {
let (mut client, mut server) = start_with_0rtt(); let (mut client, mut server) = start_with_0rtt();
let out = client.process_output(now()); let out = client.process_output(now());
let out2 = client.process_output(now());
assert_eq!(client.state(), Http3State::ZeroRtt); assert_eq!(client.state(), Http3State::ZeroRtt);
assert_eq!(*server.conn.state(), State::Init); assert_eq!(*server.conn.state(), State::Init);
let out = server.conn.process(out.dgram(), now()); server.conn.process_input(out.dgram().unwrap(), now());
let out = server.conn.process(out2.dgram(), now());
// Check that control and qpack streams are received and a // Check that control and qpack streams are received and a
// SETTINGS frame has been received. // SETTINGS frame has been received.
@@ -4175,6 +4186,8 @@ mod tests {
assert_eq!(*server.conn.state(), State::Handshaking); assert_eq!(*server.conn.state(), State::Handshaking);
let out = client.process(out.dgram(), now()); let out = client.process(out.dgram(), now());
let out = server.conn.process(out.dgram(), now());
let out = client.process(out.dgram(), now());
assert_eq!(client.state(), Http3State::Connected); assert_eq!(client.state(), Http3State::Connected);
drop(server.conn.process(out.dgram(), now())); drop(server.conn.process(out.dgram(), now()));
@@ -4193,10 +4206,12 @@ mod tests {
assert_eq!(request_stream_id, 0); assert_eq!(request_stream_id, 0);
let out = client.process_output(now()); let out = client.process_output(now());
let out2 = client.process_output(now());
assert_eq!(client.state(), Http3State::ZeroRtt); assert_eq!(client.state(), Http3State::ZeroRtt);
assert_eq!(*server.conn.state(), State::Init); assert_eq!(*server.conn.state(), State::Init);
let out = server.conn.process(out.dgram(), now()); server.conn.process_input(out.dgram().unwrap(), now());
let out = server.conn.process(out2.dgram(), now());
// Check that control and qpack streams are received and a // Check that control and qpack streams are received and a
// SETTINGS frame has been received. // SETTINGS frame has been received.
@@ -4210,6 +4225,8 @@ mod tests {
assert_eq!(*server.conn.state(), State::Handshaking); assert_eq!(*server.conn.state(), State::Handshaking);
let out = client.process(out.dgram(), now()); let out = client.process(out.dgram(), now());
let out = server.conn.process(out.dgram(), now());
let out = client.process(out.dgram(), now());
assert_eq!(client.state(), Http3State::Connected); assert_eq!(client.state(), Http3State::Connected);
let out = server.conn.process(out.dgram(), now()); let out = server.conn.process(out.dgram(), now());
assert!(server.conn.state().connected()); assert!(server.conn.state().connected());
@@ -4280,17 +4297,19 @@ mod tests {
let client_0rtt = client.process_output(now()); let client_0rtt = client.process_output(now());
assert!(client_0rtt.as_dgram_ref().is_some()); assert!(client_0rtt.as_dgram_ref().is_some());
let server_hs = server.process(client_hs.dgram(), now()); server.process_input(client_hs.dgram().unwrap(), now());
let server_hs = server.process(client_0rtt.dgram(), now());
assert!(server_hs.as_dgram_ref().is_some()); // Should produce ServerHello etc... assert!(server_hs.as_dgram_ref().is_some()); // Should produce ServerHello etc...
let server_ignored = server.process(client_0rtt.dgram(), now());
assert!(server_ignored.as_dgram_ref().is_none()); let dgram = client.process(server_hs.dgram(), now()).dgram();
let dgram = server.process(dgram, now());
// The server shouldn't receive that 0-RTT data. // The server shouldn't receive that 0-RTT data.
let recvd_stream_evt = |e| matches!(e, ConnectionEvent::NewStream { .. }); let recvd_stream_evt = |e| matches!(e, ConnectionEvent::NewStream { .. });
assert!(!server.events().any(recvd_stream_evt)); assert!(!server.events().any(recvd_stream_evt));
// Client should get a rejection. // Client should get a rejection.
let client_out = client.process(server_hs.dgram(), now()); let client_out = client.process(dgram.dgram(), now());
assert!(client_out.as_dgram_ref().is_some()); assert!(client_out.as_dgram_ref().is_some());
let recvd_0rtt_reject = |e| e == Http3ClientEvent::ZeroRttRejected; let recvd_0rtt_reject = |e| e == Http3ClientEvent::ZeroRttRejected;
assert!(client.events().any(recvd_0rtt_reject)); assert!(client.events().any(recvd_0rtt_reject));
@@ -4329,10 +4348,12 @@ mod tests {
.expect("Set resumption token"); .expect("Set resumption token");
assert_eq!(client.state(), Http3State::ZeroRtt); assert_eq!(client.state(), Http3State::ZeroRtt);
let out = client.process_output(now()); let out = client.process_output(now());
let out2 = client.process_output(now());
assert_eq!(client.state(), Http3State::ZeroRtt); assert_eq!(client.state(), Http3State::ZeroRtt);
assert_eq!(*server.conn.state(), State::Init); assert_eq!(*server.conn.state(), State::Init);
let out = server.conn.process(out.dgram(), now()); server.conn.process_input(out.dgram().unwrap(), now());
let out = server.conn.process(out2.dgram(), now());
// Check that control and qpack streams and a SETTINGS frame are received. // Check that control and qpack streams and a SETTINGS frame are received.
// Also qpack encoder stream will send "change capacity" instruction because it has // Also qpack encoder stream will send "change capacity" instruction because it has
@@ -4345,6 +4366,8 @@ mod tests {
assert_eq!(*server.conn.state(), State::Handshaking); assert_eq!(*server.conn.state(), State::Handshaking);
let out = client.process(out.dgram(), now()); let out = client.process(out.dgram(), now());
let out = server.conn.process(out.dgram(), now());
let out = client.process(out.dgram(), now());
assert_eq!(client.state(), Http3State::Connected); assert_eq!(client.state(), Http3State::Connected);
drop(server.conn.process(out.dgram(), now())); drop(server.conn.process(out.dgram(), now()));

View File

@@ -9,7 +9,7 @@ use std::collections::{HashMap, VecDeque};
use neqo_common::{qtrace, Encoder}; use neqo_common::{qtrace, Encoder};
use neqo_transport::{Connection, StreamId, StreamType}; use neqo_transport::{Connection, StreamId, StreamType};
use crate::{frames::HFrame, BufferedStream, Http3StreamType, RecvStream, Res}; use crate::{frames::HFrame, BufferedStream, Error, Http3StreamType, RecvStream, Res};
pub const HTTP3_UNI_STREAM_TYPE_CONTROL: u64 = 0x0; pub const HTTP3_UNI_STREAM_TYPE_CONTROL: u64 = 0x0;
@@ -73,14 +73,14 @@ impl ControlStreamLocal {
update_stream.stream_type(), update_stream.stream_type(),
Http3StreamType::Http | Http3StreamType::Push Http3StreamType::Http | Http3StreamType::Push
)); ));
let stream = update_stream.http_stream().unwrap(); let stream = update_stream.http_stream().ok_or(Error::Internal)?;
// in case multiple priority_updates were issued, ignore now irrelevant // in case multiple priority_updates were issued, ignore now irrelevant
if let Some(hframe) = stream.priority_update_frame() { if let Some(hframe) = stream.priority_update_frame() {
let mut enc = Encoder::new(); let mut enc = Encoder::new();
hframe.encode(&mut enc); hframe.encode(&mut enc);
if self.stream.send_atomic(conn, enc.as_ref())? { if self.stream.send_atomic(conn, enc.as_ref())? {
stream.priority_update_sent(); stream.priority_update_sent()?;
} else { } else {
self.outstanding_priority_update.push_front(update_id); self.outstanding_priority_update.push_front(update_id);
break; break;
@@ -95,7 +95,7 @@ impl ControlStreamLocal {
qtrace!("[{self}] Create a control stream"); qtrace!("[{self}] Create a control stream");
self.stream.init(conn.stream_create(StreamType::UniDi)?); self.stream.init(conn.stream_create(StreamType::UniDi)?);
self.stream self.stream
.buffer(&[u8::try_from(HTTP3_UNI_STREAM_TYPE_CONTROL).unwrap()]); .buffer(&[u8::try_from(HTTP3_UNI_STREAM_TYPE_CONTROL).map_err(|_| Error::Internal)?]);
Ok(()) Ok(())
} }

View File

@@ -17,7 +17,7 @@ use crate::{
client_events::Http3ClientEvents, client_events::Http3ClientEvents,
features::NegotiationState, features::NegotiationState,
settings::{HSettingType, HSettings}, settings::{HSettingType, HSettings},
CloseType, Http3StreamInfo, Http3StreamType, CloseType, Http3StreamInfo, Http3StreamType, Res,
}; };
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Eq, Clone)]
@@ -56,7 +56,7 @@ pub(crate) trait ExtendedConnectEvents: Debug {
reason: SessionCloseReason, reason: SessionCloseReason,
headers: Option<Vec<Header>>, headers: Option<Vec<Header>>,
); );
fn extended_connect_new_stream(&self, stream_info: Http3StreamInfo); fn extended_connect_new_stream(&self, stream_info: Http3StreamInfo) -> Res<()>;
fn new_datagram(&self, session_id: StreamId, datagram: Vec<u8>); fn new_datagram(&self, session_id: StreamId, datagram: Vec<u8>);
} }
@@ -67,12 +67,18 @@ pub(crate) enum ExtendedConnectType {
impl ExtendedConnectType { impl ExtendedConnectType {
#[must_use] #[must_use]
#[allow(clippy::unused_self)] // This will change when we have more features using ExtendedConnectType. #[expect(
clippy::unused_self,
reason = "This will change when we have more features using ExtendedConnectType."
)]
pub const fn string(self) -> &'static str { pub const fn string(self) -> &'static str {
"webtransport" "webtransport"
} }
#[allow(clippy::unused_self)] // This will change when we have more features using ExtendedConnectType. #[expect(
clippy::unused_self,
reason = "This will change when we have more features using ExtendedConnectType."
)]
#[must_use] #[must_use]
pub const fn get_stream_type(self, session_id: StreamId) -> Http3StreamType { pub const fn get_stream_type(self, session_id: StreamId) -> Http3StreamType {
Http3StreamType::WebTransport(session_id) Http3StreamType::WebTransport(session_id)

View File

@@ -82,8 +82,12 @@ fn exchange_packets(client: &mut Http3Client, server: &mut Http3Server) {
fn connect_with(client: &mut Http3Client, server: &mut Http3Server) { fn connect_with(client: &mut Http3Client, server: &mut Http3Server) {
assert_eq!(client.state(), Http3State::Initializing); assert_eq!(client.state(), Http3State::Initializing);
let out = client.process_output(now()); let out = client.process_output(now());
let out2 = client.process_output(now());
assert_eq!(client.state(), Http3State::Initializing); assert_eq!(client.state(), Http3State::Initializing);
_ = server.process(out.dgram(), now());
let out = server.process(out2.dgram(), now());
let out = client.process(out.dgram(), now());
let out = server.process(out.dgram(), now()); let out = server.process(out.dgram(), now());
let out = client.process(out.dgram(), now()); let out = client.process(out.dgram(), now());
let out = server.process(out.dgram(), now()); let out = server.process(out.dgram(), now());

View File

@@ -270,5 +270,5 @@ fn wrong_setting_value() {
assert_eq!(err, CloseReason::Application(Error::HttpSettings.code())); assert_eq!(err, CloseReason::Application(Error::HttpSettings.code()));
} }
_ => panic!("Wrong state {:?}", client.state()), _ => panic!("Wrong state {:?}", client.state()),
}; }
} }

View File

@@ -99,24 +99,23 @@ impl WebTransportSession {
/// ///
/// This function is only called with `RecvStream` and `SendStream` that also implement /// This function is only called with `RecvStream` and `SendStream` that also implement
/// the http specific functions and `http_stream()` will never return `None`. /// the http specific functions and `http_stream()` will never return `None`.
#[must_use]
pub fn new_with_http_streams( pub fn new_with_http_streams(
session_id: StreamId, session_id: StreamId,
events: Box<dyn ExtendedConnectEvents>, events: Box<dyn ExtendedConnectEvents>,
role: Role, role: Role,
mut control_stream_recv: Box<dyn RecvStream>, mut control_stream_recv: Box<dyn RecvStream>,
mut control_stream_send: Box<dyn SendStream>, mut control_stream_send: Box<dyn SendStream>,
) -> Self { ) -> Res<Self> {
let stream_event_listener = Rc::new(RefCell::new(WebTransportSessionListener::default())); let stream_event_listener = Rc::new(RefCell::new(WebTransportSessionListener::default()));
control_stream_recv control_stream_recv
.http_stream() .http_stream()
.unwrap() .ok_or(Error::Internal)?
.set_new_listener(Box::new(Rc::clone(&stream_event_listener))); .set_new_listener(Box::new(Rc::clone(&stream_event_listener)));
control_stream_send control_stream_send
.http_stream() .http_stream()
.unwrap() .ok_or(Error::Internal)?
.set_new_listener(Box::new(Rc::clone(&stream_event_listener))); .set_new_listener(Box::new(Rc::clone(&stream_event_listener)));
Self { Ok(Self {
control_stream_recv, control_stream_recv,
control_stream_send, control_stream_send,
stream_event_listener, stream_event_listener,
@@ -127,7 +126,7 @@ impl WebTransportSession {
send_streams: BTreeSet::new(), send_streams: BTreeSet::new(),
recv_streams: BTreeSet::new(), recv_streams: BTreeSet::new(),
role, role,
} })
} }
/// # Errors /// # Errors
@@ -141,7 +140,7 @@ impl WebTransportSession {
pub fn send_request(&mut self, headers: &[Header], conn: &mut Connection) -> Res<()> { pub fn send_request(&mut self, headers: &[Header], conn: &mut Connection) -> Res<()> {
self.control_stream_send self.control_stream_send
.http_stream() .http_stream()
.unwrap() .ok_or(Error::Internal)?
.send_headers(headers, conn) .send_headers(headers, conn)
} }
@@ -149,7 +148,7 @@ impl WebTransportSession {
qtrace!("[{self}] receive control data"); qtrace!("[{self}] receive control data");
let (out, _) = self.control_stream_recv.receive(conn)?; let (out, _) = self.control_stream_recv.receive(conn)?;
debug_assert!(out == ReceiveOutput::NoOutput); debug_assert!(out == ReceiveOutput::NoOutput);
self.maybe_check_headers(); self.maybe_check_headers()?;
self.read_control_stream(conn)?; self.read_control_stream(conn)?;
Ok((ReceiveOutput::NoOutput, self.state == SessionState::Done)) Ok((ReceiveOutput::NoOutput, self.state == SessionState::Done))
} }
@@ -158,33 +157,32 @@ impl WebTransportSession {
let (out, _) = self let (out, _) = self
.control_stream_recv .control_stream_recv
.http_stream() .http_stream()
.unwrap() .ok_or(Error::Internal)?
.header_unblocked(conn)?; .header_unblocked(conn)?;
debug_assert!(out == ReceiveOutput::NoOutput); debug_assert!(out == ReceiveOutput::NoOutput);
self.maybe_check_headers(); self.maybe_check_headers()?;
self.read_control_stream(conn)?; self.read_control_stream(conn)?;
Ok((ReceiveOutput::NoOutput, self.state == SessionState::Done)) Ok((ReceiveOutput::NoOutput, self.state == SessionState::Done))
} }
fn maybe_update_priority(&mut self, priority: Priority) -> bool { fn maybe_update_priority(&mut self, priority: Priority) -> Res<bool> {
self.control_stream_recv self.control_stream_recv
.http_stream() .http_stream()
.unwrap() .ok_or(Error::Internal)?
.maybe_update_priority(priority) .maybe_update_priority(priority)
} }
fn priority_update_frame(&mut self) -> Option<HFrame> { fn priority_update_frame(&mut self) -> Option<HFrame> {
self.control_stream_recv self.control_stream_recv
.http_stream() .http_stream()?
.unwrap()
.priority_update_frame() .priority_update_frame()
} }
fn priority_update_sent(&mut self) { fn priority_update_sent(&mut self) -> Res<()> {
self.control_stream_recv self.control_stream_recv
.http_stream() .http_stream()
.unwrap() .ok_or(Error::Internal)?
.priority_update_sent(); .priority_update_sent()
} }
fn send(&mut self, conn: &mut Connection) -> Res<()> { fn send(&mut self, conn: &mut Connection) -> Res<()> {
@@ -222,9 +220,9 @@ impl WebTransportSession {
/// # Panics /// # Panics
/// ///
/// This cannot panic because headers are checked before this function called. /// This cannot panic because headers are checked before this function called.
pub fn maybe_check_headers(&mut self) { pub fn maybe_check_headers(&mut self) -> Res<()> {
if SessionState::Negotiating != self.state { if SessionState::Negotiating != self.state {
return; return Ok(());
} }
if let Some((headers, interim, fin)) = self.stream_event_listener.borrow_mut().get_headers() if let Some((headers, interim, fin)) = self.stream_event_listener.borrow_mut().get_headers()
@@ -254,7 +252,7 @@ impl WebTransportSession {
None None
} }
}) })
.unwrap(); .ok_or(Error::Internal)?;
self.state = if (200..300).contains(&status) { self.state = if (200..300).contains(&status) {
if fin { if fin {
@@ -288,9 +286,10 @@ impl WebTransportSession {
}; };
} }
} }
Ok(())
} }
pub fn add_stream(&mut self, stream_id: StreamId) { pub fn add_stream(&mut self, stream_id: StreamId) -> Res<()> {
if self.state == SessionState::Active { if self.state == SessionState::Active {
if stream_id.is_bidi() { if stream_id.is_bidi() {
self.send_streams.insert(stream_id); self.send_streams.insert(stream_id);
@@ -306,9 +305,10 @@ impl WebTransportSession {
.extended_connect_new_stream(Http3StreamInfo::new( .extended_connect_new_stream(Http3StreamInfo::new(
stream_id, stream_id,
ExtendedConnectType::WebTransport.get_stream_type(self.session_id), ExtendedConnectType::WebTransport.get_stream_type(self.session_id),
)); ))?;
} }
} }
Ok(())
} }
pub fn remove_recv_stream(&mut self, stream_id: StreamId) { pub fn remove_recv_stream(&mut self, stream_id: StreamId) {
@@ -456,7 +456,7 @@ impl HttpRecvStream for Rc<RefCell<WebTransportSession>> {
self.borrow_mut().header_unblocked(conn) self.borrow_mut().header_unblocked(conn)
} }
fn maybe_update_priority(&mut self, priority: Priority) -> bool { fn maybe_update_priority(&mut self, priority: Priority) -> Res<bool> {
self.borrow_mut().maybe_update_priority(priority) self.borrow_mut().maybe_update_priority(priority)
} }
@@ -464,8 +464,8 @@ impl HttpRecvStream for Rc<RefCell<WebTransportSession>> {
self.borrow_mut().priority_update_frame() self.borrow_mut().priority_update_frame()
} }
fn priority_update_sent(&mut self) { fn priority_update_sent(&mut self) -> Res<()> {
self.borrow_mut().priority_update_sent(); self.borrow_mut().priority_update_sent()
} }
} }

View File

@@ -87,7 +87,7 @@ impl HFrame {
Self::PriorityUpdateRequest { .. } => H3_FRAME_TYPE_PRIORITY_UPDATE_REQUEST, Self::PriorityUpdateRequest { .. } => H3_FRAME_TYPE_PRIORITY_UPDATE_REQUEST,
Self::PriorityUpdatePush { .. } => H3_FRAME_TYPE_PRIORITY_UPDATE_PUSH, Self::PriorityUpdatePush { .. } => H3_FRAME_TYPE_PRIORITY_UPDATE_PUSH,
Self::Grease => { Self::Grease => {
let r = Decoder::from(&random::<8>()).decode_uint::<u64>().unwrap(); let r = u64::from_ne_bytes(random::<8>());
// Zero out the top 7 bits: 2 for being a varint; 5 to account for the *0x1f. // Zero out the top 7 bits: 2 for being a varint; 5 to account for the *0x1f.
HFrameType((r >> 7) * 0x1f + 0x21) HFrameType((r >> 7) * 0x1f + 0x21)
} }

View File

@@ -8,7 +8,11 @@ pub mod hframe;
pub mod reader; pub mod reader;
pub mod wtframe; pub mod wtframe;
#[allow(unused_imports)] #[allow(
clippy::allow_attributes,
unused_imports,
reason = "These are exported."
)]
pub use hframe::{HFrame, H3_FRAME_TYPE_HEADERS, H3_FRAME_TYPE_SETTINGS, H3_RESERVED_FRAME_TYPES}; pub use hframe::{HFrame, H3_FRAME_TYPE_HEADERS, H3_FRAME_TYPE_SETTINGS, H3_RESERVED_FRAME_TYPES};
pub use reader::{FrameReader, StreamReaderConnectionWrapper, StreamReaderRecvStreamWrapper}; pub use reader::{FrameReader, StreamReaderConnectionWrapper, StreamReaderRecvStreamWrapper};
pub use wtframe::WebTransportFrame; pub use wtframe::WebTransportFrame;

View File

@@ -4,8 +4,10 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![allow(clippy::module_name_repetitions)] #![allow(
clippy::module_name_repetitions,
reason = "<https://github.com/mozilla/neqo/issues/2284#issuecomment-2782711813>"
)]
use std::fmt::Debug; use std::fmt::Debug;
use neqo_common::{ use neqo_common::{
@@ -93,7 +95,6 @@ enum FrameReaderState {
UnknownFrameDischargeData { decoder: IncrementalDecoderIgnore }, UnknownFrameDischargeData { decoder: IncrementalDecoderIgnore },
} }
#[allow(clippy::module_name_repetitions)]
#[derive(Debug)] #[derive(Debug)]
pub struct FrameReader { pub struct FrameReader {
state: FrameReaderState, state: FrameReaderState,

View File

@@ -21,6 +21,10 @@ pub fn enc_dec<T: FrameDecoder<T>>(d: &Encoder, st: &str, remaining: usize) -> T
let mut conn_c = default_client(); let mut conn_c = default_client();
let mut conn_s = default_server(); let mut conn_s = default_server();
let out = conn_c.process_output(now()); let out = conn_c.process_output(now());
let out2 = conn_c.process_output(now());
_ = conn_s.process(out.dgram(), now());
let out = conn_s.process(out2.dgram(), now());
let out = conn_c.process(out.dgram(), now());
let out = conn_s.process(out.dgram(), now()); let out = conn_s.process(out.dgram(), now());
let out = conn_c.process(out.dgram(), now()); let out = conn_c.process(out.dgram(), now());
drop(conn_s.process(out.dgram(), now())); drop(conn_s.process(out.dgram(), now()));

View File

@@ -38,7 +38,7 @@ impl FrameReaderTest {
} }
fn process<T: FrameDecoder<T>>(&mut self, v: &[u8]) -> Option<T> { fn process<T: FrameDecoder<T>>(&mut self, v: &[u8]) -> Option<T> {
self.conn_s.stream_send(self.stream_id, v).unwrap(); self.conn_s.stream_send(self.stream_id, v).ok()?;
let out = self.conn_s.process_output(now()); let out = self.conn_s.process_output(now());
drop(self.conn_c.process(out.dgram(), now())); drop(self.conn_c.process(out.dgram(), now()));
let (frame, fin) = self let (frame, fin) = self
@@ -47,7 +47,7 @@ impl FrameReaderTest {
&mut self.conn_c, &mut self.conn_c,
self.stream_id, self.stream_id,
)) ))
.unwrap(); .ok()?;
assert!(!fin); assert!(!fin);
frame frame
} }
@@ -264,7 +264,7 @@ fn test_reading_frame<T: FrameDecoder<T> + PartialEq + Debug>(
assert!(fin); assert!(fin);
assert!(f.is_none()); assert!(f.is_none());
} }
}; }
} }
#[test] #[test]

View File

@@ -50,7 +50,6 @@ impl TryFrom<(MessageType, &str)> for PseudoHeaderState {
/// a status header or if the value of the header is 101 or cannot be parsed. /// a status header or if the value of the header is 101 or cannot be parsed.
pub fn is_interim(headers: &[Header]) -> Res<bool> { pub fn is_interim(headers: &[Header]) -> Res<bool> {
if let Some(h) = headers.iter().take(1).find_header(":status") { if let Some(h) = headers.iter().take(1).find_header(":status") {
#[allow(clippy::map_err_ignore)]
let status_code = h.value().parse::<u16>().map_err(|_| Error::InvalidHeader)?; let status_code = h.value().parse::<u16>().map_err(|_| Error::InvalidHeader)?;
if status_code == 101 { if status_code == 101 {
// https://datatracker.ietf.org/doc/html/draft-ietf-quic-http#section-4.3 // https://datatracker.ietf.org/doc/html/draft-ietf-quic-http#section-4.3

View File

@@ -214,7 +214,6 @@ pub enum Error {
// Internal errors from here. // Internal errors from here.
AlreadyClosed, AlreadyClosed,
AlreadyInitialized, AlreadyInitialized,
DecodingFrame,
FatalError, FatalError,
HttpGoaway, HttpGoaway,
Internal, Internal,
@@ -333,7 +332,7 @@ impl Error {
_ => { _ => {
debug_assert!(false, "Unexpected error"); debug_assert!(false, "Unexpected error");
} }
}; }
Self::TransportStreamDoesNotExist Self::TransportStreamDoesNotExist
} }
@@ -495,9 +494,9 @@ trait HttpRecvStream: RecvStream {
/// An error may happen while reading a stream, e.g. early close, protocol error, etc. /// 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)>; fn header_unblocked(&mut self, conn: &mut Connection) -> Res<(ReceiveOutput, bool)>;
fn maybe_update_priority(&mut self, priority: Priority) -> bool; fn maybe_update_priority(&mut self, priority: Priority) -> Res<bool>;
fn priority_update_frame(&mut self) -> Option<HFrame>; fn priority_update_frame(&mut self) -> Option<HFrame>;
fn priority_update_sent(&mut self); fn priority_update_sent(&mut self) -> Res<()>;
fn set_new_listener(&mut self, _conn_events: Box<dyn HttpRecvStreamEvents>) {} fn set_new_listener(&mut self, _conn_events: Box<dyn HttpRecvStreamEvents>) {}
fn extended_connect_wait_for_response(&self) -> bool { fn extended_connect_wait_for_response(&self) -> bool {

View File

@@ -66,7 +66,7 @@ impl Priority {
Some(ListEntry::Item(Item { Some(ListEntry::Item(Item {
bare_item: BareItem::Integer(u), bare_item: BareItem::Integer(u),
.. ..
})) if (0..=7).contains(u) => u8::try_from(*u).unwrap(), })) if (0..=7).contains(u) => u8::try_from(*u).map_err(|_| Error::Internal)?,
_ => 3, _ => 3,
}; };
let incremental = match dict.get("i") { let incremental = match dict.get("i") {
@@ -107,7 +107,6 @@ impl fmt::Display for Priority {
} }
#[derive(Debug)] #[derive(Debug)]
#[allow(clippy::module_name_repetitions)]
pub struct PriorityHandler { pub struct PriorityHandler {
push_stream: bool, push_stream: bool,
priority: Priority, priority: Priority,

View File

@@ -77,7 +77,7 @@ impl ActivePushStreams {
return None; return None;
} }
let inx = usize::try_from(u64::from(push_id - self.first_push_id)).unwrap(); let inx = usize::try_from(u64::from(push_id - self.first_push_id)).ok()?;
if inx >= self.push_streams.len() { if inx >= self.push_streams.len() {
self.push_streams.resize(inx + 1, PushState::Init); self.push_streams.resize(inx + 1, PushState::Init);
} }
@@ -116,7 +116,7 @@ impl ActivePushStreams {
.filter(|&e| e == &PushState::Closed) .filter(|&e| e == &PushState::Closed)
.count(), .count(),
) )
.unwrap() .expect("usize fits in u64")
} }
pub fn clear(&mut self) { pub fn clear(&mut self) {

View File

@@ -18,7 +18,7 @@ pub fn h3_data_moved_up(qlog: &NeqoQlog, stream_id: StreamId, amount: usize) {
let ev_data = EventData::DataMoved(qlog::events::quic::DataMoved { let ev_data = EventData::DataMoved(qlog::events::quic::DataMoved {
stream_id: Some(stream_id.as_u64()), stream_id: Some(stream_id.as_u64()),
offset: None, offset: None,
length: Some(u64::try_from(amount).unwrap()), length: Some(u64::try_from(amount).expect("usize fits in u64")),
from: Some(DataRecipient::Transport), from: Some(DataRecipient::Transport),
to: Some(DataRecipient::Application), to: Some(DataRecipient::Application),
raw: None, raw: None,
@@ -36,7 +36,7 @@ pub fn h3_data_moved_down(qlog: &NeqoQlog, stream_id: StreamId, amount: usize) {
let ev_data = EventData::DataMoved(qlog::events::quic::DataMoved { let ev_data = EventData::DataMoved(qlog::events::quic::DataMoved {
stream_id: Some(stream_id.as_u64()), stream_id: Some(stream_id.as_u64()),
offset: None, offset: None,
length: Some(u64::try_from(amount).unwrap()), length: Some(u64::try_from(amount).expect("usize fits in u64")),
from: Some(DataRecipient::Application), from: Some(DataRecipient::Application),
to: Some(DataRecipient::Transport), to: Some(DataRecipient::Transport),
raw: None, raw: None,

View File

@@ -19,7 +19,6 @@ use crate::{
MessageType, Priority, PushId, ReceiveOutput, RecvStream, Res, Stream, MessageType, Priority, PushId, ReceiveOutput, RecvStream, Res, Stream,
}; };
#[allow(clippy::module_name_repetitions)]
pub struct RecvMessageInfo { pub struct RecvMessageInfo {
pub message_type: MessageType, pub message_type: MessageType,
pub stream_type: Http3StreamType, pub stream_type: Http3StreamType,
@@ -292,7 +291,7 @@ impl RecvMessage {
break self.set_state_to_close_pending(post_readable_event); break self.set_state_to_close_pending(post_readable_event);
} }
} }
}; }
} }
RecvMessageState::DecodingHeaders { header_block, fin } => { RecvMessageState::DecodingHeaders { header_block, fin } => {
if self if self
@@ -338,7 +337,7 @@ impl RecvMessage {
// WebTransportSession // WebTransportSession
break Ok(()); break Ok(());
} }
}; }
} }
} }
@@ -460,16 +459,17 @@ impl HttpRecvStream for RecvMessage {
self.receive(conn) self.receive(conn)
} }
fn maybe_update_priority(&mut self, priority: Priority) -> bool { fn maybe_update_priority(&mut self, priority: Priority) -> Res<bool> {
self.priority_handler.maybe_update_priority(priority) Ok(self.priority_handler.maybe_update_priority(priority))
} }
fn priority_update_frame(&mut self) -> Option<HFrame> { fn priority_update_frame(&mut self) -> Option<HFrame> {
self.priority_handler.maybe_encode_frame(self.stream_id) self.priority_handler.maybe_encode_frame(self.stream_id)
} }
fn priority_update_sent(&mut self) { fn priority_update_sent(&mut self) -> Res<()> {
self.priority_handler.priority_update_sent(); self.priority_handler.priority_update_sent();
Ok(())
} }
fn set_new_listener(&mut self, conn_events: Box<dyn HttpRecvStreamEvents>) { fn set_new_listener(&mut self, conn_events: Box<dyn HttpRecvStreamEvents>) {

View File

@@ -4,8 +4,6 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![allow(clippy::module_name_repetitions)]
use std::fmt::{Debug, Formatter}; use std::fmt::{Debug, Formatter};
use url::{ParseError, Url}; use url::{ParseError, Url};

View File

@@ -151,7 +151,7 @@ impl SendMessage {
} }
fn stream_id(&self) -> StreamId { fn stream_id(&self) -> StreamId {
Option::<StreamId>::from(&self.stream).unwrap() Option::<StreamId>::from(&self.stream).expect("stream has ID")
} }
fn get_stream_info(&self) -> Http3StreamInfo { fn get_stream_info(&self) -> Http3StreamInfo {
@@ -183,7 +183,7 @@ impl SendStream for SendMessage {
// cheap, thus not worth optimizing. // cheap, thus not worth optimizing.
conn.stream_set_writable_event_low_watermark( conn.stream_set_writable_event_low_watermark(
self.stream_id(), self.stream_id(),
NonZeroUsize::new(MIN_DATA_FRAME_SIZE).unwrap(), NonZeroUsize::new(MIN_DATA_FRAME_SIZE).ok_or(Error::Internal)?,
)?; )?;
return Ok(0); return Ok(0);
} }

View File

@@ -4,8 +4,6 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![allow(clippy::module_name_repetitions)]
use std::{ use std::{
cell::{RefCell, RefMut}, cell::{RefCell, RefMut},
collections::HashMap, collections::HashMap,
@@ -118,7 +116,11 @@ impl Http3Server {
self.process(None::<Datagram>, now) self.process(None::<Datagram>, now)
} }
pub fn process(&mut self, dgram: Option<Datagram<impl AsRef<[u8]>>>, now: Instant) -> Output { pub fn process(
&mut self,
dgram: Option<Datagram<impl AsRef<[u8]> + AsMut<[u8]>>>,
now: Instant,
) -> Output {
qtrace!("[{self}] Process"); qtrace!("[{self}] Process");
let out = self.server.process(dgram, now); let out = self.server.process(dgram, now);
self.process_http3(now); self.process_http3(now);
@@ -135,9 +137,10 @@ impl Http3Server {
/// Process HTTP3 layer. /// Process HTTP3 layer.
fn process_http3(&mut self, now: Instant) { fn process_http3(&mut self, now: Instant) {
qtrace!("[{self}] Process http3 internal"); qtrace!("[{self}] Process http3 internal");
// `ActiveConnectionRef` `Hash` implementation doesnt access any of the interior mutable #[expect(
// types. clippy::mutable_key_type,
#[allow(clippy::mutable_key_type)] reason = "ActiveConnectionRef::Hash doesn't access any of the interior mutable types."
)]
let mut active_conns = self.server.active_connections(); let mut active_conns = self.server.active_connections();
active_conns.extend( active_conns.extend(
self.http3_handlers self.http3_handlers
@@ -152,7 +155,10 @@ impl Http3Server {
} }
} }
#[allow(clippy::too_many_lines)] #[expect(
clippy::too_many_lines,
reason = "Function is mostly a match statement."
)]
fn process_events(&mut self, conn: &ConnectionRef, now: Instant) { fn process_events(&mut self, conn: &ConnectionRef, now: Instant) {
let mut remove = false; let mut remove = false;
let http3_parameters = &self.http3_parameters; let http3_parameters = &self.http3_parameters;
@@ -410,8 +416,12 @@ mod tests {
fn connect_transport(server: &mut Http3Server, client: &mut Connection, resume: bool) { fn connect_transport(server: &mut Http3Server, client: &mut Connection, resume: bool) {
let c1 = client.process_output(now()); let c1 = client.process_output(now());
let s1 = server.process(c1.dgram(), now()); let c11 = client.process_output(now());
_ = server.process(c1.dgram(), now());
let s1 = server.process(c11.dgram(), now());
let c2 = client.process(s1.dgram(), now()); let c2 = client.process(s1.dgram(), now());
let s2 = server.process(c2.dgram(), now());
let c2 = client.process(s2.dgram(), now());
let needs_auth = client let needs_auth = client
.events() .events()
.any(|e| e == ConnectionEvent::AuthenticationNeeded); .any(|e| e == ConnectionEvent::AuthenticationNeeded);
@@ -430,8 +440,7 @@ mod tests {
assert!(client.state().connected()); assert!(client.state().connected());
let s2 = server.process(c2.dgram(), now()); let s2 = server.process(c2.dgram(), now());
assert_connected(server); assert_connected(server);
let c3 = client.process(s2.dgram(), now()); _ = client.process(s2.dgram(), now());
assert!(c3.dgram().is_none());
} }
// Start a client/server and check setting frame. // Start a client/server and check setting frame.

View File

@@ -12,7 +12,8 @@ use neqo_transport::{AppError, StreamId};
use crate::{ use crate::{
connection::Http3State, connection::Http3State,
features::extended_connect::{ExtendedConnectEvents, ExtendedConnectType, SessionCloseReason}, features::extended_connect::{ExtendedConnectEvents, ExtendedConnectType, SessionCloseReason},
CloseType, Http3StreamInfo, HttpRecvStreamEvents, Priority, RecvStreamEvents, SendStreamEvents, CloseType, Http3StreamInfo, HttpRecvStreamEvents, Priority, RecvStreamEvents, Res,
SendStreamEvents,
}; };
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Eq, Clone)]
@@ -69,10 +70,9 @@ pub struct Http3ServerConnEvents {
impl SendStreamEvents for Http3ServerConnEvents { impl SendStreamEvents for Http3ServerConnEvents {
fn send_closed(&self, stream_info: Http3StreamInfo, close_type: CloseType) { fn send_closed(&self, stream_info: Http3StreamInfo, close_type: CloseType) {
if close_type != CloseType::Done { if close_type != CloseType::Done {
self.insert(Http3ServerConnEvent::StreamStopSending { if let Some(error) = close_type.error() {
stream_info, self.insert(Http3ServerConnEvent::StreamStopSending { stream_info, error });
error: close_type.error().unwrap(), }
});
} }
} }
@@ -90,10 +90,9 @@ impl RecvStreamEvents for Http3ServerConnEvents {
fn recv_closed(&self, stream_info: Http3StreamInfo, close_type: CloseType) { fn recv_closed(&self, stream_info: Http3StreamInfo, close_type: CloseType) {
if close_type != CloseType::Done { if close_type != CloseType::Done {
self.remove_events_for_stream_id(stream_info); self.remove_events_for_stream_id(stream_info);
self.insert(Http3ServerConnEvent::StreamReset { if let Some(error) = close_type.error() {
stream_info, self.insert(Http3ServerConnEvent::StreamReset { stream_info, error });
error: close_type.error().unwrap(), }
});
} }
} }
} }
@@ -144,8 +143,9 @@ impl ExtendedConnectEvents for Http3ServerConnEvents {
}); });
} }
fn extended_connect_new_stream(&self, stream_info: Http3StreamInfo) { fn extended_connect_new_stream(&self, stream_info: Http3StreamInfo) -> Res<()> {
self.insert(Http3ServerConnEvent::ExtendedConnectNewStream(stream_info)); self.insert(Http3ServerConnEvent::ExtendedConnectNewStream(stream_info));
Ok(())
} }
fn new_datagram(&self, session_id: StreamId, datagram: Vec<u8>) { fn new_datagram(&self, session_id: StreamId, datagram: Vec<u8>) {

View File

@@ -4,8 +4,6 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![allow(clippy::module_name_repetitions)]
use std::{ use std::{
cell::RefCell, cell::RefCell,
collections::VecDeque, collections::VecDeque,
@@ -22,7 +20,7 @@ use crate::{
connection::{Http3State, WebTransportSessionAcceptAction}, connection::{Http3State, WebTransportSessionAcceptAction},
connection_server::Http3ServerHandler, connection_server::Http3ServerHandler,
features::extended_connect::SessionCloseReason, features::extended_connect::SessionCloseReason,
Http3StreamInfo, Http3StreamType, Priority, Res, Error, Http3StreamInfo, Http3StreamType, Priority, Res,
}; };
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -53,8 +51,6 @@ impl PartialEq for StreamHandler {
} }
} }
impl Eq for StreamHandler {}
impl StreamHandler { impl StreamHandler {
pub const fn stream_id(&self) -> StreamId { pub const fn stream_id(&self) -> StreamId {
self.stream_info.stream_id() self.stream_info.stream_id()
@@ -215,7 +211,6 @@ impl Http3OrWebTransportStream {
impl Deref for Http3OrWebTransportStream { impl Deref for Http3OrWebTransportStream {
type Target = StreamHandler; type Target = StreamHandler;
#[must_use]
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.stream_handler &self.stream_handler
} }
@@ -378,13 +373,12 @@ impl WebTransportRequest {
- u64::try_from(Encoder::varint_len( - u64::try_from(Encoder::varint_len(
self.stream_handler.stream_id().as_u64(), self.stream_handler.stream_id().as_u64(),
)) ))
.unwrap()) .map_err(|_| Error::Internal)?)
} }
} }
impl Deref for WebTransportRequest { impl Deref for WebTransportRequest {
type Target = StreamHandler; type Target = StreamHandler;
#[must_use]
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.stream_handler &self.stream_handler
} }
@@ -409,8 +403,6 @@ impl PartialEq for WebTransportRequest {
} }
} }
impl Eq for WebTransportRequest {}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum WebTransportServerEvent { pub enum WebTransportServerEvent {
NewSession { NewSession {

View File

@@ -4,8 +4,6 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![allow(clippy::module_name_repetitions)]
use std::ops::Deref; use std::ops::Deref;
use neqo_common::{Decoder, Encoder}; use neqo_common::{Decoder, Encoder};
@@ -180,7 +178,7 @@ impl HSettings {
// other supported settings here // other supported settings here
(Some(_), Some(_)) => {} // ignore unknown setting, it is fine. (Some(_), Some(_)) => {} // ignore unknown setting, it is fine.
_ => return Err(Error::NotEnoughData), _ => return Err(Error::NotEnoughData),
}; }
} }
Ok(()) Ok(())
} }

View File

@@ -4,6 +4,8 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![cfg(test)]
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use neqo_common::{event::Provider as _, qtrace, Datagram}; use neqo_common::{event::Provider as _, qtrace, Datagram};
@@ -92,8 +94,12 @@ fn process_client_events(conn: &mut Http3Client) {
fn connect_peers(hconn_c: &mut Http3Client, hconn_s: &mut Http3Server) -> Option<Datagram> { fn connect_peers(hconn_c: &mut Http3Client, hconn_s: &mut Http3Server) -> Option<Datagram> {
assert_eq!(hconn_c.state(), Http3State::Initializing); assert_eq!(hconn_c.state(), Http3State::Initializing);
let out = hconn_c.process_output(now()); // Initial let out = hconn_c.process_output(now()); // Initial
let out = hconn_s.process(out.dgram(), now()); // Initial + Handshake let out2 = hconn_c.process_output(now()); // Initial
let out = hconn_c.process(out.dgram(), now()); // ACK _ = hconn_s.process(out.dgram(), now()); // ACK
let out = hconn_s.process(out2.dgram(), now()); // Initial + Handshake
let out = hconn_c.process(out.dgram(), now());
let out = hconn_s.process(out.dgram(), now());
let out = hconn_c.process(out.dgram(), now());
drop(hconn_s.process(out.dgram(), now())); // consume ACK drop(hconn_s.process(out.dgram(), now())); // consume ACK
let authentication_needed = |e| matches!(e, Http3ClientEvent::AuthenticationNeeded); let authentication_needed = |e| matches!(e, Http3ClientEvent::AuthenticationNeeded);
assert!(hconn_c.events().any(authentication_needed)); assert!(hconn_c.events().any(authentication_needed));
@@ -119,8 +125,14 @@ fn connect_peers_with_network_propagation_delay(
assert_eq!(hconn_c.state(), Http3State::Initializing); assert_eq!(hconn_c.state(), Http3State::Initializing);
let mut now = now(); let mut now = now();
let out = hconn_c.process_output(now); // Initial let out = hconn_c.process_output(now); // Initial
let out2 = hconn_c.process_output(now); // Initial
now += net_delay; now += net_delay;
let out = hconn_s.process(out.dgram(), now); // Initial + Handshake _ = hconn_s.process(out.dgram(), now); // ACK
let out = hconn_s.process(out2.dgram(), now);
now += net_delay;
let out = hconn_c.process(out.dgram(), now);
now += net_delay;
let out = hconn_s.process(out.dgram(), now);
now += net_delay; now += net_delay;
let out = hconn_c.process(out.dgram(), now); // ACK let out = hconn_c.process(out.dgram(), now); // ACK
now += net_delay; now += net_delay;
@@ -143,7 +155,8 @@ fn connect_peers_with_network_propagation_delay(
(out.dgram(), now) (out.dgram(), now)
} }
fn connect() -> (Http3Client, Http3Server, Option<Datagram>) { #[must_use]
pub fn connect() -> (Http3Client, Http3Server, Option<Datagram>) {
let mut hconn_c = default_http3_client(); let mut hconn_c = default_http3_client();
let mut hconn_s = default_http3_server(); let mut hconn_s = default_http3_server();
@@ -242,7 +255,7 @@ fn response_103() {
/// Test [`neqo_http3::SendMessage::send_data`] to set /// Test [`neqo_http3::SendMessage::send_data`] to set
/// [`neqo_transport::SendStream::set_writable_event_low_watermark`]. /// [`neqo_transport::SendStream::set_writable_event_low_watermark`].
#[allow(clippy::cast_possible_truncation)] #[expect(clippy::cast_possible_truncation, reason = "OK in a test.")]
#[test] #[test]
fn data_writable_events_low_watermark() -> Result<(), Box<dyn std::error::Error>> { fn data_writable_events_low_watermark() -> Result<(), Box<dyn std::error::Error>> {
const STREAM_LIMIT: u64 = 5000; const STREAM_LIMIT: u64 = 5000;
@@ -446,6 +459,11 @@ fn zerortt() {
hconn_c.stream_close_send(req).unwrap(); hconn_c.stream_close_send(req).unwrap();
let out = hconn_c.process(dgram, now()); let out = hconn_c.process(dgram, now());
let out2 = hconn_c.process_output(now());
_ = hconn_s.process(out.dgram(), now());
let out = hconn_s.process(out2.dgram(), now());
let out = hconn_c.process(out.dgram(), now());
let out = hconn_s.process(out.dgram(), now()); let out = hconn_s.process(out.dgram(), now());
let mut request_stream = None; let mut request_stream = None;

View File

@@ -29,8 +29,12 @@ fn exchange_packets(client: &mut Http3Client, server: &mut Http3Server) {
fn connect_with(client: &mut Http3Client, server: &mut Http3Server) { fn connect_with(client: &mut Http3Client, server: &mut Http3Server) {
assert_eq!(client.state(), Http3State::Initializing); assert_eq!(client.state(), Http3State::Initializing);
let out = client.process_output(now()); let out = client.process_output(now());
let out2 = client.process_output(now());
assert_eq!(client.state(), Http3State::Initializing); assert_eq!(client.state(), Http3State::Initializing);
_ = server.process(out.dgram(), now());
let out = server.process(out2.dgram(), now());
let out = client.process(out.dgram(), now());
let out = server.process(out.dgram(), now()); let out = server.process(out.dgram(), now());
let out = client.process(out.dgram(), now()); let out = client.process(out.dgram(), now());
let out = server.process(out.dgram(), now()); let out = server.process(out.dgram(), now());

View File

@@ -4,6 +4,8 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![cfg(test)]
use std::sync::OnceLock; use std::sync::OnceLock;
use neqo_common::event::Provider as _; use neqo_common::event::Provider as _;

View File

@@ -4,6 +4,8 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![cfg(test)]
use std::{cell::RefCell, rc::Rc}; use std::{cell::RefCell, rc::Rc};
use neqo_common::{event::Provider as _, header::HeadersExt as _}; use neqo_common::{event::Provider as _, header::HeadersExt as _};
@@ -42,8 +44,12 @@ fn connect() -> (Http3Client, Http3Server) {
.expect("create a server"); .expect("create a server");
assert_eq!(client.state(), Http3State::Initializing); assert_eq!(client.state(), Http3State::Initializing);
let out = client.process_output(now()); let out = client.process_output(now());
let out2 = client.process_output(now());
assert_eq!(client.state(), Http3State::Initializing); assert_eq!(client.state(), Http3State::Initializing);
_ = server.process(out.dgram(), now());
let out = server.process(out2.dgram(), now());
let out = client.process(out.dgram(), now());
let out = server.process(out.dgram(), now()); let out = server.process(out.dgram(), now());
let out = client.process(out.dgram(), now()); let out = client.process(out.dgram(), now());
let out = server.process(out.dgram(), now()); let out = server.process(out.dgram(), now());

View File

@@ -1 +1 @@
{"files":{"Cargo.toml":"2ccc60ca10aabbb26b08bff596ac851e1e5e58740a53d07bea8a3dfb9835e4db","src/decoder.rs":"4d83a4f375cd6fdd01af5e9f3ad03deb7a6362c4ad1cf56d1c693e76946ae84d","src/decoder_instructions.rs":"0e6c2036372061b16f71b172e3b6f7796683c0992f5f6a39e6c4b37d9fff349a","src/encoder.rs":"77f4229ce9cb0b466ea0cd5108bb0128824f34962d87e94f76b757ce821a281f","src/encoder_instructions.rs":"033b7aeda62f8df2cf9b09a93d449cbae74376e9f49791683543874d46f64e08","src/header_block.rs":"4285d03556b6eba3a00715c9450b00af676a3ee5f3aa07a9f4e5e33944695ee3","src/huffman.rs":"6976f1b4d3e5ef849a6b080cfb2e8804bf01cfe3b9bd9e3994a319d5405cd8f3","src/huffman_decode_helper.rs":"9ce470e318b3664f58aa109bed483ab15bfd9e0b17d261ea2b609668a42a9d80","src/huffman_table.rs":"06fea766a6276ac56c7ee0326faed800a742c15fda1f33bf2513e6cc6a5e6d27","src/lib.rs":"f9bad0fe7643c618d034c4941ebd30ad5f6015b8b87b484b0ea79681d13d8b49","src/prefix.rs":"1655af52fcc58c27984d78af6639d66a26c2307995e7ff22eb309fd7e4d50a81","src/qlog.rs":"d8e8482624f0cc4388546169772846d37a0422ce10287ee2f716dfc5f7a56c3d","src/qpack_send_buf.rs":"c0c2bc22ffd72b0dc2fb2f6d5590fc6a2fa4a19cc32b1407e9373140cbf44414","src/reader.rs":"ae15506441a52ad522f15048796c37ad791680e3ca020a50f7c84f54ab95435e","src/static_table.rs":"6e5ec26e2b6bd63375d2d77e72748151d430d1629a8e497ec0d0ea21c078524a","src/stats.rs":"cb01723249f60e15a5cd7efd9cbab409fddc588d1df655ed06ba8c80e3d5d28e","src/table.rs":"f7aac571794f192ef4bed1dc4c1ddd96dac5553b0341a5a99ff2fadcc46d8f7d"},"package":null} {"files":{"Cargo.toml":"62c9acbd08bb43060ac7243b0a6b88e1cf2c679a6fd36ed7aeb32a6d90b3b139","src/decoder.rs":"06dc4b0362ddf0a502ab7f3a8622aa213aaa6ca599990471f46c918778df69e6","src/decoder_instructions.rs":"0e6c2036372061b16f71b172e3b6f7796683c0992f5f6a39e6c4b37d9fff349a","src/encoder.rs":"22ac6cf0e6cbd9be5c7d09d4b73ac396bf7a9ac0e8a8f5137dcdafb0e004f85f","src/encoder_instructions.rs":"210442f053b317c14b8630c57e9c2f75f66e9ce3de79f5ff6cd5c6fcf1fea17a","src/header_block.rs":"4285d03556b6eba3a00715c9450b00af676a3ee5f3aa07a9f4e5e33944695ee3","src/huffman.rs":"4ffaf7766b7a9e1c01f094d48c447a75516ee7be91f2302327f7041d328a8c3f","src/huffman_decode_helper.rs":"c799b85c7738cdf6a1f6ea039062d2ea5ce0b4f08789d64e90a8712d57040d2b","src/huffman_table.rs":"5a5819dd2708f88240a2f825d86aa8b5f45c28d1427c0b8463831653e8eb3527","src/lib.rs":"1cde51b6c8c3c5a187f2393ad636e7e3919e10902c53bc9cc568bfe6c74a50c4","src/prefix.rs":"31bfb11d334a6df619bcc2720621e44a656be2514fad9033531a712d47dbe672","src/qlog.rs":"d8e8482624f0cc4388546169772846d37a0422ce10287ee2f716dfc5f7a56c3d","src/qpack_send_buf.rs":"cf10375c0bc5fb27d6faaf82df8607743bd62c5772d2cfb3779d801f4650e4f2","src/reader.rs":"5f0c2ab16bc804a892a7bd2310dfd6e614fd8ab2b4f552914fca6ad32b4fb006","src/static_table.rs":"6e5ec26e2b6bd63375d2d77e72748151d430d1629a8e497ec0d0ea21c078524a","src/stats.rs":"cb01723249f60e15a5cd7efd9cbab409fddc588d1df655ed06ba8c80e3d5d28e","src/table.rs":"f7c773dd3e086ead69ffffe9850e5c9b9b007c9fdd188022cdde7d08def248eb"},"package":null}

View File

@@ -11,9 +11,9 @@
[package] [package]
edition = "2021" edition = "2021"
rust-version = "1.76.0" rust-version = "1.82.0"
name = "neqo-qpack" name = "neqo-qpack"
version = "0.12.2" version = "0.13.1"
authors = ["The Neqo Authors <necko@mozilla.com>"] authors = ["The Neqo Authors <necko@mozilla.com>"]
build = false build = false
autolib = false autolib = false
@@ -64,7 +64,7 @@ path = "./../neqo-common"
path = "./../neqo-transport" path = "./../neqo-transport"
[dependencies.qlog] [dependencies.qlog]
version = "0.13" version = "0.15.1"
default-features = false default-features = false
[dependencies.static_assertions] [dependencies.static_assertions]
@@ -75,22 +75,56 @@ default-features = false
path = "../test-fixture" path = "../test-fixture"
[lints.clippy] [lints.clippy]
allow_attributes = "warn"
allow_attributes_without_reason = "warn"
cfg_not_test = "warn" cfg_not_test = "warn"
clone_on_ref_ptr = "warn" clone_on_ref_ptr = "warn"
create_dir = "warn" create_dir = "warn"
dbg_macro = "warn"
empty_drop = "warn"
empty_enum_variants_with_brackets = "warn"
filetype_is_file = "warn"
float_cmp_const = "warn"
fn_to_numeric_cast_any = "warn"
get_unwrap = "warn" get_unwrap = "warn"
if_then_some_else_none = "warn" if_then_some_else_none = "warn"
infinite_loop = "warn"
large_include_file = "warn"
let_underscore_must_use = "warn"
let_underscore_untyped = "warn"
literal_string_with_formatting_args = "allow"
lossy_float_literal = "warn"
mem_forget = "warn"
mixed_read_write_in_expression = "warn"
module_name_repetitions = "warn"
multiple_crate_versions = "allow" multiple_crate_versions = "allow"
multiple_inherent_impl = "warn" multiple_inherent_impl = "warn"
mutex_atomic = "warn"
mutex_integer = "warn"
needless_raw_strings = "warn"
pathbuf_init_then_push = "warn" pathbuf_init_then_push = "warn"
pub_without_shorthand = "warn"
rc_buffer = "warn"
rc_mutex = "warn"
redundant_type_annotations = "warn" redundant_type_annotations = "warn"
ref_patterns = "warn" ref_patterns = "warn"
renamed_function_params = "warn" renamed_function_params = "warn"
rest_pat_in_fully_bound_structs = "warn"
self_named_module_files = "warn"
semicolon_inside_block = "warn" semicolon_inside_block = "warn"
string_lit_chars_any = "warn"
string_to_string = "warn"
suspicious_xor_used_as_pow = "warn"
try_err = "warn" try_err = "warn"
unnecessary_safety_comment = "warn"
unnecessary_safety_doc = "warn"
unnecessary_self_imports = "warn"
unneeded_field_pattern = "warn" unneeded_field_pattern = "warn"
unused_result_ok = "warn" unused_result_ok = "warn"
unused_trait_names = "warn" unused_trait_names = "warn"
unwrap_in_result = "warn"
unwrap_used = "warn"
verbose_file_reads = "warn"
[lints.clippy.cargo] [lints.clippy.cargo]
level = "warn" level = "warn"
@@ -107,7 +141,6 @@ priority = -1
[lints.rust] [lints.rust]
absolute_paths_not_starting_with_crate = "warn" absolute_paths_not_starting_with_crate = "warn"
ambiguous_negative_literals = "warn" ambiguous_negative_literals = "warn"
closure_returning_async_block = "warn"
explicit_outlives_requirements = "warn" explicit_outlives_requirements = "warn"
macro_use_extern_crate = "warn" macro_use_extern_crate = "warn"
missing_abi = "warn" missing_abi = "warn"
@@ -119,3 +152,4 @@ unit_bindings = "warn"
unused_import_braces = "warn" unused_import_braces = "warn"
unused_lifetimes = "warn" unused_lifetimes = "warn"
unused_macro_rules = "warn" unused_macro_rules = "warn"
unused_qualifications = "warn"

View File

@@ -4,6 +4,11 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![allow(
clippy::module_name_repetitions,
reason = "<https://github.com/mozilla/neqo/issues/2284#issuecomment-2782711813>"
)]
use neqo_common::{qdebug, Header}; use neqo_common::{qdebug, Header};
use neqo_transport::{Connection, StreamId}; use neqo_transport::{Connection, StreamId};
@@ -67,12 +72,9 @@ impl QPackDecoder {
self.max_table_size self.max_table_size
} }
/// # Panics
///
/// If the number of blocked streams is too large.
#[must_use] #[must_use]
pub fn get_blocked_streams(&self) -> u16 { pub const fn get_blocked_streams(&self) -> usize {
u16::try_from(self.max_blocked_streams).unwrap() self.max_blocked_streams
} }
/// returns a list of unblocked streams /// returns a list of unblocked streams
@@ -174,7 +176,6 @@ impl QPackDecoder {
/// # Panics /// # Panics
/// ///
/// Never, but rust doesn't know that. /// Never, but rust doesn't know that.
#[allow(clippy::map_err_ignore)]
pub fn send(&mut self, conn: &mut Connection) -> Res<()> { pub fn send(&mut self, conn: &mut Connection) -> Res<()> {
// Encode increment instruction if needed. // Encode increment instruction if needed.
let increment = self.table.base() - self.acked_inserts; let increment = self.table.base() - self.acked_inserts;
@@ -184,7 +185,10 @@ impl QPackDecoder {
} }
if !self.send_buf.is_empty() && self.local_stream_id.is_some() { if !self.send_buf.is_empty() && self.local_stream_id.is_some() {
let r = conn let r = conn
.stream_send(self.local_stream_id.unwrap(), &self.send_buf[..]) .stream_send(
self.local_stream_id.ok_or(Error::Internal)?,
&self.send_buf[..],
)
.map_err(|_| Error::DecoderStream)?; .map_err(|_| Error::DecoderStream)?;
qdebug!("[{self}] {r} bytes sent"); qdebug!("[{self}] {r} bytes sent");
self.send_buf.read(r); self.send_buf.read(r);

View File

@@ -4,6 +4,11 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![allow(
clippy::module_name_repetitions,
reason = "<https://github.com/mozilla/neqo/issues/2284#issuecomment-2782711813>"
)]
use std::collections::{HashMap, HashSet, VecDeque}; use std::collections::{HashMap, HashSet, VecDeque};
use neqo_common::{qdebug, qerror, qlog::NeqoQlog, qtrace, Header}; use neqo_common::{qdebug, qerror, qlog::NeqoQlog, qtrace, Header};
@@ -149,7 +154,6 @@ impl QPackEncoder {
} }
} }
#[allow(clippy::map_err_ignore)]
fn insert_count_instruction(&mut self, increment: u64) -> Res<()> { fn insert_count_instruction(&mut self, increment: u64) -> Res<()> {
self.table self.table
.increment_acked(increment) .increment_acked(increment)
@@ -540,7 +544,7 @@ mod tests {
impl TestEncoder { impl TestEncoder {
pub fn change_capacity(&mut self, capacity: u64) -> Res<()> { pub fn change_capacity(&mut self, capacity: u64) -> Res<()> {
self.encoder.set_max_capacity(capacity).unwrap(); self.encoder.set_max_capacity(capacity)?;
// We will try to really change the table only when we send the change capacity // We will try to really change the table only when we send the change capacity
// instruction. // instruction.
self.encoder.send_encoder_updates(&mut self.conn) self.encoder.send_encoder_updates(&mut self.conn)

View File

@@ -44,7 +44,7 @@ pub enum EncoderInstruction<'a> {
index: u64, index: u64,
}, },
#[cfg(test)] #[cfg(test)]
#[allow(dead_code)] #[expect(dead_code, reason = "Only used in tests.")]
NoInstruction, NoInstruction,
} }

Some files were not shown because too many files have changed in this diff Show More