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"
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"
tag = "v0.12.2"
tag = "v0.13.1"
replace-with = "vendored-sources"
[source."git+https://github.com/servo/rust-cssparser?rev=958a3f098acb92ddacdce18a7ef2c4a87ac3326f"]

43
Cargo.lock generated
View File

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

View File

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

View File

@@ -261,7 +261,10 @@ impl NeqoHttp3Conn {
.max_stream_data(StreamType::BiDi, false, max_stream_data)
.grease(static_prefs::pref!("security.tls.grease_http3_enable"))
.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.
#[cfg(feature = "fuzzing")]
@@ -417,10 +420,10 @@ impl NeqoHttp3Conn {
}
if static_prefs::pref!("network.http.http3.ecn") && stats.frame_rx.handshake_done != 0 {
if stats.ecn_tx[IpTosEcn::Ect0] > 0 {
if let Ok(ratio) = i64::try_from(
(stats.ecn_tx[IpTosEcn::Ce] * PRECISION_FACTOR) / stats.ecn_tx[IpTosEcn::Ect0],
) {
let tx_ect0_sum: u64 = stats.ecn_tx.into_values().map(|v| v[IpTosEcn::Ect0]).sum();
let tx_ce_sum: u64 = stats.ecn_tx.into_values().map(|v| v[IpTosEcn::Ce]).sum();
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);
} else {
let msg = "Failed to convert ratio to i64 for use with glean";
@@ -428,10 +431,10 @@ impl NeqoHttp3Conn {
debug_assert!(false, "{msg}");
}
}
if stats.ecn_rx[IpTosEcn::Ect0] > 0 {
if let Ok(ratio) = i64::try_from(
(stats.ecn_rx[IpTosEcn::Ce] * PRECISION_FACTOR) / stats.ecn_rx[IpTosEcn::Ect0],
) {
let rx_ect0_sum: u64 = stats.ecn_rx.into_values().map(|v| v[IpTosEcn::Ect0]).sum();
let rx_ce_sum: u64 = stats.ecn_rx.into_values().map(|v| v[IpTosEcn::Ce]).sum();
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);
} else {
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::ConnectionIdsExhausted => Self::TransportInternalErrorOther(2),
TransportError::ConnectionState => Self::TransportInternalErrorOther(3),
TransportError::DecodingFrame => Self::TransportInternalErrorOther(4),
TransportError::DecryptError => Self::TransportInternalErrorOther(5),
TransportError::IntegerOverflow => Self::TransportInternalErrorOther(7),
TransportError::InvalidInput => Self::TransportInternalErrorOther(8),
@@ -1184,6 +1186,7 @@ impl From<TransportError> for CloseError {
TransportError::QlogError => Self::TransportInternalErrorOther(27),
TransportError::NotAvailable => Self::TransportInternalErrorOther(28),
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::ConnectionIdsExhausted => "ConnectionIdsExhausted",
TransportError::ConnectionState => "ConnectionState",
TransportError::DecodingFrame => "DecodingFrame",
TransportError::DecryptError => "DecryptError",
TransportError::DisabledVersion => "DisabledVersion",
TransportError::IdleTimeout => "IdleTimeout",
@@ -1242,6 +1244,7 @@ const fn transport_error_to_glean_label(error: &TransportError) -> &'static str
TransportError::UnknownFrameType => "UnknownFrameType",
TransportError::VersionNegotiation => "VersionNegotiation",
TransportError::WrongRole => "WrongRole",
TransportError::UnknownTransportParameter => "UnknownTransportParameter",
}
}

View File

@@ -6,11 +6,11 @@ edition = "2021"
license = "MPL-2.0"
[dependencies]
neqo-bin = { tag = "v0.12.2", git = "https://github.com/mozilla/neqo" }
neqo-transport = { tag = "v0.12.2", git = "https://github.com/mozilla/neqo", features = ["gecko"] }
neqo-common = { tag = "v0.12.2", git = "https://github.com/mozilla/neqo" }
neqo-http3 = { tag = "v0.12.2", git = "https://github.com/mozilla/neqo" }
neqo-qpack = { 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.13.1", git = "https://github.com/mozilla/neqo", features = ["gecko"] }
neqo-common = { tag = "v0.13.1", git = "https://github.com/mozilla/neqo" }
neqo-http3 = { tag = "v0.13.1", git = "https://github.com/mozilla/neqo" }
neqo-qpack = { tag = "v0.13.1", git = "https://github.com/mozilla/neqo" }
log = "0.4.0"
base64 = "0.22"
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 }
[dependencies.neqo-crypto]
tag = "v0.12.2"
tag = "v0.13.1"
git = "https://github.com/mozilla/neqo"
default-features = false
features = ["gecko"]

View File

@@ -170,7 +170,7 @@ impl 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 = if self.sessions_to_close.is_empty() {
@@ -662,7 +662,7 @@ impl ::std::fmt::Display 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)
}
@@ -898,7 +898,7 @@ impl 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);
#[cfg(not(target_os = "android"))]
@@ -1027,7 +1027,7 @@ impl ::std::fmt::Display 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
}

View File

@@ -4278,6 +4278,11 @@ who = "Kershaw Chang <kershaw@mozilla.com>"
criteria = "safe-to-deploy"
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]]
who = "Kershaw Chang <kershaw@mozilla.com>"
criteria = "safe-to-run"
@@ -4309,6 +4314,11 @@ who = "Max Leonard Inden <mail@max-inden.de>"
criteria = "safe-to-deploy"
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]]
who = "Nika Layzell <nika@thelayzells.com>"
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]
edition = "2021"
rust-version = "1.76.0"
rust-version = "1.82.0"
name = "neqo-bin"
version = "0.12.2"
version = "0.13.1"
authors = ["The Neqo Authors <necko@mozilla.com>"]
build = false
autolib = false
@@ -72,7 +72,7 @@ harness = false
required-features = ["bench"]
[dependencies.clap]
version = "4.4"
version = "4.5"
features = [
"std",
"help",
@@ -118,11 +118,11 @@ path = "./../neqo-transport"
path = "./../neqo-udp"
[dependencies.qlog]
version = "0.13"
version = "0.15.1"
default-features = false
[dependencies.quinn-udp]
version = "0.5.6"
version = "0.5.11"
features = [
"direct-log",
"fast-apple-datapath",
@@ -141,12 +141,11 @@ features = [
"time",
"macros",
"rt",
"rt-multi-thread",
]
default-features = false
[dependencies.url]
version = "2.5.3"
version = "2.5"
features = ["std"]
default-features = false
@@ -173,22 +172,56 @@ features = ["sync"]
default-features = false
[lints.clippy]
allow_attributes = "warn"
allow_attributes_without_reason = "warn"
cfg_not_test = "warn"
clone_on_ref_ptr = "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"
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_inherent_impl = "warn"
mutex_atomic = "warn"
mutex_integer = "warn"
needless_raw_strings = "warn"
pathbuf_init_then_push = "warn"
pub_without_shorthand = "warn"
rc_buffer = "warn"
rc_mutex = "warn"
redundant_type_annotations = "warn"
ref_patterns = "warn"
renamed_function_params = "warn"
rest_pat_in_fully_bound_structs = "warn"
self_named_module_files = "warn"
semicolon_inside_block = "warn"
string_lit_chars_any = "warn"
string_to_string = "warn"
suspicious_xor_used_as_pow = "warn"
try_err = "warn"
unnecessary_safety_comment = "warn"
unnecessary_safety_doc = "warn"
unnecessary_self_imports = "warn"
unneeded_field_pattern = "warn"
unused_result_ok = "warn"
unused_trait_names = "warn"
unwrap_in_result = "warn"
unwrap_used = "warn"
verbose_file_reads = "warn"
[lints.clippy.cargo]
level = "warn"
@@ -205,7 +238,6 @@ priority = -1
[lints.rust]
absolute_paths_not_starting_with_crate = "warn"
ambiguous_negative_literals = "warn"
closure_returning_async_block = "warn"
explicit_outlives_requirements = "warn"
macro_use_extern_crate = "warn"
missing_abi = "warn"
@@ -217,3 +249,4 @@ unit_bindings = "warn"
unused_import_braces = "warn"
unused_lifetimes = "warn"
unused_macro_rules = "warn"
unused_qualifications = "warn"

View File

@@ -4,11 +4,13 @@
// 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 std::{env, path::PathBuf, str::FromStr as _};
use criterion::{criterion_group, criterion_main, BatchSize, Criterion, Throughput};
use neqo_bin::{client, server};
use tokio::runtime::Runtime;
use tokio::runtime::Builder;
struct Benchmark {
name: String,
@@ -42,7 +44,7 @@ fn transfer(c: &mut Criterion) {
upload: false,
},
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],
upload: true,
},
@@ -55,13 +57,14 @@ fn transfer(c: &mut Criterion) {
Throughput::Elements(requests.len() as u64)
});
group.bench_function("client", |b| {
b.to_async(Runtime::new().unwrap()).iter_batched(
|| client::client(client::Args::new(&requests, upload)),
|client| async move {
client.await.unwrap();
},
BatchSize::PerIteration,
);
b.to_async(Builder::new_current_thread().enable_all().build().unwrap())
.iter_batched(
|| client::client(client::Args::new(&requests, upload)),
|client| async move {
client.await.unwrap();
},
BatchSize::PerIteration,
);
});
group.finish();
}
@@ -69,17 +72,25 @@ fn transfer(c: &mut Criterion) {
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<()> {
let (done_sender, mut done_receiver) = tokio::sync::oneshot::channel();
std::thread::spawn(move || {
Runtime::new().unwrap().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:?}"),
};
});
Builder::new_current_thread()
.enable_all()
.build()
.unwrap()
.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
}

View File

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

View File

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

View File

@@ -4,6 +4,8 @@
// option. This file may not be copied, modified, or distributed
// 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.
use std::{
@@ -159,7 +161,11 @@ pub fn create_client(
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)
}
@@ -189,7 +195,7 @@ impl super::Client for Connection {
fn process_multiple_input<'a>(
&mut self,
dgrams: impl IntoIterator<Item = Datagram<&'a [u8]>>,
dgrams: impl IntoIterator<Item = Datagram<&'a mut [u8]>>,
now: Instant,
) {
self.process_multiple_input(dgrams, now);
@@ -302,7 +308,7 @@ impl<'b> Handler<'b> {
} else {
qdebug!(
"READ[{stream_id}]: {}",
std::str::from_utf8(read_buffer).unwrap()
std::str::from_utf8(read_buffer).map_err(|_| Error::InvalidInput)?
);
}
if fin {

View File

@@ -4,6 +4,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![expect(clippy::unwrap_used, reason = "This is example code.")]
//! An HTTP 3 client implementation.
use std::{
@@ -18,7 +20,7 @@ use std::{
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_http3::{Error, Http3Client, Http3ClientEvent, Http3Parameters, Http3State, Priority};
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};
pub struct Handler<'a> {
#[allow(clippy::struct_field_names)]
#[expect(clippy::struct_field_names, reason = "This name is more descriptive.")]
url_handler: UrlHandler<'a>,
token: Option<ResumptionToken>,
output_read_data: bool,
@@ -137,7 +139,7 @@ impl super::Client for Http3Client {
fn process_multiple_input<'a>(
&mut self,
dgrams: impl IntoIterator<Item = Datagram<&'a [u8]>>,
dgrams: impl IntoIterator<Item = Datagram<&'a mut [u8]>>,
now: Instant,
) {
self.process_multiple_input(dgrams, now);
@@ -200,9 +202,11 @@ impl super::Handler for Handler<'_> {
qwarn!("Data on unexpected stream: {stream_id}");
}
Some(handler) => loop {
let (sz, fin) = client
.read_data(Instant::now(), stream_id, &mut self.read_buffer)
.expect("Read should succeed");
let (sz, fin) = client.read_data(
Instant::now(),
stream_id,
&mut self.read_buffer,
)?;
handler.process_data_readable(
stream_id,
@@ -270,7 +274,7 @@ trait StreamHandler {
fin: bool,
data: &[u8],
output_read_data: bool,
) -> Res<bool>;
) -> Res<()>;
fn process_data_writable(&mut self, client: &mut Http3Client, stream_id: StreamId);
}
@@ -291,12 +295,12 @@ impl StreamHandler for DownloadStreamHandler {
fin: bool,
data: &[u8],
output_read_data: bool,
) -> Res<bool> {
) -> Res<()> {
if let Some(out_file) = &mut self.out_file {
if !data.is_empty() {
out_file.write_all(data)?;
}
return Ok(true);
return Ok(());
} else if !output_read_data {
qdebug!("READ[{stream_id}]: {} bytes", data.len());
} 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) {}
@@ -335,18 +339,19 @@ impl StreamHandler for UploadStreamHandler {
_fin: bool,
data: &[u8],
_output_read_data: bool,
) -> Res<bool> {
) -> Res<()> {
if let Ok(txt) = std::str::from_utf8(data) {
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() {
let upload_time = Instant::now().duration_since(self.start);
qinfo!("Stream ID: {stream_id:?}, Upload time: {upload_time:?}");
}
Ok(())
} 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) {

View File

@@ -4,7 +4,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![allow(clippy::future_not_send)]
#![expect(clippy::unwrap_used, reason = "This is example code.")]
use std::{
collections::{HashMap, VecDeque},
@@ -104,7 +104,10 @@ type Res<T> = Result<T, Error>;
#[derive(Debug, Parser)]
#[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 {
#[command(flatten)]
shared: SharedArgs,
@@ -180,7 +183,7 @@ pub struct Args {
impl Args {
#[must_use]
#[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 {
use std::str::FromStr as _;
Self {
@@ -383,7 +386,7 @@ trait Client {
fn process_output(&mut self, now: Instant) -> Output;
fn process_multiple_input<'a>(
&mut self,
dgrams: impl IntoIterator<Item = Datagram<&'a [u8]>>,
dgrams: impl IntoIterator<Item = Datagram<&'a mut [u8]>>,
now: Instant,
);
fn has_events(&self) -> bool;
@@ -431,17 +434,14 @@ impl<'a, H: Handler> Runner<'a, H> {
continue;
}
#[allow(clippy::match_same_arms)]
match (handler_done, self.client.is_closed()?) {
// more work
(false, _) => {}
// more work; or no more work, already closing connection
(true, CloseState::Closing) | (false, _) => {}
// no more work, closing connection
(true, CloseState::NotClosing) => {
self.client.close(Instant::now(), 0, "kthxbye!");
continue;
}
// no more work, already closing connection
(true, CloseState::Closing) => {}
// no more work, connection closed, terminating
(true, CloseState::Closed) => break,
}
@@ -464,10 +464,19 @@ impl<'a, H: Handler> Runner<'a, H> {
async fn process_output(&mut self) -> Result<(), io::Error> {
loop {
match self.client.process_output(Instant::now()) {
Output::Datagram(dgram) => {
self.socket.writable().await?;
self.socket.send(&dgram)?;
}
Output::Datagram(dgram) => loop {
// Optimistically attempt sending datagram. In case the OS
// 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) => {
qdebug!("Setting timeout of {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<()> {
neqo_common::log::init(
args.shared

View File

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

View File

@@ -4,6 +4,8 @@
// option. This file may not be copied, modified, or distributed
// 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 neqo_common::{event::Provider as _, hex, qdebug, qerror, qinfo, qwarn, Datagram};
@@ -55,10 +57,10 @@ impl HttpServer {
server.set_validation(ValidateAddress::Always);
}
if args.ech {
let (sk, pk) = generate_ech_keys().expect("generate ECH keys");
let (sk, pk) = generate_ech_keys().map_err(|_| Error::Internal)?;
server
.enable_ech(random::<1>()[0], "public.example", &sk, &pk)
.expect("enable ECH");
.map_err(|_| Error::Internal)?;
let cfg = server.ech_config();
qinfo!("ECHConfigList: {}", hex(cfg));
}
@@ -70,9 +72,9 @@ impl HttpServer {
read_state: HashMap::new(),
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 {
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],
})
@@ -185,14 +187,15 @@ impl 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)
}
fn process_events(&mut self, now: Instant) {
// `ActiveConnectionRef` `Hash` implementation doesnt access any of the interior mutable
// types.
#[allow(clippy::mutable_key_type)]
#[expect(
clippy::mutable_key_type,
reason = "ActiveConnectionRef::Hash doesn't access any of the interior mutable types"
)]
let active_conns = self.server.active_connections();
for acr in active_conns {
loop {

View File

@@ -4,6 +4,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![expect(clippy::unwrap_used, reason = "This is example code.")]
use std::{
cell::RefCell,
collections::HashMap,
@@ -80,7 +82,7 @@ impl Display 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)
}

View File

@@ -4,7 +4,17 @@
// option. This file may not be copied, modified, or distributed
// 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::{
cell::RefCell,
@@ -190,14 +200,12 @@ fn qns_read_response(filename: &str) -> Result<Vec<u8>, io::Error> {
fs::read(path)
}
#[allow(clippy::module_name_repetitions)]
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 has_events(&self) -> bool;
}
#[allow(clippy::module_name_repetitions)]
pub struct ServerRunner {
now: Box<dyn Fn() -> Instant>,
server: Box<dyn HttpServer>,
@@ -242,14 +250,25 @@ impl ServerRunner {
timeout: &mut Option<Pin<Box<Sleep>>>,
sockets: &mut [(SocketAddr, crate::udp::Socket)],
now: &dyn Fn() -> Instant,
mut input_dgram: Option<Datagram<&[u8]>>,
mut input_dgram: Option<Datagram<&mut [u8]>>,
) -> Result<(), io::Error> {
loop {
match server.process(input_dgram.take(), now()) {
Output::Datagram(dgram) => {
let socket = Self::find_socket(sockets, dgram.source());
socket.writable().await?;
socket.send(&dgram)?;
loop {
// 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) => {
qdebug!("Setting timeout of {new_timeout:?}");

View File

@@ -4,9 +4,11 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![expect(clippy::missing_errors_doc, reason = "Passing up tokio errors.")]
use std::{io, net::SocketAddr};
use neqo_common::Datagram;
use neqo_common::{qdebug, Datagram};
use neqo_udp::{DatagramIter, RecvBuf};
/// Ideally this would live in [`neqo-udp`]. [`neqo-udp`] is used in Firefox.
@@ -24,10 +26,36 @@ pub struct Socket {
impl Socket {
/// 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> {
const ONE_MB: usize = 1 << 20;
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 {
state: quinn_udp::UdpSocketState::new((&socket).into())?,
state,
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]
edition = "2021"
rust-version = "1.76.0"
rust-version = "1.82.0"
name = "neqo-common"
version = "0.12.2"
version = "0.13.1"
authors = ["The Neqo Authors <necko@mozilla.com>"]
build = "build.rs"
autolib = false
@@ -44,7 +44,7 @@ bench = [
"neqo-crypto/bench",
"test-fixture/bench",
]
build-fuzzing-corpus = ["hex"]
build-fuzzing-corpus = ["hex/alloc"]
ci = []
test-fixture = []
@@ -73,7 +73,6 @@ default-features = false
[dependencies.hex]
version = "0.4"
features = ["alloc"]
optional = true
default-features = false
@@ -82,7 +81,12 @@ version = "0.4"
default-features = false
[dependencies.qlog]
version = "0.13"
version = "0.15.1"
default-features = false
[dependencies.strum]
version = "0.26"
features = ["derive"]
default-features = false
[dev-dependencies.criterion]
@@ -94,7 +98,6 @@ path = "../neqo-crypto"
[dev-dependencies.regex]
version = "1.9"
features = ["unicode-perl"]
default-features = false
[dev-dependencies.test-fixture]
@@ -106,22 +109,56 @@ features = ["Win32_Media"]
default-features = false
[lints.clippy]
allow_attributes = "warn"
allow_attributes_without_reason = "warn"
cfg_not_test = "warn"
clone_on_ref_ptr = "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"
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_inherent_impl = "warn"
mutex_atomic = "warn"
mutex_integer = "warn"
needless_raw_strings = "warn"
pathbuf_init_then_push = "warn"
pub_without_shorthand = "warn"
rc_buffer = "warn"
rc_mutex = "warn"
redundant_type_annotations = "warn"
ref_patterns = "warn"
renamed_function_params = "warn"
rest_pat_in_fully_bound_structs = "warn"
self_named_module_files = "warn"
semicolon_inside_block = "warn"
string_lit_chars_any = "warn"
string_to_string = "warn"
suspicious_xor_used_as_pow = "warn"
try_err = "warn"
unnecessary_safety_comment = "warn"
unnecessary_safety_doc = "warn"
unnecessary_self_imports = "warn"
unneeded_field_pattern = "warn"
unused_result_ok = "warn"
unused_trait_names = "warn"
unwrap_in_result = "warn"
unwrap_used = "warn"
verbose_file_reads = "warn"
[lints.clippy.cargo]
level = "warn"
@@ -138,7 +175,6 @@ priority = -1
[lints.rust]
absolute_paths_not_starting_with_crate = "warn"
ambiguous_negative_literals = "warn"
closure_returning_async_block = "warn"
explicit_outlives_requirements = "warn"
macro_use_extern_crate = "warn"
missing_abi = "warn"
@@ -150,3 +186,4 @@ unit_bindings = "warn"
unused_import_braces = "warn"
unused_lifetimes = "warn"
unused_macro_rules = "warn"
unused_qualifications = "warn"

View File

@@ -4,6 +4,8 @@
// 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::{black_box, criterion_group, criterion_main, Criterion};
use neqo_common::Decoder;
use neqo_crypto::{init, randomize};

View File

@@ -4,6 +4,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![expect(clippy::unwrap_used, reason = "OK in a build script.")]
use std::env;
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.
#[cfg(any(test, feature = "test-fixture"))]
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());
}
@@ -111,7 +112,7 @@ impl<'a> Decoder<'a> {
/// unsigned integer types: `u8`, `u16`, `u32`, or `u64`.
/// Signed types will fail if the high bit is set.
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())
}
@@ -161,7 +162,6 @@ impl<'a> Decoder<'a> {
// Implement `AsRef` for `Decoder` so that values can be examined without
// moving the cursor.
impl<'a> AsRef<[u8]> for Decoder<'a> {
#[must_use]
fn as_ref(&self) -> &'a [u8] {
&self.buf[self.offset..]
}
@@ -174,7 +174,6 @@ impl Debug for Decoder<'_> {
}
impl<'a> From<&'a [u8]> for Decoder<'a> {
#[must_use]
fn from(buf: &'a [u8]) -> Self {
Decoder::new(buf)
}
@@ -184,14 +183,12 @@ impl<'a, T> From<&'a T> for Decoder<'a>
where
T: AsRef<[u8]>,
{
#[must_use]
fn from(buf: &'a T) -> Self {
Decoder::new(buf.as_ref())
}
}
impl<'b> PartialEq<Decoder<'b>> for Decoder<'_> {
#[must_use]
fn eq(&self, other: &Decoder<'b>) -> bool {
self.buf == other.buf
}
@@ -227,7 +224,7 @@ impl Encoder {
/// When `len` doesn't fit in a `u64`.
#[must_use]
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.
@@ -286,6 +283,7 @@ impl Encoder {
let mut enc = Self::with_capacity(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();
enc.encode_byte(v);
}
@@ -341,8 +339,11 @@ impl Encoder {
///
/// When `v` is longer than 2^64.
pub fn encode_vec(&mut self, n: usize, v: &[u8]) -> &mut Self {
self.encode_uint(n, u64::try_from(v.as_ref().len()).unwrap())
.encode(v)
self.encode_uint(
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.
@@ -350,7 +351,10 @@ impl Encoder {
/// # Panics
///
/// 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 {
let start = self.buf.len();
self.buf.resize(self.buf.len() + n, 0);
@@ -369,7 +373,7 @@ impl Encoder {
///
/// When `v` is longer than 2^64.
pub fn encode_vvec(&mut self, v: &[u8]) -> &mut Self {
self.encode_varint(u64::try_from(v.as_ref().len()).unwrap())
self.encode_varint(u64::try_from(v.as_ref().len()).expect("v is longer than 2^64"))
.encode(v)
}
@@ -447,14 +451,12 @@ impl AsMut<[u8]> for Encoder {
}
impl<'a> From<Decoder<'a>> for Encoder {
#[must_use]
fn from(dec: Decoder<'a>) -> Self {
Self::from(&dec.buf[dec.offset..])
}
}
impl From<&[u8]> for Encoder {
#[must_use]
fn from(buf: &[u8]) -> Self {
Self {
buf: Vec::from(buf),
@@ -463,7 +465,6 @@ impl From<&[u8]> for Encoder {
}
impl From<Encoder> for Vec<u8> {
#[must_use]
fn from(buf: Encoder) -> Self {
buf.buf
}
@@ -664,6 +665,7 @@ mod tests {
}
#[test]
#[cfg(target_pointer_width = "64")] // Test does not compile on 32-bit targets.
#[should_panic(expected = "Varint value too large")]
fn encoded_vvec_length_oob() {
_ = Encoder::vvec_len(1 << 62);

View File

@@ -4,7 +4,10 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::{net::SocketAddr, ops::Deref};
use std::{
net::SocketAddr,
ops::{Deref, DerefMut},
};
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> {
fn as_mut(&mut self) -> &mut [u8] {
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> {
type Target = [u8];
#[must_use]
fn deref(&self) -> &Self::Target {
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]
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 }
}

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]
pub fn name(&self) -> &str {
&self.name
}
#[allow(
clippy::allow_attributes,
clippy::missing_const_for_fn,
reason = "TODO: False positive on nightly."
)]
#[must_use]
pub fn value(&self) -> &str {
&self.value

View File

@@ -70,7 +70,7 @@ impl PeriodSet {
fn min(&self) -> Option<Period> {
for (i, v) in self.counts.iter().enumerate() {
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
@@ -78,7 +78,7 @@ impl PeriodSet {
}
#[cfg(target_os = "macos")]
#[allow(non_camel_case_types)]
#[expect(non_camel_case_types, reason = "These are C types.")]
mod mac {
use std::ptr::addr_of_mut;
@@ -124,9 +124,9 @@ mod mac {
}
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 =
(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;
// 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.
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 {
period: base as u32, // Base interval
computation: (base * 0.5) as u32,
@@ -302,7 +306,10 @@ impl Time {
}
#[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) {}
#[cfg(windows)]
@@ -313,7 +320,10 @@ impl Time {
}
#[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) {}
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,
// 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 {
use std::{
thread::{sleep, spawn},

View File

@@ -31,7 +31,7 @@ impl IncrementalDecoderUint {
if amount < 8 {
self.v <<= amount * 8;
}
self.v |= dv.decode_n(amount).unwrap();
self.v |= dv.decode_n(amount)?;
*r -= amount;
(*r == 0).then_some(self.v)
} else {
@@ -89,7 +89,7 @@ impl IncrementalDecoderBuffer {
/// Never; but rust doesn't know that.
pub fn consume(&mut self, dv: &mut Decoder) -> Option<Vec<u8>> {
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.remaining -= amount;
(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
// except according to those terms.
#![allow(clippy::module_name_repetitions)] // This lint doesn't work here.
mod codec;
mod datagram;
pub mod event;
@@ -21,6 +19,7 @@ pub mod tos;
use std::fmt::Write as _;
use enum_map::Enum;
use strum::Display;
#[cfg(feature = "build-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]
}
#[derive(Debug, PartialEq, Eq, Copy, Clone, Enum)]
#[derive(Debug, PartialEq, Eq, Copy, Clone, Enum, Display)]
/// Client or Server.
pub enum Role {
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)]
pub enum MessageType {
Request,

View File

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

View File

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

View File

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

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

View File

@@ -4,6 +4,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![expect(clippy::unwrap_used, reason = " OK in a build script.")]
use std::{
collections::HashMap,
env, fs,
@@ -54,13 +56,6 @@ struct Bindings {
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.
// 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.
@@ -136,21 +131,19 @@ fn build_nss(dir: PathBuf, nsstarget: &str) {
}
fn dynamic_link() {
let libs = if env::consts::OS == "windows" {
&["nssutil3.dll", "nss3.dll", "ssl3.dll"]
let dynamic_libs = if env::consts::OS == "windows" {
[
"nssutil3.dll",
"nss3.dll",
"ssl3.dll",
"libplds4.dll",
"libplc4.dll",
"libnspr4.dll",
]
} else {
&["nssutil3", "nss3", "ssl3"]
["nssutil3", "nss3", "ssl3", "plds4", "plc4", "nspr4"]
};
dynamic_link_both(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) {
for lib in dynamic_libs {
println!("cargo:rustc-link-lib=dylib={lib}");
}
}
@@ -160,35 +153,71 @@ fn static_link() {
"certdb",
"certhi",
"cryptohi",
"freebl",
"freebl_static",
if env::consts::OS == "windows" {
"libnspr4"
} else {
"nspr4"
},
"nss_static",
"nssb",
"nssdev",
"nsspki",
"nssutil",
"pk11wrap",
"pkcs12",
"pkcs7",
"smime",
"pk11wrap_static",
if env::consts::OS == "windows" {
"libplc4"
} else {
"plc4"
},
if env::consts::OS == "windows" {
"libplds4"
} else {
"plds4"
},
"softokn_static",
"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");
}
// 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 {
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> {
@@ -336,7 +365,11 @@ fn setup_standalone(nss: &str) -> Vec<String> {
"cargo:rustc-link-search=native={}",
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();
} else {
dynamic_link();
@@ -352,7 +385,10 @@ fn setup_standalone(nss: &str) -> Vec<String> {
#[cfg(feature = "gecko")]
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 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");
println!("cargo:rerun-if-changed={}", flags_path.to_str().unwrap());
let mut flags = fs::read_to_string(flags_path)
.expect("Failed to read extra-bindgen-flags file")
.split_whitespace()
.map(String::from)
let mut flags = BINDGEN_SYSTEM_FLAGS
.iter()
.chain(&NSPR_CFLAGS)
.chain(&NSS_CFLAGS)
.map(|s| s.to_string())
.collect::<Vec<_>>();
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())?,
)
}?;
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.
///
/// 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
///
/// If the input isn't authenticated or any input is too large for NSS.
@@ -144,6 +175,9 @@ impl RealAead {
) -> Res<&'a [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,
@@ -156,7 +190,41 @@ impl RealAead {
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
// except according to those terms.
#![expect(clippy::missing_errors_doc, reason = "OK here.")]
use std::fmt;
use crate::{
@@ -17,7 +19,6 @@ pub const AEAD_NULL_TAG: &[u8] = &[0x0a; 16];
pub struct AeadNull {}
impl AeadNull {
#[allow(clippy::missing_errors_doc)]
pub const fn new(
_version: Version,
_cipher: Cipher,
@@ -32,7 +33,6 @@ impl AeadNull {
AEAD_NULL_TAG.len()
}
#[allow(clippy::missing_errors_doc)]
pub fn encrypt<'a>(
&self,
_count: u64,
@@ -42,23 +42,30 @@ impl AeadNull {
) -> Res<&'a [u8]> {
let l = input.len();
output[..l].copy_from_slice(input);
output[l..l + 16].copy_from_slice(AEAD_NULL_TAG);
Ok(&output[..l + 16])
output[l..l + self.expansion()].copy_from_slice(AEAD_NULL_TAG);
Ok(&output[..l + self.expansion()])
}
#[allow(clippy::missing_errors_doc)]
pub fn decrypt<'a>(
pub fn encrypt_in_place<'a>(
&self,
_count: u64,
_aad: &[u8],
input: &[u8],
output: &'a mut [u8],
) -> Res<&'a [u8]> {
if input.len() < AEAD_NULL_TAG.len() {
data: &'a mut [u8],
) -> Res<&'a mut [u8]> {
let pos = data.len() - self.expansion();
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));
}
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:
// 1) expansion is all zeros and
// 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
&& (len_encrypted == 0 || input[..len_encrypted].iter().any(|x| *x != 0x0))
{
output[..len_encrypted].copy_from_slice(&input[..len_encrypted]);
Ok(&output[..len_encrypted])
Ok(len_encrypted)
} else {
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 {

View File

@@ -4,6 +4,15 @@
// option. This file may not be copied, modified, or distributed
// 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::{
cell::RefCell,
ffi::{CStr, CString},
@@ -122,7 +131,11 @@ macro_rules! preinfo_arg {
pub fn $v(&self) -> Option<$t> {
match self.info.valuesSet & ssl::$m {
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(
fd,
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
}
/// # Panics
/// # Errors
///
/// If `usize` is less than 32 bits and the value is too large.
#[must_use]
pub fn max_early_data(&self) -> usize {
usize::try_from(self.info.maxEarlyDataSize).unwrap()
pub fn max_early_data(&self) -> Res<usize> {
Ok(usize::try_from(self.info.maxEarlyDataSize)?)
}
/// Was ECH accepted.
@@ -222,7 +234,7 @@ impl SecretAgentInfo {
ssl::SSL_GetChannelInfo(
fd,
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() };
@@ -273,7 +285,6 @@ impl SecretAgentInfo {
/// `SecretAgent` holds the common parts of client and server.
#[derive(Debug)]
#[allow(clippy::module_name_repetitions)]
pub struct SecretAgent {
fd: *mut ssl::PRFileDesc,
secrets: SecretHolder,
@@ -345,7 +356,6 @@ impl SecretAgent {
Ok(fd)
}
#[allow(clippy::missing_const_for_fn)]
unsafe extern "C" fn auth_complete_hook(
arg: *mut c_void,
_fd: *mut ssl::PRFileDesc,
@@ -544,9 +554,7 @@ impl SecretAgent {
// 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.
let (first, rest) = protocols
.split_first()
.expect("at least one ALPN value needed");
let (first, rest) = protocols.split_first().ok_or(Error::InternalError)?;
for v in rest {
add(v.as_ref());
}
@@ -585,14 +593,16 @@ impl SecretAgent {
// This function tracks whether handshake() or handshake_raw() was used
// and prevents the other from being used.
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.raw = Some(r);
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.
#[allow(
clippy::allow_attributes,
clippy::missing_const_for_fn,
reason = "TODO: False positive on nightly."
)]
#[must_use]
pub fn alert(&self) -> Option<&Alert> {
(*self.alert).as_ref()
@@ -751,12 +766,15 @@ impl SecretAgent {
/// # Panics
///
/// If setup fails.
#[allow(clippy::branches_sharing_code)]
pub fn close(&mut self) {
// It should be safe to close multiple times.
if self.fd.is_null() {
return;
}
#[expect(
clippy::branches_sharing_code,
reason = "Moving the PR_Close call after the conditional crashes things?!"
)]
if self.raw == Some(true) {
// Need to hold the record list in scope until the close is done.
let _records = self.setup_raw().expect("Can only close");
@@ -769,7 +787,7 @@ impl SecretAgent {
unsafe {
prio::PR_Close(self.fd.cast());
}
};
}
let _output = self.io.take_output();
self.fd = null_mut();
}
@@ -793,6 +811,11 @@ impl SecretAgent {
}
/// 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]
pub fn ech_config(&self) -> &[u8] {
&self.ech_config
@@ -840,13 +863,13 @@ impl ResumptionToken {
/// A TLS Client.
#[derive(Debug)]
#[allow(clippy::box_collection)] // We need the Box.
pub struct Client {
agent: SecretAgent,
/// The name of the server we're attempting a connection to.
server_name: String,
/// Records the resumption tokens we've received.
#[expect(clippy::box_collection, reason = "We need the Box.")]
resumption: Pin<Box<Vec<ResumptionToken>>>,
}
@@ -878,12 +901,10 @@ impl Client {
arg: *mut c_void,
) -> ssl::SECStatus {
let mut info: MaybeUninit<ssl::SSLResumptionTokenInfo> = MaybeUninit::uninit();
let info_res = &ssl::SSL_GetResumptionTokenInfo(
token,
len,
info.as_mut_ptr(),
c_uint::try_from(std::mem::size_of::<ssl::SSLResumptionTokenInfo>()).unwrap(),
);
let Ok(info_len) = c_uint::try_from(size_of::<ssl::SSLResumptionTokenInfo>()) else {
return ssl::SECFailure;
};
let info_res = &ssl::SSL_GetResumptionTokenInfo(token, len, info.as_mut_ptr(), info_len);
if info_res.is_err() {
// Ignore the token.
return ssl::SECSuccess;
@@ -893,8 +914,12 @@ impl Client {
// Ignore the token.
return ssl::SECSuccess;
}
let resumption = arg.cast::<Vec<ResumptionToken>>().as_mut().unwrap();
let len = usize::try_from(len).unwrap();
let Some(resumption) = arg.cast::<Vec<ResumptionToken>>().as_mut() else {
return ssl::SECFailure;
};
let Ok(len) = usize::try_from(len) else {
return ssl::SECFailure;
};
let mut v = Vec::with_capacity(len);
v.extend_from_slice(null_safe_slice(token, len));
qdebug!("[{fd:p}] Got resumption token {}", hex_snip_middle(&v));
@@ -908,6 +933,11 @@ impl Client {
ssl::SECSuccess
}
#[allow(
clippy::allow_attributes,
clippy::missing_const_for_fn,
reason = "TODO: False positive on nightly."
)]
#[must_use]
pub fn server_name(&self) -> &str {
&self.server_name
@@ -985,7 +1015,6 @@ impl Client {
impl Deref for Client {
type Target = SecretAgent;
#[must_use]
fn deref(&self) -> &SecretAgent {
&self.agent
}
@@ -1194,7 +1223,6 @@ impl Server {
impl Deref for Server {
type Target = SecretAgent;
#[must_use]
fn deref(&self) -> &SecretAgent {
&self.agent
}
@@ -1221,7 +1249,6 @@ pub enum Agent {
impl Deref for Agent {
type Target = SecretAgent;
#[must_use]
fn deref(&self) -> &SecretAgent {
match self {
Self::Client(c) => c,
@@ -1240,14 +1267,12 @@ impl DerefMut for Agent {
}
impl From<Client> for Agent {
#[must_use]
fn from(c: Client) -> Self {
Self::Client(c)
}
}
impl From<Server> for Agent {
#[must_use]
fn from(s: Server) -> Self {
Self::Server(s)
}

View File

@@ -4,6 +4,11 @@
// option. This file may not be copied, modified, or distributed
// 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::{
cmp::min,
fmt, mem,
@@ -95,10 +100,17 @@ impl RecordList {
len: c_uint,
arg: *mut c_void,
) -> 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);
records.append(epoch, ContentType::try_from(ct).unwrap(), slice);
records.append(epoch, ct, slice);
ssl::SECSuccess
}
@@ -114,7 +126,6 @@ impl RecordList {
impl Deref for RecordList {
type Target = Vec<Record>;
#[must_use]
fn deref(&self) -> &Vec<Record> {
&self.records
}
@@ -132,7 +143,6 @@ impl Iterator for RecordListIter {
impl IntoIterator for RecordList {
type Item = Record;
type IntoIter = RecordListIter;
#[must_use]
fn into_iter(self) -> Self::IntoIter {
RecordListIter(self.records.into_iter())
}
@@ -175,7 +185,10 @@ impl AgentIoInput {
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) };
qtrace!("[{self}] read {}", hex(src));
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())
}
#[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 {
let a = addr.as_mut().unwrap();
// Cast is safe because prio::PR_AF_INET is 2
let Some(a) = addr.as_mut() else {
return PR_FAILURE;
};
a.inet.family = prio::PR_AF_INET as prio::PRUint16;
a.inet.port = 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 {
let o = opt.as_mut().unwrap();
if o.option == prio::PRSockOption::PR_SockOpt_Nonblocking {
o.value.non_blocking = 1;
return PR_SUCCESS;
if let Some(o) = opt.as_mut() {
if o.option == prio::PRSockOption::PR_SockOpt_Nonblocking {
o.value.non_blocking = 1;
return PR_SUCCESS;
}
}
PR_FAILURE
}

View File

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

View File

@@ -4,30 +4,49 @@
// option. This file may not be copied, modified, or distributed
// 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
// for values outside of those that are defined here.
pub type Alert = u8;
pub type Epoch = u16;
// TLS doesn't really have an "initial" concept that maps to QUIC so directly,
// but this should be clear enough.
pub const TLS_EPOCH_INITIAL: Epoch = 0_u16;
pub const TLS_EPOCH_ZERO_RTT: Epoch = 1_u16;
pub const TLS_EPOCH_HANDSHAKE: Epoch = 2_u16;
// Also, we don't use TLS epochs > 3.
pub const TLS_EPOCH_APPLICATION_DATA: Epoch = 3_u16;
#[derive(Default, Debug, Enum, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, FromRepr)]
#[repr(u16)]
pub enum Epoch {
// TLS doesn't really have an "initial" concept that maps to QUIC so directly,
// but this should be clear enough.
#[default]
Initial = 0,
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,
/// use this macro.
macro_rules! remap_enum {
{ $t:ident: $s:ty { $( $n:ident = $v:path ),+ $(,)? } } => {
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 ),+ $(,)? } } => {
remap_enum!{ $t: $s { $( $n = $e::$v ),+ } }
@@ -44,6 +63,7 @@ remap_enum! {
}
}
#[expect(dead_code, reason = "Code is bindgen-generated.")]
mod ciphers {
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);
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.
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
// except according to those terms.
#![allow(dead_code)]
use std::{os::raw::c_char, str::Utf8Error};
use crate::ssl::{SECStatus, SECSuccess};
include!(concat!(env!("OUT_DIR"), "/nspr_error.rs"));
#[expect(non_snake_case, dead_code, reason = "Code is bindgen-generated.")]
mod codes {
#![allow(non_snake_case)]
include!(concat!(env!("OUT_DIR"), "/nss_secerr.rs"));
include!(concat!(env!("OUT_DIR"), "/nss_sslerr.rs"));
}
pub use codes::{SECErrorCodes as sec, SSLErrorCodes as ssl};
#[expect(dead_code, reason = "Code is bindgen-generated.")]
pub mod nspr {
include!(concat!(env!("OUT_DIR"), "/nspr_err.rs"));
}
#[expect(dead_code, reason = "Some constants are not used.")]
pub mod mozpkix {
// These are manually extracted from the many bindings generated
// by bindgen when provided with the simple header:
// #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 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;
@@ -83,11 +83,9 @@ impl Error {
}
impl std::error::Error for Error {
#[must_use]
fn cause(&self) -> Option<&dyn std::error::Error> {
None
}
#[must_use]
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
None
}
@@ -100,13 +98,11 @@ impl std::fmt::Display for Error {
}
impl From<std::num::TryFromIntError> for Error {
#[must_use]
fn from(_: std::num::TryFromIntError) -> Self {
Self::IntegerOverflow
}
}
impl From<std::ffi::NulError> for Error {
#[must_use]
fn from(_: std::ffi::NulError) -> Self {
Self::InternalError
}

View File

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

View File

@@ -4,6 +4,11 @@
// option. This file may not be copied, modified, or distributed
// 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::{
cell::RefCell,
os::raw::{c_uint, c_void},
@@ -75,7 +80,6 @@ impl ExtensionTracker {
f(&mut *rc.borrow_mut())
}
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
unsafe extern "C" fn extension_writer(
_fd: *mut PRFileDesc,
message: SSLHandshakeType::Type,
@@ -86,7 +90,12 @@ impl ExtensionTracker {
) -> PRBool {
let d = std::slice::from_raw_parts_mut(data, max_len as usize);
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) {
ExtensionWriterResult::Write(sz) => {
*len = c_uint::try_from(sz).expect("integer overflow from extension writer");
@@ -106,9 +115,14 @@ impl ExtensionTracker {
arg: *mut c_void,
) -> SECStatus {
let d = null_safe_slice(data, len);
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
Self::wrap_handler_call(arg, |handler| {
// Cast is safe here because the message type is always part of the enum
#[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) {
ExtensionHandlerResult::Ok => SECSuccess,
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),
PK11Origin::PK11_OriginUnwrap,
CK_ATTRIBUTE_TYPE::from(CKA_DERIVE),
&mut Item::wrap(buf),
&mut Item::wrap(buf)?,
null_mut(),
)
};

View File

@@ -4,6 +4,11 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![allow(
clippy::module_name_repetitions,
reason = "<https://github.com/mozilla/neqo/issues/2284#issuecomment-2782711813>"
)]
use std::{
cell::RefCell,
fmt::{self, Debug},
@@ -106,7 +111,7 @@ impl HpKey {
mech,
CK_ATTRIBUTE_TYPE::from(CKA_ENCRYPT),
*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))?;
@@ -169,7 +174,7 @@ impl HpKey {
ulNonceBits: 96,
};
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 {
PK11_Encrypt(
**key,

View File

@@ -4,9 +4,6 @@
// option. This file may not be copied, modified, or distributed
// 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;
#[cfg(feature = "disable-encryption")]
pub mod aead_null;
@@ -31,7 +28,7 @@ pub mod selfencrypt;
mod ssl;
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"))]
pub use self::aead::RealAead as Aead;
@@ -63,7 +60,7 @@ mod min_version;
use min_version::MINIMUM_NSS_VERSION;
use neqo_common::qerror;
#[allow(non_upper_case_globals)]
#[expect(non_upper_case_globals, reason = "Code is bindgen-generated.")]
mod nss {
include!(concat!(env!("OUT_DIR"), "/nss_init.rs"));
}
@@ -170,6 +167,9 @@ pub fn init() -> Res<()> {
///
/// If NSS cannot be initialized.
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())));
res.as_ref().map(|_| ()).map_err(Clone::clone)
}
@@ -201,7 +201,7 @@ where
if data.is_null() || len == 0 {
&[]
} else {
#[allow(clippy::disallowed_methods)]
#[expect(clippy::disallowed_methods, reason = "This is non-null.")]
std::slice::from_raw_parts(data, len)
}
}

View File

@@ -4,11 +4,6 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![allow(dead_code)]
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
use std::{
cell::RefCell,
ops::{Deref, DerefMut},
@@ -24,7 +19,14 @@ use crate::{
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 {
include!(concat!(env!("OUT_DIR"), "/nss_p11.rs"));
}
@@ -44,6 +46,7 @@ macro_rules! scoped_ptr {
/// # Errors
///
/// 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> {
if ptr.is_null() {
Err($crate::err::Error::last_nss_error())
@@ -55,7 +58,6 @@ macro_rules! scoped_ptr {
impl Deref for $scoped {
type Target = *mut $target;
#[must_use]
fn deref(&self) -> &*mut $target {
&self.ptr
}
@@ -105,7 +107,6 @@ impl PublicKey {
}
impl Clone for PublicKey {
#[must_use]
fn clone(&self) -> Self {
let ptr = unsafe { SECKEY_CopyPublicKey(self.ptr) };
assert!(!ptr.is_null());
@@ -160,7 +161,6 @@ impl PrivateKey {
unsafe impl Send for PrivateKey {}
impl Clone for PrivateKey {
#[must_use]
fn clone(&self) -> Self {
let ptr = unsafe { SECKEY_CopyPrivateKey(self.ptr) };
assert!(!ptr.is_null());
@@ -208,7 +208,6 @@ impl SymKey {
}
impl Clone for SymKey {
#[must_use]
fn clone(&self) -> Self {
let ptr = unsafe { PK11_ReferenceSymKey(self.ptr) };
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) {
PK11_DestroyContext(ctxt, PRBool::from(true));
}
@@ -247,25 +252,25 @@ impl Item {
/// 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,
/// or those that treat their argument as `const`.
pub fn wrap(buf: &[u8]) -> SECItem {
SECItem {
pub fn wrap(buf: &[u8]) -> Res<SECItem> {
Ok(SECItem {
type_: SECItemType::siBuffer,
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.
/// 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,
/// 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;
SECItem {
Ok(SECItem {
type_: SECItemType::siBuffer,
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.
@@ -276,20 +281,6 @@ impl Item {
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) {
@@ -348,8 +339,8 @@ pub fn randomize<B: AsMut<[u8]>>(mut buf: B) -> B {
#[cfg(not(feature = "disable-random"))]
pub fn randomize<B: AsMut<[u8]>>(mut buf: B) -> B {
let m_buf = buf.as_mut();
let len = std::os::raw::c_int::try_from(m_buf.len()).unwrap();
secstatus_to_res(unsafe { PK11_GenerateRandom(m_buf.as_mut_ptr(), 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) }).expect("NSS failed");
buf
}

View File

@@ -9,7 +9,9 @@
non_upper_case_globals,
non_snake_case,
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"));

View File

@@ -44,7 +44,6 @@ scoped_ptr!(
/// 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
/// managed by tuning the parameters used to create the context.
#[allow(clippy::module_name_repetitions)]
pub struct AntiReplay {
ctx: AntiReplayContext,
}

View File

@@ -4,9 +4,11 @@
// option. This file may not be copied, modified, or distributed
// 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 strum::FromRepr;
use crate::{
agentio::as_c_void,
@@ -22,44 +24,37 @@ experimental_api!(SSL_SecretCallback(
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 {
Read,
Write,
Read = SSLSecretDirection::ssl_secret_read,
Write = SSLSecretDirection::ssl_secret_write,
}
impl From<SSLSecretDirection::Type> for SecretDirection {
#[must_use]
fn from(dir: SSLSecretDirection::Type) -> Self {
match dir {
SSLSecretDirection::ssl_secret_read => Self::Read,
SSLSecretDirection::ssl_secret_write => Self::Write,
_ => unreachable!(),
}
Self::from_repr(dir).expect("Invalid secret direction")
}
}
#[derive(Debug, Default)]
#[allow(clippy::module_name_repetitions)]
pub struct DirectionalSecrets {
// We only need to maintain 3 secrets for the epochs used during the handshake.
secrets: [Option<SymKey>; 3],
secrets: EnumMap<Epoch, SymKey>,
}
impl DirectionalSecrets {
fn put(&mut self, epoch: Epoch, key: SymKey) {
assert!(epoch > 0);
let i = (epoch - 1) as usize;
assert!(i < self.secrets.len());
// assert!(self.secrets[i].is_none());
self.secrets[i] = Some(key);
debug_assert!(epoch != Epoch::Initial);
self.secrets[epoch] = key;
}
pub fn take(&mut self, epoch: Epoch) -> Option<SymKey> {
assert!(epoch > 0);
let i = (epoch - 1) as usize;
assert!(i < self.secrets.len());
self.secrets[i].take()
if self.secrets[epoch].is_null() {
None
} else {
Some(mem::take(&mut self.secrets[epoch]))
}
}
}
@@ -77,7 +72,15 @@ impl Secrets {
secret: *mut PK11SymKey,
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);
}

View File

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

View File

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

View File

@@ -4,6 +4,11 @@
// option. This file may not be copied, modified, or distributed
// 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::{
ops::Deref,
os::raw::c_void,
@@ -139,7 +144,6 @@ impl TryInto<PRTime> for Time {
}
impl From<Time> for Instant {
#[must_use]
fn from(t: Time) -> Self {
t.t
}

View File

@@ -78,7 +78,7 @@ fn check_client_preinfo(client_preinfo: &SecretAgentPreInfo) {
assert_eq!(client_preinfo.cipher_suite(), None);
assert!(!client_preinfo.early_data());
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);
}
@@ -87,7 +87,7 @@ fn check_server_preinfo(server_preinfo: &SecretAgentPreInfo) {
assert_eq!(server_preinfo.cipher_suite(), Some(TLS_AES_128_GCM_SHA256));
assert!(!server_preinfo.early_data());
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);
}

View File

@@ -4,7 +4,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![allow(dead_code)]
#![expect(clippy::unwrap_used, reason = "OK for tests.")]
use std::{mem, time::Instant};
@@ -16,8 +16,16 @@ use neqo_crypto::{
use test_fixture::{anti_replay, fixture_init, now};
/// Consume records until the handshake state changes.
#[allow(clippy::missing_panics_doc)]
#[allow(clippy::missing_errors_doc)]
#[allow(
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(
now: Instant,
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) {
handshake(now, client, server);
qinfo!("client: {:?}", client.state());
@@ -78,13 +90,19 @@ pub fn connect(client: &mut SecretAgent, server: &mut SecretAgent) {
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) {
handshake(now(), client, server);
assert!(!client.state().is_connected());
assert!(!server.state().is_connected());
}
#[allow(clippy::allow_attributes, dead_code, reason = "False positive.")]
#[derive(Clone, Copy, Debug)]
pub enum Resumption {
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> {
matches!(mode, Resumption::WithZeroRtt).then(|| {
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]
pub fn resumption_setup(mode: Resumption) -> (Option<AntiReplay>, ResumptionToken) {
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");
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 mask = hp2.mask(&[0; 16]).expect("clone produces 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
// 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 {
include!(concat!(env!("OUT_DIR"), "/nss_init.rs"));
}

View File

@@ -5,6 +5,7 @@
// except according to those terms.
#![cfg(not(feature = "disable-encryption"))]
#![cfg(test)]
use neqo_crypto::{
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]
edition = "2021"
rust-version = "1.76.0"
rust-version = "1.82.0"
name = "neqo-http3"
version = "0.12.2"
version = "0.13.1"
authors = ["The Neqo Authors <necko@mozilla.com>"]
build = false
autolib = false
@@ -76,6 +76,12 @@ path = "tests/send_message.rs"
name = "webtransport"
path = "tests/webtransport.rs"
[[bench]]
name = "streams"
path = "benches/streams.rs"
harness = false
required-features = ["bench"]
[dependencies.enumset]
version = "1.1"
default-features = false
@@ -97,18 +103,27 @@ path = "./../neqo-qpack"
path = "./../neqo-transport"
[dependencies.qlog]
version = "0.13"
version = "0.15.1"
default-features = false
[dependencies.sfv]
version = "0.9"
default-features = false
[dependencies.strum]
version = "0.26"
features = ["derive"]
default-features = false
[dependencies.url]
version = "2.5.3"
version = "2.5"
features = ["std"]
default-features = false
[dev-dependencies.criterion]
version = "0.5"
default-features = false
[dev-dependencies.neqo-http3]
path = "."
features = ["draft-29"]
@@ -121,22 +136,56 @@ features = ["draft-29"]
path = "../test-fixture"
[lints.clippy]
allow_attributes = "warn"
allow_attributes_without_reason = "warn"
cfg_not_test = "warn"
clone_on_ref_ptr = "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"
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_inherent_impl = "warn"
mutex_atomic = "warn"
mutex_integer = "warn"
needless_raw_strings = "warn"
pathbuf_init_then_push = "warn"
pub_without_shorthand = "warn"
rc_buffer = "warn"
rc_mutex = "warn"
redundant_type_annotations = "warn"
ref_patterns = "warn"
renamed_function_params = "warn"
rest_pat_in_fully_bound_structs = "warn"
self_named_module_files = "warn"
semicolon_inside_block = "warn"
string_lit_chars_any = "warn"
string_to_string = "warn"
suspicious_xor_used_as_pow = "warn"
try_err = "warn"
unnecessary_safety_comment = "warn"
unnecessary_safety_doc = "warn"
unnecessary_self_imports = "warn"
unneeded_field_pattern = "warn"
unused_result_ok = "warn"
unused_trait_names = "warn"
unwrap_in_result = "warn"
unwrap_used = "warn"
verbose_file_reads = "warn"
[lints.clippy.cargo]
level = "warn"
@@ -153,7 +202,6 @@ priority = -1
[lints.rust]
absolute_paths_not_starting_with_crate = "warn"
ambiguous_negative_literals = "warn"
closure_returning_async_block = "warn"
explicit_outlives_requirements = "warn"
macro_use_extern_crate = "warn"
missing_abi = "warn"
@@ -165,3 +213,4 @@ unit_bindings = "warn"
unused_import_braces = "warn"
unused_lifetimes = "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};
#[derive(Debug, PartialEq, Eq)]
#[derive(Debug, PartialEq, Eq, Default)]
pub enum BufferedStream {
#[default]
Uninitialized,
Initialized { stream_id: StreamId, buf: Vec<u8> },
}
impl Default for BufferedStream {
fn default() -> Self {
Self::Uninitialized
}
Initialized {
stream_id: StreamId,
buf: Vec<u8>,
},
}
impl ::std::fmt::Display for BufferedStream {

View File

@@ -4,8 +4,6 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![allow(clippy::module_name_repetitions)]
use std::{cell::RefCell, collections::VecDeque, rc::Rc};
use neqo_common::{event::Provider as EventProvider, Header};
@@ -16,7 +14,8 @@ use crate::{
connection::Http3State,
features::extended_connect::{ExtendedConnectEvents, ExtendedConnectType, SessionCloseReason},
settings::HSettingType,
CloseType, Http3StreamInfo, HttpRecvStreamEvents, PushId, RecvStreamEvents, SendStreamEvents,
CloseType, Error, Http3StreamInfo, HttpRecvStreamEvents, PushId, RecvStreamEvents, Res,
SendStreamEvents,
};
#[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(
WebTransportEvent::NewStream {
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>) {

View File

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

View File

@@ -785,7 +785,8 @@ impl Http3Client {
/// This cannot panic. The max varint length is 8.
pub fn webtransport_max_datagram_size(&self, session_id: StreamId) -> Res<u64> {
Ok(self.conn.max_datagram_size()?
- u64::try_from(Encoder::varint_len(session_id.as_u64())).unwrap())
- u64::try_from(Encoder::varint_len(session_id.as_u64()))
.map_err(|_| Error::Internal)?)
}
/// Sets the `SendOrder` for a given stream
@@ -841,7 +842,11 @@ impl Http3Client {
}
/// 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");
if let Some(d) = dgram {
self.process_input(d, now);
@@ -859,13 +864,13 @@ impl Http3Client {
/// packets need to be sent or if a timer needs to be updated.
///
/// [1]: ../neqo_transport/enum.ConnectionEvent.html
pub fn process_input(&mut self, dgram: Datagram<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);
}
pub fn process_multiple_input(
&mut self,
dgrams: impl IntoIterator<Item = Datagram<impl AsRef<[u8]>>>,
dgrams: impl IntoIterator<Item = Datagram<impl AsRef<[u8]> + AsMut<[u8]>>>,
now: Instant,
) {
let mut dgrams = dgrams.into_iter().peekable();
@@ -1279,7 +1284,7 @@ mod tests {
use neqo_qpack::{encoder::QPackEncoder, QpackSettings};
use neqo_transport::{
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::{
anti_replay, default_server_h3, fixture_init, new_server, now,
@@ -1304,7 +1309,7 @@ mod tests {
assert_eq!(err, CloseReason::Application(expected.code()));
}
_ => panic!("Wrong state {:?}", client.state()),
};
}
}
/// Create a http3 client with default configuration.
@@ -1625,12 +1630,16 @@ mod tests {
fn handshake_only(client: &mut Http3Client, server: &mut TestServer) -> Output {
assert_eq!(client.state(), Http3State::Initializing);
let out = client.process_output(now());
let out2 = client.process_output(now());
assert_eq!(client.state(), Http3State::Initializing);
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);
let out = client.process(out.dgram(), now());
let out = server.conn.process(out.dgram(), now());
let out = client.process(out.dgram(), now());
let out = server.conn.process(out.dgram(), now());
assert!(out.as_dgram_ref().is_none());
@@ -2730,7 +2739,7 @@ mod tests {
if let ConnectionEvent::RecvStreamReadable { stream_id } = e {
if stream_id == request_stream_id {
// 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();
assert!(fin);
assert_eq!(
@@ -2803,7 +2812,7 @@ mod tests {
assert_eq!(sent, Ok(first_frame.len()));
// 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()));
// Close stream.
@@ -2812,7 +2821,7 @@ mod tests {
let mut out = client.process_output(now());
// We need to loop a bit until all data has been sent. Once for every 1K
// of data.
for _i in 0..SEND_BUFFER_SIZE / 1000 {
for _i in 0..INITIAL_RECV_WINDOW_SIZE / 1000 {
out = server.conn.process(out.dgram(), now());
out = client.process(out.dgram(), now());
}
@@ -2822,7 +2831,7 @@ mod tests {
if let ConnectionEvent::RecvStreamReadable { stream_id } = e {
if stream_id == request_stream_id {
// 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();
assert!(fin);
assert_eq!(
@@ -2875,7 +2884,7 @@ mod tests {
// After the first frame there is exactly 63+2 bytes left in the send buffer.
#[test]
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]);
}
@@ -2884,7 +2893,7 @@ mod tests {
// but we can only send 63 bytes.
#[test]
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]);
}
@@ -2893,7 +2902,7 @@ mod tests {
// but we can only send 64 bytes.
#[test]
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]);
}
@@ -2901,7 +2910,7 @@ mod tests {
// After the first frame there is exactly 16383+3 bytes left in the send buffer.
#[test]
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]);
}
@@ -2910,7 +2919,7 @@ mod tests {
// send 16383 bytes.
#[test]
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]);
}
@@ -2919,7 +2928,7 @@ mod tests {
// send 16383 bytes.
#[test]
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]);
}
@@ -2928,7 +2937,7 @@ mod tests {
// 16384 bytes.
#[test]
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]);
}
@@ -3675,7 +3684,7 @@ mod tests {
panic!("We should not receive a DataGeadable event!");
}
_ => {}
};
}
}
// ok NOW send fin
@@ -3699,7 +3708,7 @@ mod tests {
assert!(fin);
}
_ => {}
};
}
}
// 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));
}
_ => {}
};
}
}
// Stream should now be closed and gone
@@ -3800,7 +3809,7 @@ mod tests {
panic!("We should not receive a DataGeadable event!");
}
_ => {}
};
}
}
// ok NOW send fin
@@ -3824,7 +3833,7 @@ mod tests {
assert!(fin);
}
_ => {}
};
}
}
// Stream should now be closed and gone
@@ -3871,7 +3880,7 @@ mod tests {
assert!(!fin);
}
_ => {}
};
}
}
// ok NOW send fin
@@ -4158,10 +4167,12 @@ mod tests {
let (mut client, mut server) = start_with_0rtt();
let out = client.process_output(now());
let out2 = client.process_output(now());
assert_eq!(client.state(), Http3State::ZeroRtt);
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
// SETTINGS frame has been received.
@@ -4175,6 +4186,8 @@ mod tests {
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());
assert_eq!(client.state(), Http3State::Connected);
drop(server.conn.process(out.dgram(), now()));
@@ -4193,10 +4206,12 @@ mod tests {
assert_eq!(request_stream_id, 0);
let out = client.process_output(now());
let out2 = client.process_output(now());
assert_eq!(client.state(), Http3State::ZeroRtt);
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
// SETTINGS frame has been received.
@@ -4210,6 +4225,8 @@ mod tests {
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());
assert_eq!(client.state(), Http3State::Connected);
let out = server.conn.process(out.dgram(), now());
assert!(server.conn.state().connected());
@@ -4280,17 +4297,19 @@ mod tests {
let client_0rtt = client.process_output(now());
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...
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.
let recvd_stream_evt = |e| matches!(e, ConnectionEvent::NewStream { .. });
assert!(!server.events().any(recvd_stream_evt));
// Client should get a rejection.
let client_out = client.process(server_hs.dgram(), now());
let client_out = client.process(dgram.dgram(), now());
assert!(client_out.as_dgram_ref().is_some());
let recvd_0rtt_reject = |e| e == Http3ClientEvent::ZeroRttRejected;
assert!(client.events().any(recvd_0rtt_reject));
@@ -4329,10 +4348,12 @@ mod tests {
.expect("Set resumption token");
assert_eq!(client.state(), Http3State::ZeroRtt);
let out = client.process_output(now());
let out2 = client.process_output(now());
assert_eq!(client.state(), Http3State::ZeroRtt);
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.
// 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);
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);
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_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;
@@ -73,14 +73,14 @@ impl ControlStreamLocal {
update_stream.stream_type(),
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
if let Some(hframe) = stream.priority_update_frame() {
let mut enc = Encoder::new();
hframe.encode(&mut enc);
if self.stream.send_atomic(conn, enc.as_ref())? {
stream.priority_update_sent();
stream.priority_update_sent()?;
} else {
self.outstanding_priority_update.push_front(update_id);
break;
@@ -95,7 +95,7 @@ impl ControlStreamLocal {
qtrace!("[{self}] Create a control stream");
self.stream.init(conn.stream_create(StreamType::UniDi)?);
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(())
}

View File

@@ -17,7 +17,7 @@ use crate::{
client_events::Http3ClientEvents,
features::NegotiationState,
settings::{HSettingType, HSettings},
CloseType, Http3StreamInfo, Http3StreamType,
CloseType, Http3StreamInfo, Http3StreamType, Res,
};
#[derive(Debug, PartialEq, Eq, Clone)]
@@ -56,7 +56,7 @@ pub(crate) trait ExtendedConnectEvents: Debug {
reason: SessionCloseReason,
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>);
}
@@ -67,12 +67,18 @@ pub(crate) enum ExtendedConnectType {
impl ExtendedConnectType {
#[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 {
"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]
pub const fn get_stream_type(self, session_id: StreamId) -> Http3StreamType {
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) {
assert_eq!(client.state(), Http3State::Initializing);
let out = client.process_output(now());
let out2 = client.process_output(now());
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 = client.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()));
}
_ => 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
/// the http specific functions and `http_stream()` will never return `None`.
#[must_use]
pub fn new_with_http_streams(
session_id: StreamId,
events: Box<dyn ExtendedConnectEvents>,
role: Role,
mut control_stream_recv: Box<dyn RecvStream>,
mut control_stream_send: Box<dyn SendStream>,
) -> Self {
) -> Res<Self> {
let stream_event_listener = Rc::new(RefCell::new(WebTransportSessionListener::default()));
control_stream_recv
.http_stream()
.unwrap()
.ok_or(Error::Internal)?
.set_new_listener(Box::new(Rc::clone(&stream_event_listener)));
control_stream_send
.http_stream()
.unwrap()
.ok_or(Error::Internal)?
.set_new_listener(Box::new(Rc::clone(&stream_event_listener)));
Self {
Ok(Self {
control_stream_recv,
control_stream_send,
stream_event_listener,
@@ -127,7 +126,7 @@ impl WebTransportSession {
send_streams: BTreeSet::new(),
recv_streams: BTreeSet::new(),
role,
}
})
}
/// # Errors
@@ -141,7 +140,7 @@ impl WebTransportSession {
pub fn send_request(&mut self, headers: &[Header], conn: &mut Connection) -> Res<()> {
self.control_stream_send
.http_stream()
.unwrap()
.ok_or(Error::Internal)?
.send_headers(headers, conn)
}
@@ -149,7 +148,7 @@ impl WebTransportSession {
qtrace!("[{self}] receive control data");
let (out, _) = self.control_stream_recv.receive(conn)?;
debug_assert!(out == ReceiveOutput::NoOutput);
self.maybe_check_headers();
self.maybe_check_headers()?;
self.read_control_stream(conn)?;
Ok((ReceiveOutput::NoOutput, self.state == SessionState::Done))
}
@@ -158,33 +157,32 @@ impl WebTransportSession {
let (out, _) = self
.control_stream_recv
.http_stream()
.unwrap()
.ok_or(Error::Internal)?
.header_unblocked(conn)?;
debug_assert!(out == ReceiveOutput::NoOutput);
self.maybe_check_headers();
self.maybe_check_headers()?;
self.read_control_stream(conn)?;
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
.http_stream()
.unwrap()
.ok_or(Error::Internal)?
.maybe_update_priority(priority)
}
fn priority_update_frame(&mut self) -> Option<HFrame> {
self.control_stream_recv
.http_stream()
.unwrap()
.http_stream()?
.priority_update_frame()
}
fn priority_update_sent(&mut self) {
fn priority_update_sent(&mut self) -> Res<()> {
self.control_stream_recv
.http_stream()
.unwrap()
.priority_update_sent();
.ok_or(Error::Internal)?
.priority_update_sent()
}
fn send(&mut self, conn: &mut Connection) -> Res<()> {
@@ -222,9 +220,9 @@ impl WebTransportSession {
/// # Panics
///
/// 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 {
return;
return Ok(());
}
if let Some((headers, interim, fin)) = self.stream_event_listener.borrow_mut().get_headers()
@@ -254,7 +252,7 @@ impl WebTransportSession {
None
}
})
.unwrap();
.ok_or(Error::Internal)?;
self.state = if (200..300).contains(&status) {
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 stream_id.is_bidi() {
self.send_streams.insert(stream_id);
@@ -306,9 +305,10 @@ impl WebTransportSession {
.extended_connect_new_stream(Http3StreamInfo::new(
stream_id,
ExtendedConnectType::WebTransport.get_stream_type(self.session_id),
));
))?;
}
}
Ok(())
}
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)
}
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)
}
@@ -464,8 +464,8 @@ impl HttpRecvStream for Rc<RefCell<WebTransportSession>> {
self.borrow_mut().priority_update_frame()
}
fn priority_update_sent(&mut self) {
self.borrow_mut().priority_update_sent();
fn priority_update_sent(&mut self) -> Res<()> {
self.borrow_mut().priority_update_sent()
}
}

View File

@@ -87,7 +87,7 @@ impl HFrame {
Self::PriorityUpdateRequest { .. } => H3_FRAME_TYPE_PRIORITY_UPDATE_REQUEST,
Self::PriorityUpdatePush { .. } => H3_FRAME_TYPE_PRIORITY_UPDATE_PUSH,
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.
HFrameType((r >> 7) * 0x1f + 0x21)
}

View File

@@ -8,7 +8,11 @@ pub mod hframe;
pub mod reader;
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 reader::{FrameReader, StreamReaderConnectionWrapper, StreamReaderRecvStreamWrapper};
pub use wtframe::WebTransportFrame;

View File

@@ -4,8 +4,10 @@
// option. This file may not be copied, modified, or distributed
// 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 neqo_common::{
@@ -93,7 +95,6 @@ enum FrameReaderState {
UnknownFrameDischargeData { decoder: IncrementalDecoderIgnore },
}
#[allow(clippy::module_name_repetitions)]
#[derive(Debug)]
pub struct FrameReader {
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_s = default_server();
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_c.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> {
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());
drop(self.conn_c.process(out.dgram(), now()));
let (frame, fin) = self
@@ -47,7 +47,7 @@ impl FrameReaderTest {
&mut self.conn_c,
self.stream_id,
))
.unwrap();
.ok()?;
assert!(!fin);
frame
}
@@ -264,7 +264,7 @@ fn test_reading_frame<T: FrameDecoder<T> + PartialEq + Debug>(
assert!(fin);
assert!(f.is_none());
}
};
}
}
#[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.
pub fn is_interim(headers: &[Header]) -> Res<bool> {
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)?;
if status_code == 101 {
// 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.
AlreadyClosed,
AlreadyInitialized,
DecodingFrame,
FatalError,
HttpGoaway,
Internal,
@@ -333,7 +332,7 @@ impl Error {
_ => {
debug_assert!(false, "Unexpected error");
}
};
}
Self::TransportStreamDoesNotExist
}
@@ -495,9 +494,9 @@ trait HttpRecvStream: RecvStream {
/// 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 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_sent(&mut self);
fn priority_update_sent(&mut self) -> Res<()>;
fn set_new_listener(&mut self, _conn_events: Box<dyn HttpRecvStreamEvents>) {}
fn extended_connect_wait_for_response(&self) -> bool {

View File

@@ -66,7 +66,7 @@ impl Priority {
Some(ListEntry::Item(Item {
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,
};
let incremental = match dict.get("i") {
@@ -107,7 +107,6 @@ impl fmt::Display for Priority {
}
#[derive(Debug)]
#[allow(clippy::module_name_repetitions)]
pub struct PriorityHandler {
push_stream: bool,
priority: Priority,

View File

@@ -77,7 +77,7 @@ impl ActivePushStreams {
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() {
self.push_streams.resize(inx + 1, PushState::Init);
}
@@ -116,7 +116,7 @@ impl ActivePushStreams {
.filter(|&e| e == &PushState::Closed)
.count(),
)
.unwrap()
.expect("usize fits in u64")
}
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 {
stream_id: Some(stream_id.as_u64()),
offset: None,
length: Some(u64::try_from(amount).unwrap()),
length: Some(u64::try_from(amount).expect("usize fits in u64")),
from: Some(DataRecipient::Transport),
to: Some(DataRecipient::Application),
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 {
stream_id: Some(stream_id.as_u64()),
offset: None,
length: Some(u64::try_from(amount).unwrap()),
length: Some(u64::try_from(amount).expect("usize fits in u64")),
from: Some(DataRecipient::Application),
to: Some(DataRecipient::Transport),
raw: None,

View File

@@ -19,7 +19,6 @@ use crate::{
MessageType, Priority, PushId, ReceiveOutput, RecvStream, Res, Stream,
};
#[allow(clippy::module_name_repetitions)]
pub struct RecvMessageInfo {
pub message_type: MessageType,
pub stream_type: Http3StreamType,
@@ -292,7 +291,7 @@ impl RecvMessage {
break self.set_state_to_close_pending(post_readable_event);
}
}
};
}
}
RecvMessageState::DecodingHeaders { header_block, fin } => {
if self
@@ -338,7 +337,7 @@ impl RecvMessage {
// WebTransportSession
break Ok(());
}
};
}
}
}
@@ -460,16 +459,17 @@ impl HttpRecvStream for RecvMessage {
self.receive(conn)
}
fn maybe_update_priority(&mut self, priority: Priority) -> bool {
self.priority_handler.maybe_update_priority(priority)
fn maybe_update_priority(&mut self, priority: Priority) -> Res<bool> {
Ok(self.priority_handler.maybe_update_priority(priority))
}
fn priority_update_frame(&mut self) -> Option<HFrame> {
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();
Ok(())
}
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
// except according to those terms.
#![allow(clippy::module_name_repetitions)]
use std::fmt::{Debug, Formatter};
use url::{ParseError, Url};

View File

@@ -151,7 +151,7 @@ impl SendMessage {
}
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 {
@@ -183,7 +183,7 @@ impl SendStream for SendMessage {
// cheap, thus not worth optimizing.
conn.stream_set_writable_event_low_watermark(
self.stream_id(),
NonZeroUsize::new(MIN_DATA_FRAME_SIZE).unwrap(),
NonZeroUsize::new(MIN_DATA_FRAME_SIZE).ok_or(Error::Internal)?,
)?;
return Ok(0);
}

View File

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

View File

@@ -12,7 +12,8 @@ use neqo_transport::{AppError, StreamId};
use crate::{
connection::Http3State,
features::extended_connect::{ExtendedConnectEvents, ExtendedConnectType, SessionCloseReason},
CloseType, Http3StreamInfo, HttpRecvStreamEvents, Priority, RecvStreamEvents, SendStreamEvents,
CloseType, Http3StreamInfo, HttpRecvStreamEvents, Priority, RecvStreamEvents, Res,
SendStreamEvents,
};
#[derive(Debug, PartialEq, Eq, Clone)]
@@ -69,10 +70,9 @@ pub struct Http3ServerConnEvents {
impl SendStreamEvents for Http3ServerConnEvents {
fn send_closed(&self, stream_info: Http3StreamInfo, close_type: CloseType) {
if close_type != CloseType::Done {
self.insert(Http3ServerConnEvent::StreamStopSending {
stream_info,
error: close_type.error().unwrap(),
});
if let Some(error) = close_type.error() {
self.insert(Http3ServerConnEvent::StreamStopSending { stream_info, error });
}
}
}
@@ -90,10 +90,9 @@ impl RecvStreamEvents for Http3ServerConnEvents {
fn recv_closed(&self, stream_info: Http3StreamInfo, close_type: CloseType) {
if close_type != CloseType::Done {
self.remove_events_for_stream_id(stream_info);
self.insert(Http3ServerConnEvent::StreamReset {
stream_info,
error: close_type.error().unwrap(),
});
if let Some(error) = close_type.error() {
self.insert(Http3ServerConnEvent::StreamReset { stream_info, error });
}
}
}
}
@@ -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));
Ok(())
}
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
// except according to those terms.
#![allow(clippy::module_name_repetitions)]
use std::{
cell::RefCell,
collections::VecDeque,
@@ -22,7 +20,7 @@ use crate::{
connection::{Http3State, WebTransportSessionAcceptAction},
connection_server::Http3ServerHandler,
features::extended_connect::SessionCloseReason,
Http3StreamInfo, Http3StreamType, Priority, Res,
Error, Http3StreamInfo, Http3StreamType, Priority, Res,
};
#[derive(Debug, Clone)]
@@ -53,8 +51,6 @@ impl PartialEq for StreamHandler {
}
}
impl Eq for StreamHandler {}
impl StreamHandler {
pub const fn stream_id(&self) -> StreamId {
self.stream_info.stream_id()
@@ -215,7 +211,6 @@ impl Http3OrWebTransportStream {
impl Deref for Http3OrWebTransportStream {
type Target = StreamHandler;
#[must_use]
fn deref(&self) -> &Self::Target {
&self.stream_handler
}
@@ -378,13 +373,12 @@ impl WebTransportRequest {
- u64::try_from(Encoder::varint_len(
self.stream_handler.stream_id().as_u64(),
))
.unwrap())
.map_err(|_| Error::Internal)?)
}
}
impl Deref for WebTransportRequest {
type Target = StreamHandler;
#[must_use]
fn deref(&self) -> &Self::Target {
&self.stream_handler
}
@@ -409,8 +403,6 @@ impl PartialEq for WebTransportRequest {
}
}
impl Eq for WebTransportRequest {}
#[derive(Debug, Clone)]
pub enum WebTransportServerEvent {
NewSession {

View File

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

View File

@@ -4,6 +4,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![cfg(test)]
use std::time::{Duration, Instant};
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> {
assert_eq!(hconn_c.state(), Http3State::Initializing);
let out = hconn_c.process_output(now()); // Initial
let out = hconn_s.process(out.dgram(), now()); // Initial + Handshake
let out = hconn_c.process(out.dgram(), now()); // ACK
let out2 = hconn_c.process_output(now()); // Initial
_ = 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
let authentication_needed = |e| matches!(e, Http3ClientEvent::AuthenticationNeeded);
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);
let mut now = now();
let out = hconn_c.process_output(now); // Initial
let out2 = hconn_c.process_output(now); // Initial
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;
let out = hconn_c.process(out.dgram(), now); // ACK
now += net_delay;
@@ -143,7 +155,8 @@ fn connect_peers_with_network_propagation_delay(
(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_s = default_http3_server();
@@ -242,7 +255,7 @@ fn response_103() {
/// Test [`neqo_http3::SendMessage::send_data`] to set
/// [`neqo_transport::SendStream::set_writable_event_low_watermark`].
#[allow(clippy::cast_possible_truncation)]
#[expect(clippy::cast_possible_truncation, reason = "OK in a test.")]
#[test]
fn data_writable_events_low_watermark() -> Result<(), Box<dyn std::error::Error>> {
const STREAM_LIMIT: u64 = 5000;
@@ -446,6 +459,11 @@ fn zerortt() {
hconn_c.stream_close_send(req).unwrap();
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 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) {
assert_eq!(client.state(), Http3State::Initializing);
let out = client.process_output(now());
let out2 = client.process_output(now());
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 = client.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
// except according to those terms.
#![cfg(test)]
use std::sync::OnceLock;
use neqo_common::event::Provider as _;

View File

@@ -4,6 +4,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![cfg(test)]
use std::{cell::RefCell, rc::Rc};
use neqo_common::{event::Provider as _, header::HeadersExt as _};
@@ -42,8 +44,12 @@ fn connect() -> (Http3Client, Http3Server) {
.expect("create a server");
assert_eq!(client.state(), Http3State::Initializing);
let out = client.process_output(now());
let out2 = client.process_output(now());
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 = client.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]
edition = "2021"
rust-version = "1.76.0"
rust-version = "1.82.0"
name = "neqo-qpack"
version = "0.12.2"
version = "0.13.1"
authors = ["The Neqo Authors <necko@mozilla.com>"]
build = false
autolib = false
@@ -64,7 +64,7 @@ path = "./../neqo-common"
path = "./../neqo-transport"
[dependencies.qlog]
version = "0.13"
version = "0.15.1"
default-features = false
[dependencies.static_assertions]
@@ -75,22 +75,56 @@ default-features = false
path = "../test-fixture"
[lints.clippy]
allow_attributes = "warn"
allow_attributes_without_reason = "warn"
cfg_not_test = "warn"
clone_on_ref_ptr = "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"
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_inherent_impl = "warn"
mutex_atomic = "warn"
mutex_integer = "warn"
needless_raw_strings = "warn"
pathbuf_init_then_push = "warn"
pub_without_shorthand = "warn"
rc_buffer = "warn"
rc_mutex = "warn"
redundant_type_annotations = "warn"
ref_patterns = "warn"
renamed_function_params = "warn"
rest_pat_in_fully_bound_structs = "warn"
self_named_module_files = "warn"
semicolon_inside_block = "warn"
string_lit_chars_any = "warn"
string_to_string = "warn"
suspicious_xor_used_as_pow = "warn"
try_err = "warn"
unnecessary_safety_comment = "warn"
unnecessary_safety_doc = "warn"
unnecessary_self_imports = "warn"
unneeded_field_pattern = "warn"
unused_result_ok = "warn"
unused_trait_names = "warn"
unwrap_in_result = "warn"
unwrap_used = "warn"
verbose_file_reads = "warn"
[lints.clippy.cargo]
level = "warn"
@@ -107,7 +141,6 @@ priority = -1
[lints.rust]
absolute_paths_not_starting_with_crate = "warn"
ambiguous_negative_literals = "warn"
closure_returning_async_block = "warn"
explicit_outlives_requirements = "warn"
macro_use_extern_crate = "warn"
missing_abi = "warn"
@@ -119,3 +152,4 @@ unit_bindings = "warn"
unused_import_braces = "warn"
unused_lifetimes = "warn"
unused_macro_rules = "warn"
unused_qualifications = "warn"

View File

@@ -4,6 +4,11 @@
// option. This file may not be copied, modified, or distributed
// 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_transport::{Connection, StreamId};
@@ -67,12 +72,9 @@ impl QPackDecoder {
self.max_table_size
}
/// # Panics
///
/// If the number of blocked streams is too large.
#[must_use]
pub fn get_blocked_streams(&self) -> u16 {
u16::try_from(self.max_blocked_streams).unwrap()
pub const fn get_blocked_streams(&self) -> usize {
self.max_blocked_streams
}
/// returns a list of unblocked streams
@@ -174,7 +176,6 @@ impl QPackDecoder {
/// # Panics
///
/// Never, but rust doesn't know that.
#[allow(clippy::map_err_ignore)]
pub fn send(&mut self, conn: &mut Connection) -> Res<()> {
// Encode increment instruction if needed.
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() {
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)?;
qdebug!("[{self}] {r} bytes sent");
self.send_buf.read(r);

View File

@@ -4,6 +4,11 @@
// option. This file may not be copied, modified, or distributed
// 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 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<()> {
self.table
.increment_acked(increment)
@@ -540,7 +544,7 @@ mod tests {
impl TestEncoder {
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
// instruction.
self.encoder.send_encoder_updates(&mut self.conn)

View File

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

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