Bug 1942325 - update neqo to v0.12.2 r=kershaw,necko-reviewers,supply-chain-reviewers

Differential Revision: https://phabricator.services.mozilla.com/D234901
This commit is contained in:
Max Inden
2025-02-14 14:32:02 +00:00
parent 3792c997a9
commit f55183dd58
184 changed files with 6231 additions and 2595 deletions

View File

@@ -105,9 +105,9 @@ git = "https://github.com/mozilla/mp4parse-rust"
rev = "e64650a686e5c5732395cd059e17cfd3b1e5b63b" rev = "e64650a686e5c5732395cd059e17cfd3b1e5b63b"
replace-with = "vendored-sources" replace-with = "vendored-sources"
[source."git+https://github.com/mozilla/neqo?tag=v0.11.0"] [source."git+https://github.com/mozilla/neqo?tag=v0.12.2"]
git = "https://github.com/mozilla/neqo" git = "https://github.com/mozilla/neqo"
tag = "v0.11.0" tag = "v0.12.2"
replace-with = "vendored-sources" replace-with = "vendored-sources"
[source."git+https://github.com/servo/unicode-bidi?rev=ca612daf1c08c53abe07327cb3e6ef6e0a760f0c"] [source."git+https://github.com/servo/unicode-bidi?rev=ca612daf1c08c53abe07327cb3e6ef6e0a760f0c"]

48
Cargo.lock generated
View File

@@ -4405,6 +4405,20 @@ dependencies = [
"num-traits", "num-traits",
] ]
[[package]]
name = "mtu"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c30d3771729ec4349aae3b1a7c0b6b4a1500459e60b1fda95fe0657c3711574"
dependencies = [
"bindgen 0.69.4",
"cfg_aliases",
"libc",
"mozbuild",
"static_assertions",
"windows",
]
[[package]] [[package]]
name = "murmurhash3" name = "murmurhash3"
version = "0.0.5" version = "0.0.5"
@@ -4436,8 +4450,8 @@ dependencies = [
[[package]] [[package]]
name = "neqo-bin" name = "neqo-bin"
version = "0.11.0" version = "0.12.2"
source = "git+https://github.com/mozilla/neqo?tag=v0.11.0#c6d5502fb5b827473e7c5d7c4c380275cdb3d931" source = "git+https://github.com/mozilla/neqo?tag=v0.12.2#f8946d5187271b3e63e8d0209343510bdeac1451"
dependencies = [ dependencies = [
"clap", "clap",
"clap-verbosity-flag", "clap-verbosity-flag",
@@ -4458,8 +4472,8 @@ dependencies = [
[[package]] [[package]]
name = "neqo-common" name = "neqo-common"
version = "0.11.0" version = "0.12.2"
source = "git+https://github.com/mozilla/neqo?tag=v0.11.0#c6d5502fb5b827473e7c5d7c4c380275cdb3d931" source = "git+https://github.com/mozilla/neqo?tag=v0.12.2#f8946d5187271b3e63e8d0209343510bdeac1451"
dependencies = [ dependencies = [
"enum-map", "enum-map",
"env_logger", "env_logger",
@@ -4470,8 +4484,8 @@ dependencies = [
[[package]] [[package]]
name = "neqo-crypto" name = "neqo-crypto"
version = "0.11.0" version = "0.12.2"
source = "git+https://github.com/mozilla/neqo?tag=v0.11.0#c6d5502fb5b827473e7c5d7c4c380275cdb3d931" source = "git+https://github.com/mozilla/neqo?tag=v0.12.2#f8946d5187271b3e63e8d0209343510bdeac1451"
dependencies = [ dependencies = [
"bindgen 0.69.4", "bindgen 0.69.4",
"log", "log",
@@ -4485,8 +4499,8 @@ dependencies = [
[[package]] [[package]]
name = "neqo-http3" name = "neqo-http3"
version = "0.11.0" version = "0.12.2"
source = "git+https://github.com/mozilla/neqo?tag=v0.11.0#c6d5502fb5b827473e7c5d7c4c380275cdb3d931" source = "git+https://github.com/mozilla/neqo?tag=v0.12.2#f8946d5187271b3e63e8d0209343510bdeac1451"
dependencies = [ dependencies = [
"enumset", "enumset",
"log", "log",
@@ -4501,8 +4515,8 @@ dependencies = [
[[package]] [[package]]
name = "neqo-qpack" name = "neqo-qpack"
version = "0.11.0" version = "0.12.2"
source = "git+https://github.com/mozilla/neqo?tag=v0.11.0#c6d5502fb5b827473e7c5d7c4c380275cdb3d931" source = "git+https://github.com/mozilla/neqo?tag=v0.12.2#f8946d5187271b3e63e8d0209343510bdeac1451"
dependencies = [ dependencies = [
"log", "log",
"neqo-common", "neqo-common",
@@ -4513,12 +4527,13 @@ dependencies = [
[[package]] [[package]]
name = "neqo-transport" name = "neqo-transport"
version = "0.11.0" version = "0.12.2"
source = "git+https://github.com/mozilla/neqo?tag=v0.11.0#c6d5502fb5b827473e7c5d7c4c380275cdb3d931" source = "git+https://github.com/mozilla/neqo?tag=v0.12.2#f8946d5187271b3e63e8d0209343510bdeac1451"
dependencies = [ dependencies = [
"enum-map", "enum-map",
"indexmap", "indexmap",
"log", "log",
"mtu",
"neqo-common", "neqo-common",
"neqo-crypto", "neqo-crypto",
"qlog", "qlog",
@@ -4528,9 +4543,10 @@ dependencies = [
[[package]] [[package]]
name = "neqo-udp" name = "neqo-udp"
version = "0.11.0" version = "0.12.2"
source = "git+https://github.com/mozilla/neqo?tag=v0.11.0#c6d5502fb5b827473e7c5d7c4c380275cdb3d931" source = "git+https://github.com/mozilla/neqo?tag=v0.12.2#f8946d5187271b3e63e8d0209343510bdeac1451"
dependencies = [ dependencies = [
"cfg_aliases",
"log", "log",
"neqo-common", "neqo-common",
"quinn-udp", "quinn-udp",
@@ -5203,9 +5219,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]] [[package]]
name = "quinn-udp" name = "quinn-udp"
version = "0.5.9" version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c40286217b4ba3a71d644d752e6a0b71f13f1b6a2c5311acfcbe0c2418ed904" checksum = "e46f3055866785f6b92bc6164b76be02ca8f2eb4b002c0354b28cf4c119e5944"
dependencies = [ dependencies = [
"cfg_aliases", "cfg_aliases",
"libc", "libc",

View File

@@ -199,6 +199,7 @@ features = [
version = "0.58" version = "0.58"
optional = true optional = true
features = [ features = [
"Win32_Foundation",
"Win32_Graphics", "Win32_Graphics",
"Win32_Graphics_Direct3D", "Win32_Graphics_Direct3D",
"Win32_Graphics_Direct3D12", "Win32_Graphics_Direct3D12",
@@ -207,6 +208,9 @@ features = [
"Win32_Graphics_DirectComposition", "Win32_Graphics_DirectComposition",
"Win32_Graphics_Dxgi", "Win32_Graphics_Dxgi",
"Win32_Graphics_Dxgi_Common", "Win32_Graphics_Dxgi_Common",
"Win32_NetworkManagement_IpHelper",
"Win32_NetworkManagement_Ndis",
"Win32_Networking_WinSock",
"Win32_Security", "Win32_Security",
"Win32_System", "Win32_System",
"Win32_System_Diagnostics", "Win32_System_Diagnostics",

View File

@@ -14629,6 +14629,13 @@
value: false value: false
mirror: always mirror: always
# When network.http.http3.sni-slicing is set, TLS SNI slicing is enabled.
- name: network.http.http3.sni-slicing
type: RelaxedAtomicBool
value: true
mirror: always
rust: true
# When true, a http request will be upgraded to https when HTTPS RR is # When true, a http request will be upgraded to https when HTTPS RR is
# available. # available.
- name: network.dns.upgrade_with_https_rr - name: network.dns.upgrade_with_https_rr

View File

@@ -1287,7 +1287,9 @@ networking:
expires: never expires: never
labels: labels:
- capable - capable
- not-capable - black-hole
- bleaching
- received-unsent-ect-1
http_3_loss_ratio: http_3_loss_ratio:
type: custom_distribution type: custom_distribution

View File

@@ -10,11 +10,11 @@ name = "neqo_glue"
[dependencies] [dependencies]
firefox-on-glean = { path = "../../../toolkit/components/glean/api" } firefox-on-glean = { path = "../../../toolkit/components/glean/api" }
neqo-udp = { tag = "v0.11.0", git = "https://github.com/mozilla/neqo" } neqo-udp = { tag = "v0.12.2", git = "https://github.com/mozilla/neqo" }
neqo-http3 = { tag = "v0.11.0", git = "https://github.com/mozilla/neqo" } neqo-http3 = { tag = "v0.12.2", git = "https://github.com/mozilla/neqo" }
neqo-transport = { tag = "v0.11.0", git = "https://github.com/mozilla/neqo" } neqo-transport = { tag = "v0.12.2", git = "https://github.com/mozilla/neqo", features = ["gecko"] }
neqo-common = { tag = "v0.11.0", git = "https://github.com/mozilla/neqo" } neqo-common = { tag = "v0.12.2", git = "https://github.com/mozilla/neqo" }
neqo-qpack = { tag = "v0.11.0", git = "https://github.com/mozilla/neqo" } neqo-qpack = { tag = "v0.12.2", git = "https://github.com/mozilla/neqo" }
nserror = { path = "../../../xpcom/rust/nserror" } nserror = { path = "../../../xpcom/rust/nserror" }
nsstring = { path = "../../../xpcom/rust/nsstring" } nsstring = { path = "../../../xpcom/rust/nsstring" }
xpcom = { path = "../../../xpcom/rust/xpcom" } xpcom = { path = "../../../xpcom/rust/xpcom" }
@@ -29,10 +29,11 @@ uuid = { version = "1.0", features = ["v4"] }
winapi = {version = "0.3", features = ["ws2def"] } winapi = {version = "0.3", features = ["ws2def"] }
[dependencies.neqo-crypto] [dependencies.neqo-crypto]
tag = "v0.11.0" tag = "v0.12.2"
git = "https://github.com/mozilla/neqo" git = "https://github.com/mozilla/neqo"
default-features = false default-features = false
features = ["gecko"] features = ["gecko"]
[features] [features]
bench = [] # no-op, see <https://github.com/mozilla/neqo/issues/2390>
fuzzing = ["neqo-http3/disable-encryption"] fuzzing = ["neqo-http3/disable-encryption"]

View File

@@ -42,7 +42,7 @@ use winapi::shared::ws2def::{AF_INET, AF_INET6};
use xpcom::{interfaces::nsISocketProvider, AtomicRefcnt, RefCounted, RefPtr}; use xpcom::{interfaces::nsISocketProvider, AtomicRefcnt, RefCounted, RefPtr};
std::thread_local! { std::thread_local! {
static RECV_BUF: RefCell<Vec<u8>> = RefCell::new(vec![0; neqo_udp::RECV_BUF_SIZE]); static RECV_BUF: RefCell<neqo_udp::RecvBuf> = RefCell::new(neqo_udp::RecvBuf::new());
} }
#[repr(C)] #[repr(C)]
@@ -203,10 +203,6 @@ impl NeqoHttp3Conn {
let remote: SocketAddr = netaddr_to_socket_addr(remote_addr)?; let remote: SocketAddr = netaddr_to_socket_addr(remote_addr)?;
let quic_version = match alpn_conv { let quic_version = match alpn_conv {
"h3-32" => Version::Draft32,
"h3-31" => Version::Draft31,
"h3-30" => Version::Draft30,
"h3-29" => Version::Draft29,
"h3" => Version::Version1, "h3" => Version::Version1,
_ => return Err(NS_ERROR_INVALID_ARG), _ => return Err(NS_ERROR_INVALID_ARG),
}; };
@@ -232,7 +228,8 @@ impl NeqoHttp3Conn {
.cc_algorithm(cc_algorithm) .cc_algorithm(cc_algorithm)
.max_data(max_data) .max_data(max_data)
.max_stream_data(StreamType::BiDi, false, max_stream_data) .max_stream_data(StreamType::BiDi, false, max_stream_data)
.grease(static_prefs::pref!("security.tls.grease_http3_enable")); .grease(static_prefs::pref!("security.tls.grease_http3_enable"))
.sni_slicing(static_prefs::pref!("network.http.http3.sni-slicing"));
// Set a short timeout when fuzzing. // Set a short timeout when fuzzing.
#[cfg(feature = "fuzzing")] #[cfg(feature = "fuzzing")]
@@ -339,6 +336,7 @@ impl NeqoHttp3Conn {
fn record_stats_in_glean(&self) { fn record_stats_in_glean(&self) {
use firefox_on_glean::metrics::networking as glean; use firefox_on_glean::metrics::networking as glean;
use neqo_common::IpTosEcn; use neqo_common::IpTosEcn;
use neqo_transport::ecn;
// Metric values must be recorded as integers. Glean does not support // Metric values must be recorded as integers. Glean does not support
// floating point distributions. In order to represent values <1, they // floating point distributions. In order to represent values <1, they
@@ -395,12 +393,32 @@ impl NeqoHttp3Conn {
glean::http_3_ecn_ce_ect0_ratio_received glean::http_3_ecn_ce_ect0_ratio_received
.accumulate_single_sample_signed(ratio as i64); .accumulate_single_sample_signed(ratio as i64);
} }
glean::http_3_ecn_path_capability for (outcome, value) in stats.ecn_path_validation.into_iter() {
.get(&"capable") match outcome {
.add(stats.ecn_paths_capable as i32); ecn::ValidationOutcome::Capable => {
glean::http_3_ecn_path_capability glean::http_3_ecn_path_capability
.get(&"not-capable") .get(&"capable")
.add(stats.ecn_paths_not_capable as i32); .add(value as i32);
}
ecn::ValidationOutcome::NotCapable(ecn::ValidationError::BlackHole) => {
glean::http_3_ecn_path_capability
.get(&"black-hole")
.add(value as i32);
}
ecn::ValidationOutcome::NotCapable(ecn::ValidationError::Bleaching) => {
glean::http_3_ecn_path_capability
.get(&"bleaching")
.add(value as i32);
}
ecn::ValidationOutcome::NotCapable(
ecn::ValidationError::ReceivedUnsentECT1,
) => {
glean::http_3_ecn_path_capability
.get(&"received-unsent-ect-1")
.add(value as i32);
}
}
}
} }
// Ignore connections into the void. // Ignore connections into the void.
@@ -593,18 +611,15 @@ pub unsafe extern "C" fn neqo_http3conn_process_input(
}; };
} }
}; };
if dgrams.len() == 0 {
break;
}
// Attach metric instrumentation to `dgrams` iterator. // Attach metric instrumentation to `dgrams` iterator.
let mut sum = 0; let mut sum = 0;
conn.datagram_segments_received let mut segment_count = 0;
.accumulate(dgrams.len() as u64);
let datagram_segment_size_received = &mut conn.datagram_segment_size_received; let datagram_segment_size_received = &mut conn.datagram_segment_size_received;
let dgrams = dgrams.map(|d| { let dgrams = dgrams.map(|d| {
datagram_segment_size_received.accumulate(d.len() as u64); datagram_segment_size_received.accumulate(d.len() as u64);
sum += d.len(); sum += d.len();
segment_count += 1;
d d
}); });
@@ -620,6 +635,7 @@ pub unsafe extern "C" fn neqo_http3conn_process_input(
conn.conn.process_multiple_input(dgrams, Instant::now()); conn.conn.process_multiple_input(dgrams, Instant::now());
conn.datagram_size_received.accumulate(sum as u64); conn.datagram_size_received.accumulate(sum as u64);
conn.datagram_segments_received.accumulate(segment_count);
bytes_read += sum; bytes_read += sum;
} }
@@ -1463,7 +1479,7 @@ pub extern "C" fn neqo_http3conn_event(
return res; return res;
} }
Http3Event::PushPromise { Http3Event::PushPromise {
push_id, push_id: push_id.into(),
request_stream_id: request_stream_id.as_u64(), request_stream_id: request_stream_id.as_u64(),
} }
} }
@@ -1480,16 +1496,22 @@ pub extern "C" fn neqo_http3conn_event(
if res != NS_OK { if res != NS_OK {
return res; return res;
} }
Http3Event::PushHeaderReady { push_id, fin } Http3Event::PushHeaderReady {
push_id: push_id.into(),
fin,
}
} }
} }
Http3ClientEvent::PushDataReadable { push_id } => { Http3ClientEvent::PushDataReadable { push_id } => Http3Event::PushDataReadable {
Http3Event::PushDataReadable { push_id } push_id: push_id.into(),
} },
Http3ClientEvent::PushCanceled { push_id } => Http3Event::PushCanceled { push_id }, Http3ClientEvent::PushCanceled { push_id } => Http3Event::PushCanceled {
Http3ClientEvent::PushReset { push_id, error } => { push_id: push_id.into(),
Http3Event::PushReset { push_id, error } },
} Http3ClientEvent::PushReset { push_id, error } => Http3Event::PushReset {
push_id: push_id.into(),
error,
},
Http3ClientEvent::RequestsCreatable => Http3Event::RequestsCreatable, Http3ClientEvent::RequestsCreatable => Http3Event::RequestsCreatable,
Http3ClientEvent::AuthenticationNeeded => Http3Event::AuthenticationNeeded, Http3ClientEvent::AuthenticationNeeded => Http3Event::AuthenticationNeeded,
Http3ClientEvent::ZeroRttRejected => Http3Event::ZeroRttRejected, Http3ClientEvent::ZeroRttRejected => Http3Event::ZeroRttRejected,

View File

@@ -6,11 +6,11 @@ edition = "2018"
license = "MPL-2.0" license = "MPL-2.0"
[dependencies] [dependencies]
neqo-bin = { tag = "v0.11.0", git = "https://github.com/mozilla/neqo" } neqo-bin = { tag = "v0.12.2", git = "https://github.com/mozilla/neqo" }
neqo-transport = { tag = "v0.11.0", git = "https://github.com/mozilla/neqo" } neqo-transport = { tag = "v0.12.2", git = "https://github.com/mozilla/neqo", features = ["gecko"] }
neqo-common = { tag = "v0.11.0", git = "https://github.com/mozilla/neqo" } neqo-common = { tag = "v0.12.2", git = "https://github.com/mozilla/neqo" }
neqo-http3 = { tag = "v0.11.0", git = "https://github.com/mozilla/neqo" } neqo-http3 = { tag = "v0.12.2", git = "https://github.com/mozilla/neqo" }
neqo-qpack = { tag = "v0.11.0", git = "https://github.com/mozilla/neqo" } neqo-qpack = { tag = "v0.12.2", git = "https://github.com/mozilla/neqo" }
log = "0.4.0" log = "0.4.0"
base64 = "0.21" base64 = "0.21"
cfg-if = "1.0" cfg-if = "1.0"
@@ -20,7 +20,7 @@ tokio = { version = "1", features = ["rt-multi-thread"] }
mozilla-central-workspace-hack = { version = "0.1", features = ["http3server"], optional = true } mozilla-central-workspace-hack = { version = "0.1", features = ["http3server"], optional = true }
[dependencies.neqo-crypto] [dependencies.neqo-crypto]
tag = "v0.11.0" tag = "v0.12.2"
git = "https://github.com/mozilla/neqo" git = "https://github.com/mozilla/neqo"
default-features = false default-features = false
features = ["gecko"] features = ["gecko"]
@@ -32,3 +32,6 @@ bindgen = {version = "0.69", default-features = false, features = ["runtime"] }
[[bin]] [[bin]]
name = "http3server" name = "http3server"
path = "src/main.rs" path = "src/main.rs"
[features]
bench = [] # no-op, see <https://github.com/mozilla/neqo/issues/2390>

View File

@@ -1227,3 +1227,5 @@ extern "C" fn __tsan_default_suppressions() -> *const std::os::raw::c_char {
extern "C" {} extern "C" {}
#[cfg_attr(target_os = "windows", link(name = "propsys"))] #[cfg_attr(target_os = "windows", link(name = "propsys"))]
extern "C" {} extern "C" {}
#[cfg_attr(target_os = "windows", link(name = "iphlpapi"))]
extern "C" {}

View File

@@ -3399,6 +3399,16 @@ criteria = "safe-to-deploy"
version = "0.1.2" version = "0.1.2"
notes = "Developed by Mozilla staff." notes = "Developed by Mozilla staff."
[[audits.mtu]]
who = "Max Inden <mail@max-inden.de>"
criteria = "safe-to-deploy"
version = "0.2.5"
[[audits.mtu]]
who = "Max Leonard Inden <mail@max-inden.de>"
criteria = "safe-to-deploy"
delta = "0.2.5 -> 0.2.6"
[[audits.naga]] [[audits.naga]]
who = "Dzmitry Malyshau <kvark@fastmail.com>" who = "Dzmitry Malyshau <kvark@fastmail.com>"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
@@ -4049,6 +4059,11 @@ who = "Max Inden <mail@max-inden.de>"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
delta = "0.5.8 -> 0.5.9" delta = "0.5.8 -> 0.5.9"
[[audits.quinn-udp]]
who = "Max Leonard Inden <mail@max-inden.de>"
criteria = "safe-to-deploy"
delta = "0.5.9 -> 0.5.10"
[[audits.quote]] [[audits.quote]]
who = "Nika Layzell <nika@thelayzells.com>" who = "Nika Layzell <nika@thelayzells.com>"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"

View File

@@ -0,0 +1 @@
{"files":{"CODE_OF_CONDUCT.md":"f7b4cba1deaa0a77bd611c04c84ef5b6859e44c8370f7513fe688fb9531b913b","Cargo.lock":"0d7b4f80f302400b5fba9847542ab3a0e94dd50bb0d27111927a6e4037b42eef","Cargo.toml":"82ab8662dd7a9c324bd23a7234fa2b8d6df9a9cab2b9baa778039bb55666bfb8","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"4ad721b5b6a3d39ca3e2202f403d897c4a1d42896486dd58963a81f8e64ef61d","README.md":"2c9cce2eb06f0897e9b2fb68d8fa640da581bf440c26f266f8af5b3edb02dc6a","SECURITY.md":"75455814b6cf997e22a927eb979b4356d788583aa1eb96e90853aaab0f82ad1b","build.rs":"c26ac0385171924eefec5ce864067419bb023961b054abaf060d8da567dc6241","src/bsd.rs":"e680ddb399419bb67219fa1c0d3e6672d32023f325584869bd050e2f2ecc8ff9","src/lib.rs":"a59ae67763bc6bb9e6a4f7e7af61fed722ca032b667276a68e6ad687f1fe1e6e","src/linux.rs":"4e99612a04d744e2ca22b2e353faa500b37cb58caddaecb7566b4748aa1f7209","src/routesocket.rs":"be837947e2c3f9301a174499217fe8920ff492918bf85ca5eb281eb7ad2240e1","src/windows.rs":"b139c7aaa0c39415ce1773f1c9569be1a6b82dfe82334ab0c6cb80e7d232363e"},"package":"4c30d3771729ec4349aae3b1a7c0b6b4a1500459e60b1fda95fe0657c3711574"}

18
third_party/rust/mtu/CODE_OF_CONDUCT.md vendored Normal file
View File

@@ -0,0 +1,18 @@
# Community Participation Guidelines
This repository is governed by Mozilla's code of conduct and etiquette
guidelines. For more details, please read the [Mozilla Community Participation
Guidelines](https://www.mozilla.org/about/governance/policies/participation/).
## How to Report
For more information on how to report violations of the Community Participation
Guidelines, please read our '[How to
Report](https://www.mozilla.org/about/governance/policies/participation/reporting/)'
page.
## Project Specific Etiquette
Please consider the advice in the [Bugzilla etiquette
guide](https://bugzilla.mozilla.org/page.cgi?id=etiquette.html) when
contributing to this project.

369
third_party/rust/mtu/Cargo.lock generated vendored Normal file
View File

@@ -0,0 +1,369 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f2135563fb5c609d2b2b87c1e8ce7bc41b0b45430fa9661f457981503dd5bf0"
dependencies = [
"memchr",
]
[[package]]
name = "bindgen"
version = "0.69.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088"
dependencies = [
"bitflags",
"cexpr",
"clang-sys",
"itertools",
"lazy_static",
"lazycell",
"proc-macro2",
"quote",
"regex",
"rustc-hash",
"shlex",
"syn",
]
[[package]]
name = "bitflags"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]]
name = "cexpr"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
dependencies = [
"nom",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cfg_aliases"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]]
name = "clang-sys"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1"
dependencies = [
"glob",
"libc",
"libloading",
]
[[package]]
name = "either"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
[[package]]
name = "glob"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]]
name = "itertools"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
dependencies = [
"either",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "lazycell"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "libc"
version = "0.2.158"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
[[package]]
name = "libloading"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19"
dependencies = [
"cfg-if",
"windows-targets",
]
[[package]]
name = "memchr"
version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "mozbuild"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "903970ae2f248d7275214cf8f387f8ba0c4ea7e3d87a320e85493db60ce28616"
[[package]]
name = "mtu"
version = "0.2.6"
dependencies = [
"bindgen",
"cfg_aliases",
"libc",
"mozbuild",
"static_assertions",
"windows",
]
[[package]]
name = "nom"
version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
dependencies = [
"memchr",
"minimal-lexical",
]
[[package]]
name = "proc-macro2"
version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regex"
version = "1.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12de2eff854e5fa4b1295edd650e227e9d8fb0c9e90b12e7f36d6a6811791a29"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49530408a136e16e5b486e883fbb6ba058e8e4e8ae6621a77b048b314336e629"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da"
[[package]]
name = "rustc-hash"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "syn"
version = "2.0.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
[[package]]
name = "windows"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6"
dependencies = [
"windows-core",
"windows-targets",
]
[[package]]
name = "windows-core"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99"
dependencies = [
"windows-implement",
"windows-interface",
"windows-result",
"windows-strings",
"windows-targets",
]
[[package]]
name = "windows-implement"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "windows-interface"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "windows-result"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-strings"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10"
dependencies = [
"windows-result",
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"

139
third_party/rust/mtu/Cargo.toml vendored Normal file
View File

@@ -0,0 +1,139 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2021"
rust-version = "1.76.0"
name = "mtu"
version = "0.2.6"
authors = ["The Mozilla Necko Team <necko@mozilla.com>"]
build = "build.rs"
autolib = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "Obtain the local network interface MTU towards a given IP address."
homepage = "https://github.com/mozilla/mtu/"
readme = "README.md"
keywords = [
"mozilla",
"interface",
"mtu",
]
categories = [
"network-programming",
"web-programming",
]
license = "MIT OR Apache-2.0"
repository = "https://github.com/mozilla/mtu/"
[lib]
name = "mtu"
path = "src/lib.rs"
[dependencies.libc]
version = "0.2"
default-features = false
[dependencies.static_assertions]
version = "1.1"
default-features = false
[build-dependencies.bindgen]
version = "0.69"
features = ["runtime"]
default-features = false
[build-dependencies.cfg_aliases]
version = "0.2"
default-features = false
[build-dependencies.mozbuild]
version = "0.1"
optional = true
default-features = false
[features]
gecko = ["dep:mozbuild"]
[target."cfg(windows)".dependencies.windows]
version = ">=0.58,<0.60"
features = [
"Win32_Foundation",
"Win32_NetworkManagement_IpHelper",
"Win32_NetworkManagement_Ndis",
"Win32_Networking_WinSock",
]
[badges.codecov]
branch = "main"
repository = "mozilla/mtu"
[badges.is-it-maintained-issue-resolution]
branch = "main"
repository = "mozilla/mtu"
[badges.is-it-maintained-open-issues]
branch = "main"
repository = "mozilla/mtu"
[badges.maintenance]
branch = "main"
status = "actively-developed"
[lints.clippy]
cfg_not_test = "warn"
clone_on_ref_ptr = "warn"
create_dir = "warn"
get_unwrap = "warn"
if_then_some_else_none = "warn"
multiple_crate_versions = "allow"
multiple_inherent_impl = "warn"
pathbuf_init_then_push = "warn"
redundant_type_annotations = "warn"
ref_patterns = "warn"
renamed_function_params = "warn"
semicolon_inside_block = "warn"
try_err = "warn"
unneeded_field_pattern = "warn"
unused_result_ok = "warn"
unused_trait_names = "warn"
unwrap_in_result = "warn"
unwrap_used = "warn"
[lints.clippy.cargo]
level = "warn"
priority = -1
[lints.clippy.nursery]
level = "warn"
priority = -1
[lints.clippy.pedantic]
level = "warn"
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"
non_ascii_idents = "warn"
redundant_imports = "warn"
redundant_lifetimes = "warn"
trivial_numeric_casts = "warn"
unit_bindings = "warn"
unused_import_braces = "warn"
unused_lifetimes = "warn"
unused_macro_rules = "warn"

201
third_party/rust/mtu/LICENSE-APACHE vendored Normal file
View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

25
third_party/rust/mtu/LICENSE-MIT vendored Normal file
View File

@@ -0,0 +1,25 @@
Copyright (c) 2019 Mozilla Foundation
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

46
third_party/rust/mtu/README.md vendored Normal file
View File

@@ -0,0 +1,46 @@
[![Coverage Status](https://codecov.io/gh/mozilla/mtu/branch/main/graph/badge.svg)](https://codecov.io/gh/mozilla/mtu)
[![Average time to resolve an issue](https://isitmaintained.com/badge/resolution/mozilla/mtu.svg)](https://isitmaintained.com/project/mozilla/mtu "Average time to resolve an issue")
[![Percentage of issues still open](https://isitmaintained.com/badge/open/mozilla/mtu.svg)](https://isitmaintained.com/project/mozilla/mtu "Percentage of issues still open")
![Maintenance](https://img.shields.io/badge/maintenance-activly--developed-brightgreen.svg)
# mtu
A crate to return the name and maximum transmission unit (MTU) of the local network interface
towards a given destination `SocketAddr`, optionally from a given local `SocketAddr`.
## Usage
This crate exports a single function `interface_and_mtu` that returns the name and
[maximum transmission unit (MTU)](https://en.wikipedia.org/wiki/Maximum_transmission_unit)
of the outgoing network interface towards a remote destination identified by an `IpAddr`.
## Example
```rust
let destination = IpAddr::V4(Ipv4Addr::LOCALHOST);
let (name, mtu): (String, usize) = mtu::interface_and_mtu(destination).unwrap();
println!("MTU towards {destination} is {mtu} on {name}");
```
## Supported Platforms
* Linux
* macOS
* Windows
* FreeBSD
* NetBSD
* OpenBSD
* Solaris
## Notes
The returned MTU may exceed the maximum IP packet size of 65,535 bytes on some platforms for
some remote destinations. (For example, loopback destinations on Windows.)
The returned interface name is obtained from the operating system.
## Contributing
We're happy to receive PRs that improve this crate. Please take a look at our [community
guidelines](CODE_OF_CONDUCT.md) beforehand.
License: MIT OR Apache-2.0

31
third_party/rust/mtu/SECURITY.md vendored Normal file
View File

@@ -0,0 +1,31 @@
# Security Policy
This document describes how security vulnerabilities in this project should be reported.
## Supported Versions
Support for `mtu` is based on the Firefox version in which it has landed.
Versions of `mtu` in [current versions of Firefox](https://whattrainisitnow.com/calendar/) are actively supported.
The version of `mtu` that is active can be found in the Firefox repositories:
- [release](https://hg.mozilla.org/mozilla-unified/file/release/third_party/rust/mtu-transport/Cargo.toml),
- [beta](https://hg.mozilla.org/mozilla-unified/file/beta/third_party/rust/mtu-transport/Cargo.toml), and
- [trunk/central](https://hg.mozilla.org/mozilla-unified/file/central/third_party/rust/mtu-transport/Cargo.toml),
- [ESR 115](https://hg.mozilla.org/mozilla-unified/file/esr115/third_party/rust/mtu-transport/Cargo.toml).
The listed version in these files corresponds to [tags](https://github.com/mozilla/mtu/tags) on this repository.
Releases do not always correspond to a branch.
We welcome reports of security vulnerabilities in any of these released versions or the latest code on the `main` branch.
## Reporting a Vulnerability
To report a security problem with `mtu`, create a bug in Mozilla's Bugzilla instance in the [Core :: Networking](https://bugzilla.mozilla.org/enter_bug.cgi?product=Core&component=Networking) component.
**IMPORTANT: For security issues, please make sure that you check the box labelled "Many users could be harmed by this security problem".**
We advise that you check this option for anything that involves anything security-relevant, including memory safety, crashes, race conditions, and handling of confidential information.
Review Mozilla's [guides on bug reporting](https://bugzilla.mozilla.org/page.cgi?id=bug-writing.html) before you open a bug.
Mozilla operates a [bug bounty program](https://www.mozilla.org/en-US/security/bug-bounty/), for which this project is eligible.

112
third_party/rust/mtu/build.rs vendored Normal file
View File

@@ -0,0 +1,112 @@
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![allow(clippy::unwrap_used)] // OK in build scripts.
use std::env;
const BINDINGS: &str = "bindings.rs";
#[cfg(feature = "gecko")]
fn clang_args() -> Vec<String> {
use mozbuild::TOPOBJDIR;
let flags_path = TOPOBJDIR.join("netwerk/socket/neqo/extra-bindgen-flags");
println!("cargo:rerun-if-changed={}", flags_path.to_str().unwrap());
let mut flags: Vec<String> = std::fs::read_to_string(flags_path)
.expect("Failed to read extra-bindgen-flags file")
.split_whitespace()
.map(std::borrow::ToOwned::to_owned)
.collect();
flags.push(String::from("-include"));
flags.push(
TOPOBJDIR
.join("dist")
.join("include")
.join("mozilla-config.h")
.to_str()
.unwrap()
.to_string(),
);
flags
}
#[cfg(not(feature = "gecko"))]
const fn clang_args() -> Vec<String> {
Vec::new()
}
fn bindgen() {
let target_os = env::var("CARGO_CFG_TARGET_OS").expect("CARGO_CFG_TARGET_OS was not set");
// Platforms currently not supported.
//
// See <https://github.com/mozilla/mtu/issues/82>.
if matches!(target_os.as_str(), "ios" | "tvos" | "visionos") {
return;
}
if target_os == "windows" {
return;
}
let bindings = if matches!(target_os.as_str(), "linux" | "android") {
bindgen::Builder::default()
.header_contents("rtnetlink.h", "#include <linux/rtnetlink.h>")
// Only generate bindings for the following types
.allowlist_type("rtattr|rtmsg|ifinfomsg|nlmsghdr")
} else {
bindgen::Builder::default()
.header_contents(
"route.h",
"#include <sys/types.h>\n#include <sys/socket.h>\n#include <net/route.h>\n#include <net/if.h>",
)
// Only generate bindings for the following types and items
.allowlist_type("rt_msghdr|rt_metrics|if_data")
.allowlist_item("RTAX_MAX|RTM_GET|RTM_VERSION|RTA_DST|RTA_IFP")
};
let bindings = bindings
.clang_args(clang_args())
// Tell cargo to invalidate the built crate whenever any of the
// included header files changed.
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
// Constants should be generated as &CStr instead of &[u8].
.generate_cstr(true)
// Always emit explicit padding fields.
.explicit_padding(true)
// Default trait should be derived when possible
.derive_default(true)
// Finish the builder and generate the bindings.
.generate()
// Unwrap the Result and panic on failure.
.expect("Unable to generate bindings");
// Write the bindings to the $OUT_DIR/$BINDINGS file.
let out_path = std::path::PathBuf::from(env::var("OUT_DIR").unwrap()).join(BINDINGS);
bindings
.write_to_file(out_path.clone())
.expect("Couldn't write bindings!");
println!("cargo:rustc-env=BINDINGS={}", out_path.display());
}
fn main() {
// Setup cfg aliases
cfg_aliases::cfg_aliases! {
bsd: {
any(
target_os = "freebsd",
target_os = "openbsd",
target_os = "netbsd",
target_os = "solaris"
)
}
}
bindgen();
}

358
third_party/rust/mtu/src/bsd.rs vendored Normal file
View File

@@ -0,0 +1,358 @@
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::{
ffi::CStr,
io::{Error, ErrorKind, Read as _, Result, Write as _},
marker::PhantomData,
net::IpAddr,
num::TryFromIntError,
ops::Deref,
ptr, slice,
};
use libc::{
freeifaddrs, getifaddrs, getpid, if_indextoname, ifaddrs, in6_addr, in_addr, sockaddr,
sockaddr_dl, sockaddr_in, sockaddr_in6, sockaddr_storage, AF_UNSPEC, PF_ROUTE,
};
use static_assertions::{const_assert, const_assert_eq};
#[allow(
non_camel_case_types,
clippy::struct_field_names,
clippy::too_many_lines,
clippy::cognitive_complexity,
dead_code // RTA_IFP is only used on NetBSD and Solaris
)]
mod bindings {
include!(env!("BINDINGS"));
}
#[cfg(any(target_os = "netbsd", target_os = "solaris"))]
use crate::bsd::bindings::RTA_IFP;
use crate::{
aligned_by,
bsd::bindings::{if_data, rt_msghdr, RTAX_MAX, RTA_DST},
default_err,
routesocket::RouteSocket,
unlikely_err,
};
#[cfg(target_os = "macos")]
const ALIGN: usize = std::mem::size_of::<libc::c_int>();
#[cfg(bsd)]
// See https://github.com/freebsd/freebsd-src/blob/524a425d30fce3d5e47614db796046830b1f6a83/sys/net/route.h#L362-L371
// See https://github.com/NetBSD/src/blob/4b50954e98313db58d189dd87b4541929efccb09/sys/net/route.h#L329-L331
// See https://github.com/Arquivotheca/Solaris-8/blob/2ad1d32f9eeed787c5adb07eb32544276e2e2444/osnet_volume/usr/src/cmd/cmd-inet/usr.sbin/route.c#L238-L239
const ALIGN: usize = std::mem::size_of::<libc::c_long>();
#[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "openbsd"))]
asserted_const_with_type!(RTM_ADDRS, i32, RTA_DST, u32);
#[cfg(any(target_os = "netbsd", target_os = "solaris"))]
asserted_const_with_type!(RTM_ADDRS, i32, RTA_DST | RTA_IFP, u32);
#[cfg(not(target_os = "solaris"))]
type AddressFamily = u8;
#[cfg(target_os = "solaris")]
type AddressFamily = u16;
asserted_const_with_type!(AF_INET, AddressFamily, libc::AF_INET, i32);
asserted_const_with_type!(AF_INET6, AddressFamily, libc::AF_INET6, i32);
asserted_const_with_type!(AF_LINK, AddressFamily, libc::AF_LINK, i32);
asserted_const_with_type!(RTM_VERSION, u8, bindings::RTM_VERSION, u32);
asserted_const_with_type!(RTM_GET, u8, bindings::RTM_GET, u32);
const_assert!(std::mem::size_of::<sockaddr_in>() + ALIGN <= u8::MAX as usize);
const_assert!(std::mem::size_of::<sockaddr_in6>() + ALIGN <= u8::MAX as usize);
const_assert!(std::mem::size_of::<rt_msghdr>() <= u8::MAX as usize);
struct IfAddrs(*mut ifaddrs);
impl Default for IfAddrs {
fn default() -> Self {
Self(ptr::null_mut())
}
}
impl IfAddrs {
fn new() -> Result<Self> {
let mut ifap = Self::default();
// getifaddrs allocates memory for the linked list of interfaces that is freed by
// `IfAddrs::drop`.
if unsafe { getifaddrs(ptr::from_mut(&mut ifap.0)) } != 0 {
return Err(Error::last_os_error());
}
Ok(ifap)
}
const fn iter(&self) -> IfAddrPtr {
IfAddrPtr {
ptr: self.0,
_ref: PhantomData,
}
}
}
impl Drop for IfAddrs {
fn drop(&mut self) {
if !self.0.is_null() {
// Free the memory allocated by `getifaddrs`.
unsafe {
freeifaddrs(self.0);
}
}
}
}
struct IfAddrPtr<'a> {
ptr: *mut ifaddrs,
_ref: PhantomData<&'a ifaddrs>,
}
impl IfAddrPtr<'_> {
fn addr(&self) -> sockaddr {
unsafe { *self.ifa_addr }
}
fn name(&self) -> String {
unsafe { CStr::from_ptr(self.ifa_name).to_string_lossy().to_string() }
}
fn data(&self) -> Option<if_data> {
if self.ifa_data.is_null() {
None
} else {
Some(unsafe { self.ifa_data.cast::<if_data>().read() })
}
}
}
impl Deref for IfAddrPtr<'_> {
type Target = ifaddrs;
fn deref(&self) -> &Self::Target {
unsafe { self.ptr.as_ref().expect("can deref") }
}
}
impl Iterator for IfAddrPtr<'_> {
type Item = Self;
fn next(&mut self) -> Option<Self::Item> {
ptr::NonNull::new(self.ptr).map(|p| {
self.ptr = unsafe { p.as_ref().ifa_next };
IfAddrPtr {
ptr: p.as_ptr(),
_ref: PhantomData,
}
})
}
}
fn if_name_mtu(idx: u32) -> Result<(String, Option<usize>)> {
let mut name = [0; libc::IF_NAMESIZE];
// if_indextoname writes into the provided buffer.
if unsafe { if_indextoname(idx, name.as_mut_ptr()).is_null() } {
return Err(Error::last_os_error());
}
// Convert to Rust string.
let name = unsafe {
CStr::from_ptr(name.as_ptr())
.to_str()
.map_err(|err| Error::new(ErrorKind::Other, err))?
};
let mtu = IfAddrs::new()?
.iter()
.find(|ifa| ifa.addr().sa_family == AF_LINK && ifa.name() == name)
.and_then(|ifa| ifa.data())
.and_then(|ifa_data| usize::try_from(ifa_data.ifi_mtu).ok());
Ok((name.to_string(), mtu))
}
#[repr(C)]
union SockaddrStorage {
sin: sockaddr_in,
sin6: sockaddr_in6,
}
fn sockaddr_len(af: AddressFamily) -> Result<usize> {
let sa_len = match af {
AF_INET => std::mem::size_of::<sockaddr_in>(),
AF_INET6 => std::mem::size_of::<sockaddr_in6>(),
_ => {
return Err(Error::new(
ErrorKind::InvalidInput,
format!("Unsupported address family {af:?}"),
))
}
};
Ok(aligned_by(sa_len, ALIGN))
}
impl From<IpAddr> for SockaddrStorage {
fn from(ip: IpAddr) -> Self {
match ip {
IpAddr::V4(ip) => SockaddrStorage {
sin: sockaddr_in {
#[cfg(not(target_os = "solaris"))]
#[allow(clippy::cast_possible_truncation)]
// `sockaddr_in` len is <= u8::MAX per `const_assert!` above.
sin_len: std::mem::size_of::<sockaddr_in>() as u8,
sin_family: AF_INET,
sin_addr: in_addr {
s_addr: u32::from_ne_bytes(ip.octets()),
},
sin_port: 0,
sin_zero: [0; 8],
},
},
IpAddr::V6(ip) => SockaddrStorage {
sin6: sockaddr_in6 {
#[cfg(not(target_os = "solaris"))]
#[allow(clippy::cast_possible_truncation)]
// `sockaddr_in6` len is <= u8::MAX per `const_assert!` above.
sin6_len: std::mem::size_of::<sockaddr_in6>() as u8,
sin6_family: AF_INET6,
sin6_addr: in6_addr {
s6_addr: ip.octets(),
},
sin6_port: 0,
sin6_flowinfo: 0,
sin6_scope_id: 0,
#[cfg(target_os = "solaris")]
__sin6_src_id: 0,
},
},
}
}
}
#[repr(C)]
struct RouteMessage {
rtm: rt_msghdr,
sa: SockaddrStorage,
}
impl RouteMessage {
fn new(remote: IpAddr, seq: i32) -> Result<Self> {
let sa = SockaddrStorage::from(remote);
let sa_len = sockaddr_len(match remote {
IpAddr::V4(_) => AF_INET,
IpAddr::V6(_) => AF_INET6,
})?;
Ok(Self {
rtm: rt_msghdr {
#[allow(clippy::cast_possible_truncation)]
// `rt_msghdr` len + `ALIGN` is <= u8::MAX per `const_assert!` above.
rtm_msglen: (std::mem::size_of::<rt_msghdr>() + sa_len) as u16,
rtm_version: RTM_VERSION,
rtm_type: RTM_GET,
rtm_seq: seq,
rtm_addrs: RTM_ADDRS,
..Default::default()
},
sa,
})
}
const fn version(&self) -> u8 {
self.rtm.rtm_version
}
const fn kind(&self) -> u8 {
self.rtm.rtm_type
}
const fn len(&self) -> usize {
self.rtm.rtm_msglen as usize
}
}
impl From<&RouteMessage> for &[u8] {
fn from(value: &RouteMessage) -> Self {
debug_assert!(value.len() >= std::mem::size_of::<Self>());
unsafe { slice::from_raw_parts(ptr::from_ref(value).cast(), value.len()) }
}
}
impl From<&[u8]> for rt_msghdr {
fn from(value: &[u8]) -> Self {
debug_assert!(value.len() >= std::mem::size_of::<Self>());
unsafe { ptr::read_unaligned(value.as_ptr().cast()) }
}
}
fn if_index_mtu(remote: IpAddr) -> Result<(u16, Option<usize>)> {
// Open route socket.
let mut fd = RouteSocket::new(PF_ROUTE, AF_UNSPEC)?;
// Send route message.
let query_seq = RouteSocket::new_seq();
let query = RouteMessage::new(remote, query_seq)?;
let query_version = query.version();
let query_type = query.kind();
fd.write_all((&query).into())?;
// Read route messages.
let pid = unsafe { getpid() };
loop {
let mut buf = vec![
0u8;
std::mem::size_of::<rt_msghdr>() +
// There will never be `RTAX_MAX` sockaddrs attached, but it's a safe upper bound.
(RTAX_MAX as usize * std::mem::size_of::<sockaddr_storage>())
];
let len = fd.read(&mut buf[..])?;
if len < std::mem::size_of::<rt_msghdr>() {
return Err(default_err());
}
let (reply, mut sa) = buf.split_at(std::mem::size_of::<rt_msghdr>());
let reply: rt_msghdr = reply.into();
if !(reply.rtm_version == query_version
&& reply.rtm_pid == pid
&& reply.rtm_seq == query_seq)
{
continue;
}
if reply.rtm_type != query_type {
return Err(default_err());
}
// This is a reply to our query.
// This is the reply we are looking for.
// Some BSDs let us get the interface index and MTU directly from the reply.
let mtu = (reply.rtm_rmx.rmx_mtu != 0)
.then(|| usize::try_from(reply.rtm_rmx.rmx_mtu))
.transpose()
.map_err(|e: TryFromIntError| unlikely_err(e.to_string()))?;
if reply.rtm_index != 0 {
// Some BSDs return the interface index directly.
return Ok((reply.rtm_index, mtu));
}
// For others, we need to extract it from the sockaddrs.
for i in 0..RTAX_MAX {
if (reply.rtm_addrs & (1 << i)) == 0 {
continue;
}
let saddr = unsafe { ptr::read_unaligned(sa.as_ptr().cast::<sockaddr>()) };
if saddr.sa_family != AF_LINK {
(_, sa) = sa.split_at(sockaddr_len(saddr.sa_family)?);
continue;
}
let sdl = unsafe { ptr::read_unaligned(sa.as_ptr().cast::<sockaddr_dl>()) };
return Ok((sdl.sdl_index, mtu));
}
}
}
pub fn interface_and_mtu_impl(remote: IpAddr) -> Result<(String, usize)> {
let (if_index, mtu1) = if_index_mtu(remote)?;
let (if_name, mtu2) = if_name_mtu(if_index.into())?;
Ok((if_name, mtu1.or(mtu2).ok_or_else(default_err)?))
}

199
third_party/rust/mtu/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,199 @@
// 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.
//! A crate to return the name and maximum transmission unit (MTU) of the local network interface
//! towards a given destination `SocketAddr`, optionally from a given local `SocketAddr`.
//!
//! # Usage
//!
//! This crate exports a single function `interface_and_mtu` that returns the name and
//! [maximum transmission unit (MTU)](https://en.wikipedia.org/wiki/Maximum_transmission_unit)
//! of the outgoing network interface towards a remote destination identified by an `IpAddr`.
//!
//! # Example
//! ```
//! # use std::net::{IpAddr, Ipv4Addr};
//! let destination = IpAddr::V4(Ipv4Addr::LOCALHOST);
//! let (name, mtu): (String, usize) = mtu::interface_and_mtu(destination).unwrap();
//! println!("MTU towards {destination} is {mtu} on {name}");
//! ```
//!
//! # Supported Platforms
//!
//! * Linux
//! * macOS
//! * Windows
//! * FreeBSD
//! * NetBSD
//! * OpenBSD
//! * Solaris
//!
//! # Notes
//!
//! The returned MTU may exceed the maximum IP packet size of 65,535 bytes on some platforms for
//! some remote destinations. (For example, loopback destinations on Windows.)
//!
//! The returned interface name is obtained from the operating system.
//!
//! # Contributing
//!
//! We're happy to receive PRs that improve this crate. Please take a look at our [community
//! guidelines](CODE_OF_CONDUCT.md) beforehand.
use std::{
io::{Error, ErrorKind, Result},
net::IpAddr,
};
#[cfg(not(target_os = "windows"))]
macro_rules! asserted_const_with_type {
($name:ident, $t1:ty, $e:expr, $t2:ty) => {
#[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)] // Guarded by the following `const_assert_eq!`.
const $name: $t1 = $e as $t1;
const_assert_eq!($name as $t2, $e);
};
}
#[cfg(any(target_os = "macos", bsd))]
mod bsd;
#[cfg(any(target_os = "linux", target_os = "android"))]
mod linux;
#[cfg(target_os = "windows")]
mod windows;
#[cfg(not(target_os = "windows"))]
mod routesocket;
#[cfg(any(target_os = "macos", bsd))]
use bsd::interface_and_mtu_impl;
#[cfg(any(target_os = "linux", target_os = "android"))]
use linux::interface_and_mtu_impl;
#[cfg(target_os = "windows")]
use windows::interface_and_mtu_impl;
/// Prepare a default error.
fn default_err() -> Error {
Error::new(ErrorKind::NotFound, "Local interface MTU not found")
}
/// Prepare an error for cases that "should never happen".
#[cfg(not(target_os = "windows"))]
fn unlikely_err(msg: String) -> Error {
debug_assert!(false, "{msg}");
Error::new(ErrorKind::Other, msg)
}
/// Align `size` to the next multiple of `align` (which needs to be a power of two).
#[cfg(not(target_os = "windows"))]
const fn aligned_by(size: usize, align: usize) -> usize {
if size == 0 {
align
} else {
1 + ((size - 1) | (align - 1))
}
}
// Platforms currently not supported.
//
// See <https://github.com/mozilla/mtu/issues/82>.
#[cfg(any(target_os = "ios", target_os = "tvos", target_os = "visionos"))]
pub fn interface_and_mtu_impl(remote: IpAddr) -> Result<(String, usize)> {
return Err(default_err());
}
/// Return the name and maximum transmission unit (MTU) of the outgoing network interface towards a
/// remote destination identified by an [`IpAddr`],
///
/// The returned MTU may exceed the maximum IP packet size of 65,535 bytes on some platforms for
/// some remote destinations. (For example, loopback destinations on Windows.)
///
/// The returned interface name is obtained from the operating system.
///
/// # Errors
///
/// This function returns an error if the local interface MTU cannot be determined.
pub fn interface_and_mtu(remote: IpAddr) -> Result<(String, usize)> {
interface_and_mtu_impl(remote)
}
#[cfg(test)]
mod test {
use std::{
env,
net::{IpAddr, Ipv4Addr, Ipv6Addr},
};
use crate::interface_and_mtu;
#[derive(Debug)]
struct NameMtu<'a>(Option<&'a str>, usize);
impl PartialEq<NameMtu<'_>> for (String, usize) {
fn eq(&self, other: &NameMtu<'_>) -> bool {
other.0.map_or(true, |name| name == self.0) && other.1 == self.1
}
}
#[cfg(any(target_os = "macos", target_os = "freebsd",))]
const LOOPBACK: &[NameMtu] = &[NameMtu(Some("lo0"), 16_384), NameMtu(Some("lo0"), 16_384)];
#[cfg(any(target_os = "linux", target_os = "android"))]
const LOOPBACK: &[NameMtu] = &[NameMtu(Some("lo"), 65_536), NameMtu(Some("lo"), 65_536)];
#[cfg(target_os = "windows")]
const LOOPBACK: &[NameMtu] = &[
NameMtu(Some("loopback_0"), 4_294_967_295),
NameMtu(Some("loopback_0"), 4_294_967_295),
];
#[cfg(target_os = "openbsd")]
const LOOPBACK: &[NameMtu] = &[NameMtu(Some("lo0"), 32_768), NameMtu(Some("lo0"), 32_768)];
#[cfg(target_os = "netbsd")]
const LOOPBACK: &[NameMtu] = &[NameMtu(Some("lo0"), 33_624), NameMtu(Some("lo0"), 33_624)];
#[cfg(target_os = "solaris")]
// Note: Different loopback MTUs for IPv4 and IPv6?!
const LOOPBACK: &[NameMtu] = &[NameMtu(Some("lo0"), 8_232), NameMtu(Some("lo0"), 8_252)];
// Non-loopback interface names are unpredictable, so we only check the MTU.
const INET: NameMtu = NameMtu(None, 1_500);
#[test]
fn loopback_v4() {
assert_eq!(
interface_and_mtu(IpAddr::V4(Ipv4Addr::LOCALHOST)).unwrap(),
LOOPBACK[0]
);
}
#[test]
fn loopback_v6() {
assert_eq!(
interface_and_mtu(IpAddr::V6(Ipv6Addr::LOCALHOST)).unwrap(),
LOOPBACK[1]
);
}
#[test]
fn inet_v4() {
assert_eq!(
interface_and_mtu(IpAddr::V4(Ipv4Addr::new(
104, 16, 132, 229 // cloudflare.com
)))
.unwrap(),
INET
);
}
#[test]
fn inet_v6() {
match interface_and_mtu(IpAddr::V6(Ipv6Addr::new(
0x2606, 0x4700, 0, 0, 0, 0, 0x6810, 0x84e5, // cloudflare.com
))) {
Ok(res) => assert_eq!(res, INET),
// The GitHub CI environment does not have IPv6 connectivity.
Err(_) => assert!(env::var("GITHUB_ACTIONS").is_ok()),
}
}
}

351
third_party/rust/mtu/src/linux.rs vendored Normal file
View File

@@ -0,0 +1,351 @@
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::{
ffi::CStr,
io::{Error, ErrorKind, Read as _, Result, Write as _},
net::IpAddr,
num::TryFromIntError,
ptr, slice,
};
use libc::{
c_int, AF_NETLINK, ARPHRD_NONE, IFLA_IFNAME, IFLA_MTU, NETLINK_ROUTE, RTA_DST, RTA_OIF,
RTM_GETLINK, RTM_GETROUTE, RTM_NEWLINK, RTM_NEWROUTE, RTN_UNICAST, RT_SCOPE_UNIVERSE,
RT_TABLE_MAIN,
};
use static_assertions::{const_assert, const_assert_eq};
use crate::{aligned_by, default_err, routesocket::RouteSocket, unlikely_err};
#[allow(
clippy::struct_field_names,
non_camel_case_types,
clippy::too_many_lines
)]
mod bindings {
include!(env!("BINDINGS"));
}
use bindings::{ifinfomsg, nlmsghdr, rtattr, rtmsg};
asserted_const_with_type!(AF_INET, u8, libc::AF_INET, i32);
asserted_const_with_type!(AF_INET6, u8, libc::AF_INET6, i32);
asserted_const_with_type!(AF_UNSPEC, u8, libc::AF_UNSPEC, i32);
asserted_const_with_type!(NLM_F_REQUEST, u16, libc::NLM_F_REQUEST, c_int);
asserted_const_with_type!(NLM_F_ACK, u16, libc::NLM_F_ACK, c_int);
asserted_const_with_type!(NLMSG_ERROR, u16, libc::NLMSG_ERROR, c_int);
const_assert!(std::mem::size_of::<nlmsghdr>() <= u8::MAX as usize);
const_assert!(std::mem::size_of::<rtmsg>() <= u8::MAX as usize);
const_assert!(std::mem::size_of::<rtattr>() <= u8::MAX as usize);
const_assert!(std::mem::size_of::<ifinfomsg>() <= u8::MAX as usize);
const NETLINK_BUFFER_SIZE: usize = 8192; // See netlink(7) man page.
#[repr(C)]
enum AddrBytes {
V4([u8; 4]),
V6([u8; 16]),
}
impl AddrBytes {
const fn new(ip: IpAddr) -> Self {
match ip {
IpAddr::V4(ip) => Self::V4(ip.octets()),
IpAddr::V6(ip) => Self::V6(ip.octets()),
}
}
const fn len(&self) -> usize {
match self {
Self::V4(_) => 4,
Self::V6(_) => 16,
}
}
}
impl From<AddrBytes> for [u8; 16] {
fn from(addr: AddrBytes) -> Self {
match addr {
AddrBytes::V4(bytes) => {
let mut v6 = [0; 16];
v6[..4].copy_from_slice(&bytes);
v6
}
AddrBytes::V6(bytes) => bytes,
}
}
}
#[repr(C)]
#[derive(Default)]
struct IfIndexMsg {
nlmsg: nlmsghdr,
rtm: rtmsg,
rt: rtattr,
addr: [u8; 16],
}
impl IfIndexMsg {
fn new(remote: IpAddr, nlmsg_seq: u32) -> Self {
let addr = AddrBytes::new(remote);
#[allow(clippy::cast_possible_truncation)]
// Structs lens are <= u8::MAX per `const_assert!`s above; `addr_bytes` is max. 16 for IPv6.
let nlmsg_len = (std::mem::size_of::<nlmsghdr>()
+ std::mem::size_of::<rtmsg>()
+ std::mem::size_of::<rtattr>()
+ addr.len()) as u32;
Self {
nlmsg: nlmsghdr {
nlmsg_len,
nlmsg_type: RTM_GETROUTE,
nlmsg_flags: NLM_F_REQUEST | NLM_F_ACK,
nlmsg_seq,
..Default::default()
},
rtm: rtmsg {
rtm_family: match remote {
IpAddr::V4(_) => AF_INET,
IpAddr::V6(_) => AF_INET6,
},
rtm_dst_len: match remote {
IpAddr::V4(_) => 32,
IpAddr::V6(_) => 128,
},
rtm_table: RT_TABLE_MAIN,
rtm_scope: RT_SCOPE_UNIVERSE,
rtm_type: RTN_UNICAST,
..Default::default()
},
rt: rtattr {
#[allow(clippy::cast_possible_truncation)]
// Structs len is <= u8::MAX per `const_assert!` above; `addr_bytes` is max. 16 for IPv6.
rta_len: (std::mem::size_of::<rtattr>() + addr.len()) as u16,
rta_type: RTA_DST,
},
addr: addr.into(),
}
}
const fn len(&self) -> usize {
let len = self.nlmsg.nlmsg_len as usize;
debug_assert!(len <= std::mem::size_of::<Self>());
len
}
}
impl From<&IfIndexMsg> for &[u8] {
fn from(value: &IfIndexMsg) -> Self {
unsafe { slice::from_raw_parts(ptr::from_ref(value).cast(), value.len()) }
}
}
impl TryFrom<&[u8]> for nlmsghdr {
type Error = Error;
fn try_from(value: &[u8]) -> Result<Self> {
if value.len() < std::mem::size_of::<Self>() {
return Err(default_err());
}
Ok(unsafe { ptr::read_unaligned(value.as_ptr().cast()) })
}
}
fn parse_c_int(buf: &[u8]) -> Result<c_int> {
let bytes = <&[u8] as TryInto<[u8; std::mem::size_of::<c_int>()]>>::try_into(
&buf[..std::mem::size_of::<c_int>()],
)
.map_err(|_| default_err())?;
Ok(c_int::from_ne_bytes(bytes))
}
fn read_msg_with_seq(fd: &mut RouteSocket, seq: u32, kind: u16) -> Result<(nlmsghdr, Vec<u8>)> {
loop {
let buf = &mut [0u8; NETLINK_BUFFER_SIZE];
let len = fd.read(buf.as_mut_slice())?;
let mut next = &buf[..len];
while std::mem::size_of::<nlmsghdr>() <= next.len() {
let (hdr, mut msg) = next.split_at(std::mem::size_of::<nlmsghdr>());
let hdr: nlmsghdr = hdr.try_into()?;
// `msg` has the remainder of this message plus any following messages.
// Strip those it off and assign them to `next`.
debug_assert!(std::mem::size_of::<nlmsghdr>() <= hdr.nlmsg_len as usize);
(msg, next) = msg.split_at(hdr.nlmsg_len as usize - std::mem::size_of::<nlmsghdr>());
if hdr.nlmsg_seq != seq {
continue;
}
if hdr.nlmsg_type == NLMSG_ERROR {
// Extract the error code and return it.
let err = parse_c_int(msg)?;
if err != 0 {
return Err(Error::from_raw_os_error(-err));
}
} else if hdr.nlmsg_type == kind {
// Return the header and the message.
return Ok((hdr, msg.to_vec()));
}
}
}
}
impl TryFrom<&[u8]> for rtattr {
type Error = Error;
fn try_from(value: &[u8]) -> Result<Self> {
if value.len() < std::mem::size_of::<Self>() {
return Err(default_err());
}
Ok(unsafe { ptr::read_unaligned(value.as_ptr().cast()) })
}
}
struct RtAttr<'a> {
hdr: rtattr,
msg: &'a [u8],
}
impl<'a> RtAttr<'a> {
fn new(bytes: &'a [u8]) -> Result<Self> {
debug_assert!(bytes.len() >= std::mem::size_of::<rtattr>());
let (hdr, mut msg) = bytes.split_at(std::mem::size_of::<rtattr>());
let hdr: rtattr = hdr.try_into()?;
let aligned_len = aligned_by(hdr.rta_len.into(), 4);
debug_assert!(std::mem::size_of::<rtattr>() <= aligned_len);
(msg, _) = msg.split_at(aligned_len - std::mem::size_of::<rtattr>());
Ok(Self { hdr, msg })
}
}
struct RtAttrs<'a>(&'a [u8]);
impl<'a> Iterator for RtAttrs<'a> {
type Item = RtAttr<'a>;
fn next(&mut self) -> Option<Self::Item> {
if std::mem::size_of::<rtattr>() <= self.0.len() {
let attr = RtAttr::new(self.0).ok()?;
let aligned_len = aligned_by(attr.hdr.rta_len.into(), 4);
debug_assert!(self.0.len() >= aligned_len);
self.0 = self.0.split_at(aligned_len).1;
Some(attr)
} else {
None
}
}
}
fn if_index(remote: IpAddr, fd: &mut RouteSocket) -> Result<i32> {
// Send RTM_GETROUTE message to get the interface index associated with the destination.
let msg_seq = RouteSocket::new_seq();
let msg = IfIndexMsg::new(remote, msg_seq);
fd.write_all((&msg).into())?;
// Receive RTM_GETROUTE response.
let (_hdr, mut buf) = read_msg_with_seq(fd, msg_seq, RTM_NEWROUTE)?;
debug_assert!(std::mem::size_of::<rtmsg>() <= buf.len());
let buf = buf.split_off(std::mem::size_of::<rtmsg>());
// Parse through the attributes to find the interface index.
for attr in RtAttrs(buf.as_slice()).by_ref() {
if attr.hdr.rta_type == RTA_OIF {
// We have our interface index.
return parse_c_int(attr.msg);
}
}
Err(default_err())
}
#[repr(C)]
struct IfInfoMsg {
nlmsg: nlmsghdr,
ifim: ifinfomsg,
}
impl IfInfoMsg {
fn new(if_index: i32, nlmsg_seq: u32) -> Self {
#[allow(clippy::cast_possible_truncation)]
// Structs lens are <= u8::MAX per `const_assert!`s above.
let nlmsg_len = (std::mem::size_of::<nlmsghdr>() + std::mem::size_of::<ifinfomsg>()) as u32;
Self {
nlmsg: nlmsghdr {
nlmsg_len,
nlmsg_type: RTM_GETLINK,
nlmsg_flags: NLM_F_REQUEST | NLM_F_ACK,
nlmsg_seq,
..Default::default()
},
ifim: ifinfomsg {
ifi_family: AF_UNSPEC,
ifi_type: ARPHRD_NONE,
ifi_index: if_index,
..Default::default()
},
}
}
const fn len(&self) -> usize {
self.nlmsg.nlmsg_len as usize
}
}
impl From<&IfInfoMsg> for &[u8] {
fn from(value: &IfInfoMsg) -> Self {
debug_assert!(value.len() >= std::mem::size_of::<Self>());
unsafe { slice::from_raw_parts(ptr::from_ref(value).cast(), value.len()) }
}
}
fn if_name_mtu(if_index: i32, fd: &mut RouteSocket) -> Result<(String, usize)> {
// Send RTM_GETLINK message to get interface information for the given interface index.
let msg_seq = RouteSocket::new_seq();
let msg = IfInfoMsg::new(if_index, msg_seq);
fd.write_all((&msg).into())?;
// Receive RTM_GETLINK response.
let (_hdr, mut buf) = read_msg_with_seq(fd, msg_seq, RTM_NEWLINK)?;
debug_assert!(std::mem::size_of::<ifinfomsg>() <= buf.len());
let buf = buf.split_off(std::mem::size_of::<ifinfomsg>());
// Parse through the attributes to find the interface name and MTU.
let mut ifname = None;
let mut mtu = None;
for attr in RtAttrs(buf.as_slice()).by_ref() {
match attr.hdr.rta_type {
IFLA_IFNAME => {
let name = CStr::from_bytes_until_nul(attr.msg)
.map_err(|err| Error::new(ErrorKind::Other, err))?;
ifname = Some(
name.to_str()
.map_err(|err| Error::new(ErrorKind::Other, err))?
.to_string(),
);
}
IFLA_MTU => {
mtu = Some(
parse_c_int(attr.msg)?
.try_into()
.map_err(|e: TryFromIntError| unlikely_err(e.to_string()))?,
);
}
_ => (),
}
if let (Some(ifname), Some(mtu)) = (ifname.as_ref(), mtu.as_ref()) {
return Ok((ifname.clone(), *mtu));
}
}
Err(default_err())
}
pub fn interface_and_mtu_impl(remote: IpAddr) -> Result<(String, usize)> {
// Create a netlink socket.
let mut fd = RouteSocket::new(AF_NETLINK, NETLINK_ROUTE)?;
let if_index = if_index(remote, &mut fd)?;
if_name_mtu(if_index, &mut fd)
}

81
third_party/rust/mtu/src/routesocket.rs vendored Normal file
View File

@@ -0,0 +1,81 @@
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::{
io::{Error, Read, Result, Write},
num::TryFromIntError,
os::fd::{AsRawFd, FromRawFd as _, OwnedFd},
sync::atomic::Ordering,
};
use libc::{fsync, read, socket, write, SOCK_RAW};
use crate::unlikely_err;
#[cfg(any(target_os = "linux", target_os = "android"))]
type AtomicRouteSocketSeq = std::sync::atomic::AtomicU32;
#[cfg(any(target_os = "linux", target_os = "android"))]
type RouteSocketSeq = u32;
#[cfg(not(any(target_os = "linux", target_os = "android")))]
type AtomicRouteSocketSeq = std::sync::atomic::AtomicI32;
#[cfg(not(any(target_os = "linux", target_os = "android")))]
type RouteSocketSeq = i32;
static SEQ: AtomicRouteSocketSeq = AtomicRouteSocketSeq::new(0);
pub struct RouteSocket(OwnedFd);
impl RouteSocket {
pub fn new(domain: libc::c_int, protocol: libc::c_int) -> Result<Self> {
let fd = unsafe { socket(domain, SOCK_RAW, protocol) };
if fd == -1 {
return Err(Error::last_os_error());
}
Ok(Self(unsafe { OwnedFd::from_raw_fd(fd) }))
}
pub fn new_seq() -> RouteSocketSeq {
SEQ.fetch_add(1, Ordering::Relaxed)
}
}
impl AsRawFd for RouteSocket {
fn as_raw_fd(&self) -> i32 {
self.0.as_raw_fd()
}
}
fn check_result(res: isize) -> Result<usize> {
if res == -1 {
Err(Error::last_os_error())
} else {
Ok(res
.try_into()
.map_err(|e: TryFromIntError| unlikely_err(e.to_string()))?)
}
}
impl Write for RouteSocket {
fn write(&mut self, buf: &[u8]) -> Result<usize> {
let res = unsafe { write(self.as_raw_fd(), buf.as_ptr().cast(), buf.len()) };
check_result(res)
}
fn flush(&mut self) -> Result<()> {
let res = unsafe { fsync(self.as_raw_fd()) };
check_result(res as isize).and(Ok(()))
}
}
impl Read for RouteSocket {
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
// If we've written a well-formed message into the kernel via `write`, we should be able to
// read a well-formed message back out, and not block.
let res = unsafe { read(self.as_raw_fd(), buf.as_mut_ptr().cast(), buf.len()) };
check_result(res)
}
}

143
third_party/rust/mtu/src/windows.rs vendored Normal file
View File

@@ -0,0 +1,143 @@
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::{
ffi::CStr,
io::{Error, ErrorKind, Result},
net::IpAddr,
ptr, slice,
};
use windows::Win32::{
Foundation::NO_ERROR,
NetworkManagement::{
IpHelper::{
if_indextoname, FreeMibTable, GetBestInterfaceEx, GetIpInterfaceTable,
MIB_IPINTERFACE_ROW, MIB_IPINTERFACE_TABLE,
},
Ndis::IF_MAX_STRING_SIZE,
},
Networking::WinSock::{
AF_INET, AF_INET6, IN6_ADDR, IN6_ADDR_0, IN_ADDR, IN_ADDR_0, SOCKADDR, SOCKADDR_IN,
SOCKADDR_IN6, SOCKADDR_INET,
},
};
use crate::default_err;
struct MibTablePtr(*mut MIB_IPINTERFACE_TABLE);
impl MibTablePtr {
fn mut_ptr_ptr(&mut self) -> *mut *mut MIB_IPINTERFACE_TABLE {
ptr::from_mut(&mut self.0)
}
}
impl Default for MibTablePtr {
fn default() -> Self {
Self(ptr::null_mut())
}
}
impl Drop for MibTablePtr {
fn drop(&mut self) {
if !self.0.is_null() {
// Free the memory allocated by GetIpInterfaceTable.
unsafe {
FreeMibTable(self.0.cast());
}
}
}
}
pub fn interface_and_mtu_impl(remote: IpAddr) -> Result<(String, usize)> {
// Convert remote to Windows SOCKADDR_INET format. The SOCKADDR_INET union contains an IPv4 or
// an IPv6 address.
//
// See https://learn.microsoft.com/en-us/windows/win32/api/ws2ipdef/ns-ws2ipdef-sockaddr_inet
let dst = match remote {
IpAddr::V4(ip) => {
// Initialize the `SOCKADDR_IN` variant of `SOCKADDR_INET` based on `ip`.
SOCKADDR_INET {
Ipv4: SOCKADDR_IN {
sin_family: AF_INET,
sin_addr: IN_ADDR {
S_un: IN_ADDR_0 {
S_addr: u32::to_be(ip.into()),
},
},
..Default::default()
},
}
}
IpAddr::V6(ip) => {
// Initialize the `SOCKADDR_IN6` variant of `SOCKADDR_INET` based on `ip`.
SOCKADDR_INET {
Ipv6: SOCKADDR_IN6 {
sin6_family: AF_INET6,
sin6_addr: IN6_ADDR {
u: IN6_ADDR_0 { Byte: ip.octets() },
},
..Default::default()
},
}
}
};
// Get the interface index of the best outbound interface towards `dst`.
let mut idx = 0;
let res = unsafe {
// We're now casting `&dst` to a `SOCKADDR` pointer. This is OK based on
// https://learn.microsoft.com/en-us/windows/win32/winsock/sockaddr-2.
// With that, we call `GetBestInterfaceEx` to get the interface index into `idx`.
// See https://learn.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-getbestinterfaceex
GetBestInterfaceEx(
ptr::from_ref(&dst).cast::<SOCKADDR>(),
ptr::from_mut(&mut idx),
)
};
if res != 0 {
return Err(Error::last_os_error());
}
// Get a list of all interfaces with associated metadata.
let mut if_table = MibTablePtr::default();
// GetIpInterfaceTable allocates memory, which MibTablePtr::drop will free.
let family = if remote.is_ipv4() { AF_INET } else { AF_INET6 };
if unsafe { GetIpInterfaceTable(family, if_table.mut_ptr_ptr()) } != NO_ERROR {
return Err(Error::last_os_error());
}
// Make a slice
let ifaces = unsafe {
slice::from_raw_parts::<MIB_IPINTERFACE_ROW>(
&(*if_table.0).Table[0],
(*if_table.0).NumEntries as usize,
)
};
// Find the local interface matching `idx`.
for iface in ifaces {
if iface.InterfaceIndex == idx {
// Get the MTU.
let mtu: usize = iface.NlMtu.try_into().map_err(|_| default_err())?;
// Get the interface name.
let mut interfacename = [0u8; IF_MAX_STRING_SIZE as usize];
// if_indextoname writes into the provided buffer.
if unsafe { if_indextoname(iface.InterfaceIndex, &mut interfacename).is_null() } {
return Err(default_err());
}
// Convert the interface name to a Rust string.
let name = CStr::from_bytes_until_nul(interfacename.as_ref())
.map_err(|_| default_err())?
.to_str()
.map_err(|err| Error::new(ErrorKind::Other, err))?
.to_string();
// We found our interface information.
return Ok((name, mtu));
}
}
Err(default_err())
}

View File

@@ -1 +1 @@
{"files":{"Cargo.toml":"f45efe77b2a12f3c4b84a412b25a22ebac2cc97c5fd8df8a774f9757267986b6","benches/main.rs":"1f00060094b0f77ac83b2f0817560bb6198c95742a7bf9330258906f3a4f57fe","src/bin/client.rs":"db77efd75dc0745b6dd983ab8fa3bc8f5f9111967f0d90d23cb19140a940246d","src/bin/server.rs":"2f7ab3c7a98117bd162e6fd07abef1d21791d1bb240db3aae61afa6ff72df83a","src/client/http09.rs":"ac3526f3689dfe61ac336c90f936a2113a47b1112eb6e765d2112898f42ff38f","src/client/http3.rs":"40f6b94a31edceb911609a75929df67dcab87d6c88501b14adfca0e31dbb26c0","src/client/mod.rs":"c15859494d447cd777a0cce41bd7e36d9ffc7bbab494ed2f0c9eb7d058e3bdbe","src/lib.rs":"acbf69e2c99d2ca47d3356fad9b4d08acf4a2a9b481e27af2ccb46dc19cd50e6","src/send_data.rs":"7537b7a0f67e9077cf121cc3d0fcd42edb085801dd4baf25b14c22a01f1bf04f","src/server/http09.rs":"3dbde3e801532b6e83bb86eeea5d87ec91294bb43c05c956c3783a7722780a5e","src/server/http3.rs":"7f30864eb165cb614cd5bdbf4d59f736a2409316fe848e6295b8618099e5cbdd","src/server/mod.rs":"d42c2991f3a2854309785800f007b1dafd72232e1f6870a6e9cf60387202382b","src/udp.rs":"4aadb956e50f961241b2850e6f3bdf715ccbac943e3ab585f4b46e755d03d2de"},"package":null} {"files":{"Cargo.toml":"30905a8b427891bbf7a6b46b846b49140d553f9dfd978f4e24aaaff43a0d50fa","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}

View File

@@ -13,7 +13,7 @@
edition = "2021" edition = "2021"
rust-version = "1.76.0" rust-version = "1.76.0"
name = "neqo-bin" name = "neqo-bin"
version = "0.11.0" version = "0.12.2"
authors = ["The Neqo Authors <necko@mozilla.com>"] authors = ["The Neqo Authors <necko@mozilla.com>"]
build = false build = false
autolib = false autolib = false
@@ -39,6 +39,9 @@ categories = [
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
repository = "https://github.com/mozilla/neqo/" repository = "https://github.com/mozilla/neqo/"
[package.metadata.cargo-machete]
ignored = ["log"]
[lib] [lib]
name = "neqo_bin" name = "neqo_bin"
path = "src/lib.rs" path = "src/lib.rs"
@@ -112,7 +115,10 @@ default-features = false
[dependencies.quinn-udp] [dependencies.quinn-udp]
version = "0.5.6" version = "0.5.6"
features = ["direct-log"] features = [
"direct-log",
"fast-apple-datapath",
]
default-features = false default-features = false
[dependencies.regex] [dependencies.regex]
@@ -141,16 +147,48 @@ version = "0.5"
features = ["async_tokio"] features = ["async_tokio"]
default-features = false default-features = false
[dev-dependencies.neqo-bin]
path = "."
features = ["draft-29"]
[dev-dependencies.neqo-http3]
path = "./../neqo-http3"
features = ["draft-29"]
[dev-dependencies.neqo-transport]
path = "./../neqo-transport"
features = ["draft-29"]
[dev-dependencies.tokio] [dev-dependencies.tokio]
version = "1" version = "1"
features = ["sync"] features = ["sync"]
default-features = false default-features = false
[features] [features]
bench = ["neqo-transport/bench"] bench = [
"neqo-bin/bench",
"neqo-http3/bench",
"neqo-transport/bench",
]
draft-29 = []
[lints.clippy] [lints.clippy]
cfg_not_test = "warn"
clone_on_ref_ptr = "warn"
create_dir = "warn"
get_unwrap = "warn"
if_then_some_else_none = "warn"
multiple_crate_versions = "allow" multiple_crate_versions = "allow"
multiple_inherent_impl = "warn"
pathbuf_init_then_push = "warn"
redundant_type_annotations = "warn"
ref_patterns = "warn"
renamed_function_params = "warn"
semicolon_inside_block = "warn"
try_err = "warn"
unneeded_field_pattern = "warn"
unused_result_ok = "warn"
unused_trait_names = "warn"
[lints.clippy.cargo] [lints.clippy.cargo]
level = "warn" level = "warn"
@@ -163,3 +201,19 @@ priority = -1
[lints.clippy.pedantic] [lints.clippy.pedantic]
level = "warn" level = "warn"
priority = -1 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"
non_ascii_idents = "warn"
redundant_imports = "warn"
redundant_lifetimes = "warn"
trivial_numeric_casts = "warn"
unit_bindings = "warn"
unused_import_braces = "warn"
unused_lifetimes = "warn"
unused_macro_rules = "warn"

View File

@@ -4,7 +4,7 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
use std::{env, path::PathBuf, str::FromStr}; use std::{env, path::PathBuf, str::FromStr as _};
use criterion::{criterion_group, criterion_main, BatchSize, Criterion, Throughput}; use criterion::{criterion_group, criterion_main, BatchSize, Criterion, Throughput};
use neqo_bin::{client, server}; use neqo_bin::{client, server};
@@ -17,7 +17,6 @@ struct Benchmark {
} }
fn transfer(c: &mut Criterion) { fn transfer(c: &mut Criterion) {
neqo_common::log::init(Some(log::LevelFilter::Off));
neqo_crypto::init_db(PathBuf::from_str("../test-fixture/db").unwrap()).unwrap(); neqo_crypto::init_db(PathBuf::from_str("../test-fixture/db").unwrap()).unwrap();
let done_sender = spawn_server(); let done_sender = spawn_server();

View File

@@ -4,7 +4,7 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
use clap::Parser; use clap::Parser as _;
#[tokio::main] #[tokio::main]
async fn main() -> Result<(), neqo_bin::client::Error> { async fn main() -> Result<(), neqo_bin::client::Error> {

View File

@@ -4,7 +4,7 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
use clap::Parser; use clap::Parser as _;
#[tokio::main] #[tokio::main]
async fn main() -> Result<(), neqo_bin::server::Error> { async fn main() -> Result<(), neqo_bin::server::Error> {

View File

@@ -10,7 +10,7 @@ use std::{
cell::RefCell, cell::RefCell,
collections::{HashMap, VecDeque}, collections::{HashMap, VecDeque},
fs::File, fs::File,
io::{BufWriter, Write}, io::{BufWriter, Write as _},
net::SocketAddr, net::SocketAddr,
path::PathBuf, path::PathBuf,
rc::Rc, rc::Rc,
@@ -61,7 +61,7 @@ impl super::Handler for Handler<'_> {
self.needs_key_update = false; self.needs_key_update = false;
self.download_urls(client); self.download_urls(client);
} }
Err(neqo_transport::Error::KeyUpdateBlocked) => (), Err(Error::KeyUpdateBlocked) => (),
Err(e) => return Err(e.into()), Err(e) => return Err(e.into()),
} }
} }
@@ -213,7 +213,7 @@ impl super::Client for Connection {
} }
fn has_events(&self) -> bool { fn has_events(&self) -> bool {
neqo_common::event::Provider::has_events(self) Provider::has_events(self)
} }
} }
@@ -301,8 +301,7 @@ impl<'b> Handler<'b> {
qdebug!("READ[{stream_id}]: {} bytes", read_buffer.len()); qdebug!("READ[{stream_id}]: {} bytes", read_buffer.len());
} else { } else {
qdebug!( qdebug!(
"READ[{}]: {}", "READ[{stream_id}]: {}",
stream_id,
std::str::from_utf8(read_buffer).unwrap() std::str::from_utf8(read_buffer).unwrap()
); );
} }

View File

@@ -11,7 +11,7 @@ use std::{
collections::{HashMap, VecDeque}, collections::{HashMap, VecDeque},
fmt::Display, fmt::Display,
fs::File, fs::File,
io::{BufWriter, Write}, io::{BufWriter, Write as _},
net::SocketAddr, net::SocketAddr,
path::PathBuf, path::PathBuf,
rc::Rc, rc::Rc,
@@ -155,7 +155,7 @@ impl super::Client for Http3Client {
} }
fn has_events(&self) -> bool { fn has_events(&self) -> bool {
neqo_common::event::Provider::has_events(self) Provider::has_events(self)
} }
} }
@@ -302,7 +302,7 @@ impl StreamHandler for DownloadStreamHandler {
} else if let Ok(txt) = std::str::from_utf8(data) { } else if let Ok(txt) = std::str::from_utf8(data) {
qdebug!("READ[{stream_id}]: {txt}"); qdebug!("READ[{stream_id}]: {txt}");
} else { } else {
qdebug!("READ[{}]: 0x{}", stream_id, hex(data)); qdebug!("READ[{stream_id}]: 0x{}", hex(data));
} }
if fin { if fin {
@@ -344,7 +344,7 @@ impl StreamHandler for UploadStreamHandler {
qinfo!("Stream ID: {stream_id:?}, Upload time: {upload_time:?}"); qinfo!("Stream ID: {stream_id:?}, Upload time: {upload_time:?}");
} }
} else { } else {
panic!("Unexpected data [{}]: 0x{}", stream_id, hex(data)); panic!("Unexpected data [{stream_id}]: 0x{}", hex(data));
} }
Ok(true) Ok(true)
} }

View File

@@ -11,7 +11,7 @@ use std::{
fmt::{self, Display}, fmt::{self, Display},
fs::{create_dir_all, File, OpenOptions}, fs::{create_dir_all, File, OpenOptions},
io::{self, BufWriter}, io::{self, BufWriter},
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, ToSocketAddrs}, net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, ToSocketAddrs as _},
path::PathBuf, path::PathBuf,
pin::Pin, pin::Pin,
process::exit, process::exit,
@@ -21,7 +21,7 @@ use std::{
use clap::Parser; use clap::Parser;
use futures::{ use futures::{
future::{select, Either}, future::{select, Either},
FutureExt, TryFutureExt, FutureExt as _, TryFutureExt as _,
}; };
use neqo_common::{qdebug, qerror, qinfo, qlog::NeqoQlog, qwarn, Datagram, Role}; use neqo_common::{qdebug, qerror, qinfo, qlog::NeqoQlog, qwarn, Datagram, Role};
use neqo_crypto::{ use neqo_crypto::{
@@ -30,6 +30,7 @@ use neqo_crypto::{
}; };
use neqo_http3::Output; use neqo_http3::Output;
use neqo_transport::{AppError, CloseReason, ConnectionId, Version}; use neqo_transport::{AppError, CloseReason, ConnectionId, Version};
use neqo_udp::RecvBuf;
use tokio::time::Sleep; use tokio::time::Sleep;
use url::{Host, Origin, Url}; use url::{Host, Origin, Url};
@@ -47,7 +48,7 @@ pub enum Error {
IoError(io::Error), IoError(io::Error),
QlogError(qlog::Error), QlogError(qlog::Error),
TransportError(neqo_transport::Error), TransportError(neqo_transport::Error),
ApplicationError(neqo_transport::AppError), ApplicationError(AppError),
CryptoError(neqo_crypto::Error), CryptoError(neqo_crypto::Error),
} }
@@ -81,8 +82,8 @@ impl From<neqo_transport::Error> for Error {
} }
} }
impl From<neqo_transport::CloseReason> for Error { impl From<CloseReason> for Error {
fn from(err: neqo_transport::CloseReason) -> Self { fn from(err: CloseReason) -> Self {
match err { match err {
CloseReason::Transport(e) => Self::TransportError(e), CloseReason::Transport(e) => Self::TransportError(e),
CloseReason::Application(e) => Self::ApplicationError(e), CloseReason::Application(e) => Self::ApplicationError(e),
@@ -181,9 +182,9 @@ impl Args {
#[cfg(any(test, feature = "bench"))] #[cfg(any(test, feature = "bench"))]
#[allow(clippy::missing_panics_doc)] #[allow(clippy::missing_panics_doc)]
pub fn new(requests: &[usize], upload: bool) -> Self { pub fn new(requests: &[usize], upload: bool) -> Self {
use std::str::FromStr; use std::str::FromStr as _;
Self { Self {
shared: crate::SharedArgs::default(), shared: SharedArgs::default(),
urls: requests urls: requests
.iter() .iter()
.map(|r| Url::from_str(&format!("http://[::1]:12345/{r}")).unwrap()) .map(|r| Url::from_str(&format!("http://[::1]:12345/{r}")).unwrap())
@@ -238,10 +239,14 @@ impl Args {
// Only use v1 for most QNS tests. // Only use v1 for most QNS tests.
self.shared.quic_parameters.quic_version = vec![Version::Version1]; self.shared.quic_parameters.quic_version = vec![Version::Version1];
// This is the default for all tests except http3. // This is the default for all tests except http3.
self.shared.use_old_http = true; self.shared.alpn = String::from("hq-interop");
// Wireshark can't reassemble sliced CRYPTO frames, which causes tests to fail.
// So let's turn that off by default, and only enable for some known-good QNS tests.
self.shared.quic_parameters.no_sni_slicing = true;
match testcase.as_str() { match testcase.as_str() {
"http3" => { "http3" => {
self.shared.use_old_http = false; self.shared.quic_parameters.no_sni_slicing = false;
self.shared.alpn = String::from("h3");
if let Some(testcase) = &self.test { if let Some(testcase) = &self.test {
if testcase.as_str() != "upload" { if testcase.as_str() != "upload" {
qerror!("Unsupported test case: {testcase}"); qerror!("Unsupported test case: {testcase}");
@@ -264,6 +269,7 @@ impl Args {
qerror!("Warning: zerortt test won't work without >1 URL"); qerror!("Warning: zerortt test won't work without >1 URL");
exit(127); exit(127);
} }
self.shared.quic_parameters.no_sni_slicing = false;
self.resume = true; self.resume = true;
// PMTUD probes inflate what we sent in 1-RTT, causing QNS to fail the test. // PMTUD probes inflate what we sent in 1-RTT, causing QNS to fail the test.
self.shared.quic_parameters.no_pmtud = true; self.shared.quic_parameters.no_pmtud = true;
@@ -284,6 +290,7 @@ impl Args {
self.key_update = true; self.key_update = true;
} }
"v2" => { "v2" => {
self.shared.quic_parameters.no_sni_slicing = false;
// Use default version set for this test (which allows compatible vneg.) // Use default version set for this test (which allows compatible vneg.)
self.shared.quic_parameters.quic_version.clear(); self.shared.quic_parameters.quic_version.clear();
} }
@@ -394,7 +401,7 @@ struct Runner<'a, H: Handler> {
handler: H, handler: H,
timeout: Option<Pin<Box<Sleep>>>, timeout: Option<Pin<Box<Sleep>>>,
args: &'a Args, args: &'a Args,
recv_buf: Vec<u8>, recv_buf: RecvBuf,
} }
impl<'a, H: Handler> Runner<'a, H> { impl<'a, H: Handler> Runner<'a, H> {
@@ -412,7 +419,7 @@ impl<'a, H: Handler> Runner<'a, H> {
handler, handler,
args, args,
timeout: None, timeout: None,
recv_buf: vec![0; neqo_udp::RECV_BUF_SIZE], recv_buf: RecvBuf::new(),
} }
} }
@@ -462,7 +469,7 @@ impl<'a, H: Handler> Runner<'a, H> {
self.socket.send(&dgram)?; self.socket.send(&dgram)?;
} }
Output::Callback(new_timeout) => { Output::Callback(new_timeout) => {
qdebug!("Setting timeout of {:?}", new_timeout); qdebug!("Setting timeout of {new_timeout:?}");
self.timeout = Some(Box::pin(tokio::time::sleep(new_timeout))); self.timeout = Some(Box::pin(tokio::time::sleep(new_timeout)));
break; break;
} }
@@ -481,9 +488,6 @@ impl<'a, H: Handler> Runner<'a, H> {
let Some(dgrams) = self.socket.recv(self.local_addr, &mut self.recv_buf)? else { let Some(dgrams) = self.socket.recv(self.local_addr, &mut self.recv_buf)? else {
break; break;
}; };
if dgrams.len() == 0 {
break;
}
self.client.process_multiple_input(dgrams, Instant::now()); self.client.process_multiple_input(dgrams, Instant::now());
self.process_output().await?; self.process_output().await?;
} }
@@ -508,9 +512,9 @@ fn qlog_new(args: &Args, hostname: &str, cid: &ConnectionId) -> Res<NeqoQlog> {
NeqoQlog::enabled_with_file( NeqoQlog::enabled_with_file(
qlog_dir, qlog_dir,
Role::Client, Role::Client,
Some("Example qlog".to_string()), Some("Neqo client qlog".to_string()),
Some("Example qlog description".to_string()), Some("Neqo client qlog".to_string()),
format!("{hostname}-{cid}"), format!("client-{hostname}-{cid}"),
) )
.map_err(Error::QlogError) .map_err(Error::QlogError)
} }
@@ -553,7 +557,7 @@ pub async fn client(mut args: Args) -> Res<()> {
for ((host, port), mut urls) in urls_by_origin(&args.urls) { for ((host, port), mut urls) in urls_by_origin(&args.urls) {
if args.resume && urls.len() < 2 { if args.resume && urls.len() < 2 {
qerror!("Resumption to {host} cannot work without at least 2 URLs."); qerror!("Resumption to {host} cannot work without at least 2 URLs");
exit(127); exit(127);
} }
@@ -570,10 +574,8 @@ pub async fn client(mut args: Args) -> Res<()> {
let mut socket = crate::udp::Socket::bind(local_addr_for(&remote_addr, 0))?; let mut socket = crate::udp::Socket::bind(local_addr_for(&remote_addr, 0))?;
let real_local = socket.local_addr().unwrap(); let real_local = socket.local_addr().unwrap();
qinfo!( qinfo!(
"{} Client connecting: {:?} -> {:?}", "{} Client connecting: {real_local:?} -> {remote_addr:?}",
if args.shared.use_old_http { "H9" } else { "H3" }, args.shared.alpn
real_local,
remote_addr,
); );
let hostname = format!("{host}"); let hostname = format!("{host}");
@@ -588,21 +590,21 @@ pub async fn client(mut args: Args) -> Res<()> {
first = false; first = false;
token = if args.shared.use_old_http { token = if args.shared.alpn == "h3" {
let client = let client = http3::create_client(&args, real_local, remote_addr, &hostname, token)
http09::create_client(&args, real_local, remote_addr, &hostname, token) .expect("failed to create client");
.expect("failed to create client");
let handler = http09::Handler::new(to_request, &args); let handler = http3::Handler::new(to_request, &args);
Runner::new(real_local, &mut socket, client, handler, &args) Runner::new(real_local, &mut socket, client, handler, &args)
.run() .run()
.await? .await?
} else { } else {
let client = http3::create_client(&args, real_local, remote_addr, &hostname, token) let client =
.expect("failed to create client"); http09::create_client(&args, real_local, remote_addr, &hostname, token)
.expect("failed to create client");
let handler = http3::Handler::new(to_request, &args); let handler = http09::Handler::new(to_request, &args);
Runner::new(real_local, &mut socket, client, handler, &args) Runner::new(real_local, &mut socket, client, handler, &args)
.run() .run()

View File

@@ -9,7 +9,7 @@
use std::{ use std::{
fmt::{self, Display}, fmt::{self, Display},
net::{SocketAddr, ToSocketAddrs}, net::{SocketAddr, ToSocketAddrs as _},
path::PathBuf, path::PathBuf,
time::Duration, time::Duration,
}; };
@@ -63,10 +63,6 @@ pub struct SharedArgs {
/// Enable special behavior for use with QUIC Network Simulator /// Enable special behavior for use with QUIC Network Simulator
pub qns_test: Option<String>, pub qns_test: Option<String>,
#[arg(name = "use-old-http", short = 'o', long)]
/// Use http 0.9 instead of HTTP/3
pub use_old_http: bool,
#[command(flatten)] #[command(flatten)]
pub quic_parameters: QuicParameters, pub quic_parameters: QuicParameters,
} }
@@ -83,7 +79,6 @@ impl Default for SharedArgs {
max_blocked_streams: 10, max_blocked_streams: 10,
ciphers: vec![], ciphers: vec![],
qns_test: None, qns_test: None,
use_old_http: false,
quic_parameters: QuicParameters::default(), quic_parameters: QuicParameters::default(),
} }
} }
@@ -117,7 +112,7 @@ pub struct QuicParameters {
/// The idle timeout for connections, in seconds. /// The idle timeout for connections, in seconds.
pub idle_timeout: u64, pub idle_timeout: u64,
#[arg(long = "cc", default_value = "newreno")] #[arg(long = "cc", default_value = "cubic")]
/// The congestion controller to use. /// The congestion controller to use.
pub congestion_control: CongestionControlAlgorithm, pub congestion_control: CongestionControlAlgorithm,
@@ -129,6 +124,10 @@ pub struct QuicParameters {
/// Whether to disable path MTU discovery. /// Whether to disable path MTU discovery.
pub no_pmtud: bool, pub no_pmtud: bool,
#[arg(long)]
/// Whether to slice the SNI.
pub no_sni_slicing: bool,
#[arg(name = "preferred-address-v4", long)] #[arg(name = "preferred-address-v4", long)]
/// An IPv4 address for the server preferred address. /// An IPv4 address for the server preferred address.
pub preferred_address_v4: Option<String>, pub preferred_address_v4: Option<String>,
@@ -146,11 +145,12 @@ impl Default for QuicParameters {
max_streams_bidi: 16, max_streams_bidi: 16,
max_streams_uni: 16, max_streams_uni: 16,
idle_timeout: 30, idle_timeout: 30,
congestion_control: CongestionControlAlgorithm::NewReno, congestion_control: CongestionControlAlgorithm::Cubic,
no_pacing: false, no_pacing: false,
no_pmtud: false, no_pmtud: false,
preferred_address_v4: None, preferred_address_v4: None,
preferred_address_v6: None, preferred_address_v6: None,
no_sni_slicing: false,
} }
} }
} }
@@ -168,9 +168,8 @@ impl QuicParameters {
assert_eq!( assert_eq!(
opt.is_some(), opt.is_some(),
addr.is_some(), addr.is_some(),
"unable to resolve '{}' to an {} address", "unable to resolve '{}' to an {v} address",
opt.as_ref().unwrap(), opt.as_ref().unwrap(),
v,
); );
addr addr
} }
@@ -224,7 +223,8 @@ impl QuicParameters {
.idle_timeout(Duration::from_secs(self.idle_timeout)) .idle_timeout(Duration::from_secs(self.idle_timeout))
.cc_algorithm(self.congestion_control) .cc_algorithm(self.congestion_control)
.pacing(!self.no_pacing) .pacing(!self.no_pacing)
.pmtud(!self.no_pmtud); .pmtud(!self.no_pmtud)
.sni_slicing(!self.no_sni_slicing);
params = if let Some(pa) = self.preferred_address() { params = if let Some(pa) = self.preferred_address() {
params.preferred_address(pa) params.preferred_address(pa)
} else { } else {
@@ -240,10 +240,8 @@ impl QuicParameters {
} else { } else {
let version = match alpn { let version = match alpn {
"h3" | "hq-interop" => Version::Version1, "h3" | "hq-interop" => Version::Version1,
#[cfg(feature = "draft-29")]
"h3-29" | "hq-29" => Version::Draft29, "h3-29" | "hq-29" => Version::Draft29,
"h3-30" | "hq-30" => Version::Draft30,
"h3-31" | "hq-31" => Version::Draft31,
"h3-32" | "hq-32" => Version::Draft32,
_ => Version::default(), _ => Version::default(),
}; };
params.versions(version, Version::all()) params.versions(version, Version::all())
@@ -273,7 +271,7 @@ impl std::error::Error for Error {}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::{fs, path::PathBuf, str::FromStr, time::SystemTime}; use std::{fs, path::PathBuf, str::FromStr as _, time::SystemTime};
use crate::{client, server}; use crate::{client, server};
@@ -283,15 +281,14 @@ mod tests {
impl TempDir { impl TempDir {
fn new() -> Self { fn new() -> Self {
let mut dir = std::env::temp_dir(); let dir = std::env::temp_dir().join(format!(
dir.push(format!(
"neqo-bin-test-{}", "neqo-bin-test-{}",
SystemTime::now() SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH) .duration_since(SystemTime::UNIX_EPOCH)
.unwrap() .unwrap()
.as_secs() .as_secs()
)); ));
fs::create_dir(&dir).unwrap(); fs::create_dir_all(&dir).unwrap();
Self { path: dir } Self { path: dir }
} }

View File

@@ -6,7 +6,7 @@
use std::{borrow::Cow, cell::RefCell, collections::HashMap, fmt::Display, rc::Rc, time::Instant}; use std::{borrow::Cow, cell::RefCell, collections::HashMap, fmt::Display, rc::Rc, time::Instant};
use neqo_common::{event::Provider, hex, qdebug, qerror, qinfo, qwarn, Datagram}; use neqo_common::{event::Provider as _, hex, qdebug, qerror, qinfo, qwarn, Datagram};
use neqo_crypto::{generate_ech_keys, random, AllowZeroRtt, AntiReplay}; use neqo_crypto::{generate_ech_keys, random, AllowZeroRtt, AntiReplay};
use neqo_http3::Error; use neqo_http3::Error;
use neqo_transport::{ use neqo_transport::{
@@ -82,17 +82,17 @@ impl HttpServer {
let url_dbg = String::from_utf8(partial.clone()) let url_dbg = String::from_utf8(partial.clone())
.unwrap_or_else(|_| format!("<invalid UTF-8: {}>", hex(&partial))); .unwrap_or_else(|_| format!("<invalid UTF-8: {}>", hex(&partial)));
if partial.len() < 4096 { if partial.len() < 4096 {
qdebug!("Saving partial URL: {}", url_dbg); qdebug!("Saving partial URL: {url_dbg}");
self.read_state.insert(stream_id, partial); self.read_state.insert(stream_id, partial);
} else { } else {
qdebug!("Giving up on partial URL {}", url_dbg); qdebug!("Giving up on partial URL {url_dbg}");
conn.borrow_mut().stream_stop_sending(stream_id, 0).unwrap(); conn.borrow_mut().stream_stop_sending(stream_id, 0).unwrap();
} }
} }
fn stream_readable(&mut self, stream_id: StreamId, conn: &ConnectionRef) { fn stream_readable(&mut self, stream_id: StreamId, conn: &ConnectionRef) {
if !stream_id.is_client_initiated() || !stream_id.is_bidi() { if !stream_id.is_client_initiated() || !stream_id.is_bidi() {
qdebug!("Stream {} not client-initiated bidi, ignoring", stream_id); qdebug!("Stream {stream_id} not client-initiated bidi, ignoring");
return; return;
} }
let (sz, fin) = conn let (sz, fin) = conn

View File

@@ -12,7 +12,7 @@ use std::{
time::Instant, time::Instant,
}; };
use neqo_common::{hex, qdebug, qerror, qinfo, Datagram, Header}; use neqo_common::{header::HeadersExt as _, hex, qdebug, qerror, qinfo, Datagram, Header};
use neqo_crypto::{generate_ech_keys, random, AntiReplay}; use neqo_crypto::{generate_ech_keys, random, AntiReplay};
use neqo_http3::{ use neqo_http3::{
Http3OrWebTransportStream, Http3Parameters, Http3Server, Http3ServerEvent, StreamId, Http3OrWebTransportStream, Http3Parameters, Http3Server, Http3ServerEvent, StreamId,
@@ -94,15 +94,12 @@ impl super::HttpServer for HttpServer {
} => { } => {
qdebug!("Headers (request={stream} fin={fin}): {headers:?}"); qdebug!("Headers (request={stream} fin={fin}): {headers:?}");
if headers if headers.contains_header(":method", "POST") {
.iter()
.any(|h| h.name() == ":method" && h.value() == "POST")
{
self.posts.insert(stream, 0); self.posts.insert(stream, 0);
continue; continue;
} }
let Some(path) = headers.iter().find(|&h| h.name() == ":path") else { let Some(path) = headers.find_header(":path") else {
stream stream
.cancel_fetch(neqo_http3::Error::HttpRequestIncomplete.code()) .cancel_fetch(neqo_http3::Error::HttpRequestIncomplete.code())
.unwrap(); .unwrap();

View File

@@ -10,7 +10,7 @@ use std::{
cell::RefCell, cell::RefCell,
fmt::{self, Display}, fmt::{self, Display},
fs, io, fs, io,
net::{SocketAddr, ToSocketAddrs}, net::{SocketAddr, ToSocketAddrs as _},
path::PathBuf, path::PathBuf,
pin::Pin, pin::Pin,
process::exit, process::exit,
@@ -21,7 +21,7 @@ use std::{
use clap::Parser; use clap::Parser;
use futures::{ use futures::{
future::{select, select_all, Either}, future::{select, select_all, Either},
FutureExt, FutureExt as _,
}; };
use neqo_common::{qdebug, qerror, qinfo, qwarn, Datagram}; use neqo_common::{qdebug, qerror, qinfo, qwarn, Datagram};
use neqo_crypto::{ use neqo_crypto::{
@@ -29,6 +29,7 @@ use neqo_crypto::{
init_db, AntiReplay, Cipher, init_db, AntiReplay, Cipher,
}; };
use neqo_transport::{Output, RandomConnectionIdGenerator, Version}; use neqo_transport::{Output, RandomConnectionIdGenerator, Version};
use neqo_udp::RecvBuf;
use tokio::time::Sleep; use tokio::time::Sleep;
use crate::SharedArgs; use crate::SharedArgs;
@@ -121,9 +122,9 @@ pub struct Args {
#[cfg(any(test, feature = "bench"))] #[cfg(any(test, feature = "bench"))]
impl Default for Args { impl Default for Args {
fn default() -> Self { fn default() -> Self {
use std::str::FromStr; use std::str::FromStr as _;
Self { Self {
shared: crate::SharedArgs::default(), shared: SharedArgs::default(),
hosts: vec!["[::]:12345".to_string()], hosts: vec!["[::]:12345".to_string()],
db: PathBuf::from_str("../test-fixture/db").unwrap(), db: PathBuf::from_str("../test-fixture/db").unwrap(),
key: "key".to_string(), key: "key".to_string(),
@@ -202,7 +203,7 @@ pub struct ServerRunner {
server: Box<dyn HttpServer>, server: Box<dyn HttpServer>,
timeout: Option<Pin<Box<Sleep>>>, timeout: Option<Pin<Box<Sleep>>>,
sockets: Vec<(SocketAddr, crate::udp::Socket)>, sockets: Vec<(SocketAddr, crate::udp::Socket)>,
recv_buf: Vec<u8>, recv_buf: RecvBuf,
} }
impl ServerRunner { impl ServerRunner {
@@ -217,7 +218,7 @@ impl ServerRunner {
server, server,
timeout: None, timeout: None,
sockets, sockets,
recv_buf: vec![0; neqo_udp::RECV_BUF_SIZE], recv_buf: RecvBuf::new(),
} }
} }
@@ -251,7 +252,7 @@ impl ServerRunner {
socket.send(&dgram)?; socket.send(&dgram)?;
} }
Output::Callback(new_timeout) => { Output::Callback(new_timeout) => {
qdebug!("Setting timeout of {:?}", new_timeout); qdebug!("Setting timeout of {new_timeout:?}");
*timeout = Some(Box::pin(tokio::time::sleep(new_timeout))); *timeout = Some(Box::pin(tokio::time::sleep(new_timeout)));
break; break;
} }
@@ -263,7 +264,7 @@ impl ServerRunner {
async fn read_and_process(&mut self, sockets_index: usize) -> Result<(), io::Error> { async fn read_and_process(&mut self, sockets_index: usize) -> Result<(), io::Error> {
loop { loop {
let (host, socket) = self.sockets.get_mut(sockets_index).unwrap(); let (host, socket) = &mut self.sockets[sockets_index];
let Some(input_dgrams) = socket.recv(*host, &mut self.recv_buf)? else { let Some(input_dgrams) = socket.recv(*host, &mut self.recv_buf)? else {
break; break;
}; };
@@ -341,8 +342,6 @@ enum Ready {
} }
pub async fn server(mut args: Args) -> Res<()> { pub async fn server(mut args: Args) -> Res<()> {
const HQ_INTEROP: &str = "hq-interop";
neqo_common::log::init( neqo_common::log::init(
args.shared args.shared
.verbose .verbose
@@ -362,17 +361,15 @@ pub async fn server(mut args: Args) -> Res<()> {
args.shared.quic_parameters.quic_version = vec![Version::Version1]; args.shared.quic_parameters.quic_version = vec![Version::Version1];
} }
} else { } else {
qwarn!("Both -V and --qns-test were set. Ignoring testcase specific versions."); qwarn!("Both -V and --qns-test were set. Ignoring testcase specific versions");
} }
// These are the default for all tests except http3. // These are the default for all tests except http3.
args.shared.use_old_http = true; args.shared.alpn = String::from("hq-interop");
args.shared.alpn = String::from(HQ_INTEROP);
// TODO: More options to deduplicate with client? // TODO: More options to deduplicate with client?
match testcase.as_str() { match testcase.as_str() {
"http3" => { "http3" => {
args.shared.use_old_http = false; args.shared.alpn = String::from("h3");
args.shared.alpn = "h3".into();
} }
"zerortt" => args.shared.quic_parameters.max_streams_bidi = 100, "zerortt" => args.shared.quic_parameters.max_streams_bidi = 100,
"handshake" | "transfer" | "resumption" | "multiconnect" | "v2" | "ecn" => {} "handshake" | "transfer" | "resumption" | "multiconnect" | "v2" | "ecn" => {}
@@ -396,7 +393,7 @@ pub async fn server(mut args: Args) -> Res<()> {
let hosts = args.listen_addresses(); let hosts = args.listen_addresses();
if hosts.is_empty() { if hosts.is_empty() {
qerror!("No valid hosts defined"); qerror!("No valid hosts defined");
Err(io::Error::new(io::ErrorKind::InvalidInput, "No hosts"))?; return Err(io::Error::new(io::ErrorKind::InvalidInput, "No hosts").into());
} }
let sockets = hosts let sockets = hosts
.into_iter() .into_iter()
@@ -414,12 +411,12 @@ pub async fn server(mut args: Args) -> Res<()> {
.expect("unable to setup anti-replay"); .expect("unable to setup anti-replay");
let cid_mgr = Rc::new(RefCell::new(RandomConnectionIdGenerator::new(10))); let cid_mgr = Rc::new(RefCell::new(RandomConnectionIdGenerator::new(10)));
let server: Box<dyn HttpServer> = if args.shared.use_old_http { let server: Box<dyn HttpServer> = if args.shared.alpn == "h3" {
Box::new(http3::HttpServer::new(&args, anti_replay, cid_mgr))
} else {
Box::new( Box::new(
http09::HttpServer::new(&args, anti_replay, cid_mgr).expect("We cannot make a server!"), http09::HttpServer::new(&args, anti_replay, cid_mgr).expect("We cannot make a server!"),
) )
} else {
Box::new(http3::HttpServer::new(&args, anti_replay, cid_mgr))
}; };
ServerRunner::new(Box::new(move || args.now()), server, sockets) ServerRunner::new(Box::new(move || args.now()), server, sockets)

View File

@@ -7,7 +7,7 @@
use std::{io, net::SocketAddr}; use std::{io, net::SocketAddr};
use neqo_common::Datagram; use neqo_common::Datagram;
use neqo_udp::DatagramIter; use neqo_udp::{DatagramIter, RecvBuf};
/// Ideally this would live in [`neqo-udp`]. [`neqo-udp`] is used in Firefox. /// Ideally this would live in [`neqo-udp`]. [`neqo-udp`] is used in Firefox.
/// ///
@@ -59,7 +59,7 @@ impl Socket {
pub fn recv<'a>( pub fn recv<'a>(
&self, &self,
local_address: SocketAddr, local_address: SocketAddr,
recv_buf: &'a mut [u8], recv_buf: &'a mut RecvBuf,
) -> Result<Option<DatagramIter<'a>>, io::Error> { ) -> Result<Option<DatagramIter<'a>>, io::Error> {
self.inner self.inner
.try_io(tokio::io::Interest::READABLE, || { .try_io(tokio::io::Interest::READABLE, || {

View File

@@ -1 +1 @@
{"files":{"Cargo.toml":"d7f253814c9d1e8fcae6916ca37c18efafd511895d85b6b193b4cc648a1b3fca","build.rs":"ee5dc521f3d8e18c2b617192d6b6e678f7f2f9886fdd34c0c1c5ef841419b248","src/codec.rs":"6c12d9db7066497f2566e83efc825ae984d04a6b5176010c93d394904103aeed","src/datagram.rs":"e8bf176d3b120028731388c17344d03b8195e5fd70f4d03e37144ac5ae5951f5","src/event.rs":"289cf8e265c33e7cded58820ac81e5b575e3f84dd52fa18b0761f4094fb361c0","src/fuzz.rs":"1ca74a34bdc97fedecf8a63c4a13cc487d1b2212398fb76f67792c822002138d","src/header.rs":"480a7848466249a78acddbf0bc0b4a096189abc14a89ad1a0943be571add2c2b","src/hrtime.rs":"37447c51c7fd84baad31bc420bf9170c1f4e71356bb6d102bd5651ddf69a2f89","src/incrdecoder.rs":"5c45034e61e75c76d2bca8b075c3e7a3cdd8af8c82b67c76283a2b08ab11846b","src/lib.rs":"2381fc00127a7eaf2265c3a13dc1e1d5843e048f3a8a1c97f1e6621c038de380","src/log.rs":"6ed99e15707c4256ae793011ed2f4b33aa81fed70205aaf5f8d3cd11ad451cf0","src/qlog.rs":"c230d1e3b6f80489253dc689eab44c9df75ef0c5170ab91ddd2dc9e4dc9234a2","src/tos.rs":"28fd9acfce06f68ac6691efd2609618850182f77ef3717ce2db07bfac19a9396","tests/log.rs":"a11e21fb570258ca93bb40e3923817d381e1e605accbc3aed1df5a0a9918b41d"},"package":null} {"files":{"Cargo.toml":"40a22ca3fd5fd76384120c87a895e923701d78d4b1ec30481f80afff82cbf3f6","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}

View File

@@ -13,7 +13,7 @@
edition = "2021" edition = "2021"
rust-version = "1.76.0" rust-version = "1.76.0"
name = "neqo-common" name = "neqo-common"
version = "0.11.0" version = "0.12.2"
authors = ["The Neqo Authors <necko@mozilla.com>"] authors = ["The Neqo Authors <necko@mozilla.com>"]
build = "build.rs" build = "build.rs"
autolib = false autolib = false
@@ -48,6 +48,12 @@ bench = false
name = "log" name = "log"
path = "tests/log.rs" path = "tests/log.rs"
[[bench]]
name = "decoder"
path = "benches/decoder.rs"
harness = false
required-features = ["bench"]
[dependencies.enum-map] [dependencies.enum-map]
version = "2.7" version = "2.7"
default-features = false default-features = false
@@ -70,6 +76,13 @@ default-features = false
version = "0.13" version = "0.13"
default-features = false default-features = false
[dev-dependencies.criterion]
version = "0.5"
default-features = false
[dev-dependencies.neqo-crypto]
path = "../neqo-crypto"
[dev-dependencies.regex] [dev-dependencies.regex]
version = "1.9" version = "1.9"
features = ["unicode-perl"] features = ["unicode-perl"]
@@ -79,8 +92,13 @@ default-features = false
path = "../test-fixture" path = "../test-fixture"
[features] [features]
bench = [
"neqo-crypto/bench",
"test-fixture/bench",
]
build-fuzzing-corpus = ["hex"] build-fuzzing-corpus = ["hex"]
ci = [] ci = []
test-fixture = []
[target."cfg(windows)".dependencies.windows] [target."cfg(windows)".dependencies.windows]
version = "0.58" version = "0.58"
@@ -88,7 +106,22 @@ features = ["Win32_Media"]
default-features = false default-features = false
[lints.clippy] [lints.clippy]
cfg_not_test = "warn"
clone_on_ref_ptr = "warn"
create_dir = "warn"
get_unwrap = "warn"
if_then_some_else_none = "warn"
multiple_crate_versions = "allow" multiple_crate_versions = "allow"
multiple_inherent_impl = "warn"
pathbuf_init_then_push = "warn"
redundant_type_annotations = "warn"
ref_patterns = "warn"
renamed_function_params = "warn"
semicolon_inside_block = "warn"
try_err = "warn"
unneeded_field_pattern = "warn"
unused_result_ok = "warn"
unused_trait_names = "warn"
[lints.clippy.cargo] [lints.clippy.cargo]
level = "warn" level = "warn"
@@ -101,3 +134,19 @@ priority = -1
[lints.clippy.pedantic] [lints.clippy.pedantic]
level = "warn" level = "warn"
priority = -1 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"
non_ascii_idents = "warn"
redundant_imports = "warn"
redundant_lifetimes = "warn"
trivial_numeric_casts = "warn"
unit_bindings = "warn"
unused_import_braces = "warn"
unused_lifetimes = "warn"
unused_macro_rules = "warn"

View File

@@ -0,0 +1,52 @@
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use neqo_common::Decoder;
use neqo_crypto::{init, randomize};
fn randomize_buffer(n: usize, mask: u8) -> Vec<u8> {
let mut buf = vec![0; n];
// NSS doesn't like randomizing larger buffers, so chunk them up.
// https://searchfox.org/nss/rev/968939484921b0ceecca189cd1b66e97950c39da/lib/freebl/drbg.c#29
for chunk in buf.chunks_mut(0x10000) {
randomize(chunk);
}
// Masking the top bits off causes the resulting values to be interpreted as
// smaller varints, which stresses the decoder differently.
// This is worth testing because most varints contain small values.
for x in &mut buf[..] {
*x &= mask;
}
buf
}
fn decoder(c: &mut Criterion, count: usize, mask: u8) {
c.bench_function(&format!("decode {count} bytes, mask {mask:x}"), |b| {
b.iter_batched_ref(
|| randomize_buffer(count, mask),
|buf| {
let mut dec = Decoder::new(&buf[..]);
while black_box(dec.decode_varint()).is_some() {
// Do nothing;
}
},
criterion::BatchSize::SmallInput,
);
});
}
fn benchmark_decoder(c: &mut Criterion) {
init().unwrap();
for mask in [0xff, 0x7f, 0x3f] {
for exponent in [12, 20] {
decoder(c, 1 << exponent, mask);
}
}
}
criterion_group!(benches, benchmark_decoder);
criterion_main!(benches);

View File

@@ -44,34 +44,28 @@ impl<'a> Decoder<'a> {
} }
/// Skip helper that panics if `n` is `None` or not able to fit in `usize`. /// Skip helper that panics if `n` is `None` or not able to fit in `usize`.
/// 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>) { fn skip_inner(&mut self, n: Option<u64>) {
self.skip(usize::try_from(n.expect("invalid length")).unwrap()); self.skip(usize::try_from(n.expect("invalid length")).unwrap());
} }
/// Skip a vector. Panics if there isn't enough space. /// Skip a vector. Panics if there isn't enough space.
/// Only use this for tests because we panic rather than reporting a result. /// Only use this for tests because we panic rather than reporting a result.
#[cfg(any(test, feature = "test-fixture"))]
pub fn skip_vec(&mut self, n: usize) { pub fn skip_vec(&mut self, n: usize) {
let len = self.decode_uint(n); let len = self.decode_n(n);
self.skip_inner(len); self.skip_inner(len);
} }
/// Skip a variable length vector. Panics if there isn't enough space. /// Skip a variable length vector. Panics if there isn't enough space.
/// Only use this for tests because we panic rather than reporting a result. /// Only use this for tests because we panic rather than reporting a result.
#[cfg(any(test, feature = "test-fixture"))]
pub fn skip_vvec(&mut self) { pub fn skip_vvec(&mut self) {
let len = self.decode_varint(); let len = self.decode_varint();
self.skip_inner(len); self.skip_inner(len);
} }
/// Decodes (reads) a single byte.
pub fn decode_byte(&mut self) -> Option<u8> {
if self.remaining() < 1 {
return None;
}
let b = self.buf[self.offset];
self.offset += 1;
Some(b)
}
/// Provides the next byte without moving the read position. /// Provides the next byte without moving the read position.
#[must_use] #[must_use]
pub const fn peek_byte(&self) -> Option<u8> { pub const fn peek_byte(&self) -> Option<u8> {
@@ -92,33 +86,43 @@ impl<'a> Decoder<'a> {
Some(res) Some(res)
} }
/// Decodes an unsigned integer of length 1..=8. #[inline]
/// pub(crate) fn decode_n(&mut self, n: usize) -> Option<u64> {
/// # Panics debug_assert!(n > 0 && n <= 8);
///
/// This panics if `n` is not in the range `1..=8`.
pub fn decode_uint(&mut self, n: usize) -> Option<u64> {
assert!(n > 0 && n <= 8);
if self.remaining() < n { if self.remaining() < n {
return None; return None;
} }
let mut v = 0_u64; Some(if n == 1 {
for i in 0..n { let v = u64::from(self.buf[self.offset]);
let b = self.buf[self.offset + i]; self.offset += 1;
v = v << 8 | u64::from(b); v
} } else {
self.offset += n; let mut buf = [0; 8];
Some(v) buf[8 - n..].copy_from_slice(&self.buf[self.offset..self.offset + n]);
self.offset += n;
u64::from_be_bytes(buf)
})
}
/// Decodes a big-endian, unsigned integer value into the target type.
/// This returns `None` if there is not enough data remaining
/// or if the conversion to the identified type fails.
/// Conversion is via `u64`, so failures are impossible for
/// 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>());
v.and_then(|v| T::try_from(v).ok())
} }
/// Decodes a QUIC varint. /// Decodes a QUIC varint.
pub fn decode_varint(&mut self) -> Option<u64> { pub fn decode_varint(&mut self) -> Option<u64> {
let b1 = self.decode_byte()?; let b1 = self.decode_n(1)?;
match b1 >> 6 { match b1 >> 6 {
0 => Some(u64::from(b1 & 0x3f)), 0 => Some(b1),
1 => Some((u64::from(b1 & 0x3f) << 8) | self.decode_uint(1)?), 1 => Some(((b1 & 0x3f) << 8) | self.decode_n(1)?),
2 => Some((u64::from(b1 & 0x3f) << 24) | self.decode_uint(3)?), 2 => Some(((b1 & 0x3f) << 24) | self.decode_n(3)?),
3 => Some((u64::from(b1 & 0x3f) << 56) | self.decode_uint(7)?), 3 => Some(((b1 & 0x3f) << 56) | self.decode_n(7)?),
_ => unreachable!(), _ => unreachable!(),
} }
} }
@@ -143,7 +147,7 @@ impl<'a> Decoder<'a> {
/// Decodes a TLS-style length-prefixed buffer. /// Decodes a TLS-style length-prefixed buffer.
pub fn decode_vec(&mut self, n: usize) -> Option<&'a [u8]> { pub fn decode_vec(&mut self, n: usize) -> Option<&'a [u8]> {
let len = self.decode_uint(n); let len = self.decode_n(n);
self.decode_checked(len) self.decode_checked(len)
} }
@@ -272,6 +276,7 @@ impl Encoder {
/// # Panics /// # Panics
/// ///
/// When `s` contains non-hex values or an odd number of values. /// When `s` contains non-hex values or an odd number of values.
#[cfg(any(test, feature = "test-fixture"))]
#[must_use] #[must_use]
pub fn from_hex(s: impl AsRef<str>) -> Self { pub fn from_hex(s: impl AsRef<str>) -> Self {
let s = s.as_ref(); let s = s.as_ref();
@@ -481,16 +486,28 @@ mod tests {
let enc = Encoder::from_hex("0123"); let enc = Encoder::from_hex("0123");
let mut dec = enc.as_decoder(); let mut dec = enc.as_decoder();
assert_eq!(dec.decode_byte().unwrap(), 0x01); assert_eq!(dec.decode_uint::<u8>().unwrap(), 0x01);
assert_eq!(dec.decode_byte().unwrap(), 0x23); assert_eq!(dec.decode_uint::<u8>().unwrap(), 0x23);
assert!(dec.decode_byte().is_none()); assert!(dec.decode_uint::<u8>().is_none());
}
#[test]
fn peek_byte() {
let enc = Encoder::from_hex("01");
let mut dec = enc.as_decoder();
assert_eq!(dec.offset(), 0);
assert_eq!(dec.peek_byte().unwrap(), 0x01);
dec.skip(1);
assert_eq!(dec.offset(), 1);
assert!(dec.peek_byte().is_none());
} }
#[test] #[test]
fn decode_byte_short() { fn decode_byte_short() {
let enc = Encoder::from_hex(""); let enc = Encoder::from_hex("");
let mut dec = enc.as_decoder(); let mut dec = enc.as_decoder();
assert!(dec.decode_byte().is_none()); assert!(dec.decode_uint::<u8>().is_none());
} }
#[test] #[test]
@@ -501,7 +518,7 @@ mod tests {
assert!(dec.decode(2).is_none()); assert!(dec.decode(2).is_none());
let mut dec = Decoder::from(&[]); let mut dec = Decoder::from(&[]);
assert_eq!(dec.decode_remainder().len(), 0); assert!(dec.decode_remainder().is_empty());
} }
#[test] #[test]

View File

@@ -7,7 +7,7 @@
use std::{ use std::{
collections::hash_map::DefaultHasher, collections::hash_map::DefaultHasher,
fs::File, fs::File,
hash::{Hash, Hasher}, hash::{Hash as _, Hasher as _},
io::Write, io::Write,
path::Path, path::Path,
}; };

View File

@@ -46,3 +46,29 @@ impl Header {
&self.value &self.value
} }
} }
impl<T: AsRef<str>, U: AsRef<str>> PartialEq<(T, U)> for Header {
fn eq(&self, other: &(T, U)) -> bool {
self.name == other.0.as_ref() && self.value == other.1.as_ref()
}
}
pub trait HeadersExt<'h> {
fn contains_header<T: AsRef<str>, U: AsRef<str>>(self, name: T, value: U) -> bool;
fn find_header<T: AsRef<str> + 'h>(self, name: T) -> Option<&'h Header>;
}
impl<'h, H> HeadersExt<'h> for H
where
H: IntoIterator<Item = &'h Header> + 'h,
{
fn contains_header<T: AsRef<str>, U: AsRef<str>>(self, name: T, value: U) -> bool {
let (name, value) = (name.as_ref(), value.as_ref());
self.into_iter().any(|h| h == &(name, value))
}
fn find_header<T: AsRef<str> + 'h>(self, name: T) -> Option<&'h Header> {
let name = name.as_ref();
self.into_iter().find(|h| h.name == name)
}
}

View File

@@ -80,7 +80,7 @@ impl PeriodSet {
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
mod mac { mod mac {
use std::{mem::size_of, ptr::addr_of_mut}; use std::ptr::addr_of_mut;
// These are manually extracted from the many bindings generated // These are manually extracted from the many bindings generated
// by bindgen when provided with the simple header: // by bindgen when provided with the simple header:
@@ -126,7 +126,7 @@ mod mac {
const THREAD_TIME_CONSTRAINT_POLICY: thread_policy_flavor_t = 2; const THREAD_TIME_CONSTRAINT_POLICY: thread_policy_flavor_t = 2;
#[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_possible_truncation)]
const THREAD_TIME_CONSTRAINT_POLICY_COUNT: mach_msg_type_number_t = const THREAD_TIME_CONSTRAINT_POLICY_COUNT: mach_msg_type_number_t =
(size_of::<thread_time_constraint_policy>() / size_of::<integer_t>()) (std::mem::size_of::<thread_time_constraint_policy>() / std::mem::size_of::<integer_t>())
as mach_msg_type_number_t; as mach_msg_type_number_t;
// These function definitions are taken from a comment in <thread_policy.h>. // These function definitions are taken from a comment in <thread_policy.h>.
@@ -370,14 +370,9 @@ impl Drop for Time {
} }
} }
// Only run these tests in CI on platforms other than MacOS and Windows, where the timer // Only run these tests in CI on Linux, where the timer accuracies are OK enough to pass the tests,
// inaccuracies are too high to pass the tests. // but only when not running sanitizers.
#[cfg(all( #[cfg(all(test, target_os = "linux", not(neqo_sanitize)))]
test,
not(all(any(target_os = "macos", target_os = "windows"), feature = "ci")),
// Sanitizers are too slow to uphold timing assumptions.
not(neqo_sanitize),
))]
mod test { mod test {
use std::{ use std::{
thread::{sleep, spawn}, thread::{sleep, spawn},

View File

@@ -31,15 +31,11 @@ impl IncrementalDecoderUint {
if amount < 8 { if amount < 8 {
self.v <<= amount * 8; self.v <<= amount * 8;
} }
self.v |= dv.decode_uint(amount).unwrap(); self.v |= dv.decode_n(amount).unwrap();
*r -= amount; *r -= amount;
if *r == 0 { (*r == 0).then_some(self.v)
Some(self.v)
} else {
None
}
} else { } else {
let (v, remaining) = dv.decode_byte().map_or_else( let (v, remaining) = dv.decode_uint::<u8>().map_or_else(
|| unreachable!(), || unreachable!(),
|b| { |b| {
( (
@@ -56,11 +52,7 @@ impl IncrementalDecoderUint {
); );
self.remaining = Some(remaining); self.remaining = Some(remaining);
self.v = v; self.v = v;
if remaining == 0 { (remaining == 0).then_some(v)
Some(v)
} else {
None
}
} }
} }
@@ -100,11 +92,7 @@ impl IncrementalDecoderBuffer {
let b = dv.decode(amount).unwrap(); let b = dv.decode(amount).unwrap();
self.v.extend_from_slice(b); self.v.extend_from_slice(b);
self.remaining -= amount; self.remaining -= amount;
if self.remaining == 0 { (self.remaining == 0).then(|| mem::take(&mut self.v))
Some(mem::take(&mut self.v))
} else {
None
}
} }
} }

View File

@@ -18,7 +18,7 @@ pub mod log;
pub mod qlog; pub mod qlog;
pub mod tos; pub mod tos;
use std::fmt::Write; use std::fmt::Write as _;
use enum_map::Enum; use enum_map::Enum;

View File

@@ -7,44 +7,13 @@
#![allow(clippy::module_name_repetitions)] #![allow(clippy::module_name_repetitions)]
use std::{ use std::{
io::Write, io::Write as _,
sync::{Once, OnceLock}, sync::{Once, OnceLock},
time::{Duration, Instant}, time::{Duration, Instant},
}; };
use env_logger::Builder; use env_logger::Builder;
#[macro_export]
macro_rules! do_log {
(target: $target:expr, $lvl:expr, $($arg:tt)+) => ({
let lvl = $lvl;
if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() {
::log::logger().log(
&::log::Record::builder()
.args(format_args!($($arg)+))
.level(lvl)
.target($target)
.module_path_static(Some(module_path!()))
.file_static(Some(file!()))
.line(Some(line!()))
.build()
);
}
});
($lvl:expr, $($arg:tt)+) => ($crate::do_log!(target: module_path!(), $lvl, $($arg)+))
}
#[macro_export]
macro_rules! log_subject {
($lvl:expr, $subject:expr) => {{
if $lvl <= ::log::max_level() {
format!("{}", $subject)
} else {
String::new()
}
}};
}
fn since_start() -> Duration { fn since_start() -> Duration {
static START_TIME: OnceLock<Instant> = OnceLock::new(); static START_TIME: OnceLock<Instant> = OnceLock::new();
START_TIME.get_or_init(Instant::now).elapsed() START_TIME.get_or_init(Instant::now).elapsed()
@@ -66,7 +35,7 @@ pub fn init(level_filter: Option<log::LevelFilter>) {
let elapsed = since_start(); let elapsed = since_start();
writeln!( writeln!(
buf, buf,
"{}s{:3}ms {} {}", "{}.{:03} {} {}",
elapsed.as_secs(), elapsed.as_secs(),
elapsed.as_millis() % 1000, elapsed.as_millis() % 1000,
record.level(), record.level(),
@@ -74,42 +43,55 @@ pub fn init(level_filter: Option<log::LevelFilter>) {
) )
}); });
if let Err(e) = builder.try_init() { if let Err(e) = builder.try_init() {
do_log!(::log::Level::Warn, "Logging initialization error {:?}", e); eprintln!("Logging initialization error {e:?}");
} else { } else {
do_log!(::log::Level::Debug, "Logging initialized"); ::log::debug!("Logging initialized");
} }
}); });
} }
#[macro_export] #[macro_export]
macro_rules! log_invoke { // TODO: Enable `#[clippy::format_args]` once our MSRV is >= 1.84
($lvl:expr, $ctx:expr, $($arg:tt)*) => ( {
::neqo_common::log::init(None);
::neqo_common::do_log!($lvl, "[{}] {}", $ctx, format!($($arg)*));
} )
}
#[macro_export]
macro_rules! qerror { macro_rules! qerror {
([$ctx:expr], $($arg:tt)*) => (::neqo_common::log_invoke!(::log::Level::Error, $ctx, $($arg)*);); ($($arg:tt)*) => ( {
($($arg:tt)*) => ( { ::neqo_common::log::init(None); ::neqo_common::do_log!(::log::Level::Error, $($arg)*); } ); #[cfg(any(test, feature = "bench"))]
::neqo_common::log::init(None);
::log::error!($($arg)*);
} );
} }
#[macro_export] #[macro_export]
// TODO: Enable `#[clippy::format_args]` once our MSRV is >= 1.84
macro_rules! qwarn { macro_rules! qwarn {
([$ctx:expr], $($arg:tt)*) => (::neqo_common::log_invoke!(::log::Level::Warn, $ctx, $($arg)*);); ($($arg:tt)*) => ( {
($($arg:tt)*) => ( { ::neqo_common::log::init(None); ::neqo_common::do_log!(::log::Level::Warn, $($arg)*); } ); #[cfg(any(test, feature = "bench"))]
::neqo_common::log::init(None);
::log::warn!($($arg)*);
} );
} }
#[macro_export] #[macro_export]
// TODO: Enable `#[clippy::format_args]` once our MSRV is >= 1.84
macro_rules! qinfo { macro_rules! qinfo {
([$ctx:expr], $($arg:tt)*) => (::neqo_common::log_invoke!(::log::Level::Info, $ctx, $($arg)*);); ($($arg:tt)*) => ( {
($($arg:tt)*) => ( { ::neqo_common::log::init(None); ::neqo_common::do_log!(::log::Level::Info, $($arg)*); } ); #[cfg(any(test, feature = "bench"))]
::neqo_common::log::init(None);
::log::info!($($arg)*);
} );
} }
#[macro_export] #[macro_export]
// TODO: Enable `#[clippy::format_args]` once our MSRV is >= 1.84
macro_rules! qdebug { macro_rules! qdebug {
([$ctx:expr], $($arg:tt)*) => (::neqo_common::log_invoke!(::log::Level::Debug, $ctx, $($arg)*);); ($($arg:tt)*) => ( {
($($arg:tt)*) => ( { ::neqo_common::log::init(None); ::neqo_common::do_log!(::log::Level::Debug, $($arg)*); } ); #[cfg(any(test, feature = "bench"))]
::neqo_common::log::init(None);
::log::debug!($($arg)*);
} );
} }
#[macro_export] #[macro_export]
// TODO: Enable `#[clippy::format_args]` once our MSRV is >= 1.84
macro_rules! qtrace { macro_rules! qtrace {
([$ctx:expr], $($arg:tt)*) => (::neqo_common::log_invoke!(::log::Level::Trace, $ctx, $($arg)*);); ($($arg:tt)*) => ( {
($($arg:tt)*) => ( { ::neqo_common::log::init(None); ::neqo_common::do_log!(::log::Level::Trace, $($arg)*); } ); #[cfg(any(test, feature = "bench"))]
::neqo_common::log::init(None);
::log::trace!($($arg)*);
} );
} }

View File

@@ -59,7 +59,7 @@ impl NeqoQlog {
title, title,
description, description,
None, None,
std::time::Instant::now(), Instant::now(),
new_trace(role), new_trace(role),
qlog::events::EventImportance::Base, qlog::events::EventImportance::Base,
Box::new(BufWriter::new(file)), Box::new(BufWriter::new(file)),
@@ -149,11 +149,7 @@ impl NeqoQlog {
{ {
if let Some(inner) = self.inner.borrow_mut().as_mut() { if let Some(inner) = self.inner.borrow_mut().as_mut() {
if let Err(e) = f(&mut inner.streamer) { if let Err(e) = f(&mut inner.streamer) {
crate::do_log!( log::error!("Qlog event generation failed with error {e}; closing qlog.");
::log::Level::Error,
"Qlog event generation failed with error {}; closing qlog.",
e
);
*self.inner.borrow_mut() = None; *self.inner.borrow_mut() = None;
} }
} }
@@ -169,13 +165,13 @@ impl fmt::Debug for NeqoQlogShared {
impl Drop for NeqoQlogShared { impl Drop for NeqoQlogShared {
fn drop(&mut self) { fn drop(&mut self) {
if let Err(e) = self.streamer.finish_log() { if let Err(e) = self.streamer.finish_log() {
crate::do_log!(::log::Level::Error, "Error dropping NeqoQlog: {}", e); log::error!("Error dropping NeqoQlog: {e}");
} }
} }
} }
#[must_use] #[must_use]
pub fn new_trace(role: Role) -> qlog::TraceSeq { pub fn new_trace(role: Role) -> TraceSeq {
TraceSeq { TraceSeq {
vantage_point: VantagePoint { vantage_point: VantagePoint {
name: Some(format!("neqo-{role}")), name: Some(format!("neqo-{role}")),
@@ -186,7 +182,7 @@ pub fn new_trace(role: Role) -> qlog::TraceSeq {
flow: None, flow: None,
}, },
title: Some(format!("neqo-{role} trace")), title: Some(format!("neqo-{role} trace")),
description: Some("Example qlog trace description".to_string()), description: Some(format!("neqo-{role} trace")),
configuration: Some(Configuration { configuration: Some(Configuration {
time_offset: Some(0.0), time_offset: Some(0.0),
original_uris: None, original_uris: None,
@@ -233,7 +229,7 @@ mod test {
let (log, contents) = test_fixture::new_neqo_qlog(); let (log, contents) = test_fixture::new_neqo_qlog();
log.add_event_with_instant(|| Some(Event::with_time(0.0, EV_DATA)), Instant::now()); log.add_event_with_instant(|| Some(Event::with_time(0.0, EV_DATA)), Instant::now());
assert_eq!( assert_eq!(
Regex::new("\"time\":[0-9].[0-9]*,") Regex::new("\"time\":[0-9]+.[0-9]+,")
.unwrap() .unwrap()
.replace(&contents.to_string(), "\"time\":0.0,"), .replace(&contents.to_string(), "\"time\":0.0,"),
format!("{EXPECTED_LOG_HEADER}{EXPECTED_LOG_EVENT}"), format!("{EXPECTED_LOG_HEADER}{EXPECTED_LOG_EVENT}"),

View File

@@ -62,7 +62,7 @@ impl IpTosEcn {
} }
} }
/// Diffserv Codepoints, mapped to the upper six bits of the TOS field. /// Diffserv codepoints, mapped to the upper six bits of the TOS field.
/// <https://www.iana.org/assignments/dscp-registry/dscp-registry.xhtml> /// <https://www.iana.org/assignments/dscp-registry/dscp-registry.xhtml>
#[derive(Copy, Clone, PartialEq, Eq, Enum, Default, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Enum, Default, Debug)]
#[repr(u8)] #[repr(u8)]

View File

@@ -19,40 +19,9 @@ fn basic() {
fn args() { fn args() {
let num = 1; let num = 1;
let obj = std::time::Instant::now(); let obj = std::time::Instant::now();
qerror!("error {} {:?}", num, obj); qerror!("error {num} {obj:?}");
qwarn!("warn {} {:?}", num, obj); qwarn!("warn {num} {obj:?}");
qinfo!("info {} {:?}", num, obj); qinfo!("info {num} {obj:?}");
qdebug!("debug {} {:?}", num, obj); qdebug!("debug {num} {obj:?}");
qtrace!("trace {} {:?}", num, obj); qtrace!("trace {num} {obj:?}");
}
#[test]
fn context() {
let context = "context";
qerror!([context], "error");
qwarn!([context], "warn");
qinfo!([context], "info");
qdebug!([context], "debug");
qtrace!([context], "trace");
}
#[test]
fn context_comma() {
let obj = vec![1, 2, 3];
let context = "context";
qerror!([context], "error {:?}", obj);
qwarn!([context], "warn {:?}", obj);
qinfo!([context], "info {:?}", obj);
qdebug!([context], "debug {:?}", obj);
qtrace!([context], "trace {:?}", obj);
}
#[test]
fn context_expr() {
let context = vec![1, 2, 3];
qerror!([format!("{:x?}", context)], "error");
qwarn!([format!("{:x?}", context)], "warn");
qinfo!([format!("{:x?}", context)], "info");
qdebug!([format!("{:x?}", context)], "debug");
qtrace!([format!("{:x?}", context)], "trace");
} }

View File

@@ -1 +1 @@
{"files":{"Cargo.toml":"4c2750f8c99df82e117073f3958e83fd51e03f0d8ca3aaba4c81990f4875fb64","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":"6410bcbe717a6b9ea6f11209b0888033358113ebc05b8a95cec1980d1360be4d","src/aead_null.rs":"81163fafef59bd2800bd0a078d53d0f05ee114f0e22165717823a5ff1cb908af","src/agent.rs":"38eb130b2b66edfaae05f154cdb718aee4a8c4dcffbf349b0ceb2287d4cbbb6e","src/agentio.rs":"6d86ff8d6319bf6c3dd7124b8d60271e3e1accd07a7b43ba54e81be51c8d2a98","src/auth.rs":"ced1a18f691894984244088020ea25dc1ee678603317f0c7dfc8b8842fa750b4","src/cert.rs":"8e75e69ec3544474b21f8915a7559463889c2f608b201dee274a8d701880950e","src/constants.rs":"58e296e314825753b2ab1d6effe9a1386421dc568f6ebfa8e95a95acb87205da","src/ech.rs":"75dd192423e8996d9061da5e9c20d30bff5153b9344132eda4fe321c4c141870","src/err.rs":"2366501e0b48933a6a2e1c5b934aa55108c093729c84878b45e1e012e4e45d51","src/exp.rs":"d953873e87430b1c84d4a83c8eb3815041f5585b210bbaf59ae2c4d0057f5edd","src/ext.rs":"cbf7d9f5ecabf4b8c9efd6c334637ab1596ec5266d38ab8d2d6ceae305283deb","src/hkdf.rs":"8745ba761be821c1819cedf6dfd91f8b3148c6718053a4a74f33eb50c7d0cc40","src/hp.rs":"510a4a7f278203aa306ead05608f99397edc3806dc22b0af9e28c665b43ae56c","src/lib.rs":"30632dacb1b6ed9321e42ca1aaa2b71db8d4878eeb27c608e4eabdc0b76bcdba","src/min_version.rs":"c6e1f98b9f56db0622ac38c1be131c55acf4a0f09ed0d6283f4d6308e2d1301a","src/p11.rs":"375397b18fcdf36dcdd22c164c8572dd83caf01b8d0065be3029444b197e1464","src/prio.rs":"5cf0105e78b1db43c65283208174abc3714a41dbb4d5cd80ac547a5a5a7c627c","src/replay.rs":"5cda39bc8fa8a07c493b761b8dfb5cbc9f669f97a2df7832a028ab366b3426be","src/result.rs":"0587cbb6aace71a7f9765ef7c01dcd9f73a49dcc6331e1d8fe4de2aef6ca65b6","src/secrets.rs":"2c47935c5b8c42363897881eaa0c171e84cf031e57a6e1387b99327080e8dd60","src/selfencrypt.rs":"018c2dacabd3e463fdadd5707715b23c26c261c4c7d86e66c62f0acec986cad9","src/ssl.rs":"450cf9fb327515de612b29e3a0f6e15499e6c0b523790e5986fd71a9ea5e76af","src/time.rs":"ade63a72ae90796d7fcccadbb15efc4594fcdb68913a914a657d4556fde88f62","tests/aead.rs":"e36ae77802df1ea6d17cfd1bd2178a3706089577d6fd1554ca86e748b8b235b9","tests/agent.rs":"cbd0011f1d33281883a45d433228221062424c94e86decade5697731c08a1c52","tests/ext.rs":"57af4e2df211fa8afdb73125d4344ef5c70c1ea4579107c3e6f5746308ee3e7b","tests/handshake.rs":"aa904736d36cc5d5cc0c4f6053b529987f33f944a73411bf08e01d30c4867186","tests/hkdf.rs":"1d2098dc8398395864baf13e4886cfd1da6d36118727c3b264f457ee3da6b048","tests/hp.rs":"ccda23018dac70b3ff3742afcb0fbae0735be9aeb36644a4ae2b1d7c9126801c","tests/init.rs":"3e15150c4b324c06ca5e8935618e4008da53dc0ef4b69325d150831e87dc0b63","tests/selfencrypt.rs":"8d10840b41629bf449a6b3a551377315e8a05ca26c6b041548748196652c5909"},"package":null} {"files":{"Cargo.toml":"6953ac93fcc661e47b15e627f031ac21f4b4ab77fa125871a339cc964b812cb3","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}

View File

@@ -13,7 +13,7 @@
edition = "2021" edition = "2021"
rust-version = "1.76.0" rust-version = "1.76.0"
name = "neqo-crypto" name = "neqo-crypto"
version = "0.11.0" version = "0.12.2"
authors = ["The Neqo Authors <necko@mozilla.com>"] authors = ["The Neqo Authors <necko@mozilla.com>"]
build = "build.rs" build = "build.rs"
autolib = false autolib = false
@@ -113,12 +113,28 @@ version = "0.5"
default-features = false default-features = false
[features] [features]
bench = []
disable-encryption = [] disable-encryption = []
disable-random = [] disable-random = []
gecko = ["mozbuild"] gecko = ["mozbuild"]
[lints.clippy] [lints.clippy]
cfg_not_test = "warn"
clone_on_ref_ptr = "warn"
create_dir = "warn"
get_unwrap = "warn"
if_then_some_else_none = "warn"
multiple_crate_versions = "allow" multiple_crate_versions = "allow"
multiple_inherent_impl = "warn"
pathbuf_init_then_push = "warn"
redundant_type_annotations = "warn"
ref_patterns = "warn"
renamed_function_params = "warn"
semicolon_inside_block = "warn"
try_err = "warn"
unneeded_field_pattern = "warn"
unused_result_ok = "warn"
unused_trait_names = "warn"
[lints.clippy.cargo] [lints.clippy.cargo]
level = "warn" level = "warn"
@@ -131,3 +147,19 @@ priority = -1
[lints.clippy.pedantic] [lints.clippy.pedantic]
level = "warn" level = "warn"
priority = -1 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"
non_ascii_idents = "warn"
redundant_imports = "warn"
redundant_lifetimes = "warn"
trivial_numeric_casts = "warn"
unit_bindings = "warn"
unused_import_braces = "warn"
unused_lifetimes = "warn"
unused_macro_rules = "warn"

View File

@@ -17,7 +17,7 @@ use crate::{
experimental_api, experimental_api,
p11::{PK11SymKey, SymKey}, p11::{PK11SymKey, SymKey},
scoped_ptr, scoped_ptr,
ssl::{self, PRUint16, PRUint64, PRUint8, SSLAeadContext}, ssl::{PRUint16, PRUint64, PRUint8, SSLAeadContext},
}; };
experimental_api!(SSL_MakeAead( experimental_api!(SSL_MakeAead(
@@ -80,7 +80,7 @@ impl RealAead {
prefix: &str, prefix: &str,
) -> Res<Self> { ) -> Res<Self> {
let p = prefix.as_bytes(); let p = prefix.as_bytes();
let mut ctx: *mut ssl::SSLAeadContext = null_mut(); let mut ctx: *mut SSLAeadContext = null_mut();
SSL_MakeAead( SSL_MakeAead(
version, version,
cipher, cipher,

View File

@@ -7,7 +7,7 @@
use std::{ use std::{
cell::RefCell, cell::RefCell,
ffi::{CStr, CString}, ffi::{CStr, CString},
mem::{self, MaybeUninit}, mem::MaybeUninit,
ops::{Deref, DerefMut}, ops::{Deref, DerefMut},
os::raw::{c_uint, c_void}, os::raw::{c_uint, c_void},
pin::Pin, pin::Pin,
@@ -107,7 +107,7 @@ fn get_alpn(fd: *mut ssl::PRFileDesc, pre: bool) -> Res<Option<String>> {
} }
_ => None, _ => None,
}; };
qtrace!([format!("{fd:p}")], "got ALPN {:?}", alpn); qtrace!("[{fd:p}] got ALPN {alpn:?}");
Ok(alpn) Ok(alpn)
} }
@@ -135,7 +135,7 @@ impl SecretAgentPreInfo {
ssl::SSL_GetPreliminaryChannelInfo( ssl::SSL_GetPreliminaryChannelInfo(
fd, fd,
info.as_mut_ptr(), info.as_mut_ptr(),
c_uint::try_from(mem::size_of::<ssl::SSLPreliminaryChannelInfo>())?, c_uint::try_from(std::mem::size_of::<ssl::SSLPreliminaryChannelInfo>())?,
) )
})?; })?;
@@ -222,7 +222,7 @@ impl SecretAgentInfo {
ssl::SSL_GetChannelInfo( ssl::SSL_GetChannelInfo(
fd, fd,
info.as_mut_ptr(), info.as_mut_ptr(),
c_uint::try_from(mem::size_of::<ssl::SSLChannelInfo>())?, c_uint::try_from(std::mem::size_of::<ssl::SSLChannelInfo>())?,
) )
})?; })?;
let info = unsafe { info.assume_init() }; let info = unsafe { info.assume_init() };
@@ -337,7 +337,9 @@ impl SecretAgent {
ssl::SSL_ImportFD(null_mut(), base_fd.cast()) ssl::SSL_ImportFD(null_mut(), base_fd.cast())
}; };
if fd.is_null() { if fd.is_null() {
unsafe { prio::PR_Close(base_fd) }; unsafe {
prio::PR_Close(base_fd);
}
return Err(Error::CreateSslSocket); return Err(Error::CreateSslSocket);
} }
Ok(fd) Ok(fd)
@@ -347,8 +349,8 @@ impl SecretAgent {
unsafe extern "C" fn auth_complete_hook( unsafe extern "C" fn auth_complete_hook(
arg: *mut c_void, arg: *mut c_void,
_fd: *mut ssl::PRFileDesc, _fd: *mut ssl::PRFileDesc,
_check_sig: ssl::PRBool, _check_sig: PRBool,
_is_server: ssl::PRBool, _is_server: PRBool,
) -> ssl::SECStatus { ) -> ssl::SECStatus {
let auth_required_ptr = arg.cast::<bool>(); let auth_required_ptr = arg.cast::<bool>();
*auth_required_ptr = true; *auth_required_ptr = true;
@@ -369,7 +371,7 @@ impl SecretAgent {
if st.is_none() { if st.is_none() {
*st = Some(alert.description); *st = Some(alert.description);
} else { } else {
qwarn!([format!("{fd:p}")], "duplicate alert {}", alert.description); qwarn!("[{fd:p}] duplicate alert {}", alert.description);
} }
} }
} }
@@ -394,7 +396,7 @@ impl SecretAgent {
self.now.bind(self.fd)?; self.now.bind(self.fd)?;
self.configure(grease)?; self.configure(grease)?;
secstatus_to_res(unsafe { ssl::SSL_ResetHandshake(self.fd, ssl::PRBool::from(is_server)) }) secstatus_to_res(unsafe { ssl::SSL_ResetHandshake(self.fd, PRBool::from(is_server)) })
} }
/// Default configuration. /// Default configuration.
@@ -429,7 +431,7 @@ impl SecretAgent {
/// If NSS can't enable or disable ciphers. /// If NSS can't enable or disable ciphers.
pub fn set_ciphers(&mut self, ciphers: &[Cipher]) -> Res<()> { pub fn set_ciphers(&mut self, ciphers: &[Cipher]) -> Res<()> {
if self.state != HandshakeState::New { if self.state != HandshakeState::New {
qwarn!([self], "Cannot enable ciphers in state {:?}", self.state); qwarn!("[{self}] Cannot enable ciphers in state {:?}", self.state);
return Err(Error::InternalError); return Err(Error::InternalError);
} }
@@ -438,13 +440,13 @@ impl SecretAgent {
for i in 0..cipher_count { for i in 0..cipher_count {
let p = all_ciphers.wrapping_add(i); let p = all_ciphers.wrapping_add(i);
secstatus_to_res(unsafe { secstatus_to_res(unsafe {
ssl::SSL_CipherPrefSet(self.fd, i32::from(*p), ssl::PRBool::from(false)) ssl::SSL_CipherPrefSet(self.fd, i32::from(*p), PRBool::from(false))
})?; })?;
} }
for c in ciphers { for c in ciphers {
secstatus_to_res(unsafe { secstatus_to_res(unsafe {
ssl::SSL_CipherPrefSet(self.fd, i32::from(*c), ssl::PRBool::from(true)) ssl::SSL_CipherPrefSet(self.fd, i32::from(*c), PRBool::from(true))
})?; })?;
} }
Ok(()) Ok(())
@@ -600,8 +602,8 @@ impl SecretAgent {
/// Calling this function returns None until the connection is complete. /// Calling this function returns None until the connection is complete.
#[must_use] #[must_use]
pub const fn info(&self) -> Option<&SecretAgentInfo> { pub const fn info(&self) -> Option<&SecretAgentInfo> {
match self.state { match &self.state {
HandshakeState::Complete(ref info) => Some(info), HandshakeState::Complete(info) => Some(info),
_ => None, _ => None,
} }
} }
@@ -644,7 +646,7 @@ impl SecretAgent {
fn capture_error<T>(&mut self, res: Res<T>) -> Res<T> { fn capture_error<T>(&mut self, res: Res<T>) -> Res<T> {
if let Err(e) = res { if let Err(e) = res {
let e = ech::convert_ech_error(self.fd, e); let e = ech::convert_ech_error(self.fd, e);
qwarn!([self], "error: {:?}", e); qwarn!("[{self}] error: {e:?}");
self.state = HandshakeState::Failed(e.clone()); self.state = HandshakeState::Failed(e.clone());
Err(e) Err(e)
} else { } else {
@@ -669,7 +671,7 @@ impl SecretAgent {
let info = self.capture_error(SecretAgentInfo::new(self.fd))?; let info = self.capture_error(SecretAgentInfo::new(self.fd))?;
HandshakeState::Complete(info) HandshakeState::Complete(info)
}; };
qdebug!([self], "state -> {:?}", self.state); qdebug!("[{self}] state -> {:?}", self.state);
Ok(()) Ok(())
} }
@@ -692,8 +694,8 @@ impl SecretAgent {
// Within this scope, _h maintains a mutable reference to self.io. // Within this scope, _h maintains a mutable reference to self.io.
let _h = self.io.wrap(input); let _h = self.io.wrap(input);
match self.state { match self.state {
HandshakeState::Authenticated(ref err) => unsafe { HandshakeState::Authenticated(err) => unsafe {
ssl::SSL_AuthCertificateComplete(self.fd, *err) ssl::SSL_AuthCertificateComplete(self.fd, err)
}, },
_ => unsafe { ssl::SSL_ForceHandshake(self.fd) }, _ => unsafe { ssl::SSL_ForceHandshake(self.fd) },
} }
@@ -726,10 +728,10 @@ impl SecretAgent {
let records = self.setup_raw()?; let records = self.setup_raw()?;
// Fire off any authentication we might need to complete. // Fire off any authentication we might need to complete.
if let HandshakeState::Authenticated(ref err) = self.state { if let HandshakeState::Authenticated(err) = self.state {
let result = let result =
secstatus_to_res(unsafe { ssl::SSL_AuthCertificateComplete(self.fd, *err) }); secstatus_to_res(unsafe { ssl::SSL_AuthCertificateComplete(self.fd, err) });
qdebug!([self], "SSL_AuthCertificateComplete: {:?}", result); qdebug!("[{self}] SSL_AuthCertificateComplete: {result:?}");
// This should return SECSuccess, so don't use update_state(). // This should return SECSuccess, so don't use update_state().
self.capture_error(result)?; self.capture_error(result)?;
} }
@@ -758,11 +760,15 @@ impl SecretAgent {
if self.raw == Some(true) { if self.raw == Some(true) {
// Need to hold the record list in scope until the close is done. // Need to hold the record list in scope until the close is done.
let _records = self.setup_raw().expect("Can only close"); let _records = self.setup_raw().expect("Can only close");
unsafe { prio::PR_Close(self.fd.cast()) }; unsafe {
prio::PR_Close(self.fd.cast());
}
} else { } else {
// Need to hold the IO wrapper in scope until the close is done. // Need to hold the IO wrapper in scope until the close is done.
let _io = self.io.wrap(&[]); let _io = self.io.wrap(&[]);
unsafe { prio::PR_Close(self.fd.cast()) }; unsafe {
prio::PR_Close(self.fd.cast());
}
}; };
let _output = self.io.take_output(); let _output = self.io.take_output();
self.fd = null_mut(); self.fd = null_mut();
@@ -876,7 +882,7 @@ impl Client {
token, token,
len, len,
info.as_mut_ptr(), info.as_mut_ptr(),
c_uint::try_from(mem::size_of::<ssl::SSLResumptionTokenInfo>()).unwrap(), c_uint::try_from(std::mem::size_of::<ssl::SSLResumptionTokenInfo>()).unwrap(),
); );
if info_res.is_err() { if info_res.is_err() {
// Ignore the token. // Ignore the token.
@@ -891,11 +897,7 @@ impl Client {
let len = usize::try_from(len).unwrap(); let len = usize::try_from(len).unwrap();
let mut v = Vec::with_capacity(len); let mut v = Vec::with_capacity(len);
v.extend_from_slice(null_safe_slice(token, len)); v.extend_from_slice(null_safe_slice(token, len));
qdebug!( qdebug!("[{fd:p}] Got resumption token {}", hex_snip_middle(&v));
[format!("{fd:p}")],
"Got resumption token {}",
hex_snip_middle(&v)
);
if resumption.len() >= MAX_TICKETS { if resumption.len() >= MAX_TICKETS {
resumption.remove(0); resumption.remove(0);
@@ -965,7 +967,7 @@ impl Client {
/// Error returned when the configuration is invalid. /// Error returned when the configuration is invalid.
pub fn enable_ech(&mut self, ech_config_list: impl AsRef<[u8]>) -> Res<()> { pub fn enable_ech(&mut self, ech_config_list: impl AsRef<[u8]>) -> Res<()> {
let config = ech_config_list.as_ref(); let config = ech_config_list.as_ref();
qdebug!([self], "Enable ECH for a server: {}", hex_with_len(config)); qdebug!("[{self}] Enable ECH for a server: {}", hex_with_len(config));
self.ech_config = Vec::from(config); self.ech_config = Vec::from(config);
if config.is_empty() { if config.is_empty() {
unsafe { ech::SSL_EnableTls13GreaseEch(self.agent.fd, PRBool::from(true)) } unsafe { ech::SSL_EnableTls13GreaseEch(self.agent.fd, PRBool::from(true)) }
@@ -1016,7 +1018,7 @@ pub enum ZeroRttCheckResult {
/// A `ZeroRttChecker` is used by the agent to validate the application token (as provided by /// A `ZeroRttChecker` is used by the agent to validate the application token (as provided by
/// `send_ticket`) /// `send_ticket`)
pub trait ZeroRttChecker: std::fmt::Debug + std::marker::Unpin { pub trait ZeroRttChecker: std::fmt::Debug + Unpin {
fn check(&self, token: &[u8]) -> ZeroRttCheckResult; fn check(&self, token: &[u8]) -> ZeroRttCheckResult;
} }
@@ -1068,7 +1070,7 @@ impl Server {
return Err(Error::CertificateLoading); return Err(Error::CertificateLoading);
}; };
let key_ptr = unsafe { p11::PK11_FindKeyByAnyCert(*cert, null_mut()) }; let key_ptr = unsafe { p11::PK11_FindKeyByAnyCert(*cert, null_mut()) };
let Ok(key) = p11::PrivateKey::from_ptr(key_ptr) else { let Ok(key) = PrivateKey::from_ptr(key_ptr) else {
return Err(Error::CertificateLoading); return Err(Error::CertificateLoading);
}; };
secstatus_to_res(unsafe { secstatus_to_res(unsafe {
@@ -1175,7 +1177,7 @@ impl Server {
pk: &PublicKey, pk: &PublicKey,
) -> Res<()> { ) -> Res<()> {
let cfg = ech::encode_config(config, public_name, pk)?; let cfg = ech::encode_config(config, public_name, pk)?;
qdebug!([self], "Enable ECH for a server: {}", hex_with_len(&cfg)); qdebug!("[{self}] Enable ECH for a server: {}", hex_with_len(&cfg));
unsafe { unsafe {
ech::SSL_SetServerEchConfigs( ech::SSL_SetServerEchConfigs(
self.agent.fd, self.agent.fd,
@@ -1213,8 +1215,8 @@ impl ::std::fmt::Display for Server {
/// A generic container for Client or Server. /// A generic container for Client or Server.
#[derive(Debug)] #[derive(Debug)]
pub enum Agent { pub enum Agent {
Client(crate::agent::Client), Client(Client),
Server(crate::agent::Server), Server(Server),
} }
impl Deref for Agent { impl Deref for Agent {

View File

@@ -52,7 +52,7 @@ impl Record {
// Shoves this record into the socket, returns true if blocked. // Shoves this record into the socket, returns true if blocked.
pub(crate) fn write(self, fd: *mut ssl::PRFileDesc) -> Res<()> { pub(crate) fn write(self, fd: *mut ssl::PRFileDesc) -> Res<()> {
qtrace!("write {:?}", self); qtrace!("write {self:?}");
unsafe { unsafe {
ssl::SSL_RecordLayerData( ssl::SSL_RecordLayerData(
fd, fd,
@@ -177,7 +177,7 @@ impl AgentIoInput {
#[allow(clippy::disallowed_methods)] // We just checked if this was empty. #[allow(clippy::disallowed_methods)] // We just checked if this was empty.
let src = unsafe { std::slice::from_raw_parts(self.input, amount) }; let src = unsafe { std::slice::from_raw_parts(self.input, amount) };
qtrace!([self], "read {}", hex(src)); qtrace!("[{self}] read {}", hex(src));
let dst = unsafe { std::slice::from_raw_parts_mut(buf, amount) }; let dst = unsafe { std::slice::from_raw_parts_mut(buf, amount) };
dst.copy_from_slice(src); dst.copy_from_slice(src);
self.input = self.input.wrapping_add(amount); self.input = self.input.wrapping_add(amount);
@@ -186,7 +186,7 @@ impl AgentIoInput {
} }
fn reset(&mut self) { fn reset(&mut self) {
qtrace!([self], "reset"); qtrace!("[{self}] reset");
self.input = null(); self.input = null();
self.available = 0; self.available = 0;
} }
@@ -230,12 +230,12 @@ impl AgentIo {
// Stage output from TLS into the output buffer. // Stage output from TLS into the output buffer.
fn save_output(&mut self, buf: *const u8, count: usize) { fn save_output(&mut self, buf: *const u8, count: usize) {
let slice = unsafe { null_safe_slice(buf, count) }; let slice = unsafe { null_safe_slice(buf, count) };
qtrace!([self], "save output {}", hex(slice)); qtrace!("[{self}] save output {}", hex(slice));
self.output.extend_from_slice(slice); self.output.extend_from_slice(slice);
} }
pub fn take_output(&mut self) -> Vec<u8> { pub fn take_output(&mut self) -> Vec<u8> {
qtrace!([self], "take output"); qtrace!("[{self}] take output");
mem::take(&mut self.output) mem::take(&mut self.output)
} }
} }

View File

@@ -46,7 +46,7 @@ fn stapled_ocsp_responses(fd: *mut PRFileDesc) -> Option<Vec<Vec<u8>>> {
Some(ocsp_ptr) => { Some(ocsp_ptr) => {
let mut ocsp_helper: Vec<Vec<u8>> = Vec::new(); let mut ocsp_helper: Vec<Vec<u8>> = Vec::new();
let Ok(len) = isize::try_from(unsafe { ocsp_ptr.as_ref().len }) else { let Ok(len) = isize::try_from(unsafe { ocsp_ptr.as_ref().len }) else {
qerror!([format!("{fd:p}")], "Received illegal OSCP length"); qerror!("[{fd:p}] Received illegal OSCP length");
return None; return None;
}; };
for idx in 0..len { for idx in 0..len {
@@ -72,23 +72,6 @@ fn signed_cert_timestamp(fd: *mut PRFileDesc) -> Option<Vec<u8>> {
}) })
} }
impl CertificateInfo {
pub(crate) fn new(fd: *mut PRFileDesc) -> Option<Self> {
peer_certificate_chain(fd).map(|certs| Self {
certs,
stapled_ocsp_responses: stapled_ocsp_responses(fd),
signed_cert_timestamp: signed_cert_timestamp(fd),
})
}
}
impl CertificateInfo {
#[must_use]
pub fn iter(&self) -> ItemArrayIterator<'_> {
self.certs.into_iter()
}
}
impl<'a> IntoIterator for &'a CertificateInfo { impl<'a> IntoIterator for &'a CertificateInfo {
type IntoIter = ItemArrayIterator<'a>; type IntoIter = ItemArrayIterator<'a>;
type Item = &'a [u8]; type Item = &'a [u8];
@@ -98,6 +81,19 @@ impl<'a> IntoIterator for &'a CertificateInfo {
} }
impl CertificateInfo { impl CertificateInfo {
pub(crate) fn new(fd: *mut PRFileDesc) -> Option<Self> {
peer_certificate_chain(fd).map(|certs| Self {
certs,
stapled_ocsp_responses: stapled_ocsp_responses(fd),
signed_cert_timestamp: signed_cert_timestamp(fd),
})
}
#[must_use]
pub fn iter(&self) -> ItemArrayIterator<'_> {
self.certs.into_iter()
}
#[must_use] #[must_use]
pub const fn stapled_ocsp_responses(&self) -> &Option<Vec<Vec<u8>>> { pub const fn stapled_ocsp_responses(&self) -> &Option<Vec<Vec<u8>>> {
&self.stapled_ocsp_responses &self.stapled_ocsp_responses

View File

@@ -146,7 +146,7 @@ pub fn generate_keys() -> Res<(PrivateKey, PublicKey)> {
assert_eq!(secret_ptr.is_null(), public_ptr.is_null()); assert_eq!(secret_ptr.is_null(), public_ptr.is_null());
let sk = PrivateKey::from_ptr(secret_ptr)?; let sk = PrivateKey::from_ptr(secret_ptr)?;
let pk = PublicKey::from_ptr(public_ptr)?; let pk = PublicKey::from_ptr(public_ptr)?;
qtrace!("Generated key pair: sk={:?} pk={:?}", sk, pk); qtrace!("Generated key pair: sk={sk:?} pk={pk:?}");
Ok((sk, pk)) Ok((sk, pk))
} }

View File

@@ -70,7 +70,7 @@ mod nss {
// Need to map the types through. // Need to map the types through.
fn secstatus_to_res(code: nss::SECStatus) -> Res<()> { fn secstatus_to_res(code: nss::SECStatus) -> Res<()> {
crate::err::secstatus_to_res(code as crate::ssl::SECStatus) err::secstatus_to_res(code)
} }
enum NssLoaded { enum NssLoaded {
@@ -106,7 +106,7 @@ fn version_check() -> Res<()> {
/// This allows us to use SSLTRACE in all of our unit tests and programs. /// This allows us to use SSLTRACE in all of our unit tests and programs.
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
fn enable_ssl_trace() -> Res<()> { fn enable_ssl_trace() -> Res<()> {
let opt = ssl::Opt::Locking.as_int(); let opt = Opt::Locking.as_int();
let mut v: ::std::os::raw::c_int = 0; let mut v: ::std::os::raw::c_int = 0;
secstatus_to_res(unsafe { ssl::SSL_OptionGetDefault(opt, &mut v) }) secstatus_to_res(unsafe { ssl::SSL_OptionGetDefault(opt, &mut v) })
} }

View File

@@ -11,7 +11,6 @@
use std::{ use std::{
cell::RefCell, cell::RefCell,
mem,
ops::{Deref, DerefMut}, ops::{Deref, DerefMut},
os::raw::c_uint, os::raw::c_uint,
ptr::null_mut, ptr::null_mut,
@@ -265,7 +264,7 @@ impl Item {
SECItem { SECItem {
type_: SECItemType::siBuffer, type_: SECItemType::siBuffer,
data: data.cast_mut().cast(), data: data.cast_mut().cast(),
len: c_uint::try_from(mem::size_of::<T>()).unwrap(), len: c_uint::try_from(std::mem::size_of::<T>()).unwrap(),
} }
} }

View File

@@ -83,7 +83,7 @@ mod tests {
assert_eq!(code, -12273); assert_eq!(code, -12273);
assert_eq!( assert_eq!(
desc, desc,
"SSL received a record with an incorrect Message Authentication Code." "SSL received a record with an incorrect Message Authentication Code"
); );
} }
_ => unreachable!(), _ => unreachable!(),

View File

@@ -88,7 +88,7 @@ impl Secrets {
} }
fn put(&mut self, dir: SecretDirection, epoch: Epoch, key: SymKey) { fn put(&mut self, dir: SecretDirection, epoch: Epoch, key: SymKey) {
qdebug!("{:?} secret available for {:?}: {:?}", dir, epoch, key); qdebug!("{dir:?} secret available for {epoch:?}: {key:?}");
let keys = match dir { let keys = match dir {
SecretDirection::Read => &mut self.r, SecretDirection::Read => &mut self.r,
SecretDirection::Write => &mut self.w, SecretDirection::Write => &mut self.w,

View File

@@ -61,7 +61,7 @@ impl SelfEncrypt {
self.old_key = Some(mem::replace(&mut self.key, new_key)); self.old_key = Some(mem::replace(&mut self.key, new_key));
let (kid, _) = self.key_id.overflowing_add(1); let (kid, _) = self.key_id.overflowing_add(1);
self.key_id = kid; self.key_id = kid;
qinfo!(["SelfEncrypt"], "Rotated keys to {}", self.key_id); qinfo!("[SelfEncrypt] Rotated keys to {}", self.key_id);
Ok(()) Ok(())
} }
@@ -99,8 +99,7 @@ impl SelfEncrypt {
output.resize(encoded_len, 0); output.resize(encoded_len, 0);
cipher.encrypt(0, extended_aad.as_ref(), plaintext, &mut output[offset..])?; cipher.encrypt(0, extended_aad.as_ref(), plaintext, &mut output[offset..])?;
qtrace!( qtrace!(
["SelfEncrypt"], "[SelfEncrypt] seal {} {} -> {}",
"seal {} {} -> {}",
hex(aad), hex(aad),
hex(plaintext), hex(plaintext),
hex(&output) hex(&output)
@@ -150,8 +149,7 @@ impl SelfEncrypt {
let final_len = decrypted.len(); let final_len = decrypted.len();
output.truncate(final_len); output.truncate(final_len);
qtrace!( qtrace!(
["SelfEncrypt"], "[SelfEncrypt] open {} {} -> {}",
"open {} {} -> {}",
hex(aad), hex(aad),
hex(ciphertext), hex(ciphertext),
hex(&output) hex(&output)

View File

@@ -233,7 +233,7 @@ mod test {
init(); init();
let base = get_base(); let base = get_base();
let delta_micros = PRTime::try_from(DELTA.as_micros()).unwrap(); let delta_micros = PRTime::try_from(DELTA.as_micros()).unwrap();
println!("{} - {}", base.prtime, delta_micros); println!("{} - {delta_micros}", base.prtime);
let t = Time::try_from(base.prtime - delta_micros).unwrap(); let t = Time::try_from(base.prtime - delta_micros).unwrap();
assert_eq!(Instant::from(t) + DELTA, base.instant); assert_eq!(Instant::from(t) + DELTA, base.instant);
} }

View File

@@ -116,7 +116,7 @@ impl ZeroRttChecker for PermissiveZeroRttChecker {
} }
fn zero_rtt_setup(mode: Resumption, client: &Client, server: &mut Server) -> Option<AntiReplay> { fn zero_rtt_setup(mode: Resumption, client: &Client, server: &mut Server) -> Option<AntiReplay> {
if matches!(mode, Resumption::WithZeroRtt) { matches!(mode, Resumption::WithZeroRtt).then(|| {
client.enable_0rtt().expect("should enable 0-RTT on client"); client.enable_0rtt().expect("should enable 0-RTT on client");
let anti_replay = anti_replay(); let anti_replay = anti_replay();
@@ -127,10 +127,8 @@ fn zero_rtt_setup(mode: Resumption, client: &Client, server: &mut Server) -> Opt
Box::new(PermissiveZeroRttChecker { resuming: false }), Box::new(PermissiveZeroRttChecker { resuming: false }),
) )
.expect("should enable 0-RTT on server"); .expect("should enable 0-RTT on server");
Some(anti_replay) anti_replay
} else { })
None
}
} }
#[allow(clippy::missing_panics_doc)] #[allow(clippy::missing_panics_doc)]

View File

@@ -4,8 +4,6 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
use std::mem;
use neqo_crypto::{ use neqo_crypto::{
constants::{ constants::{
Cipher, TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384, TLS_CHACHA20_POLY1305_SHA256, Cipher, TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384, TLS_CHACHA20_POLY1305_SHA256,
@@ -71,12 +69,12 @@ fn chacha20_ctr() {
#[should_panic(expected = "out of range")] #[should_panic(expected = "out of range")]
fn aes_short() { fn aes_short() {
let hp = make_hp(TLS_AES_128_GCM_SHA256); let hp = make_hp(TLS_AES_128_GCM_SHA256);
mem::drop(hp.mask(&[0; 15])); drop(hp.mask(&[0; 15]));
} }
#[test] #[test]
#[should_panic(expected = "out of range")] #[should_panic(expected = "out of range")]
fn chacha20_short() { fn chacha20_short() {
let hp = make_hp(TLS_CHACHA20_POLY1305_SHA256); let hp = make_hp(TLS_CHACHA20_POLY1305_SHA256);
mem::drop(hp.mask(&[0; 15])); drop(hp.mask(&[0; 15]));
} }

View File

@@ -1 +1 @@
{"files":{"Cargo.toml":"98ce446932fc062c3a45558e73380b08d0dfbeac321ed0111a62062720451b6f","src/buffered_send_stream.rs":"dfb248c66ea65418b0c7798c2ecaa3ed70ef1af818ef58d53ef742b3445077b7","src/client_events.rs":"77fedca72ce54956eaba3fb7103085d196a631b764662584ea2629224c5c234e","src/conn_params.rs":"7f0df52bceda1923aef2b7c5c64a532f49ea083ea45e3dcd5bd4b03031b89643","src/connection.rs":"c82b016f839c5d3b1ec24cd76b372aec4f0e832ed68d792171579f9e649b3824","src/connection_client.rs":"a27423973be27501bb8e8ae169938fa215322fa78f7f5b95cb418adc6fa8b7a1","src/connection_server.rs":"cf4da2cdd823e31d2352e45de84d366c45bd3d8adf38c9151a84d808bda80209","src/control_stream_local.rs":"20917762c7e7c1112c56abf1cbaf0ad7f0eab97d8db9a3b10ff524315a235670","src/control_stream_remote.rs":"3729f67aa0681b1dbd4147063890f8440f27d82454776500ae964a17cda4d6b5","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":"7828af3887acc5b141ec1af459069eeb4f2d95e7c9e5639047006c179ce6a355","src/features/extended_connect/tests/webtransport/negotiation.rs":"6ddb604a0aa521335ab84ff07718e485f926a27da730d5697c33d6df62af39d6","src/features/extended_connect/tests/webtransport/sessions.rs":"6ed8c6247a84916cf6bb6dc4eeded20f1d80c6d2ea8f256975786c5f3ab2efcb","src/features/extended_connect/tests/webtransport/streams.rs":"eab84efc920b766ea105a47d34bec565f79f64f6c6be1b7f4945d78bddb462fd","src/features/extended_connect/webtransport_session.rs":"debe63b81c8c3c49da9f2b9abb92ea05fb95745a8ee725a956dfeffa53dc9574","src/features/extended_connect/webtransport_streams.rs":"9855d77705acb7d21566333c4b297816e363be2ade14b8685fd1df4a4861cf74","src/features/mod.rs":"89056df3a868cb0037963c942fc27093cc16d84538ffca2d4759f9a6a6c74c7f","src/frames/hframe.rs":"de2c3d1a9205b0459fe676d7d5e1c0e463d3c1dd9e5f518a07b2e4ebbe66e3ec","src/frames/mod.rs":"0e6d49888d723b2c2c73df11020ceb88d9f062e9d4dc436eb38173e0b772d905","src/frames/reader.rs":"01acff3c6bb9d2a0c2ff68b054276fab8d61a47679bec9084d75c4f680a959b3","src/frames/tests/hframe.rs":"53941fd7656f5e424d499278e6d9ba93ce716f219e86fe6fa08c058ea92f8d7b","src/frames/tests/mod.rs":"6cb78d24bbab27f877d0526deb3e9a26694a23a9ce8ebe664947a852a3d92747","src/frames/tests/reader.rs":"6fb66c7a03acfc2e231e7bb3d020c902b59366a7523e488d118b24440ac68501","src/frames/tests/wtframe.rs":"c6598d24f5e12972f02de6e1394362671633982db637a07e1c0bb9b56d93ea2a","src/frames/wtframe.rs":"0f0366e590f7409580459e8a8b86fc48308ca7585837dddd7c319581a9a5a972","src/headers_checks.rs":"69964deb121721be01df7174c177543c161389295ce1450d348369279e312ba4","src/lib.rs":"3fb980eee46bee8dcb97ad9d55014555d8994a7a2d040ca223f2d28fe7d923ef","src/priority.rs":"946307329f31819d969093406ae5448f7923343ccc112221ea6eedf86cf447dc","src/push_controller.rs":"7f8b668d7ff16372693830ac4d3e6834e77465762a0a8d77ab7f9e883c2fb919","src/qlog.rs":"85187b6eee73d0ce085bc42dfc30999f4a51f92daa02a1a1ac1228a490131082","src/qpack_decoder_receiver.rs":"eb06c4be59da567fef70c20daa2c0f165c768131165479a210e69659f168b88f","src/qpack_encoder_receiver.rs":"831f3da9ec17966286786ba3f2c723395a132e65d6a33b4ec341fe7640c1a53d","src/recv_message.rs":"8b2fb49850560b32dcdd7a90933361ef7d61bc42daad3f2952462913d49e8787","src/request_target.rs":"9720b9f87d66a7c2301bba7de5a5a9300f547613a63153a4d35c7a7506a59b31","src/send_message.rs":"be4e9f64db2c25eb7176b84695e608e768115d62e615d389a33d26f7cd5b0c6c","src/server.rs":"c6a231fea182acd4f7e064578a1ad85a5fa0f618f3e0842d499f84f841bbf9da","src/server_connection_events.rs":"1396baab265a814045ccfe63d637a4fdc32a667b5eb2925fa4951f5c3078fb20","src/server_events.rs":"02fc8c0711efd758fb1ddee27d257c12ed35e2a989e7bf3de44bd662dc8234e3","src/settings.rs":"d0f8c546e70161422a029a40564b9e9b953fe671c60835196b16f3364779eaf9","src/stream_type_reader.rs":"115d50bbaa304a74d601614b755bcb626572ab89d5db7bfae9fff8ad64270722","tests/httpconn.rs":"72b4f66fc9b9efeb070907da35f1db2d320d232ef74380fd36ad7c2ddd213076","tests/priority.rs":"3b0e03d6a8fbde52c695130bb3e40d3b70cb74ee826af28db577060911bcbc03","tests/send_message.rs":"9540259485e8b7df1d07ff8abdc8cb86d5f32d736aea3bce28e8b0ecc00a9f5b","tests/webtransport.rs":"3bfcfddd57a8fe262c597b756982d671065a593d99d09d42b04f954a27a2a5fa"},"package":null} {"files":{"Cargo.toml":"a3bea7b8e1efb0a5a78f4184ffa79bc436836b516e34d313bab8f450cdfb02df","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}

View File

@@ -13,7 +13,7 @@
edition = "2021" edition = "2021"
rust-version = "1.76.0" rust-version = "1.76.0"
name = "neqo-http3" name = "neqo-http3"
version = "0.11.0" version = "0.12.2"
authors = ["The Neqo Authors <necko@mozilla.com>"] authors = ["The Neqo Authors <necko@mozilla.com>"]
build = false build = false
autolib = false autolib = false
@@ -39,6 +39,9 @@ categories = [
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
repository = "https://github.com/mozilla/neqo/" repository = "https://github.com/mozilla/neqo/"
[package.metadata.cargo-machete]
ignored = ["log"]
[lib] [lib]
name = "neqo_http3" name = "neqo_http3"
path = "src/lib.rs" path = "src/lib.rs"
@@ -93,17 +96,47 @@ version = "2.5.3"
features = ["std"] features = ["std"]
default-features = false default-features = false
[dev-dependencies.neqo-http3]
path = "."
features = ["draft-29"]
[dev-dependencies.neqo-transport]
path = "./../neqo-transport"
features = ["draft-29"]
[dev-dependencies.test-fixture] [dev-dependencies.test-fixture]
path = "../test-fixture" path = "../test-fixture"
[features] [features]
bench = [
"neqo-common/bench",
"neqo-crypto/bench",
"neqo-qpack/bench",
"neqo-transport/bench",
]
disable-encryption = [ disable-encryption = [
"neqo-transport/disable-encryption", "neqo-transport/disable-encryption",
"neqo-crypto/disable-encryption", "neqo-crypto/disable-encryption",
] ]
draft-29 = []
[lints.clippy] [lints.clippy]
cfg_not_test = "warn"
clone_on_ref_ptr = "warn"
create_dir = "warn"
get_unwrap = "warn"
if_then_some_else_none = "warn"
multiple_crate_versions = "allow" multiple_crate_versions = "allow"
multiple_inherent_impl = "warn"
pathbuf_init_then_push = "warn"
redundant_type_annotations = "warn"
ref_patterns = "warn"
renamed_function_params = "warn"
semicolon_inside_block = "warn"
try_err = "warn"
unneeded_field_pattern = "warn"
unused_result_ok = "warn"
unused_trait_names = "warn"
[lints.clippy.cargo] [lints.clippy.cargo]
level = "warn" level = "warn"
@@ -116,3 +149,19 @@ priority = -1
[lints.clippy.pedantic] [lints.clippy.pedantic]
level = "warn" level = "warn"
priority = -1 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"
non_ascii_idents = "warn"
redundant_imports = "warn"
redundant_lifetimes = "warn"
trivial_numeric_casts = "warn"
unit_bindings = "warn"
unused_import_braces = "warn"
unused_lifetimes = "warn"
unused_macro_rules = "warn"

View File

@@ -49,7 +49,7 @@ impl BufferedStream {
/// # Panics /// # Panics
/// ///
/// This functon cannot be called before the `BufferedStream` is initialized. /// This function cannot be called before the `BufferedStream` is initialized.
pub fn buffer(&mut self, to_buf: &[u8]) { pub fn buffer(&mut self, to_buf: &[u8]) {
if let Self::Initialized { buf, .. } = self { if let Self::Initialized { buf, .. } = self {
buf.extend_from_slice(to_buf); buf.extend_from_slice(to_buf);
@@ -62,14 +62,14 @@ impl BufferedStream {
/// ///
/// Returns `neqo_transport` errors. /// Returns `neqo_transport` errors.
pub fn send_buffer(&mut self, conn: &mut Connection) -> Res<usize> { pub fn send_buffer(&mut self, conn: &mut Connection) -> Res<usize> {
let label = ::neqo_common::log_subject!(::log::Level::Debug, self); let label = format!("{self}");
let Self::Initialized { stream_id, buf } = self else { let Self::Initialized { stream_id, buf } = self else {
return Ok(0); return Ok(0);
}; };
if buf.is_empty() { if buf.is_empty() {
return Ok(0); return Ok(0);
} }
qtrace!([label], "sending data."); qtrace!("[{label}] sending data");
let sent = conn.stream_send(*stream_id, &buf[..])?; let sent = conn.stream_send(*stream_id, &buf[..])?;
if sent == 0 { if sent == 0 {
return Ok(0); return Ok(0);

View File

@@ -16,7 +16,7 @@ use crate::{
connection::Http3State, connection::Http3State,
features::extended_connect::{ExtendedConnectEvents, ExtendedConnectType, SessionCloseReason}, features::extended_connect::{ExtendedConnectEvents, ExtendedConnectType, SessionCloseReason},
settings::HSettingType, settings::HSettingType,
CloseType, Http3StreamInfo, HttpRecvStreamEvents, RecvStreamEvents, SendStreamEvents, CloseType, Http3StreamInfo, HttpRecvStreamEvents, PushId, RecvStreamEvents, SendStreamEvents,
}; };
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Eq, Clone)]
@@ -68,24 +68,24 @@ pub enum Http3ClientEvent {
}, },
/// A new push promise. /// A new push promise.
PushPromise { PushPromise {
push_id: u64, push_id: PushId,
request_stream_id: StreamId, request_stream_id: StreamId,
headers: Vec<Header>, headers: Vec<Header>,
}, },
/// A push response headers are ready. /// A push response headers are ready.
PushHeaderReady { PushHeaderReady {
push_id: u64, push_id: PushId,
headers: Vec<Header>, headers: Vec<Header>,
interim: bool, interim: bool,
fin: bool, fin: bool,
}, },
/// New bytes are available on a push stream for reading. /// New bytes are available on a push stream for reading.
PushDataReadable { push_id: u64 }, PushDataReadable { push_id: PushId },
/// A push has been canceled. /// A push has been canceled.
PushCanceled { push_id: u64 }, PushCanceled { push_id: PushId },
/// A push stream was been reset due to a `HttpGeneralProtocol` error. /// A push stream was been reset due to a `HttpGeneralProtocol` error.
/// Most common case are malformed response headers. /// Most common case are malformed response headers.
PushReset { push_id: u64, error: AppError }, PushReset { push_id: PushId, error: AppError },
/// New stream can be created /// New stream can be created
RequestsCreatable, RequestsCreatable,
/// Cert authentication needed /// Cert authentication needed
@@ -196,7 +196,7 @@ impl ExtendedConnectEvents for Http3ClientEvents {
headers, headers,
})); }));
} else { } else {
unreachable!("There is only ExtendedConnectType::WebTransport."); unreachable!("There is only ExtendedConnectType::WebTransport");
} }
} }
@@ -216,7 +216,7 @@ impl ExtendedConnectEvents for Http3ClientEvents {
}, },
)); ));
} else { } else {
unreachable!("There are no other types."); unreachable!("There are no other types");
} }
} }
@@ -240,7 +240,7 @@ impl ExtendedConnectEvents for Http3ClientEvents {
} }
impl Http3ClientEvents { impl Http3ClientEvents {
pub fn push_promise(&self, push_id: u64, request_stream_id: StreamId, headers: Vec<Header>) { pub fn push_promise(&self, push_id: PushId, request_stream_id: StreamId, headers: Vec<Header>) {
self.insert(Http3ClientEvent::PushPromise { self.insert(Http3ClientEvent::PushPromise {
push_id, push_id,
request_stream_id, request_stream_id,
@@ -248,12 +248,12 @@ impl Http3ClientEvents {
}); });
} }
pub fn push_canceled(&self, push_id: u64) { pub fn push_canceled(&self, push_id: PushId) {
self.remove_events_for_push_id(push_id); self.remove_events_for_push_id(push_id);
self.insert(Http3ClientEvent::PushCanceled { push_id }); self.insert(Http3ClientEvent::PushCanceled { push_id });
} }
pub fn push_reset(&self, push_id: u64, error: AppError) { pub fn push_reset(&self, push_id: PushId, error: AppError) {
self.remove_events_for_push_id(push_id); self.remove_events_for_push_id(push_id);
self.insert(Http3ClientEvent::PushReset { push_id, error }); self.insert(Http3ClientEvent::PushReset { push_id, error });
} }
@@ -336,7 +336,7 @@ impl Http3ClientEvents {
}); });
} }
pub fn has_push(&self, push_id: u64) -> bool { pub fn has_push(&self, push_id: PushId) -> bool {
for iter in &*self.events.borrow() { for iter in &*self.events.borrow() {
if matches!(iter, Http3ClientEvent::PushPromise{push_id:x, ..} if *x == push_id) { if matches!(iter, Http3ClientEvent::PushPromise{push_id:x, ..} if *x == push_id) {
return true; return true;
@@ -345,7 +345,7 @@ impl Http3ClientEvents {
false false
} }
pub fn remove_events_for_push_id(&self, push_id: u64) { pub fn remove_events_for_push_id(&self, push_id: PushId) {
self.remove(|evt| { self.remove(|evt| {
matches!(evt, matches!(evt,
Http3ClientEvent::PushPromise{ push_id: x, .. } Http3ClientEvent::PushPromise{ push_id: x, .. }

View File

@@ -35,7 +35,7 @@ use crate::{
qpack_decoder_receiver::DecoderRecvStream, qpack_decoder_receiver::DecoderRecvStream,
qpack_encoder_receiver::EncoderRecvStream, qpack_encoder_receiver::EncoderRecvStream,
recv_message::{RecvMessage, RecvMessageInfo}, recv_message::{RecvMessage, RecvMessageInfo},
request_target::{AsRequestTarget, RequestTarget}, request_target::{AsRequestTarget, RequestTarget as _},
send_message::SendMessage, send_message::SendMessage,
settings::{HSettingType, HSettings, HttpZeroRttChecker}, settings::{HSettingType, HSettings, HttpZeroRttChecker},
stream_type_reader::NewStreamHeadReader, stream_type_reader::NewStreamHeadReader,
@@ -159,7 +159,7 @@ The API consists of:
Each `Http3Connection` holds a list of stream handlers. Each send and receive-handler is registered in Each `Http3Connection` holds a list of stream handlers. Each send and receive-handler is registered in
`send_streams` and `recv_streams`. Unidirectional streams are registered only on one of the lists `send_streams` and `recv_streams`. Unidirectional streams are registered only on one of the lists
and bidirectional streams are registered in both lists and the 2 handlers are independent, e.g. one and bidirectional streams are registered in both lists and the 2 handlers are independent, e.g. one
can be closed and removed ane second may still be active. can be closed and removed and second may still be active.
The only streams that are not registered are the local control stream, local QPACK decoder stream, The only streams that are not registered are the local control stream, local QPACK decoder stream,
and local QPACK encoder stream. These streams are send-streams and sending data on this stream is and local QPACK encoder stream. These streams are send-streams and sending data on this stream is
@@ -195,7 +195,7 @@ are local or remote:
type has been decoded. After this point the stream: type has been decoded. After this point the stream:
- will be regegistered with the appropriate handler, - will be regegistered with the appropriate handler,
- will be canceled if is an unknown stream type or - will be canceled if is an unknown stream type or
- the connection will fail if it is unallowed stream type (receiveing HTTP request on the - the connection will fail if it is unallowed stream type (receiving HTTP request on the
client-side). client-side).
The output is handled in `handle_new_stream`, for control, qpack streams and partially The output is handled in `handle_new_stream`, for control, qpack streams and partially
@@ -277,12 +277,12 @@ For example for `Http` stream the listener will produce `HeaderReady` and `Da
A `WebTransport` session is connected to a control stream that is in essence an HTTP transaction. A `WebTransport` session is connected to a control stream that is in essence an HTTP transaction.
Therefore, `WebTransportSession` will internally use a `SendMessage` and `RecvMessage` handler to Therefore, `WebTransportSession` will internally use a `SendMessage` and `RecvMessage` handler to
handle parsing and sending of HTTP part of the control stream. When HTTP headers are exchenged, handle parsing and sending of HTTP part of the control stream. When HTTP headers are exchanged,
`WebTransportSession` will take over handling of stream data. `WebTransportSession` sets `WebTransportSession` will take over handling of stream data. `WebTransportSession` sets
`WebTransportSessionListener` as the `RecvMessage` event listener. `WebTransportSessionListener` as the `RecvMessage` event listener.
`WebTransportSendStream` and `WebTransportRecvStream` are associated with a `WebTransportSession` `WebTransportSendStream` and `WebTransportRecvStream` are associated with a `WebTransportSession`
and they will be canceled if the session is closed. To be avle to do this `WebTransportSession` and they will be canceled if the session is closed. To be able to do this `WebTransportSession`
holds a list of its active streams and clean up is done in `remove_extended_connect`. holds a list of its active streams and clean up is done in `remove_extended_connect`.
### `WebTransportSendStream` and `WebTransportRecvStream` ### `WebTransportSendStream` and `WebTransportRecvStream`
@@ -352,7 +352,7 @@ impl Http3Connection {
/// This function creates and initializes, i.e. send stream type, the control and qpack /// This function creates and initializes, i.e. send stream type, the control and qpack
/// streams. /// streams.
fn initialize_http3_connection(&mut self, conn: &mut Connection) -> Res<()> { fn initialize_http3_connection(&mut self, conn: &mut Connection) -> Res<()> {
qdebug!([self], "Initialize the http3 connection."); qdebug!("[{self}] Initialize the http3 connection");
self.control_stream_local.create(conn)?; self.control_stream_local.create(conn)?;
self.send_settings(); self.send_settings();
@@ -361,7 +361,7 @@ impl Http3Connection {
} }
fn send_settings(&mut self) { fn send_settings(&mut self) {
qdebug!([self], "Send settings."); qdebug!("[{self}] Send settings");
self.control_stream_local.queue_frame(&HFrame::Settings { self.control_stream_local.queue_frame(&HFrame::Settings {
settings: HSettings::from(&self.local_params), settings: HSettings::from(&self.local_params),
}); });
@@ -374,7 +374,7 @@ impl Http3Connection {
} }
fn create_qpack_streams(&self, conn: &mut Connection) -> Res<()> { fn create_qpack_streams(&self, conn: &mut Connection) -> Res<()> {
qdebug!([self], "create_qpack_streams."); qdebug!("[{self}] create_qpack_streams");
self.qpack_encoder self.qpack_encoder
.borrow_mut() .borrow_mut()
.add_send_stream(conn.stream_create(StreamType::UniDi)?); .add_send_stream(conn.stream_create(StreamType::UniDi)?);
@@ -458,7 +458,7 @@ impl Http3Connection {
/// This is called when a `ConnectionEvent::NewStream` event is received. This register the /// This is called when a `ConnectionEvent::NewStream` event is received. This register the
/// stream with a `NewStreamHeadReader` handler. /// stream with a `NewStreamHeadReader` handler.
pub fn add_new_stream(&mut self, stream_id: StreamId) { pub fn add_new_stream(&mut self, stream_id: StreamId) {
qtrace!([self], "A new stream: {}.", stream_id); qtrace!("[{self}] A new stream: {stream_id}");
self.recv_streams.insert( self.recv_streams.insert(
stream_id, stream_id,
Box::new(NewStreamHeadReader::new(stream_id, self.role)), Box::new(NewStreamHeadReader::new(stream_id, self.role)),
@@ -468,7 +468,7 @@ impl Http3Connection {
/// The function calls `receive` for a stream. It also deals with the outcome of a read by /// The function calls `receive` for a stream. It also deals with the outcome of a read by
/// calling `handle_stream_manipulation_output`. /// calling `handle_stream_manipulation_output`.
fn stream_receive(&mut self, conn: &mut Connection, stream_id: StreamId) -> Res<ReceiveOutput> { fn stream_receive(&mut self, conn: &mut Connection, stream_id: StreamId) -> Res<ReceiveOutput> {
qtrace!([self], "Readable stream {}.", stream_id); qtrace!("[{self}] Readable stream {stream_id}");
if let Some(recv_stream) = self.recv_streams.get_mut(&stream_id) { if let Some(recv_stream) = self.recv_streams.get_mut(&stream_id) {
let res = recv_stream.receive(conn); let res = recv_stream.receive(conn);
@@ -485,7 +485,7 @@ impl Http3Connection {
conn: &mut Connection, conn: &mut Connection,
) -> Res<()> { ) -> Res<()> {
for stream_id in unblocked_streams { for stream_id in unblocked_streams {
qdebug!([self], "Stream {} is unblocked", stream_id); qdebug!("[{self}] Stream {stream_id} is unblocked");
if let Some(r) = self.recv_streams.get_mut(&stream_id) { if let Some(r) = self.recv_streams.get_mut(&stream_id) {
let res = r let res = r
.http_stream() .http_stream()
@@ -501,7 +501,7 @@ impl Http3Connection {
/// This function handles reading from all streams, i.e. control, qpack, request/response /// This function handles reading from all streams, i.e. control, qpack, request/response
/// stream and unidi stream that are still do not have a type. /// stream and unidi stream that are still do not have a type.
/// The function cannot handle: /// The function cannot handle:
/// 1) a `Push(_)`, `Htttp` or `WebTransportStream(_)` stream /// 1) a `Push(_)`, `Http` or `WebTransportStream(_)` stream
/// 2) frames `MaxPushId`, `PriorityUpdateRequest`, `PriorityUpdateRequestPush` or `Goaway` must /// 2) frames `MaxPushId`, `PriorityUpdateRequest`, `PriorityUpdateRequestPush` or `Goaway` must
/// be handled by `Http3Client`/`Server`. /// be handled by `Http3Client`/`Server`.
/// ///
@@ -551,12 +551,7 @@ impl Http3Connection {
app_error: AppError, app_error: AppError,
conn: &mut Connection, conn: &mut Connection,
) -> Res<()> { ) -> Res<()> {
qinfo!( qinfo!("[{self}] Handle a stream reset stream_id={stream_id} app_err={app_error}");
[self],
"Handle a stream reset stream_id={} app_err={}",
stream_id,
app_error
);
self.close_recv(stream_id, CloseType::ResetRemote(app_error), conn) self.close_recv(stream_id, CloseType::ResetRemote(app_error), conn)
} }
@@ -567,12 +562,7 @@ impl Http3Connection {
app_error: AppError, app_error: AppError,
conn: &mut Connection, conn: &mut Connection,
) -> Res<()> { ) -> Res<()> {
qinfo!( qinfo!("[{self}] Handle stream_stop_sending stream_id={stream_id} app_err={app_error}");
[self],
"Handle stream_stop_sending stream_id={} app_err={}",
stream_id,
app_error
);
if self.send_stream_is_critical(stream_id) { if self.send_stream_is_critical(stream_id) {
return Err(Error::HttpClosedCriticalStream); return Err(Error::HttpClosedCriticalStream);
@@ -585,7 +575,7 @@ impl Http3Connection {
/// This is called when `neqo_transport::Connection` state has been change to take proper /// This is called when `neqo_transport::Connection` state has been change to take proper
/// actions in the HTTP3 layer. /// actions in the HTTP3 layer.
pub fn handle_state_change(&mut self, conn: &mut Connection, state: &State) -> Res<bool> { pub fn handle_state_change(&mut self, conn: &mut Connection, state: &State) -> Res<bool> {
qdebug!([self], "Handle state change {:?}", state); qdebug!("[{self}] Handle state change {state:?}");
match state { match state {
State::Handshaking => { State::Handshaking => {
if self.role == Role::Server if self.role == Role::Server
@@ -649,7 +639,7 @@ impl Http3Connection {
self.recv_streams.clear(); self.recv_streams.clear();
Ok(()) Ok(())
} else { } else {
debug_assert!(false, "Zero rtt rejected in the wrong state."); debug_assert!(false, "Zero rtt rejected in the wrong state");
Err(Error::HttpInternal(3)) Err(Error::HttpInternal(3))
} }
} }
@@ -696,15 +686,10 @@ impl Http3Connection {
} }
NewStreamType::Push(push_id) => { NewStreamType::Push(push_id) => {
qinfo!( qinfo!("[{self}] A new push stream {stream_id} push_id:{push_id}");
[self],
"A new push stream {} push_id:{}.",
stream_id,
push_id
);
} }
NewStreamType::Decoder => { NewStreamType::Decoder => {
qdebug!([self], "A new remote qpack encoder stream {}", stream_id); qdebug!("[{self}] A new remote qpack encoder stream {stream_id}");
self.check_stream_exists(Http3StreamType::Decoder)?; self.check_stream_exists(Http3StreamType::Decoder)?;
self.recv_streams.insert( self.recv_streams.insert(
stream_id, stream_id,
@@ -715,7 +700,7 @@ impl Http3Connection {
); );
} }
NewStreamType::Encoder => { NewStreamType::Encoder => {
qdebug!([self], "A new remote qpack decoder stream {}", stream_id); qdebug!("[{self}] A new remote qpack decoder stream {stream_id}");
self.check_stream_exists(Http3StreamType::Encoder)?; self.check_stream_exists(Http3StreamType::Encoder)?;
self.recv_streams.insert( self.recv_streams.insert(
stream_id, stream_id,
@@ -726,7 +711,7 @@ impl Http3Connection {
); );
} }
NewStreamType::Http(_) => { NewStreamType::Http(_) => {
qinfo!([self], "A new http stream {}.", stream_id); qinfo!("[{self}] A new http stream {stream_id}");
} }
NewStreamType::WebTransportStream(session_id) => { NewStreamType::WebTransportStream(session_id) => {
let session_exists = self let session_exists = self
@@ -737,14 +722,13 @@ impl Http3Connection {
conn.stream_stop_sending(stream_id, Error::HttpStreamCreation.code())?; conn.stream_stop_sending(stream_id, Error::HttpStreamCreation.code())?;
return Ok(ReceiveOutput::NoOutput); return Ok(ReceiveOutput::NoOutput);
} }
// set incoming WebTransport streams to be fair (share bandwidth) // Set incoming WebTransport streams to be fair (share bandwidth).
conn.stream_fairness(stream_id, true).ok(); // We may call this with an invalid stream ID, so ignore that error.
qinfo!( match conn.stream_fairness(stream_id, true) {
[self], Ok(()) | Err(neqo_transport::Error::InvalidStreamId) => (),
"A new WebTransport stream {} for session {}.", Err(e) => return Err(Error::from(e)),
stream_id, };
session_id qinfo!("[{self}] A new WebTransport stream {stream_id} for session {session_id}");
);
} }
NewStreamType::Unknown => { NewStreamType::Unknown => {
conn.stream_stop_sending(stream_id, Error::HttpStreamCreation.code())?; conn.stream_stop_sending(stream_id, Error::HttpStreamCreation.code())?;
@@ -764,7 +748,7 @@ impl Http3Connection {
/// This is called when an application closes the connection. /// This is called when an application closes the connection.
pub fn close(&mut self, error: AppError) { pub fn close(&mut self, error: AppError) {
qdebug!([self], "Close connection error {:?}.", error); qdebug!("[{self}] Close connection error {error:?}");
self.state = Http3State::Closing(CloseReason::Application(error)); self.state = Http3State::Closing(CloseReason::Application(error));
if (!self.send_streams.is_empty() || !self.recv_streams.is_empty()) && (error == 0) { if (!self.send_streams.is_empty() || !self.recv_streams.is_empty()) && (error == 0) {
qwarn!("close(0) called when streams still active"); qwarn!("close(0) called when streams still active");
@@ -805,7 +789,7 @@ impl Http3Connection {
Ok((_, false)) => {} Ok((_, false)) => {}
Err(e) => { Err(e) => {
if e.stream_reset_error() && !self.recv_stream_is_critical(stream_id) { if e.stream_reset_error() && !self.recv_stream_is_critical(stream_id) {
mem::drop(conn.stream_stop_sending(stream_id, e.code())); drop(conn.stream_stop_sending(stream_id, e.code()));
self.close_recv(stream_id, CloseType::LocalError(e.code()), conn)?; self.close_recv(stream_id, CloseType::LocalError(e.code()), conn)?;
return Ok((U::default(), false)); return Ok((U::default(), false));
} }
@@ -850,8 +834,7 @@ impl Http3Connection {
T: AsRequestTarget<'t> + ?Sized + Debug, T: AsRequestTarget<'t> + ?Sized + Debug,
{ {
qinfo!( qinfo!(
[self], "[{self}] Fetch method={} target: {:?}",
"Fetch method={} target: {:?}",
request.method, request.method,
request.target, request.target,
); );
@@ -902,7 +885,7 @@ impl Http3Connection {
MessageType::Request, MessageType::Request,
stream_type, stream_type,
stream_id, stream_id,
self.qpack_encoder.clone(), Rc::clone(&self.qpack_encoder),
send_events, send_events,
); );
@@ -951,7 +934,7 @@ impl Http3Connection {
stream_id: StreamId, stream_id: StreamId,
buf: &mut [u8], buf: &mut [u8],
) -> Res<(usize, bool)> { ) -> Res<(usize, bool)> {
qdebug!([self], "read_data from stream {}.", stream_id); qdebug!("[{self}] read_data from stream {stream_id}");
let res = self let res = self
.recv_streams .recv_streams
.get_mut(&stream_id) .get_mut(&stream_id)
@@ -968,12 +951,7 @@ impl Http3Connection {
stream_id: StreamId, stream_id: StreamId,
error: AppError, error: AppError,
) -> Res<()> { ) -> Res<()> {
qinfo!( qinfo!("[{self}] Reset sending side of stream {stream_id} error={error}");
[self],
"Reset sending side of stream {} error={}.",
stream_id,
error
);
if self.send_stream_is_critical(stream_id) { if self.send_stream_is_critical(stream_id) {
return Err(Error::InvalidStreamId); return Err(Error::InvalidStreamId);
@@ -990,12 +968,7 @@ impl Http3Connection {
stream_id: StreamId, stream_id: StreamId,
error: AppError, error: AppError,
) -> Res<()> { ) -> Res<()> {
qinfo!( qinfo!("[{self}] Send stop sending for stream {stream_id} error={error}");
[self],
"Send stop sending for stream {} error={}.",
stream_id,
error
);
if self.recv_stream_is_critical(stream_id) { if self.recv_stream_is_critical(stream_id) {
return Err(Error::InvalidStreamId); return Err(Error::InvalidStreamId);
} }
@@ -1043,7 +1016,7 @@ impl Http3Connection {
error: AppError, error: AppError,
conn: &mut Connection, conn: &mut Connection,
) -> Res<()> { ) -> Res<()> {
qinfo!([self], "cancel_fetch {} error={}.", stream_id, error); qinfo!("[{self}] cancel_fetch {stream_id} error={error}");
let send_stream = self.send_streams.get(&stream_id); let send_stream = self.send_streams.get(&stream_id);
let recv_stream = self.recv_streams.get(&stream_id); let recv_stream = self.recv_streams.get(&stream_id);
match (send_stream, recv_stream) { match (send_stream, recv_stream) {
@@ -1056,7 +1029,7 @@ impl Http3Connection {
return Err(Error::InvalidStreamId); return Err(Error::InvalidStreamId);
} }
// Stream may be already be closed and we may get an error here, but we do not care. // Stream may be already be closed and we may get an error here, but we do not care.
mem::drop(self.stream_reset_send(conn, stream_id, error)); drop(self.stream_reset_send(conn, stream_id, error));
} }
(None, Some(s)) => { (None, Some(s)) => {
if !matches!( if !matches!(
@@ -1069,7 +1042,7 @@ impl Http3Connection {
} }
// Stream may be already be closed and we may get an error here, but we do not care. // Stream may be already be closed and we may get an error here, but we do not care.
mem::drop(self.stream_stop_sending(conn, stream_id, error)); drop(self.stream_stop_sending(conn, stream_id, error));
} }
(Some(s), Some(r)) => { (Some(s), Some(r)) => {
debug_assert_eq!(s.stream_type(), r.stream_type()); debug_assert_eq!(s.stream_type(), r.stream_type());
@@ -1080,9 +1053,9 @@ impl Http3Connection {
return Err(Error::InvalidStreamId); return Err(Error::InvalidStreamId);
} }
// Stream may be already be closed and we may get an error here, but we do not care. // Stream may be already be closed and we may get an error here, but we do not care.
mem::drop(self.stream_reset_send(conn, stream_id, error)); drop(self.stream_reset_send(conn, stream_id, error));
// Stream may be already be closed and we may get an error here, but we do not care. // Stream may be already be closed and we may get an error here, but we do not care.
mem::drop(self.stream_stop_sending(conn, stream_id, error)); drop(self.stream_stop_sending(conn, stream_id, error));
} }
} }
Ok(()) Ok(())
@@ -1090,7 +1063,7 @@ impl Http3Connection {
/// This is called when an application wants to close the sending side of a stream. /// This is called when an application wants to close the sending side of a stream.
pub fn stream_close_send(&mut self, conn: &mut Connection, stream_id: StreamId) -> Res<()> { pub fn stream_close_send(&mut self, conn: &mut Connection, stream_id: StreamId) -> Res<()> {
qdebug!([self], "Close the sending side for stream {}.", stream_id); qdebug!("[{self}] Close the sending side for stream {stream_id}");
debug_assert!(self.state.active()); debug_assert!(self.state.active());
let send_stream = self let send_stream = self
.send_streams .send_streams
@@ -1098,7 +1071,7 @@ impl Http3Connection {
.ok_or(Error::InvalidStreamId)?; .ok_or(Error::InvalidStreamId)?;
// The following function may return InvalidStreamId from the transport layer if the stream // The following function may return InvalidStreamId from the transport layer if the stream
// has been closed already. It is ok to ignore it here. // has been closed already. It is ok to ignore it here.
mem::drop(send_stream.close(conn)); drop(send_stream.close(conn));
if send_stream.done() { if send_stream.done() {
self.remove_send_stream(stream_id, conn); self.remove_send_stream(stream_id, conn);
} else if send_stream.has_data_to_send() { } else if send_stream.has_data_to_send() {
@@ -1117,7 +1090,7 @@ impl Http3Connection {
where where
T: AsRequestTarget<'x> + ?Sized + Debug, T: AsRequestTarget<'x> + ?Sized + Debug,
{ {
qinfo!([self], "Create WebTransport"); qinfo!("[{self}] Create WebTransport");
if !self.webtransport_enabled() { if !self.webtransport_enabled() {
return Err(Error::Unavailable); return Err(Error::Unavailable);
} }
@@ -1133,8 +1106,8 @@ impl Http3Connection {
))); )));
self.add_streams( self.add_streams(
id, id,
Box::new(extended_conn.clone()), Box::new(Rc::clone(&extended_conn)),
Box::new(extended_conn.clone()), Box::new(Rc::clone(&extended_conn)),
); );
let final_headers = Self::create_fetch_headers(&RequestDescription { let final_headers = Self::create_fetch_headers(&RequestDescription {
@@ -1158,10 +1131,7 @@ impl Http3Connection {
events: Box<dyn ExtendedConnectEvents>, events: Box<dyn ExtendedConnectEvents>,
accept_res: &WebTransportSessionAcceptAction, accept_res: &WebTransportSessionAcceptAction,
) -> Res<()> { ) -> Res<()> {
qtrace!( qtrace!("Respond to WebTransport session with accept={accept_res}");
"Respond to WebTransport session with accept={}.",
accept_res
);
if !self.webtransport_enabled() { if !self.webtransport_enabled() {
return Err(Error::Unavailable); return Err(Error::Unavailable);
} }
@@ -1177,6 +1147,7 @@ impl Http3Connection {
} }
let send_stream = self.send_streams.get_mut(&stream_id); let send_stream = self.send_streams.get_mut(&stream_id);
conn.stream_keep_alive(stream_id, true)?;
match (send_stream, recv_stream, accept_res) { match (send_stream, recv_stream, accept_res) {
(None, None, _) => Err(Error::InvalidStreamId), (None, None, _) => Err(Error::InvalidStreamId),
@@ -1191,7 +1162,7 @@ impl Http3Connection {
.send_headers(headers, conn) .send_headers(headers, conn)
.is_ok() .is_ok()
{ {
mem::drop(self.stream_close_send(conn, stream_id)); drop(self.stream_close_send(conn, stream_id));
// TODO issue 1294: add a timer to clean up the recv_stream if the peer does not // TODO issue 1294: add a timer to clean up the recv_stream if the peer does not
// do that in a short time. // do that in a short time.
self.streams_with_pending_data.insert(stream_id); self.streams_with_pending_data.insert(stream_id);
@@ -1216,7 +1187,7 @@ impl Http3Connection {
))); )));
self.add_streams( self.add_streams(
stream_id, stream_id,
Box::new(extended_conn.clone()), Box::new(Rc::clone(&extended_conn)),
Box::new(extended_conn), Box::new(extended_conn),
); );
self.streams_with_pending_data.insert(stream_id); self.streams_with_pending_data.insert(stream_id);
@@ -1236,7 +1207,7 @@ impl Http3Connection {
error: u32, error: u32,
message: &str, message: &str,
) -> Res<()> { ) -> Res<()> {
qtrace!("Clos WebTransport session {:?}", session_id); qtrace!("Close WebTransport session {session_id:?}");
let send_stream = self let send_stream = self
.send_streams .send_streams
.get_mut(&session_id) .get_mut(&session_id)
@@ -1262,11 +1233,7 @@ impl Http3Connection {
send_events: Box<dyn SendStreamEvents>, send_events: Box<dyn SendStreamEvents>,
recv_events: Box<dyn RecvStreamEvents>, recv_events: Box<dyn RecvStreamEvents>,
) -> Res<StreamId> { ) -> Res<StreamId> {
qtrace!( qtrace!("Create new WebTransport stream session={session_id} type={stream_type:?}");
"Create new WebTransport stream session={} type={:?}",
session_id,
stream_type
);
let wt = self let wt = self
.recv_streams .recv_streams
@@ -1282,8 +1249,7 @@ impl Http3Connection {
.stream_create(stream_type) .stream_create(stream_type)
.map_err(|e| Error::map_stream_create_errors(&e))?; .map_err(|e| Error::map_stream_create_errors(&e))?;
// Set outgoing WebTransport streams to be fair (share bandwidth) // Set outgoing WebTransport streams to be fair (share bandwidth)
// This really can't fail, panics if it does conn.stream_fairness(stream_id, true)?;
conn.stream_fairness(stream_id, true).unwrap();
self.webtransport_create_stream_internal( self.webtransport_create_stream_internal(
wt, wt,
@@ -1303,11 +1269,7 @@ impl Http3Connection {
send_events: Box<dyn SendStreamEvents>, send_events: Box<dyn SendStreamEvents>,
recv_events: Box<dyn RecvStreamEvents>, recv_events: Box<dyn RecvStreamEvents>,
) -> Res<()> { ) -> Res<()> {
qtrace!( qtrace!("Create new WebTransport stream session={session_id} stream_id={stream_id}");
"Create new WebTransport stream session={} stream_id={}",
session_id,
stream_id
);
let wt = self let wt = self
.recv_streams .recv_streams
@@ -1336,7 +1298,6 @@ impl Http3Connection {
recv_events: Box<dyn RecvStreamEvents>, recv_events: Box<dyn RecvStreamEvents>,
local: bool, local: bool,
) { ) {
// TODO conn.stream_keep_alive(stream_id, true)?;
webtransport_session.borrow_mut().add_stream(stream_id); webtransport_session.borrow_mut().add_stream(stream_id);
if stream_id.stream_type() == StreamType::UniDi { if stream_id.stream_type() == StreamType::UniDi {
if local { if local {
@@ -1368,7 +1329,7 @@ impl Http3Connection {
stream_id, stream_id,
session_id, session_id,
send_events, send_events,
webtransport_session.clone(), Rc::clone(&webtransport_session),
local, local,
)), )),
Box::new(WebTransportRecvStream::new( Box::new(WebTransportRecvStream::new(
@@ -1401,7 +1362,7 @@ impl Http3Connection {
/// `PriorityUpdateRequestPush` which handling is specific to the client and server, we must /// `PriorityUpdateRequestPush` which handling is specific to the client and server, we must
/// give them to the specific client/server handler. /// give them to the specific client/server handler.
fn handle_control_frame(&mut self, f: HFrame) -> Res<Option<HFrame>> { fn handle_control_frame(&mut self, f: HFrame) -> Res<Option<HFrame>> {
qdebug!([self], "Handle a control frame {:?}", f); qdebug!("[{self}] Handle a control frame {f:?}");
if !matches!(f, HFrame::Settings { .. }) if !matches!(f, HFrame::Settings { .. })
&& !matches!( && !matches!(
self.settings_state, self.settings_state,
@@ -1432,7 +1393,7 @@ impl Http3Connection {
} }
fn handle_settings(&mut self, new_settings: HSettings) -> Res<()> { fn handle_settings(&mut self, new_settings: HSettings) -> Res<()> {
qdebug!([self], "Handle SETTINGS frame."); qdebug!("[{self}] Handle SETTINGS frame");
match &self.settings_state { match &self.settings_state {
Http3RemoteSettingsState::NotReceived => { Http3RemoteSettingsState::NotReceived => {
self.set_qpack_settings(&new_settings)?; self.set_qpack_settings(&new_settings)?;
@@ -1455,11 +1416,7 @@ impl Http3Connection {
} }
if zero_rtt_value > new_value { if zero_rtt_value > new_value {
qerror!( qerror!(
[self], "[{self}] The new({new_value}) and the old value({zero_rtt_value}) of setting {st:?} do not match"
"The new({}) and the old value({}) of setting {:?} do not match",
new_value,
zero_rtt_value,
st
); );
return Err(Error::HttpSettings); return Err(Error::HttpSettings);
} }
@@ -1478,7 +1435,7 @@ impl Http3Connection {
} }
} }
if qpack_changed { if qpack_changed {
qdebug!([self], "Settings after zero rtt differ."); qdebug!("[{self}] Settings after zero rtt differ");
self.set_qpack_settings(&(new_settings))?; self.set_qpack_settings(&(new_settings))?;
} }
self.settings_state = Http3RemoteSettingsState::Received(new_settings); self.settings_state = Http3RemoteSettingsState::Received(new_settings);
@@ -1577,20 +1534,20 @@ impl Http3Connection {
let (recv, send) = wt.borrow_mut().take_sub_streams(); let (recv, send) = wt.borrow_mut().take_sub_streams();
for id in recv { for id in recv {
qtrace!("Remove the extended connect sub receiver stream {}", id); qtrace!("Remove the extended connect sub receiver stream {id}");
// Use CloseType::ResetRemote so that an event will be sent. CloseType::LocalError would // Use CloseType::ResetRemote so that an event will be sent. CloseType::LocalError would
// have the same effect. // have the same effect.
if let Some(mut s) = self.recv_streams.remove(&id) { if let Some(mut s) = self.recv_streams.remove(&id) {
mem::drop(s.reset(CloseType::ResetRemote(Error::HttpRequestCancelled.code()))); drop(s.reset(CloseType::ResetRemote(Error::HttpRequestCancelled.code())));
} }
mem::drop(conn.stream_stop_sending(id, Error::HttpRequestCancelled.code())); drop(conn.stream_stop_sending(id, Error::HttpRequestCancelled.code()));
} }
for id in send { for id in send {
qtrace!("Remove the extended connect sub send stream {}", id); qtrace!("Remove the extended connect sub send stream {id}");
if let Some(mut s) = self.send_streams.remove(&id) { if let Some(mut s) = self.send_streams.remove(&id) {
s.handle_stop_sending(CloseType::ResetRemote(Error::HttpRequestCancelled.code())); s.handle_stop_sending(CloseType::ResetRemote(Error::HttpRequestCancelled.code()));
} }
mem::drop(conn.stream_reset_send(id, Error::HttpRequestCancelled.code())); drop(conn.stream_reset_send(id, Error::HttpRequestCancelled.code()));
} }
} }
@@ -1600,7 +1557,7 @@ impl Http3Connection {
conn: &mut Connection, conn: &mut Connection,
) -> Option<Box<dyn RecvStream>> { ) -> Option<Box<dyn RecvStream>> {
let stream = self.recv_streams.remove(&stream_id); let stream = self.recv_streams.remove(&stream_id);
if let Some(ref s) = stream { if let Some(s) = &stream {
if s.stream_type() == Http3StreamType::ExtendedConnect { if s.stream_type() == Http3StreamType::ExtendedConnect {
self.send_streams.remove(&stream_id).unwrap(); self.send_streams.remove(&stream_id).unwrap();
if let Some(wt) = s.webtransport() { if let Some(wt) = s.webtransport() {
@@ -1617,7 +1574,7 @@ impl Http3Connection {
conn: &mut Connection, conn: &mut Connection,
) -> Option<Box<dyn SendStream>> { ) -> Option<Box<dyn SendStream>> {
let stream = self.send_streams.remove(&stream_id); let stream = self.send_streams.remove(&stream_id);
if let Some(ref s) = stream { if let Some(s) = &stream {
if s.stream_type() == Http3StreamType::ExtendedConnect { if s.stream_type() == Http3StreamType::ExtendedConnect {
if let Some(wt) = self.recv_streams.remove(&stream_id).unwrap().webtransport() { if let Some(wt) = self.recv_streams.remove(&stream_id).unwrap().webtransport() {
self.remove_extended_connect(&wt, conn); self.remove_extended_connect(&wt, conn);

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,7 @@
use std::{rc::Rc, time::Instant}; use std::{rc::Rc, time::Instant};
use neqo_common::{event::Provider, qdebug, qinfo, qtrace, Header, MessageType, Role}; use neqo_common::{event::Provider as _, qdebug, qinfo, qtrace, Header, MessageType, Role};
use neqo_transport::{ use neqo_transport::{
AppError, Connection, ConnectionEvent, DatagramTracking, StreamId, StreamType, AppError, Connection, ConnectionEvent, DatagramTracking, StreamId, StreamType,
}; };
@@ -102,7 +102,7 @@ impl Http3ServerHandler {
/// ///
/// An error will be returned if stream does not exist. /// An error will be returned if stream does not exist.
pub fn stream_close_send(&mut self, stream_id: StreamId, conn: &mut Connection) -> Res<()> { pub fn stream_close_send(&mut self, stream_id: StreamId, conn: &mut Connection) -> Res<()> {
qdebug!([self], "Close sending side stream={}.", stream_id); qdebug!("[{self}] Close sending side stream={stream_id}");
self.base_handler.stream_close_send(conn, stream_id)?; self.base_handler.stream_close_send(conn, stream_id)?;
self.needs_processing = true; self.needs_processing = true;
Ok(()) Ok(())
@@ -120,7 +120,7 @@ impl Http3ServerHandler {
error: AppError, error: AppError,
conn: &mut Connection, conn: &mut Connection,
) -> Res<()> { ) -> Res<()> {
qinfo!([self], "cancel_fetch {} error={}.", stream_id, error); qinfo!("[{self}] cancel_fetch {stream_id} error={error}");
self.needs_processing = true; self.needs_processing = true;
self.base_handler.cancel_fetch(stream_id, error, conn) self.base_handler.cancel_fetch(stream_id, error, conn)
} }
@@ -131,7 +131,7 @@ impl Http3ServerHandler {
error: AppError, error: AppError,
conn: &mut Connection, conn: &mut Connection,
) -> Res<()> { ) -> Res<()> {
qinfo!([self], "stream_stop_sending {} error={}.", stream_id, error); qinfo!("[{self}] stream_stop_sending {stream_id} error={error}");
self.needs_processing = true; self.needs_processing = true;
self.base_handler self.base_handler
.stream_stop_sending(conn, stream_id, error) .stream_stop_sending(conn, stream_id, error)
@@ -143,7 +143,7 @@ impl Http3ServerHandler {
error: AppError, error: AppError,
conn: &mut Connection, conn: &mut Connection,
) -> Res<()> { ) -> Res<()> {
qinfo!([self], "stream_reset_send {} error={}.", stream_id, error); qinfo!("[{self}] stream_reset_send {stream_id} error={error}");
self.needs_processing = true; self.needs_processing = true;
self.base_handler.stream_reset_send(conn, stream_id, error) self.base_handler.stream_reset_send(conn, stream_id, error)
} }
@@ -215,7 +215,7 @@ impl Http3ServerHandler {
/// Process HTTTP3 layer. /// Process HTTTP3 layer.
pub fn process_http3(&mut self, conn: &mut Connection, now: Instant) { pub fn process_http3(&mut self, conn: &mut Connection, now: Instant) {
qtrace!([self], "Process http3 internal."); qtrace!("[{self}] Process http3 internal");
if matches!(self.base_handler.state(), Http3State::Closed(..)) { if matches!(self.base_handler.state(), Http3State::Closed(..)) {
return; return;
} }
@@ -254,7 +254,7 @@ impl Http3ServerHandler {
} }
fn close(&mut self, conn: &mut Connection, now: Instant, err: &Error) { fn close(&mut self, conn: &mut Connection, now: Instant, err: &Error) {
qinfo!([self], "Connection error: {}.", err); qinfo!("[{self}] Connection error: {err}");
conn.close(now, err.code(), format!("{err}")); conn.close(now, err.code(), format!("{err}"));
self.base_handler.close(err.code()); self.base_handler.close(err.code());
self.events self.events
@@ -263,9 +263,9 @@ impl Http3ServerHandler {
// If this return an error the connection must be closed. // If this return an error the connection must be closed.
fn check_connection_events(&mut self, conn: &mut Connection, now: Instant) -> Res<()> { fn check_connection_events(&mut self, conn: &mut Connection, now: Instant) -> Res<()> {
qtrace!([self], "Check connection events."); qtrace!("[{self}] Check connection events");
while let Some(e) = conn.next_event() { while let Some(e) = conn.next_event() {
qdebug!([self], "check_connection_events - event {e:?}."); qdebug!("[{self}] check_connection_events - event {e:?}");
match e { match e {
ConnectionEvent::NewStream { stream_id } => { ConnectionEvent::NewStream { stream_id } => {
self.base_handler.add_new_stream(stream_id); self.base_handler.add_new_stream(stream_id);
@@ -325,7 +325,7 @@ impl Http3ServerHandler {
MessageType::Response, MessageType::Response,
Http3StreamType::Http, Http3StreamType::Http,
stream_id, stream_id,
self.base_handler.qpack_encoder.clone(), Rc::clone(&self.base_handler.qpack_encoder),
Box::new(self.events.clone()), Box::new(self.events.clone()),
)), )),
Box::new(RecvMessage::new( Box::new(RecvMessage::new(
@@ -387,7 +387,7 @@ impl Http3ServerHandler {
Ok(()) Ok(())
} }
_ => unreachable!( _ => unreachable!(
"we should only put MaxPushId, Goaway and PriorityUpdates into control_frames." "we should only put MaxPushId, Goaway and PriorityUpdates into control_frames"
), ),
}?; }?;
} }
@@ -411,7 +411,7 @@ impl Http3ServerHandler {
stream_id: StreamId, stream_id: StreamId,
buf: &mut [u8], buf: &mut [u8],
) -> Res<(usize, bool)> { ) -> Res<(usize, bool)> {
qdebug!([self], "read_data from stream {}.", stream_id); qdebug!("[{self}] read_data from stream {stream_id}");
let res = self.base_handler.read_data(conn, stream_id, buf); let res = self.base_handler.read_data(conn, stream_id, buf);
if let Err(e) = &res { if let Err(e) = &res {
if e.connection_error() { if e.connection_error() {

View File

@@ -92,7 +92,7 @@ impl ControlStreamLocal {
/// Create a control stream. /// Create a control stream.
pub fn create(&mut self, conn: &mut Connection) -> Res<()> { pub fn create(&mut self, conn: &mut Connection) -> Res<()> {
qtrace!([self], "Create a control stream."); qtrace!("[{self}] Create a control stream");
self.stream.init(conn.stream_create(StreamType::UniDi)?); self.stream.init(conn.stream_create(StreamType::UniDi)?);
self.stream self.stream
.buffer(&[u8::try_from(HTTP3_UNI_STREAM_TYPE_CONTROL).unwrap()]); .buffer(&[u8::try_from(HTTP3_UNI_STREAM_TYPE_CONTROL).unwrap()]);

View File

@@ -36,7 +36,7 @@ impl ControlStreamRemote {
/// Check if a stream is the control stream and read received data. /// Check if a stream is the control stream and read received data.
pub fn receive_single(&mut self, conn: &mut Connection) -> Res<Option<HFrame>> { pub fn receive_single(&mut self, conn: &mut Connection) -> Res<Option<HFrame>> {
qdebug!([self], "Receiving data."); qdebug!("[{self}] Receiving data");
match self match self
.frame_reader .frame_reader
.receive(&mut StreamReaderConnectionWrapper::new( .receive(&mut StreamReaderConnectionWrapper::new(
@@ -45,7 +45,7 @@ impl ControlStreamRemote {
))? { ))? {
(_, true) => Err(Error::HttpClosedCriticalStream), (_, true) => Err(Error::HttpClosedCriticalStream),
(s, false) => { (s, false) => {
qdebug!([self], "received {:?}", s); qdebug!("[{self}] received {s:?}");
Ok(s) Ok(s)
} }
} }

View File

@@ -10,7 +10,7 @@ mod sessions;
mod streams; mod streams;
use std::{cell::RefCell, rc::Rc, time::Duration}; use std::{cell::RefCell, rc::Rc, time::Duration};
use neqo_common::event::Provider; use neqo_common::{event::Provider as _, header::HeadersExt as _};
use neqo_crypto::AuthenticationStatus; use neqo_crypto::AuthenticationStatus;
use neqo_transport::{ConnectionParameters, Pmtud, StreamId, StreamType}; use neqo_transport::{ConnectionParameters, Pmtud, StreamId, StreamType};
use test_fixture::{ use test_fixture::{
@@ -60,6 +60,13 @@ pub fn default_http3_server(server_params: Http3Parameters) -> Http3Server {
.expect("create a server") .expect("create a server")
} }
pub fn assert_wt(headers: &[Header]) {
assert!(
headers.contains_header(":method", "CONNECT")
&& headers.contains_header(":protocol", "webtransport")
);
}
fn exchange_packets(client: &mut Http3Client, server: &mut Http3Server) { fn exchange_packets(client: &mut Http3Client, server: &mut Http3Server) {
let mut out = None; let mut out = None;
loop { loop {
@@ -71,7 +78,7 @@ fn exchange_packets(client: &mut Http3Client, server: &mut Http3Server) {
} }
} }
// Perform only Quic transport handshake. // Perform only QUIC transport handshake.
fn connect_with(client: &mut Http3Client, server: &mut Http3Server) { fn connect_with(client: &mut Http3Client, server: &mut Http3Server) {
assert_eq!(client.state(), Http3State::Initializing); assert_eq!(client.state(), Http3State::Initializing);
let out = client.process_output(now()); let out = client.process_output(now());
@@ -92,13 +99,13 @@ fn connect_with(client: &mut Http3Client, server: &mut Http3Server) {
assert_eq!(client.state(), Http3State::Connected); assert_eq!(client.state(), Http3State::Connected);
// Exchange H3 setttings // Exchange H3 settings
let out = server.process(out.dgram(), now()); let out = server.process(out.dgram(), now());
let out = client.process(out.dgram(), now()); let out = client.process(out.dgram(), now());
let out = server.process(out.dgram(), now()); let out = server.process(out.dgram(), now());
let out = client.process(out.dgram(), now()); let out = client.process(out.dgram(), now());
let out = server.process(out.dgram(), now()); let out = server.process(out.dgram(), now());
std::mem::drop(client.process(out.dgram(), now())); drop(client.process(out.dgram(), now()));
} }
fn connect( fn connect(
@@ -148,14 +155,7 @@ impl WtTest {
session, session,
headers, headers,
}) => { }) => {
assert!( assert_wt(&headers);
headers
.iter()
.any(|h| h.name() == ":method" && h.value() == "CONNECT")
&& headers
.iter()
.any(|h| h.name() == ":protocol" && h.value() == "webtransport")
);
session.response(accept).unwrap(); session.response(accept).unwrap();
wt_server_session = Some(session); wt_server_session = Some(session);
} }
@@ -183,7 +183,7 @@ impl WtTest {
}) if ( }) if (
stream_id == wt_session_id && stream_id == wt_session_id &&
status == 200 && status == 200 &&
headers.contains(&Header::new(":status", "200")) headers.contains_header(":status", "200")
) )
) )
}; };

View File

@@ -6,7 +6,7 @@
use std::time::Duration; use std::time::Duration;
use neqo_common::{event::Provider, Encoder}; use neqo_common::{event::Provider as _, Encoder};
use neqo_crypto::AuthenticationStatus; use neqo_crypto::AuthenticationStatus;
use neqo_transport::{CloseReason, Connection, StreamType}; use neqo_transport::{CloseReason, Connection, StreamType};
use test_fixture::{default_server_h3, now}; use test_fixture::{default_server_h3, now};
@@ -88,7 +88,7 @@ fn zero_rtt(
// exchange token // exchange token
let out = server.process_output(now()); let out = server.process_output(now());
// We do not have a token so we need to wait for a resumption token timer to trigger. // We do not have a token so we need to wait for a resumption token timer to trigger.
std::mem::drop(client.process(out.dgram(), now() + Duration::from_millis(250))); drop(client.process(out.dgram(), now() + Duration::from_millis(250)));
assert_eq!(client.state(), Http3State::Connected); assert_eq!(client.state(), Http3State::Connected);
let token = client let token = client
.events() .events()
@@ -105,7 +105,7 @@ fn zero_rtt(
let mut server = default_http3_server(Http3Parameters::default().webtransport(server_resumed)); let mut server = default_http3_server(Http3Parameters::default().webtransport(server_resumed));
client client
.enable_resumption(now(), &token) .enable_resumption(now(), &token)
.expect("Set resumption token."); .expect("Set resumption token");
assert_eq!(client.state(), Http3State::ZeroRtt); assert_eq!(client.state(), Http3State::ZeroRtt);
exchange_packets(&mut client, &mut server); exchange_packets(&mut client, &mut server);

View File

@@ -4,16 +4,14 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
use std::mem; use neqo_common::{event::Provider as _, header::HeadersExt as _, Encoder};
use neqo_common::{event::Provider, Encoder};
use neqo_transport::StreamType; use neqo_transport::StreamType;
use test_fixture::now; use test_fixture::now;
use crate::{ use crate::{
features::extended_connect::{ features::extended_connect::{
tests::webtransport::{ tests::webtransport::{
default_http3_client, default_http3_server, wt_default_parameters, WtTest, assert_wt, default_http3_client, default_http3_server, wt_default_parameters, WtTest,
}, },
SessionCloseReason, SessionCloseReason,
}, },
@@ -26,7 +24,7 @@ use crate::{
#[test] #[test]
fn wt_session() { fn wt_session() {
let mut wt = WtTest::new(); let mut wt = WtTest::new();
mem::drop(wt.create_wt_session()); drop(wt.create_wt_session());
} }
#[test] #[test]
@@ -134,14 +132,7 @@ fn wt_session_response_with_1xx() {
headers, headers,
}) = event }) = event
{ {
assert!( assert_wt(&headers);
headers
.iter()
.any(|h| h.name() == ":method" && h.value() == "CONNECT")
&& headers
.iter()
.any(|h| h.name() == ":protocol" && h.value() == "webtransport")
);
wt_server_session = Some(session); wt_server_session = Some(session);
} }
} }
@@ -168,7 +159,7 @@ fn wt_session_response_with_1xx() {
}) if ( }) if (
stream_id == wt_session_id && stream_id == wt_session_id &&
status == 200 && status == 200 &&
headers.contains(&Header::new(":status", "200")) headers.contains_header(":status", "200")
) )
) )
}; };
@@ -209,14 +200,7 @@ fn wt_session_respone_200_with_fin() {
headers, headers,
}) = event }) = event
{ {
assert!( assert_wt(&headers);
headers
.iter()
.any(|h| h.name() == ":method" && h.value() == "CONNECT")
&& headers
.iter()
.any(|h| h.name() == ":protocol" && h.value() == "webtransport")
);
wt_server_session = Some(session); wt_server_session = Some(session);
} }
} }

View File

@@ -4,8 +4,6 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
use std::mem;
use neqo_transport::StreamType; use neqo_transport::StreamType;
use crate::{ use crate::{
@@ -94,7 +92,7 @@ fn wt_server_stream_bidi() {
wt.send_data_server(&wt_server_stream, BUF_SERVER); wt.send_data_server(&wt_server_stream, BUF_SERVER);
wt.receive_data_client(wt_server_stream.stream_id(), true, BUF_SERVER, false); wt.receive_data_client(wt_server_stream.stream_id(), true, BUF_SERVER, false);
wt.send_data_client(wt_server_stream.stream_id(), BUF_CLIENT); wt.send_data_client(wt_server_stream.stream_id(), BUF_CLIENT);
mem::drop(wt.receive_data_server(wt_server_stream.stream_id(), false, BUF_CLIENT, false)); drop(wt.receive_data_server(wt_server_stream.stream_id(), false, BUF_CLIENT, false));
let stats = wt.send_stream_stats(wt_server_stream.stream_id()).unwrap(); let stats = wt.send_stream_stats(wt_server_stream.stream_id()).unwrap();
assert_eq!(stats.bytes_written(), BUF_CLIENT.len() as u64); assert_eq!(stats.bytes_written(), BUF_CLIENT.len() as u64);
assert_eq!(stats.bytes_sent(), BUF_CLIENT.len() as u64); assert_eq!(stats.bytes_sent(), BUF_CLIENT.len() as u64);
@@ -161,7 +159,7 @@ fn wt_server_stream_bidi_close() {
wt.receive_data_client(wt_server_stream.stream_id(), true, BUF_SERVER, true); wt.receive_data_client(wt_server_stream.stream_id(), true, BUF_SERVER, true);
wt.send_data_client(wt_server_stream.stream_id(), BUF_CLIENT); wt.send_data_client(wt_server_stream.stream_id(), BUF_CLIENT);
wt.close_stream_sending_client(wt_server_stream.stream_id()); wt.close_stream_sending_client(wt_server_stream.stream_id());
mem::drop(wt.receive_data_server(wt_server_stream.stream_id(), false, BUF_CLIENT, true)); drop(wt.receive_data_server(wt_server_stream.stream_id(), false, BUF_CLIENT, true));
} }
#[test] #[test]
@@ -172,7 +170,7 @@ fn wt_client_stream_uni_reset() {
let wt_session = wt.create_wt_session(); let wt_session = wt.create_wt_session();
let wt_stream = wt.create_wt_stream_client(wt_session.stream_id(), StreamType::UniDi); let wt_stream = wt.create_wt_stream_client(wt_session.stream_id(), StreamType::UniDi);
wt.send_data_client(wt_stream, BUF_CLIENT); wt.send_data_client(wt_stream, BUF_CLIENT);
mem::drop(wt.receive_data_server(wt_stream, true, BUF_CLIENT, false)); drop(wt.receive_data_server(wt_stream, true, BUF_CLIENT, false));
wt.reset_stream_client(wt_stream); wt.reset_stream_client(wt_stream);
wt.receive_reset_server(wt_stream, Error::HttpNoError.code()); wt.receive_reset_server(wt_stream, Error::HttpNoError.code());
} }
@@ -315,7 +313,7 @@ fn wt_client_session_close_1() {
let bidi_from_client = wt.create_wt_stream_client(wt_session.stream_id(), StreamType::BiDi); let bidi_from_client = wt.create_wt_stream_client(wt_session.stream_id(), StreamType::BiDi);
wt.send_data_client(bidi_from_client, BUF); wt.send_data_client(bidi_from_client, BUF);
std::mem::drop(wt.receive_data_server(bidi_from_client, true, BUF, false)); drop(wt.receive_data_server(bidi_from_client, true, BUF, false));
wt.cancel_session_client(wt_session.stream_id()); wt.cancel_session_client(wt_session.stream_id());
@@ -350,7 +348,7 @@ fn wt_client_session_close_2() {
let unidi_from_client = wt.create_wt_stream_client(wt_session.stream_id(), StreamType::UniDi); let unidi_from_client = wt.create_wt_stream_client(wt_session.stream_id(), StreamType::UniDi);
wt.send_data_client(unidi_from_client, BUF); wt.send_data_client(unidi_from_client, BUF);
std::mem::drop(wt.receive_data_server(unidi_from_client, true, BUF, false)); drop(wt.receive_data_server(unidi_from_client, true, BUF, false));
wt.cancel_session_client(wt_session.stream_id()); wt.cancel_session_client(wt_session.stream_id());
@@ -385,7 +383,7 @@ fn wt_client_session_close_3() {
let unidi_from_client = wt.create_wt_stream_client(wt_session.stream_id(), StreamType::UniDi); let unidi_from_client = wt.create_wt_stream_client(wt_session.stream_id(), StreamType::UniDi);
wt.send_data_client(unidi_from_client, BUF); wt.send_data_client(unidi_from_client, BUF);
std::mem::drop(wt.receive_data_server(unidi_from_client, true, BUF, false)); drop(wt.receive_data_server(unidi_from_client, true, BUF, false));
wt.close_stream_sending_client(unidi_from_client); wt.close_stream_sending_client(unidi_from_client);
wt.cancel_session_client(wt_session.stream_id()); wt.cancel_session_client(wt_session.stream_id());
@@ -450,7 +448,7 @@ fn wt_client_session_close_5() {
let unidi_from_client = wt.create_wt_stream_client(wt_session.stream_id(), StreamType::UniDi); let unidi_from_client = wt.create_wt_stream_client(wt_session.stream_id(), StreamType::UniDi);
wt.send_data_client(unidi_from_client, BUF); wt.send_data_client(unidi_from_client, BUF);
mem::drop(wt.receive_data_server(unidi_from_client, true, BUF, false)); drop(wt.receive_data_server(unidi_from_client, true, BUF, false));
wt.reset_stream_client(unidi_from_client); wt.reset_stream_client(unidi_from_client);
wt.cancel_session_client(wt_session.stream_id()); wt.cancel_session_client(wt_session.stream_id());
@@ -700,10 +698,10 @@ fn wt_client_session_close_13() {
let bidi_client_1 = wt.create_wt_stream_client(wt_session.stream_id(), StreamType::BiDi); let bidi_client_1 = wt.create_wt_stream_client(wt_session.stream_id(), StreamType::BiDi);
wt.send_data_client(bidi_client_1, BUF); wt.send_data_client(bidi_client_1, BUF);
std::mem::drop(wt.receive_data_server(bidi_client_1, true, BUF, false)); drop(wt.receive_data_server(bidi_client_1, true, BUF, false));
let bidi_client_2 = wt.create_wt_stream_client(wt_session.stream_id(), StreamType::BiDi); let bidi_client_2 = wt.create_wt_stream_client(wt_session.stream_id(), StreamType::BiDi);
wt.send_data_client(bidi_client_2, BUF); wt.send_data_client(bidi_client_2, BUF);
std::mem::drop(wt.receive_data_server(bidi_client_2, true, BUF, false)); drop(wt.receive_data_server(bidi_client_2, true, BUF, false));
wt.cancel_session_client(wt_session.stream_id()); wt.cancel_session_client(wt_session.stream_id());
@@ -750,7 +748,7 @@ fn wt_client_session_server_close_1() {
let bidi_client = wt.create_wt_stream_client(wt_session.stream_id(), StreamType::BiDi); let bidi_client = wt.create_wt_stream_client(wt_session.stream_id(), StreamType::BiDi);
wt.send_data_client(bidi_client, BUF); wt.send_data_client(bidi_client, BUF);
std::mem::drop(wt.receive_data_server(bidi_client, true, BUF, false)); drop(wt.receive_data_server(bidi_client, true, BUF, false));
wt.cancel_session_server(&wt_session); wt.cancel_session_server(&wt_session);
@@ -784,7 +782,7 @@ fn wt_client_session_server_close_2() {
let unidi_client = wt.create_wt_stream_client(wt_session.stream_id(), StreamType::UniDi); let unidi_client = wt.create_wt_stream_client(wt_session.stream_id(), StreamType::UniDi);
wt.send_data_client(unidi_client, BUF); wt.send_data_client(unidi_client, BUF);
std::mem::drop(wt.receive_data_server(unidi_client, true, BUF, false)); drop(wt.receive_data_server(unidi_client, true, BUF, false));
wt.cancel_session_server(&wt_session); wt.cancel_session_server(&wt_session);
@@ -1064,10 +1062,10 @@ fn wt_client_session_server_close_11() {
let bidi_client_1 = wt.create_wt_stream_client(wt_session.stream_id(), StreamType::BiDi); let bidi_client_1 = wt.create_wt_stream_client(wt_session.stream_id(), StreamType::BiDi);
wt.send_data_client(bidi_client_1, BUF); wt.send_data_client(bidi_client_1, BUF);
std::mem::drop(wt.receive_data_server(bidi_client_1, true, BUF, false)); drop(wt.receive_data_server(bidi_client_1, true, BUF, false));
let bidi_client_2 = wt.create_wt_stream_client(wt_session.stream_id(), StreamType::BiDi); let bidi_client_2 = wt.create_wt_stream_client(wt_session.stream_id(), StreamType::BiDi);
wt.send_data_client(bidi_client_2, BUF); wt.send_data_client(bidi_client_2, BUF);
std::mem::drop(wt.receive_data_server(bidi_client_2, true, BUF, false)); drop(wt.receive_data_server(bidi_client_2, true, BUF, false));
wt.cancel_session_server(&wt_session); wt.cancel_session_server(&wt_session);

View File

@@ -73,7 +73,7 @@ impl WebTransportSession {
first_frame_type: None, first_frame_type: None,
}, },
qpack_decoder, qpack_decoder,
Box::new(stream_event_listener.clone()), Box::new(Rc::clone(&stream_event_listener)),
None, None,
PriorityHandler::new(false, Priority::default()), PriorityHandler::new(false, Priority::default()),
)), )),
@@ -82,7 +82,7 @@ impl WebTransportSession {
Http3StreamType::ExtendedConnect, Http3StreamType::ExtendedConnect,
session_id, session_id,
qpack_encoder, qpack_encoder,
Box::new(stream_event_listener.clone()), Box::new(Rc::clone(&stream_event_listener)),
)), )),
stream_event_listener, stream_event_listener,
session_id, session_id,
@@ -111,11 +111,11 @@ impl WebTransportSession {
control_stream_recv control_stream_recv
.http_stream() .http_stream()
.unwrap() .unwrap()
.set_new_listener(Box::new(stream_event_listener.clone())); .set_new_listener(Box::new(Rc::clone(&stream_event_listener)));
control_stream_send control_stream_send
.http_stream() .http_stream()
.unwrap() .unwrap()
.set_new_listener(Box::new(stream_event_listener.clone())); .set_new_listener(Box::new(Rc::clone(&stream_event_listener)));
Self { Self {
control_stream_recv, control_stream_recv,
control_stream_send, control_stream_send,
@@ -146,7 +146,7 @@ impl WebTransportSession {
} }
fn receive(&mut self, conn: &mut Connection) -> Res<(ReceiveOutput, bool)> { fn receive(&mut self, conn: &mut Connection) -> Res<(ReceiveOutput, bool)> {
qtrace!([self], "receive control data"); qtrace!("[{self}] receive control data");
let (out, _) = self.control_stream_recv.receive(conn)?; let (out, _) = self.control_stream_recv.receive(conn)?;
debug_assert!(out == ReceiveOutput::NoOutput); debug_assert!(out == ReceiveOutput::NoOutput);
self.maybe_check_headers(); self.maybe_check_headers();
@@ -229,11 +229,7 @@ impl WebTransportSession {
if let Some((headers, interim, fin)) = self.stream_event_listener.borrow_mut().get_headers() if let Some((headers, interim, fin)) = self.stream_event_listener.borrow_mut().get_headers()
{ {
qtrace!( qtrace!("ExtendedConnect response headers {headers:?}, fin={fin}");
"ExtendedConnect response headers {:?}, fin={}",
headers,
fin
);
if interim { if interim {
if fin { if fin {
@@ -346,7 +342,7 @@ impl WebTransportSession {
&mut self.control_stream_recv, &mut self.control_stream_recv,
)) ))
.map_err(|_| Error::HttpGeneralProtocolStream)?; .map_err(|_| Error::HttpGeneralProtocolStream)?;
qtrace!([self], "Received frame: {:?} fin={}", f, fin); qtrace!("[{self}] Received frame: {f:?} fin={fin}");
if let Some(WebTransportFrame::CloseSession { error, message }) = f { if let Some(WebTransportFrame::CloseSession { error, message }) = f {
self.events.session_end( self.events.session_end(
ExtendedConnectType::WebTransport, ExtendedConnectType::WebTransport,
@@ -410,7 +406,7 @@ impl WebTransportSession {
buf: &[u8], buf: &[u8],
id: impl Into<DatagramTracking>, id: impl Into<DatagramTracking>,
) -> Res<()> { ) -> Res<()> {
qtrace!([self], "send_datagram state={:?}", self.state); qtrace!("[{self}] send_datagram state={:?}", self.state);
if self.state == SessionState::Active { if self.state == SessionState::Active {
let mut dgram_data = Encoder::default(); let mut dgram_data = Encoder::default();
dgram_data.encode_varint(self.session_id.as_u64() / 4); dgram_data.encode_varint(self.session_id.as_u64() / 4);
@@ -451,7 +447,7 @@ impl RecvStream for Rc<RefCell<WebTransportSession>> {
} }
fn webtransport(&self) -> Option<Rc<RefCell<WebTransportSession>>> { fn webtransport(&self) -> Option<Rc<RefCell<WebTransportSession>>> {
Some(self.clone()) Some(Self::clone(self))
} }
} }

View File

@@ -63,8 +63,7 @@ impl NegotiationState {
} = self } = self
{ {
qtrace!( qtrace!(
"set_negotiated {:?} to {}", "set_negotiated {feature_type:?} to {}",
feature_type,
settings.get(*feature_type) settings.get(*feature_type)
); );
let cb = mem::take(listener); let cb = mem::take(listener);

View File

@@ -4,13 +4,13 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
use std::{fmt::Debug, io::Write}; use std::{fmt::Debug, io::Write as _};
use neqo_common::{Decoder, Encoder}; use neqo_common::{Decoder, Encoder};
use neqo_crypto::random; use neqo_crypto::random;
use neqo_transport::StreamId; use neqo_transport::StreamId;
use crate::{frames::reader::FrameDecoder, settings::HSettings, Error, Priority, Res}; use crate::{frames::reader::FrameDecoder, settings::HSettings, Error, Priority, PushId, Res};
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct HFrameType(pub u64); pub struct HFrameType(pub u64);
@@ -48,20 +48,20 @@ pub enum HFrame {
header_block: Vec<u8>, header_block: Vec<u8>,
}, },
CancelPush { CancelPush {
push_id: u64, push_id: PushId,
}, },
Settings { Settings {
settings: HSettings, settings: HSettings,
}, },
PushPromise { PushPromise {
push_id: u64, push_id: PushId,
header_block: Vec<u8>, header_block: Vec<u8>,
}, },
Goaway { Goaway {
stream_id: StreamId, stream_id: StreamId,
}, },
MaxPushId { MaxPushId {
push_id: u64, push_id: PushId,
}, },
Grease, Grease,
PriorityUpdateRequest { PriorityUpdateRequest {
@@ -87,7 +87,9 @@ impl HFrame {
Self::PriorityUpdateRequest { .. } => H3_FRAME_TYPE_PRIORITY_UPDATE_REQUEST, Self::PriorityUpdateRequest { .. } => H3_FRAME_TYPE_PRIORITY_UPDATE_REQUEST,
Self::PriorityUpdatePush { .. } => H3_FRAME_TYPE_PRIORITY_UPDATE_PUSH, Self::PriorityUpdatePush { .. } => H3_FRAME_TYPE_PRIORITY_UPDATE_PUSH,
Self::Grease => { Self::Grease => {
HFrameType(Decoder::from(&random::<7>()).decode_uint(7).unwrap() * 0x1f + 0x21) let r = Decoder::from(&random::<8>()).decode_uint::<u64>().unwrap();
// Zero out the top 7 bits: 2 for being a varint; 5 to account for the *0x1f.
HFrameType((r >> 7) * 0x1f + 0x21)
} }
} }
} }
@@ -115,7 +117,9 @@ impl HFrame {
push_id, push_id,
header_block, header_block,
} => { } => {
enc.encode_varint((header_block.len() + (Encoder::varint_len(*push_id))) as u64); enc.encode_varint(
(header_block.len() + (Encoder::varint_len(u64::from(*push_id)))) as u64,
);
enc.encode_varint(*push_id); enc.encode_varint(*push_id);
enc.encode(header_block); enc.encode(header_block);
} }
@@ -170,12 +174,12 @@ impl FrameDecoder<Self> for HFrame {
} else if let Some(payload) = data { } else if let Some(payload) = data {
let mut dec = Decoder::from(payload); let mut dec = Decoder::from(payload);
Ok(match frame_type { Ok(match frame_type {
H3_FRAME_TYPE_DATA => unreachable!("DATA frame has been handled already."), H3_FRAME_TYPE_DATA => unreachable!("DATA frame has been handled already"),
H3_FRAME_TYPE_HEADERS => Some(Self::Headers { H3_FRAME_TYPE_HEADERS => Some(Self::Headers {
header_block: dec.decode_remainder().to_vec(), header_block: dec.decode_remainder().to_vec(),
}), }),
H3_FRAME_TYPE_CANCEL_PUSH => Some(Self::CancelPush { H3_FRAME_TYPE_CANCEL_PUSH => Some(Self::CancelPush {
push_id: dec.decode_varint().ok_or(Error::HttpFrame)?, push_id: dec.decode_varint().ok_or(Error::HttpFrame)?.into(),
}), }),
H3_FRAME_TYPE_SETTINGS => { H3_FRAME_TYPE_SETTINGS => {
let mut settings = HSettings::default(); let mut settings = HSettings::default();
@@ -189,14 +193,14 @@ impl FrameDecoder<Self> for HFrame {
Some(Self::Settings { settings }) Some(Self::Settings { settings })
} }
H3_FRAME_TYPE_PUSH_PROMISE => Some(Self::PushPromise { H3_FRAME_TYPE_PUSH_PROMISE => Some(Self::PushPromise {
push_id: dec.decode_varint().ok_or(Error::HttpFrame)?, push_id: dec.decode_varint().ok_or(Error::HttpFrame)?.into(),
header_block: dec.decode_remainder().to_vec(), header_block: dec.decode_remainder().to_vec(),
}), }),
H3_FRAME_TYPE_GOAWAY => Some(Self::Goaway { H3_FRAME_TYPE_GOAWAY => Some(Self::Goaway {
stream_id: StreamId::new(dec.decode_varint().ok_or(Error::HttpFrame)?), stream_id: StreamId::new(dec.decode_varint().ok_or(Error::HttpFrame)?),
}), }),
H3_FRAME_TYPE_MAX_PUSH_ID => Some(Self::MaxPushId { H3_FRAME_TYPE_MAX_PUSH_ID => Some(Self::MaxPushId {
push_id: dec.decode_varint().ok_or(Error::HttpFrame)?, push_id: dec.decode_varint().ok_or(Error::HttpFrame)?.into(),
}), }),
H3_FRAME_TYPE_PRIORITY_UPDATE_REQUEST | H3_FRAME_TYPE_PRIORITY_UPDATE_PUSH => { H3_FRAME_TYPE_PRIORITY_UPDATE_REQUEST | H3_FRAME_TYPE_PRIORITY_UPDATE_PUSH => {
let element_id = dec.decode_varint().ok_or(Error::HttpFrame)?; let element_id = dec.decode_varint().ok_or(Error::HttpFrame)?;

View File

@@ -24,7 +24,7 @@ pub trait FrameDecoder<T> {
/// # Errors /// # Errors
/// ///
/// Returns `HttpFrameUnexpected` if frames is not alowed, i.e. is a `H3_RESERVED_FRAME_TYPES`. /// Returns `HttpFrameUnexpected` if frames is not allowed, i.e. is a `H3_RESERVED_FRAME_TYPES`.
fn frame_type_allowed(_frame_type: HFrameType) -> Res<()> { fn frame_type_allowed(_frame_type: HFrameType) -> Res<()> {
Ok(()) Ok(())
} }
@@ -154,7 +154,7 @@ impl FrameReader {
} }
} }
/// returns true if quic stream was closed. /// Returns true if QUIC stream was closed.
/// ///
/// # Errors /// # Errors
/// ///
@@ -173,7 +173,7 @@ impl FrameReader {
{ {
(0, f) => (None, false, f), (0, f) => (None, false, f),
(amount, f) => { (amount, f) => {
qtrace!("FrameReader::receive: reading {} byte, fin={}", amount, f); qtrace!("FrameReader::receive: reading {amount} byte, fin={f}");
(self.consume::<T>(Decoder::from(&buf[..amount]))?, true, f) (self.consume::<T>(Decoder::from(&buf[..amount]))?, true, f)
} }
}; };
@@ -203,16 +203,15 @@ impl FrameReader {
match &mut self.state { match &mut self.state {
FrameReaderState::GetType { decoder } => { FrameReaderState::GetType { decoder } => {
if let Some(v) = decoder.consume(&mut input) { if let Some(v) = decoder.consume(&mut input) {
qtrace!("FrameReader::receive: read frame type {}", v); qtrace!("FrameReader::receive: read frame type {v}");
self.frame_type_decoded::<T>(HFrameType(v))?; self.frame_type_decoded::<T>(HFrameType(v))?;
} }
} }
FrameReaderState::GetLength { decoder } => { FrameReaderState::GetLength { decoder } => {
if let Some(len) = decoder.consume(&mut input) { if let Some(len) = decoder.consume(&mut input) {
qtrace!( qtrace!(
"FrameReader::receive: frame type {:?} length {}", "FrameReader::receive: frame type {:?} length {len}",
self.frame_type, self.frame_type
len
); );
return self.frame_length_decoded::<T>(len); return self.frame_length_decoded::<T>(len);
} }
@@ -235,9 +234,6 @@ impl FrameReader {
} }
Ok(None) Ok(None)
} }
}
impl FrameReader {
fn frame_type_decoded<T: FrameDecoder<T>>(&mut self, frame_type: HFrameType) -> Res<()> { fn frame_type_decoded<T: FrameDecoder<T>>(&mut self, frame_type: HFrameType) -> Res<()> {
T::frame_type_allowed(frame_type)?; T::frame_type_allowed(frame_type)?;
self.frame_type = frame_type; self.frame_type = frame_type;

View File

@@ -12,7 +12,7 @@ use super::enc_dec_hframe;
use crate::{ use crate::{
frames::HFrame, frames::HFrame,
settings::{HSetting, HSettingType, HSettings}, settings::{HSetting, HSettingType, HSettings},
Priority, Priority, PushId,
}; };
#[test] #[test]
@@ -31,7 +31,9 @@ fn headers_frame() {
#[test] #[test]
fn cancel_push_frame4() { fn cancel_push_frame4() {
let f = HFrame::CancelPush { push_id: 5 }; let f = HFrame::CancelPush {
push_id: PushId::new(5),
};
enc_dec_hframe(&f, "030105", 0); enc_dec_hframe(&f, "030105", 0);
} }
@@ -46,7 +48,7 @@ fn settings_frame4() {
#[test] #[test]
fn push_promise_frame4() { fn push_promise_frame4() {
let f = HFrame::PushPromise { let f = HFrame::PushPromise {
push_id: 4, push_id: PushId::new(4),
header_block: vec![0x61, 0x62, 0x63, 0x64], header_block: vec![0x61, 0x62, 0x63, 0x64],
}; };
enc_dec_hframe(&f, "05050461626364", 0); enc_dec_hframe(&f, "05050461626364", 0);

View File

@@ -4,8 +4,6 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
use std::mem;
use neqo_common::Encoder; use neqo_common::Encoder;
use neqo_crypto::AuthenticationStatus; use neqo_crypto::AuthenticationStatus;
use neqo_transport::StreamType; use neqo_transport::StreamType;
@@ -25,21 +23,21 @@ pub fn enc_dec<T: FrameDecoder<T>>(d: &Encoder, st: &str, remaining: usize) -> T
let out = conn_c.process_output(now()); let out = conn_c.process_output(now());
let out = conn_s.process(out.dgram(), now()); let out = conn_s.process(out.dgram(), now());
let out = conn_c.process(out.dgram(), now()); let out = conn_c.process(out.dgram(), now());
mem::drop(conn_s.process(out.dgram(), now())); drop(conn_s.process(out.dgram(), now()));
conn_c.authenticated(AuthenticationStatus::Ok, now()); conn_c.authenticated(AuthenticationStatus::Ok, now());
let out = conn_c.process_output(now()); let out = conn_c.process_output(now());
mem::drop(conn_s.process(out.dgram(), now())); drop(conn_s.process(out.dgram(), now()));
// create a stream // create a stream
let stream_id = conn_s.stream_create(StreamType::BiDi).unwrap(); let stream_id = conn_s.stream_create(StreamType::BiDi).unwrap();
let mut fr: FrameReader = FrameReader::new(); let mut fr = FrameReader::new();
// conver string into u8 vector // convert string into u8 vector
let buf = Encoder::from_hex(st); let buf = Encoder::from_hex(st);
conn_s.stream_send(stream_id, buf.as_ref()).unwrap(); conn_s.stream_send(stream_id, buf.as_ref()).unwrap();
let out = conn_s.process_output(now()); let out = conn_s.process_output(now());
mem::drop(conn_c.process(out.dgram(), now())); drop(conn_c.process(out.dgram(), now()));
let (frame, fin) = fr let (frame, fin) = fr
.receive::<T>(&mut StreamReaderConnectionWrapper::new( .receive::<T>(&mut StreamReaderConnectionWrapper::new(

View File

@@ -4,7 +4,7 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
use std::{fmt::Debug, mem}; use std::fmt::Debug;
use neqo_common::Encoder; use neqo_common::Encoder;
use neqo_transport::{Connection, StreamId, StreamType}; use neqo_transport::{Connection, StreamId, StreamType};
@@ -15,7 +15,7 @@ use crate::{
reader::FrameDecoder, FrameReader, HFrame, StreamReaderConnectionWrapper, WebTransportFrame, reader::FrameDecoder, FrameReader, HFrame, StreamReaderConnectionWrapper, WebTransportFrame,
}, },
settings::{HSetting, HSettingType, HSettings}, settings::{HSetting, HSettingType, HSettings},
Error, Error, PushId,
}; };
struct FrameReaderTest { struct FrameReaderTest {
@@ -40,7 +40,7 @@ impl FrameReaderTest {
fn process<T: FrameDecoder<T>>(&mut self, v: &[u8]) -> Option<T> { fn process<T: FrameDecoder<T>>(&mut self, v: &[u8]) -> Option<T> {
self.conn_s.stream_send(self.stream_id, v).unwrap(); self.conn_s.stream_send(self.stream_id, v).unwrap();
let out = self.conn_s.process_output(now()); let out = self.conn_s.process_output(now());
mem::drop(self.conn_c.process(out.dgram(), now())); drop(self.conn_c.process(out.dgram(), now()));
let (frame, fin) = self let (frame, fin) = self
.fr .fr
.receive::<T>(&mut StreamReaderConnectionWrapper::new( .receive::<T>(&mut StreamReaderConnectionWrapper::new(
@@ -112,7 +112,7 @@ fn frame_reading_with_stream_push_promise() {
header_block, header_block,
} = frame.unwrap() } = frame.unwrap()
{ {
assert_eq!(push_id, 257); assert_eq!(push_id, PushId::new(257));
assert_eq!(header_block, &[0x1, 0x2, 0x3]); assert_eq!(header_block, &[0x1, 0x2, 0x3]);
} else { } else {
panic!("wrong frame type"); panic!("wrong frame type");
@@ -128,7 +128,7 @@ fn frame_reading_with_stream_data() {
let frame = fr.process(&[0x0, 0x3, 0x1, 0x2, 0x3]).unwrap(); let frame = fr.process(&[0x0, 0x3, 0x1, 0x2, 0x3]).unwrap();
assert!(matches!(frame, HFrame::Data { len } if len == 3)); assert!(matches!(frame, HFrame::Data { len } if len == 3));
// payloead is still on the stream. // payload is still on the stream.
// assert that we have 3 bytes in the stream // assert that we have 3 bytes in the stream
let mut buf = [0_u8; 100]; let mut buf = [0_u8; 100];
let (amount, _) = fr.conn_c.stream_recv(fr.stream_id, &mut buf).unwrap(); let (amount, _) = fr.conn_c.stream_recv(fr.stream_id, &mut buf).unwrap();
@@ -150,11 +150,11 @@ fn unknown_frame() {
buf.resize(UNKNOWN_FRAME_LEN + buf.len(), 0); buf.resize(UNKNOWN_FRAME_LEN + buf.len(), 0);
assert!(fr.process::<HFrame>(&buf).is_none()); assert!(fr.process::<HFrame>(&buf).is_none());
// now receive a CANCEL_PUSH fram to see that frame reader is ok. // now receive a CANCEL_PUSH frame to see that frame reader is ok.
let frame = fr.process(&[0x03, 0x01, 0x05]); let frame = fr.process(&[0x03, 0x01, 0x05]);
assert!(frame.is_some()); assert!(frame.is_some());
if let HFrame::CancelPush { push_id } = frame.unwrap() { if let HFrame::CancelPush { push_id } = frame.unwrap() {
assert!(push_id == 5); assert!(push_id == PushId::new(5));
} else { } else {
panic!("wrong frame type"); panic!("wrong frame type");
} }
@@ -194,7 +194,7 @@ fn unknown_wt_frame() {
buf.resize(UNKNOWN_FRAME_LEN + buf.len(), 0); buf.resize(UNKNOWN_FRAME_LEN + buf.len(), 0);
assert!(fr.process::<WebTransportFrame>(&buf).is_none()); assert!(fr.process::<WebTransportFrame>(&buf).is_none());
// now receive a WT_FRAME_CLOSE_SESSION fram to see that frame reader is ok. // now receive a WT_FRAME_CLOSE_SESSION frame to see that frame reader is ok.
let frame = fr.process(&[ let frame = fr.process(&[
0x68, 0x43, 0x09, 0x00, 0x00, 0x00, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x68, 0x43, 0x09, 0x00, 0x00, 0x00, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f,
]); ]);
@@ -231,12 +231,12 @@ fn test_reading_frame<T: FrameDecoder<T> + PartialEq + Debug>(
} }
let out = fr.conn_s.process_output(now()); let out = fr.conn_s.process_output(now());
mem::drop(fr.conn_c.process(out.dgram(), now())); drop(fr.conn_c.process(out.dgram(), now()));
if matches!(test_to_send, FrameReadingTestSend::DataThenFin) { if matches!(test_to_send, FrameReadingTestSend::DataThenFin) {
fr.conn_s.stream_close_send(fr.stream_id).unwrap(); fr.conn_s.stream_close_send(fr.stream_id).unwrap();
let out = fr.conn_s.process_output(now()); let out = fr.conn_s.process_output(now());
mem::drop(fr.conn_c.process(out.dgram(), now())); drop(fr.conn_c.process(out.dgram(), now()));
} }
let rv = fr.fr.receive::<T>(&mut StreamReaderConnectionWrapper::new( let rv = fr.fr.receive::<T>(&mut StreamReaderConnectionWrapper::new(
@@ -318,7 +318,7 @@ fn test_complete_and_incomplete_frame<T: FrameDecoder<T> + PartialEq + Debug>(
done_state: usize, done_state: usize,
) { ) {
use std::cmp::Ordering; use std::cmp::Ordering;
// Let's consume partial frames. It is enough to test partal frames // Let's consume partial frames. It is enough to test partial frames
// up to 10 byte. 10 byte is greater than frame type and frame // up to 10 byte. 10 byte is greater than frame type and frame
// length and bit of data. // length and bit of data.
let len = std::cmp::min(buf.len() - 1, 10); let len = std::cmp::min(buf.len() - 1, 10);
@@ -417,7 +417,9 @@ fn complete_and_incomplete_frames() {
test_complete_and_incomplete_frame::<HFrame>(&buf, buf.len()); test_complete_and_incomplete_frame::<HFrame>(&buf, buf.len());
// H3_FRAME_TYPE_CANCEL_PUSH // H3_FRAME_TYPE_CANCEL_PUSH
let f = HFrame::CancelPush { push_id: 5 }; let f = HFrame::CancelPush {
push_id: PushId::new(5),
};
let mut enc = Encoder::default(); let mut enc = Encoder::default();
f.encode(&mut enc); f.encode(&mut enc);
let buf: Vec<_> = enc.into(); let buf: Vec<_> = enc.into();
@@ -434,7 +436,7 @@ fn complete_and_incomplete_frames() {
// H3_FRAME_TYPE_PUSH_PROMISE // H3_FRAME_TYPE_PUSH_PROMISE
let f = HFrame::PushPromise { let f = HFrame::PushPromise {
push_id: 4, push_id: PushId::new(4),
header_block: HEADER_BLOCK.to_vec(), header_block: HEADER_BLOCK.to_vec(),
}; };
let mut enc = Encoder::default(); let mut enc = Encoder::default();
@@ -452,7 +454,9 @@ fn complete_and_incomplete_frames() {
test_complete_and_incomplete_frame::<HFrame>(&buf, buf.len()); test_complete_and_incomplete_frame::<HFrame>(&buf, buf.len());
// H3_FRAME_TYPE_MAX_PUSH_ID // H3_FRAME_TYPE_MAX_PUSH_ID
let f = HFrame::MaxPushId { push_id: 5 }; let f = HFrame::MaxPushId {
push_id: PushId::new(5),
};
let mut enc = Encoder::default(); let mut enc = Encoder::default();
f.encode(&mut enc); f.encode(&mut enc);
let buf: Vec<_> = enc.into(); let buf: Vec<_> = enc.into();
@@ -479,11 +483,11 @@ fn frame_reading_when_stream_is_closed_before_sending_data() {
fr.conn_s.stream_send(fr.stream_id, &[0x00]).unwrap(); fr.conn_s.stream_send(fr.stream_id, &[0x00]).unwrap();
let out = fr.conn_s.process_output(now()); let out = fr.conn_s.process_output(now());
mem::drop(fr.conn_c.process(out.dgram(), now())); drop(fr.conn_c.process(out.dgram(), now()));
assert_eq!(Ok(()), fr.conn_c.stream_close_send(fr.stream_id)); assert_eq!(Ok(()), fr.conn_c.stream_close_send(fr.stream_id));
let out = fr.conn_c.process_output(now()); let out = fr.conn_c.process_output(now());
mem::drop(fr.conn_s.process(out.dgram(), now())); drop(fr.conn_s.process(out.dgram(), now()));
assert_eq!( assert_eq!(
Ok((None, true)), Ok((None, true)),
fr.fr fr.fr
@@ -502,11 +506,11 @@ fn wt_frame_reading_when_stream_is_closed_before_sending_data() {
fr.conn_s.stream_send(fr.stream_id, &[0x00]).unwrap(); fr.conn_s.stream_send(fr.stream_id, &[0x00]).unwrap();
let out = fr.conn_s.process_output(now()); let out = fr.conn_s.process_output(now());
mem::drop(fr.conn_c.process(out.dgram(), now())); drop(fr.conn_c.process(out.dgram(), now()));
assert_eq!(Ok(()), fr.conn_c.stream_close_send(fr.stream_id)); assert_eq!(Ok(()), fr.conn_c.stream_close_send(fr.stream_id));
let out = fr.conn_c.process_output(now()); let out = fr.conn_c.process_output(now());
mem::drop(fr.conn_s.process(out.dgram(), now())); drop(fr.conn_s.process(out.dgram(), now()));
assert_eq!( assert_eq!(
Ok((None, true)), Ok((None, true)),
fr.fr fr.fr

View File

@@ -37,8 +37,7 @@ impl FrameDecoder<Self> for WebTransportFrame {
if frame_len > WT_FRAME_CLOSE_MAX_MESSAGE_SIZE + 4 { if frame_len > WT_FRAME_CLOSE_MAX_MESSAGE_SIZE + 4 {
return Err(Error::HttpMessageError); return Err(Error::HttpMessageError);
} }
let error = let error = dec.decode_uint().ok_or(Error::HttpMessageError)?;
u32::try_from(dec.decode_uint(4).ok_or(Error::HttpMessageError)?).unwrap();
let Ok(message) = String::from_utf8(dec.decode_remainder().to_vec()) else { let Ok(message) = String::from_utf8(dec.decode_remainder().to_vec()) else {
return Err(Error::HttpMessageError); return Err(Error::HttpMessageError);
}; };

View File

@@ -5,7 +5,7 @@
// except according to those terms. // except according to those terms.
use enumset::{enum_set, EnumSet, EnumSetType}; use enumset::{enum_set, EnumSet, EnumSetType};
use neqo_common::Header; use neqo_common::{header::HeadersExt as _, Header};
use crate::{Error, MessageType, Res}; use crate::{Error, MessageType, Res};
@@ -49,10 +49,9 @@ impl TryFrom<(MessageType, &str)> for PseudoHeaderState {
/// Returns an error if response headers do not contain /// Returns an error if response headers do not contain
/// a status header or if the value of the header is 101 or cannot be parsed. /// a status header or if the value of the header is 101 or cannot be parsed.
pub fn is_interim(headers: &[Header]) -> Res<bool> { pub fn is_interim(headers: &[Header]) -> Res<bool> {
let status = headers.iter().take(1).find(|h| h.name() == ":status"); if let Some(h) = headers.iter().take(1).find_header(":status") {
if let Some(h) = status {
#[allow(clippy::map_err_ignore)] #[allow(clippy::map_err_ignore)]
let status_code = h.value().parse::<i32>().map_err(|_| Error::InvalidHeader)?; let status_code = h.value().parse::<u16>().map_err(|_| Error::InvalidHeader)?;
if status_code == 101 { if status_code == 101 {
// https://datatracker.ietf.org/doc/html/draft-ietf-quic-http#section-4.3 // https://datatracker.ietf.org/doc/html/draft-ietf-quic-http#section-4.3
Err(Error::InvalidHeader) Err(Error::InvalidHeader)
@@ -224,4 +223,18 @@ mod tests {
fn valid_webtransport_connect() { fn valid_webtransport_connect() {
assert!(headers_valid(&create_connect_headers(), MessageType::Request).is_ok()); assert!(headers_valid(&create_connect_headers(), MessageType::Request).is_ok());
} }
#[test]
fn invalid_webtransport_connect_with_status() {
assert!(headers_valid(
[
create_connect_headers(),
vec![Header::new(":status", "200")]
]
.concat()
.as_slice(),
MessageType::Request
)
.is_err());
}
} }

View File

@@ -145,6 +145,7 @@ mod frames;
mod headers_checks; mod headers_checks;
mod priority; mod priority;
mod push_controller; mod push_controller;
mod push_id;
mod qlog; mod qlog;
mod qpack_decoder_receiver; mod qpack_decoder_receiver;
mod qpack_encoder_receiver; mod qpack_encoder_receiver;
@@ -174,6 +175,7 @@ use neqo_transport::{
AppError, Connection, Error as TransportError, RecvStreamStats, SendStreamStats, AppError, Connection, Error as TransportError, RecvStreamStats, SendStreamStats,
}; };
pub use priority::Priority; pub use priority::Priority;
pub use push_id::PushId;
pub use server::Http3Server; pub use server::Http3Server;
pub use server_events::{ pub use server_events::{
Http3OrWebTransportStream, Http3ServerEvent, WebTransportRequest, WebTransportServerEvent, Http3OrWebTransportStream, Http3ServerEvent, WebTransportRequest, WebTransportServerEvent,

View File

@@ -20,7 +20,7 @@ use crate::{
client_events::{Http3ClientEvent, Http3ClientEvents}, client_events::{Http3ClientEvent, Http3ClientEvents},
connection::Http3Connection, connection::Http3Connection,
frames::HFrame, frames::HFrame,
CloseType, Error, Http3StreamInfo, HttpRecvStreamEvents, RecvStreamEvents, Res, CloseType, Error, Http3StreamInfo, HttpRecvStreamEvents, PushId, RecvStreamEvents, Res,
}; };
/// `PushStates`: /// `PushStates`:
@@ -57,27 +57,27 @@ enum PushState {
#[derive(Debug)] #[derive(Debug)]
struct ActivePushStreams { struct ActivePushStreams {
push_streams: VecDeque<PushState>, push_streams: VecDeque<PushState>,
first_push_id: u64, first_push_id: PushId,
} }
impl ActivePushStreams { impl ActivePushStreams {
pub const fn new() -> Self { pub const fn new() -> Self {
Self { Self {
push_streams: VecDeque::new(), push_streams: VecDeque::new(),
first_push_id: 0, first_push_id: PushId::new(0),
} }
} }
/// Returns None if a stream has been closed already. /// Returns None if a stream has been closed already.
pub fn get_mut( pub fn get_mut(
&mut self, &mut self,
push_id: u64, push_id: PushId,
) -> Option<&mut <usize as SliceIndex<[PushState]>>::Output> { ) -> Option<&mut <usize as SliceIndex<[PushState]>>::Output> {
if push_id < self.first_push_id { if push_id < self.first_push_id {
return None; return None;
} }
let inx = usize::try_from(push_id - self.first_push_id).unwrap(); let inx = usize::try_from(u64::from(push_id - self.first_push_id)).unwrap();
if inx >= self.push_streams.len() { if inx >= self.push_streams.len() {
self.push_streams.resize(inx + 1, PushState::Init); self.push_streams.resize(inx + 1, PushState::Init);
} }
@@ -88,19 +88,19 @@ impl ActivePushStreams {
} }
/// Returns None if a stream has been closed already. /// Returns None if a stream has been closed already.
pub fn get(&mut self, push_id: u64) -> Option<&mut PushState> { pub fn get(&mut self, push_id: PushId) -> Option<&mut PushState> {
self.get_mut(push_id) self.get_mut(push_id)
} }
/// Returns the State of a closed push stream or None for already closed streams. /// Returns the State of a closed push stream or None for already closed streams.
pub fn close(&mut self, push_id: u64) -> Option<PushState> { pub fn close(&mut self, push_id: PushId) -> Option<PushState> {
match self.get_mut(push_id) { match self.get_mut(push_id) {
None | Some(PushState::Closed) => None, None | Some(PushState::Closed) => None,
Some(s) => { Some(s) => {
let res = mem::replace(s, PushState::Closed); let res = mem::replace(s, PushState::Closed);
while self.push_streams.front() == Some(&PushState::Closed) { while self.push_streams.front() == Some(&PushState::Closed) {
self.push_streams.pop_front(); self.push_streams.pop_front();
self.first_push_id += 1; self.first_push_id.next();
} }
Some(res) Some(res)
} }
@@ -108,7 +108,7 @@ impl ActivePushStreams {
} }
#[must_use] #[must_use]
pub fn number_done(&self) -> u64 { pub fn number_done(&self) -> PushId {
self.first_push_id self.first_push_id
+ u64::try_from( + u64::try_from(
self.push_streams self.push_streams
@@ -120,7 +120,7 @@ impl ActivePushStreams {
} }
pub fn clear(&mut self) { pub fn clear(&mut self) {
self.first_push_id = 0; self.first_push_id = PushId::new(0);
self.push_streams.clear(); self.push_streams.clear();
} }
} }
@@ -135,7 +135,7 @@ impl ActivePushStreams {
/// ///
/// The `PushController` handles: /// The `PushController` handles:
/// `PUSH_PROMISE` frame: frames may change the push state from Init to `PushPromise` and from /// `PUSH_PROMISE` frame: frames may change the push state from Init to `PushPromise` and from
/// `OnlyPushStream` to `Active`. Frames for a closed steams are ignored. /// `OnlyPushStream` to `Active`. Frames for a closed streams are ignored.
/// `CANCEL_PUSH` frame: (`handle_cancel_push` will be called). If a push is in state `PushPromise` /// `CANCEL_PUSH` frame: (`handle_cancel_push` will be called). If a push is in state `PushPromise`
/// or `Active`, any posted events will be removed and a `PushCanceled` event /// or `Active`, any posted events will be removed and a `PushCanceled` event
/// will be posted. If a push is in state `OnlyPushStream` or `Active` the /// will be posted. If a push is in state `OnlyPushStream` or `Active` the
@@ -146,7 +146,7 @@ impl ActivePushStreams {
#[derive(Debug)] #[derive(Debug)]
pub struct PushController { pub struct PushController {
max_concurent_push: u64, max_concurent_push: u64,
current_max_push_id: u64, current_max_push_id: PushId,
// push_streams holds the states of push streams. // push_streams holds the states of push streams.
// We keep a stream until the stream has been closed. // We keep a stream until the stream has been closed.
push_streams: ActivePushStreams, push_streams: ActivePushStreams,
@@ -156,24 +156,22 @@ pub struct PushController {
conn_events: Http3ClientEvents, conn_events: Http3ClientEvents,
} }
impl Display for PushController {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(f, "Push controller")
}
}
impl PushController { impl PushController {
pub const fn new(max_concurent_push: u64, conn_events: Http3ClientEvents) -> Self { pub const fn new(max_concurent_push: u64, conn_events: Http3ClientEvents) -> Self {
Self { Self {
max_concurent_push, max_concurent_push,
current_max_push_id: 0, current_max_push_id: PushId::new(0),
push_streams: ActivePushStreams::new(), push_streams: ActivePushStreams::new(),
conn_events, conn_events,
} }
} }
}
impl Display for PushController {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(f, "Push controler")
}
}
impl PushController {
/// A new `push_promise` has been received. /// A new `push_promise` has been received.
/// ///
/// # Errors /// # Errors
@@ -181,15 +179,12 @@ impl PushController {
/// `HttpId` if `push_id` greater than it is allowed has been received. /// `HttpId` if `push_id` greater than it is allowed has been received.
pub fn new_push_promise( pub fn new_push_promise(
&mut self, &mut self,
push_id: u64, push_id: PushId,
ref_stream_id: StreamId, ref_stream_id: StreamId,
new_headers: Vec<Header>, new_headers: Vec<Header>,
) -> Res<()> { ) -> Res<()> {
qtrace!( qtrace!(
[self], "[{self}] New push promise push_id={push_id} headers={new_headers:?} max_push={}",
"New push promise push_id={} headers={:?} max_push={}",
push_id,
new_headers,
self.max_concurent_push self.max_concurent_push
); );
@@ -197,7 +192,7 @@ impl PushController {
match self.push_streams.get_mut(push_id) { match self.push_streams.get_mut(push_id) {
None => { None => {
qtrace!("Push has been closed already {}.", push_id); qtrace!("Push has been closed already {push_id}");
Ok(()) Ok(())
} }
Some(push_state) => match push_state { Some(push_state) => match push_state {
@@ -236,18 +231,13 @@ impl PushController {
} }
} }
pub fn add_new_push_stream(&mut self, push_id: u64, stream_id: StreamId) -> Res<bool> { pub fn add_new_push_stream(&mut self, push_id: PushId, stream_id: StreamId) -> Res<bool> {
qtrace!( qtrace!("A new push stream with push_id={push_id} stream_id={stream_id}");
"A new push stream with push_id={} stream_id={}",
push_id,
stream_id
);
self.check_push_id(push_id)?; self.check_push_id(push_id)?;
self.push_streams.get_mut(push_id).map_or_else( self.push_streams.get_mut(push_id).map_or_else(
|| { || {
qinfo!("Push has been closed already."); qinfo!("Push has been closed already");
Ok(false) Ok(false)
}, },
|push_state| match push_state { |push_state| match push_state {
@@ -269,17 +259,17 @@ impl PushController {
// The following state have already have a push stream: // The following state have already have a push stream:
// PushState::OnlyPushStream | PushState::Active // PushState::OnlyPushStream | PushState::Active
_ => { _ => {
qerror!("Duplicate push stream."); qerror!("Duplicate push stream");
Err(Error::HttpId) Err(Error::HttpId)
} }
}, },
) )
} }
fn check_push_id(&self, push_id: u64) -> Res<()> { fn check_push_id(&self, push_id: PushId) -> Res<()> {
// Check if push id is greater than what we allow. // Check if push id is greater than what we allow.
if push_id > self.current_max_push_id { if push_id > self.current_max_push_id {
qerror!("Push id is greater than current_max_push_id."); qerror!("Push id is greater than current_max_push_id");
Err(Error::HttpId) Err(Error::HttpId)
} else { } else {
Ok(()) Ok(())
@@ -288,17 +278,17 @@ impl PushController {
pub fn handle_cancel_push( pub fn handle_cancel_push(
&mut self, &mut self,
push_id: u64, push_id: PushId,
conn: &mut Connection, conn: &mut Connection,
base_handler: &mut Http3Connection, base_handler: &mut Http3Connection,
) -> Res<()> { ) -> Res<()> {
qtrace!("CANCEL_PUSH frame has been received, push_id={}", push_id); qtrace!("CANCEL_PUSH frame has been received, push_id={push_id}");
self.check_push_id(push_id)?; self.check_push_id(push_id)?;
match self.push_streams.close(push_id) { match self.push_streams.close(push_id) {
None => { None => {
qtrace!("Push has already been closed (push_id={}).", push_id); qtrace!("Push has already been closed (push_id={push_id})");
Ok(()) Ok(())
} }
Some(ps) => match ps { Some(ps) => match ps {
@@ -310,7 +300,7 @@ impl PushController {
} }
PushState::OnlyPushStream { stream_id, .. } PushState::OnlyPushStream { stream_id, .. }
| PushState::Active { stream_id, .. } => { | PushState::Active { stream_id, .. } => {
mem::drop(base_handler.stream_stop_sending( drop(base_handler.stream_stop_sending(
conn, conn,
stream_id, stream_id,
Error::HttpRequestCancelled.code(), Error::HttpRequestCancelled.code(),
@@ -324,8 +314,8 @@ impl PushController {
} }
} }
pub fn close(&mut self, push_id: u64) { pub fn close(&mut self, push_id: PushId) {
qtrace!("Push stream has been closed."); qtrace!("Push stream has been closed");
if let Some(push_state) = self.push_streams.close(push_id) { if let Some(push_state) = self.push_streams.close(push_id) {
debug_assert!(matches!(push_state, PushState::Active { .. })); debug_assert!(matches!(push_state, PushState::Active { .. }));
} else { } else {
@@ -335,17 +325,17 @@ impl PushController {
pub fn cancel( pub fn cancel(
&mut self, &mut self,
push_id: u64, push_id: PushId,
conn: &mut Connection, conn: &mut Connection,
base_handler: &mut Http3Connection, base_handler: &mut Http3Connection,
) -> Res<()> { ) -> Res<()> {
qtrace!("Cancel push_id={}", push_id); qtrace!("Cancel push_id={push_id}");
self.check_push_id(push_id)?; self.check_push_id(push_id)?;
match self.push_streams.get(push_id) { match self.push_streams.get(push_id) {
None => { None => {
qtrace!("Push has already been closed."); qtrace!("Push has already been closed");
// If we have some events for the push_id in the event queue, the caller still does // If we have some events for the push_id in the event queue, the caller still does
// not not know that the push has been closed. Otherwise return // not not know that the push has been closed. Otherwise return
// InvalidStreamId. // InvalidStreamId.
@@ -365,7 +355,7 @@ impl PushController {
Some(PushState::Active { stream_id, .. }) => { Some(PushState::Active { stream_id, .. }) => {
self.conn_events.remove_events_for_push_id(push_id); self.conn_events.remove_events_for_push_id(push_id);
// Cancel the stream. The transport stream may already be done, so ignore an error. // Cancel the stream. The transport stream may already be done, so ignore an error.
mem::drop(base_handler.stream_stop_sending( drop(base_handler.stream_stop_sending(
conn, conn,
*stream_id, *stream_id,
Error::HttpRequestCancelled.code(), Error::HttpRequestCancelled.code(),
@@ -377,9 +367,8 @@ impl PushController {
} }
} }
pub fn push_stream_reset(&mut self, push_id: u64, close_type: CloseType) { pub fn push_stream_reset(&mut self, push_id: PushId, close_type: CloseType) {
qtrace!("Push stream has been reset, push_id={}", push_id); qtrace!("Push stream has been reset, push_id={push_id}");
if let Some(push_state) = self.push_streams.get(push_id) { if let Some(push_state) = self.push_streams.get(push_id) {
match push_state { match push_state {
PushState::OnlyPushStream { .. } => { PushState::OnlyPushStream { .. } => {
@@ -397,14 +386,14 @@ impl PushController {
_ => { _ => {
debug_assert!( debug_assert!(
false, false,
"Reset cannot actually happen because we do not have a stream." "Reset cannot actually happen because we do not have a stream"
); );
} }
} }
} }
} }
pub fn get_active_stream_id(&mut self, push_id: u64) -> Option<StreamId> { pub fn get_active_stream_id(&mut self, push_id: PushId) -> Option<StreamId> {
match self.push_streams.get(push_id) { match self.push_streams.get(push_id) {
Some(PushState::Active { stream_id, .. }) => Some(*stream_id), Some(PushState::Active { stream_id, .. }) => Some(*stream_id),
_ => None, _ => None,
@@ -414,7 +403,7 @@ impl PushController {
pub fn maybe_send_max_push_id_frame(&mut self, base_handler: &mut Http3Connection) { pub fn maybe_send_max_push_id_frame(&mut self, base_handler: &mut Http3Connection) {
let push_done = self.push_streams.number_done(); let push_done = self.push_streams.number_done();
if self.max_concurent_push > 0 if self.max_concurent_push > 0
&& (self.current_max_push_id - push_done) <= (self.max_concurent_push / 2) && (self.current_max_push_id - push_done) <= (self.max_concurent_push / 2).into()
{ {
self.current_max_push_id = push_done + self.max_concurent_push; self.current_max_push_id = push_done + self.max_concurent_push;
base_handler.queue_control_frame(&HFrame::MaxPushId { base_handler.queue_control_frame(&HFrame::MaxPushId {
@@ -424,7 +413,7 @@ impl PushController {
} }
pub fn handle_zero_rtt_rejected(&mut self) { pub fn handle_zero_rtt_rejected(&mut self) {
self.current_max_push_id = 0; self.current_max_push_id = PushId::new(0);
} }
pub fn clear(&mut self) { pub fn clear(&mut self) {
@@ -435,10 +424,10 @@ impl PushController {
self.max_concurent_push > 0 self.max_concurent_push > 0
} }
pub fn new_stream_event(&mut self, push_id: u64, event: Http3ClientEvent) { pub fn new_stream_event(&mut self, push_id: PushId, event: Http3ClientEvent) {
match self.push_streams.get_mut(push_id) { match self.push_streams.get_mut(push_id) {
None => { None => {
debug_assert!(false, "Push has been closed already."); debug_assert!(false, "Push has been closed already");
} }
Some(PushState::OnlyPushStream { events, .. }) => { Some(PushState::OnlyPushStream { events, .. }) => {
events.push(event); events.push(event);
@@ -460,12 +449,12 @@ impl PushController {
/// a `push_promise` has not been yet received for the stream. /// a `push_promise` has not been yet received for the stream.
#[derive(Debug)] #[derive(Debug)]
pub struct RecvPushEvents { pub struct RecvPushEvents {
push_id: u64, push_id: PushId,
push_handler: Rc<RefCell<PushController>>, push_handler: Rc<RefCell<PushController>>,
} }
impl RecvPushEvents { impl RecvPushEvents {
pub const fn new(push_id: u64, push_handler: Rc<RefCell<PushController>>) -> Self { pub const fn new(push_id: PushId, push_handler: Rc<RefCell<PushController>>) -> Self {
Self { Self {
push_id, push_id,
push_handler, push_handler,

View File

@@ -0,0 +1,55 @@
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::ops::{Add, Sub};
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd)]
pub struct PushId(u64);
impl PushId {
#[must_use]
pub const fn new(id: u64) -> Self {
Self(id)
}
pub fn next(&mut self) {
self.0 += 1;
}
}
impl From<u64> for PushId {
fn from(id: u64) -> Self {
Self(id)
}
}
impl From<PushId> for u64 {
fn from(id: PushId) -> Self {
id.0
}
}
impl ::std::fmt::Display for PushId {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl Sub for PushId {
type Output = Self;
fn sub(self, rhs: Self) -> Self {
Self(self.0 - rhs.0)
}
}
impl Add<u64> for PushId {
type Output = Self;
fn add(self, rhs: u64) -> Self {
Self(self.0 + rhs)
}
}

View File

@@ -6,7 +6,7 @@
use std::{cell::RefCell, cmp::min, collections::VecDeque, fmt::Debug, rc::Rc}; use std::{cell::RefCell, cmp::min, collections::VecDeque, fmt::Debug, rc::Rc};
use neqo_common::{qdebug, qinfo, qtrace, Header}; use neqo_common::{header::HeadersExt as _, qdebug, qinfo, qtrace, Header};
use neqo_qpack::decoder::QPackDecoder; use neqo_qpack::decoder::QPackDecoder;
use neqo_transport::{Connection, StreamId}; use neqo_transport::{Connection, StreamId};
@@ -16,7 +16,7 @@ use crate::{
priority::PriorityHandler, priority::PriorityHandler,
push_controller::PushController, push_controller::PushController,
qlog, CloseType, Error, Http3StreamInfo, Http3StreamType, HttpRecvStream, HttpRecvStreamEvents, qlog, CloseType, Error, Http3StreamInfo, Http3StreamType, HttpRecvStream, HttpRecvStreamEvents,
MessageType, Priority, ReceiveOutput, RecvStream, Res, Stream, MessageType, Priority, PushId, ReceiveOutput, RecvStream, Res, Stream,
}; };
#[allow(clippy::module_name_repetitions)] #[allow(clippy::module_name_repetitions)]
@@ -61,7 +61,7 @@ enum RecvMessageState {
#[derive(Debug)] #[derive(Debug)]
struct PushInfo { struct PushInfo {
push_id: u64, push_id: PushId,
header_block: Vec<u8>, header_block: Vec<u8>,
} }
@@ -126,7 +126,7 @@ impl RecvMessage {
RecvMessageState::WaitingForFinAfterTrailers {..} => { RecvMessageState::WaitingForFinAfterTrailers {..} => {
return Err(Error::HttpFrameUnexpected); return Err(Error::HttpFrameUnexpected);
} }
_ => unreachable!("This functions is only called in WaitingForResponseHeaders | WaitingForData | WaitingForFinAfterTrailers state.") _ => unreachable!("This functions is only called in WaitingForResponseHeaders | WaitingForData | WaitingForFinAfterTrailers state")
} }
Ok(()) Ok(())
} }
@@ -146,13 +146,13 @@ impl RecvMessage {
}; };
} }
} }
_ => unreachable!("This functions is only called in WaitingForResponseHeaders | WaitingForData | WaitingForFinAfterTrailers state.") _ => unreachable!("This functions is only called in WaitingForResponseHeaders | WaitingForData | WaitingForFinAfterTrailers state")
} }
Ok(()) Ok(())
} }
fn add_headers(&mut self, mut headers: Vec<Header>, fin: bool) -> Res<()> { fn add_headers(&mut self, mut headers: Vec<Header>, fin: bool) -> Res<()> {
qtrace!([self], "Add new headers fin={}", fin); qtrace!("[{self}] Add new headers fin={fin}");
let interim = match self.message_type { let interim = match self.message_type {
MessageType::Request => false, MessageType::Request => false,
MessageType::Response => is_interim(&headers)?, MessageType::Response => is_interim(&headers)?,
@@ -167,12 +167,8 @@ impl RecvMessage {
} }
let is_web_transport = self.message_type == MessageType::Request let is_web_transport = self.message_type == MessageType::Request
&& headers && headers.contains_header(":method", "CONNECT")
.iter() && headers.contains_header(":protocol", "webtransport");
.any(|h| h.name() == ":method" && h.value() == "CONNECT")
&& headers
.iter()
.any(|h| h.name() == ":protocol" && h.value() == "webtransport");
if is_web_transport { if is_web_transport {
self.conn_events self.conn_events
.extended_connect_new_session(self.stream_id, headers); .extended_connect_new_session(self.stream_id, headers);
@@ -203,7 +199,10 @@ impl RecvMessage {
fn set_state_to_close_pending(&mut self, post_readable_event: bool) -> Res<()> { fn set_state_to_close_pending(&mut self, post_readable_event: bool) -> Res<()> {
// Stream has received fin. Depending on headers state set header_ready // Stream has received fin. Depending on headers state set header_ready
// or data_readable event so that app can pick up the fin. // or data_readable event so that app can pick up the fin.
qtrace!([self], "set_state_to_close_pending: state={:?}", self.state); qtrace!(
"[{self}] set_state_to_close_pending: state={:?}",
self.state
);
match self.state { match self.state {
RecvMessageState::WaitingForResponseHeaders { .. } => { RecvMessageState::WaitingForResponseHeaders { .. } => {
@@ -216,7 +215,7 @@ impl RecvMessage {
self.conn_events.data_readable(self.get_stream_info()); self.conn_events.data_readable(self.get_stream_info());
} }
} }
_ => unreachable!("Closing an already closed transaction."), _ => unreachable!("Closing an already closed transaction"),
} }
if !matches!(self.state, RecvMessageState::Closed) { if !matches!(self.state, RecvMessageState::Closed) {
self.state = RecvMessageState::ClosePending; self.state = RecvMessageState::ClosePending;
@@ -224,7 +223,7 @@ impl RecvMessage {
Ok(()) Ok(())
} }
fn handle_push_promise(&mut self, push_id: u64, header_block: Vec<u8>) -> Res<()> { fn handle_push_promise(&mut self, push_id: PushId, header_block: Vec<u8>) -> Res<()> {
if self.push_handler.is_none() { if self.push_handler.is_none() {
return Err(Error::HttpFrameUnexpected); return Err(Error::HttpFrameUnexpected);
} }
@@ -254,9 +253,8 @@ impl RecvMessage {
} }
fn receive_internal(&mut self, conn: &mut Connection, post_readable_event: bool) -> Res<()> { fn receive_internal(&mut self, conn: &mut Connection, post_readable_event: bool) -> Res<()> {
let label = ::neqo_common::log_subject!(::log::Level::Debug, self);
loop { loop {
qdebug!([label], "state={:?}.", self.state); qdebug!("[{self}] state={:?}", self.state);
match &mut self.state { match &mut self.state {
// In the following 3 states we need to read frames. // In the following 3 states we need to read frames.
RecvMessageState::WaitingForResponseHeaders { frame_reader } RecvMessageState::WaitingForResponseHeaders { frame_reader }
@@ -272,11 +270,7 @@ impl RecvMessage {
(None, false) => break Ok(()), (None, false) => break Ok(()),
(Some(frame), fin) => { (Some(frame), fin) => {
qdebug!( qdebug!(
[self], "[{self}] A new frame has been received: {frame:?}; state={:?} fin={fin}", self.state,
"A new frame has been received: {:?}; state={:?} fin={}",
frame,
self.state,
fin,
); );
match frame { match frame {
HFrame::Headers { header_block } => { HFrame::Headers { header_block } => {
@@ -300,10 +294,7 @@ impl RecvMessage {
} }
}; };
} }
RecvMessageState::DecodingHeaders { RecvMessageState::DecodingHeaders { header_block, fin } => {
ref header_block,
fin,
} => {
if self if self
.qpack_decoder .qpack_decoder
.borrow() .borrow()
@@ -311,8 +302,7 @@ impl RecvMessage {
&& !self.blocked_push_promise.is_empty() && !self.blocked_push_promise.is_empty()
{ {
qinfo!( qinfo!(
[self], "[{self}] decoding header is blocked waiting for a push_promise header block"
"decoding header is blocked waiting for a push_promise header block."
); );
break Ok(()); break Ok(());
} }
@@ -330,7 +320,7 @@ impl RecvMessage {
break Ok(()); break Ok(());
} }
} else { } else {
qinfo!([self], "decoding header is blocked."); qinfo!("[{self}] decoding header is blocked");
break Ok(()); break Ok(());
} }
} }

View File

@@ -119,7 +119,7 @@ impl SendMessage {
encoder: Rc<RefCell<QPackEncoder>>, encoder: Rc<RefCell<QPackEncoder>>,
conn_events: Box<dyn SendStreamEvents>, conn_events: Box<dyn SendStreamEvents>,
) -> Self { ) -> Self {
qdebug!("Create a request stream_id={}", stream_id); qdebug!("Create a request stream_id={stream_id}");
Self { Self {
state: MessageState::WaitingForHeaders, state: MessageState::WaitingForHeaders,
message_type, message_type,
@@ -166,7 +166,7 @@ impl Stream for SendMessage {
} }
impl SendStream for SendMessage { impl SendStream for SendMessage {
fn send_data(&mut self, conn: &mut Connection, buf: &[u8]) -> Res<usize> { fn send_data(&mut self, conn: &mut Connection, buf: &[u8]) -> Res<usize> {
qtrace!([self], "send_body: len={}", buf.len()); qtrace!("[{self}] send_body: len={}", buf.len());
self.state.new_data()?; self.state.new_data()?;
@@ -200,12 +200,7 @@ impl SendStream for SendMessage {
min(buf.len(), available - 9) min(buf.len(), available - 9)
}; };
qdebug!( qdebug!("[{self}] send_request_body: available={available} to_send={to_send}");
[self],
"send_request_body: available={} to_send={}.",
available,
to_send
);
let data_frame = HFrame::Data { let data_frame = HFrame::Data {
len: to_send as u64, len: to_send as u64,
@@ -250,14 +245,14 @@ impl SendStream for SendMessage {
fn send(&mut self, conn: &mut Connection) -> Res<()> { fn send(&mut self, conn: &mut Connection) -> Res<()> {
let sent = Error::map_error(self.stream.send_buffer(conn), Error::HttpInternal(5))?; let sent = Error::map_error(self.stream.send_buffer(conn), Error::HttpInternal(5))?;
qtrace!([self], "{} bytes sent", sent); qtrace!("[{self}] {sent} bytes sent");
if !self.stream.has_buffered_data() { if !self.stream.has_buffered_data() {
if self.state.done() { if self.state.done() {
Error::map_error( Error::map_error(
conn.stream_close_send(self.stream_id()), conn.stream_close_send(self.stream_id()),
Error::HttpInternal(6), Error::HttpInternal(6),
)?; )?;
qtrace!([self], "done sending request"); qtrace!("[{self}] done sending request");
} else { } else {
// DataWritable is just a signal for an application to try to write more data, // DataWritable is just a signal for an application to try to write more data,
// if writing fails it is fine. Therefore we do not need to properly check // if writing fails it is fine. Therefore we do not need to properly check

View File

@@ -119,13 +119,13 @@ impl Http3Server {
} }
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]>>>, now: Instant) -> Output {
qtrace!([self], "Process."); qtrace!("[{self}] Process");
let out = self.server.process(dgram, now); let out = self.server.process(dgram, now);
self.process_http3(now); self.process_http3(now);
// If we do not that a dgram already try again after process_http3. // If we do not that a dgram already try again after process_http3.
match out { match out {
Output::Datagram(d) => { Output::Datagram(d) => {
qtrace!([self], "Send packet: {:?}", d); qtrace!("[{self}] Send packet: {d:?}");
Output::Datagram(d) Output::Datagram(d)
} }
_ => self.server.process(Option::<Datagram>::None, now), _ => self.server.process(Option::<Datagram>::None, now),
@@ -134,7 +134,7 @@ impl Http3Server {
/// Process HTTP3 layer. /// Process HTTP3 layer.
fn process_http3(&mut self, now: Instant) { fn process_http3(&mut self, now: Instant) {
qtrace!([self], "Process http3 internal."); qtrace!("[{self}] Process http3 internal");
// `ActiveConnectionRef` `Hash` implementation doesnt access any of the interior mutable // `ActiveConnectionRef` `Hash` implementation doesnt access any of the interior mutable
// types. // types.
#[allow(clippy::mutable_key_type)] #[allow(clippy::mutable_key_type)]
@@ -152,6 +152,7 @@ impl Http3Server {
} }
} }
#[allow(clippy::too_many_lines)]
fn process_events(&mut self, conn: &ConnectionRef, now: Instant) { fn process_events(&mut self, conn: &ConnectionRef, now: Instant) {
let mut remove = false; let mut remove = false;
let http3_parameters = &self.http3_parameters; let http3_parameters = &self.http3_parameters;
@@ -172,7 +173,11 @@ impl Http3Server {
headers, headers,
fin, fin,
} => self.events.headers( } => self.events.headers(
Http3OrWebTransportStream::new(conn.clone(), handler.clone(), stream_info), Http3OrWebTransportStream::new(
conn.clone(),
Rc::clone(handler),
stream_info,
),
headers, headers,
fin, fin,
), ),
@@ -188,15 +193,19 @@ impl Http3Server {
} }
Http3ServerConnEvent::DataWritable { stream_info } => self Http3ServerConnEvent::DataWritable { stream_info } => self
.events .events
.data_writable(conn.clone(), handler.clone(), stream_info), .data_writable(conn.clone(), Rc::clone(handler), stream_info),
Http3ServerConnEvent::StreamReset { stream_info, error } => { Http3ServerConnEvent::StreamReset { stream_info, error } => {
self.events self.events.stream_reset(
.stream_reset(conn.clone(), handler.clone(), stream_info, error); conn.clone(),
Rc::clone(handler),
stream_info,
error,
);
} }
Http3ServerConnEvent::StreamStopSending { stream_info, error } => { Http3ServerConnEvent::StreamStopSending { stream_info, error } => {
self.events.stream_stop_sending( self.events.stream_stop_sending(
conn.clone(), conn.clone(),
handler.clone(), Rc::clone(handler),
stream_info, stream_info,
error, error,
); );
@@ -216,7 +225,7 @@ impl Http3Server {
} }
Http3ServerConnEvent::ExtendedConnect { stream_id, headers } => { Http3ServerConnEvent::ExtendedConnect { stream_id, headers } => {
self.events.webtransport_new_session( self.events.webtransport_new_session(
WebTransportRequest::new(conn.clone(), handler.clone(), stream_id), WebTransportRequest::new(conn.clone(), Rc::clone(handler), stream_id),
headers, headers,
); );
} }
@@ -226,7 +235,7 @@ impl Http3Server {
headers, headers,
.. ..
} => self.events.webtransport_session_closed( } => self.events.webtransport_session_closed(
WebTransportRequest::new(conn.clone(), handler.clone(), stream_id), WebTransportRequest::new(conn.clone(), Rc::clone(handler), stream_id),
reason, reason,
headers, headers,
), ),
@@ -234,14 +243,14 @@ impl Http3Server {
.events .events
.webtransport_new_stream(Http3OrWebTransportStream::new( .webtransport_new_stream(Http3OrWebTransportStream::new(
conn.clone(), conn.clone(),
handler.clone(), Rc::clone(handler),
stream_info, stream_info,
)), )),
Http3ServerConnEvent::ExtendedConnectDatagram { Http3ServerConnEvent::ExtendedConnectDatagram {
session_id, session_id,
datagram, datagram,
} => self.events.webtransport_datagram( } => self.events.webtransport_datagram(
WebTransportRequest::new(conn.clone(), handler.clone(), session_id), WebTransportRequest::new(conn.clone(), Rc::clone(handler), session_id),
datagram, datagram,
), ),
} }
@@ -294,7 +303,7 @@ fn prepare_data(
data.resize(amount, 0); data.resize(amount, 0);
} }
events.data(conn.clone(), handler.clone(), stream_info, data, fin); events.data(conn.clone(), Rc::clone(handler), stream_info, data, fin);
} }
if amount < MAX_EVENT_DATA_SIZE || fin { if amount < MAX_EVENT_DATA_SIZE || fin {
break; break;
@@ -311,11 +320,10 @@ fn prepare_data(
mod tests { mod tests {
use std::{ use std::{
collections::HashMap, collections::HashMap,
mem,
ops::{Deref, DerefMut}, ops::{Deref, DerefMut},
}; };
use neqo_common::{event::Provider, Encoder}; use neqo_common::{event::Provider as _, Encoder};
use neqo_crypto::{AuthenticationStatus, ZeroRttCheckResult, ZeroRttChecker}; use neqo_crypto::{AuthenticationStatus, ZeroRttCheckResult, ZeroRttChecker};
use neqo_qpack::{encoder::QPackEncoder, QpackSettings}; use neqo_qpack::{encoder::QPackEncoder, QpackSettings};
use neqo_transport::{ use neqo_transport::{
@@ -502,11 +510,11 @@ mod tests {
(server, client) (server, client)
} }
// Test http3 connection inintialization. // Test http3 connection initialization.
// The server will open the control and qpack streams and send SETTINGS frame. // The server will open the control and qpack streams and send SETTINGS frame.
#[test] #[test]
fn server_connect() { fn server_connect() {
mem::drop(connect_and_receive_settings()); drop(connect_and_receive_settings());
} }
struct PeerConnection { struct PeerConnection {
@@ -559,9 +567,9 @@ mod tests {
assert_eq!(sent, Ok(1)); assert_eq!(sent, Ok(1));
let out1 = neqo_trans_conn.process_output(now()); let out1 = neqo_trans_conn.process_output(now());
let out2 = server.process(out1.dgram(), now()); let out2 = server.process(out1.dgram(), now());
mem::drop(neqo_trans_conn.process(out2.dgram(), now())); drop(neqo_trans_conn.process(out2.dgram(), now()));
// assert no error occured. // assert no error occurred.
assert_not_closed(server); assert_not_closed(server);
PeerConnection { PeerConnection {
@@ -579,7 +587,7 @@ mod tests {
// Server: Test receiving a new control stream and a SETTINGS frame. // Server: Test receiving a new control stream and a SETTINGS frame.
#[test] #[test]
fn server_receive_control_frame() { fn server_receive_control_frame() {
mem::drop(connect()); drop(connect());
} }
// Server: Test that the connection will be closed if control stream // Server: Test that the connection will be closed if control stream
@@ -685,13 +693,13 @@ mod tests {
test_wrong_frame_on_control_stream(&[0x0, 0x2, 0x1, 0x2]); test_wrong_frame_on_control_stream(&[0x0, 0x2, 0x1, 0x2]);
} }
// send HEADERS frame on a cortrol stream // send HEADERS frame on a control stream
#[test] #[test]
fn server_headers_frame_on_control_stream() { fn server_headers_frame_on_control_stream() {
test_wrong_frame_on_control_stream(&[0x1, 0x2, 0x1, 0x2]); test_wrong_frame_on_control_stream(&[0x1, 0x2, 0x1, 0x2]);
} }
// send PUSH_PROMISE frame on a cortrol stream // send PUSH_PROMISE frame on a control stream
#[test] #[test]
fn server_push_promise_frame_on_control_stream() { fn server_push_promise_frame_on_control_stream() {
test_wrong_frame_on_control_stream(&[0x5, 0x2, 0x1, 0x2]); test_wrong_frame_on_control_stream(&[0x5, 0x2, 0x1, 0x2]);
@@ -710,9 +718,9 @@ mod tests {
.unwrap(); .unwrap();
let out = peer_conn.process_output(now()); let out = peer_conn.process_output(now());
let out = hconn.process(out.dgram(), now()); let out = hconn.process(out.dgram(), now());
mem::drop(peer_conn.process(out.dgram(), now())); drop(peer_conn.process(out.dgram(), now()));
let out = hconn.process_output(now()); let out = hconn.process_output(now());
mem::drop(peer_conn.process(out.dgram(), now())); drop(peer_conn.process(out.dgram(), now()));
// check for stop-sending with Error::HttpStreamCreation. // check for stop-sending with Error::HttpStreamCreation.
let mut stop_sending_event_found = false; let mut stop_sending_event_found = false;
@@ -741,7 +749,7 @@ mod tests {
_ = peer_conn.stream_send(push_stream_id, &[0x1]).unwrap(); _ = peer_conn.stream_send(push_stream_id, &[0x1]).unwrap();
let out = peer_conn.process_output(now()); let out = peer_conn.process_output(now());
let out = hconn.process(out.dgram(), now()); let out = hconn.process(out.dgram(), now());
mem::drop(peer_conn.conn.process(out.dgram(), now())); drop(peer_conn.conn.process(out.dgram(), now()));
assert_closed(&hconn, &Error::HttpStreamCreation); assert_closed(&hconn, &Error::HttpStreamCreation);
} }
@@ -836,7 +844,7 @@ mod tests {
fn test_incomplete_frame(res: &[u8]) { fn test_incomplete_frame(res: &[u8]) {
let (mut hconn, mut peer_conn) = connect_and_receive_settings(); let (mut hconn, mut peer_conn) = connect_and_receive_settings();
// send an incomplete reequest. // send an incomplete request.
let stream_id = peer_conn.stream_create(StreamType::BiDi).unwrap(); let stream_id = peer_conn.stream_create(StreamType::BiDi).unwrap();
peer_conn.stream_send(stream_id, res).unwrap(); peer_conn.stream_send(stream_id, res).unwrap();
peer_conn.stream_close_send(stream_id).unwrap(); peer_conn.stream_close_send(stream_id).unwrap();

View File

@@ -35,7 +35,7 @@ pub struct StreamHandler {
impl ::std::fmt::Display for StreamHandler { impl ::std::fmt::Display for StreamHandler {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
let conn: &Connection = &self.conn.borrow(); let conn: &Connection = &self.conn.borrow();
write!(f, "conn={} stream_info={:?}", conn, self.stream_info) write!(f, "conn={conn} stream_info={:?}", self.stream_info)
} }
} }
@@ -115,10 +115,8 @@ impl StreamHandler {
/// It may return `InvalidStreamId` if a stream does not exist anymore. /// It may return `InvalidStreamId` if a stream does not exist anymore.
pub fn stream_stop_sending(&self, app_error: AppError) -> Res<()> { pub fn stream_stop_sending(&self, app_error: AppError) -> Res<()> {
qdebug!( qdebug!(
[self], "[{self}] stop sending stream_id:{} error:{app_error}",
"stop sending stream_id:{} error:{}.", self.stream_info.stream_id()
self.stream_info.stream_id(),
app_error
); );
self.handler.borrow_mut().stream_stop_sending( self.handler.borrow_mut().stream_stop_sending(
self.stream_info.stream_id(), self.stream_info.stream_id(),
@@ -134,10 +132,8 @@ impl StreamHandler {
/// It may return `InvalidStreamId` if a stream does not exist anymore. /// It may return `InvalidStreamId` if a stream does not exist anymore.
pub fn stream_reset_send(&self, app_error: AppError) -> Res<()> { pub fn stream_reset_send(&self, app_error: AppError) -> Res<()> {
qdebug!( qdebug!(
[self], "[{self}] reset send stream_id:{} error:{app_error}",
"reset send stream_id:{} error:{}.", self.stream_info.stream_id()
self.stream_info.stream_id(),
app_error
); );
self.handler.borrow_mut().stream_reset_send( self.handler.borrow_mut().stream_reset_send(
self.stream_info.stream_id(), self.stream_info.stream_id(),
@@ -152,7 +148,7 @@ impl StreamHandler {
/// ///
/// It may return `InvalidStreamId` if a stream does not exist anymore /// It may return `InvalidStreamId` if a stream does not exist anymore
pub fn cancel_fetch(&self, app_error: AppError) -> Res<()> { pub fn cancel_fetch(&self, app_error: AppError) -> Res<()> {
qdebug!([self], "reset error:{}.", app_error); qdebug!("[{self}] reset error:{app_error}");
self.handler.borrow_mut().cancel_fetch( self.handler.borrow_mut().cancel_fetch(
self.stream_info.stream_id(), self.stream_info.stream_id(),
app_error, app_error,
@@ -202,7 +198,7 @@ impl Http3OrWebTransportStream {
/// ///
/// It may return `InvalidStreamId` if a stream does not exist anymore. /// It may return `InvalidStreamId` if a stream does not exist anymore.
pub fn send_data(&self, data: &[u8]) -> Res<usize> { pub fn send_data(&self, data: &[u8]) -> Res<usize> {
qdebug!([self], "Set new response."); qdebug!("[{self}] Set new response");
self.stream_handler.send_data(data) self.stream_handler.send_data(data)
} }
@@ -212,7 +208,7 @@ impl Http3OrWebTransportStream {
/// ///
/// It may return `InvalidStreamId` if a stream does not exist anymore. /// It may return `InvalidStreamId` if a stream does not exist anymore.
pub fn stream_close_send(&self) -> Res<()> { pub fn stream_close_send(&self) -> Res<()> {
qdebug!([self], "Set new response."); qdebug!("[{self}] Set new response");
self.stream_handler.stream_close_send() self.stream_handler.stream_close_send()
} }
} }
@@ -283,7 +279,7 @@ impl WebTransportRequest {
/// ///
/// It may return `InvalidStreamId` if a stream does not exist anymore. /// It may return `InvalidStreamId` if a stream does not exist anymore.
pub fn response(&self, accept: &WebTransportSessionAcceptAction) -> Res<()> { pub fn response(&self, accept: &WebTransportSessionAcceptAction) -> Res<()> {
qdebug!([self], "Set a response for a WebTransport session."); qdebug!("[{self}] Set a response for a WebTransport session");
self.stream_handler self.stream_handler
.handler .handler
.borrow_mut() .borrow_mut()
@@ -335,7 +331,7 @@ impl WebTransportRequest {
Ok(Http3OrWebTransportStream::new( Ok(Http3OrWebTransportStream::new(
self.stream_handler.conn.clone(), self.stream_handler.conn.clone(),
self.stream_handler.handler.clone(), Rc::clone(&self.stream_handler.handler),
Http3StreamInfo::new(id, Http3StreamType::WebTransport(session_id)), Http3StreamInfo::new(id, Http3StreamType::WebTransport(session_id)),
)) ))
} }

View File

@@ -11,7 +11,7 @@ use neqo_transport::{Connection, StreamId, StreamType};
use crate::{ use crate::{
control_stream_local::HTTP3_UNI_STREAM_TYPE_CONTROL, control_stream_local::HTTP3_UNI_STREAM_TYPE_CONTROL,
frames::{hframe::HFrameType, reader::FrameDecoder, HFrame, H3_FRAME_TYPE_HEADERS}, frames::{hframe::HFrameType, reader::FrameDecoder, HFrame, H3_FRAME_TYPE_HEADERS},
CloseType, Error, Http3StreamType, ReceiveOutput, RecvStream, Res, Stream, CloseType, Error, Http3StreamType, PushId, ReceiveOutput, RecvStream, Res, Stream,
}; };
pub const HTTP3_UNI_STREAM_TYPE_PUSH: u64 = 0x1; pub const HTTP3_UNI_STREAM_TYPE_PUSH: u64 = 0x1;
@@ -23,7 +23,7 @@ pub enum NewStreamType {
Control, Control,
Decoder, Decoder,
Encoder, Encoder,
Push(u64), Push(PushId),
WebTransportStream(u64), WebTransportStream(u64),
Http(u64), Http(u64),
Unknown, Unknown,
@@ -137,7 +137,7 @@ impl NewStreamHeadReader {
return Ok(None); return Ok(None);
}; };
qtrace!("Decoded uint {}", output); qtrace!("Decoded uint {output}");
match self { match self {
Self::ReadType { Self::ReadType {
role, stream_id, .. role, stream_id, ..
@@ -178,12 +178,12 @@ impl NewStreamHeadReader {
Self::ReadId { stream_type, .. } => { Self::ReadId { stream_type, .. } => {
let is_push = *stream_type == HTTP3_UNI_STREAM_TYPE_PUSH; let is_push = *stream_type == HTTP3_UNI_STREAM_TYPE_PUSH;
*self = Self::Done; *self = Self::Done;
qtrace!("New Stream stream push_id={}", output); qtrace!("New Stream stream push_id={output}");
if fin { if fin {
return Err(Error::HttpGeneralProtocol); return Err(Error::HttpGeneralProtocol);
} }
return if is_push { return if is_push {
Ok(Some(NewStreamType::Push(output))) Ok(Some(NewStreamType::Push(PushId::new(output))))
} else { } else {
Ok(Some(NewStreamType::WebTransportStream(output))) Ok(Some(NewStreamType::WebTransportStream(output)))
}; };
@@ -204,7 +204,7 @@ impl NewStreamHeadReader {
Some(NewStreamType::Http(_)) => Err(Error::HttpFrame), Some(NewStreamType::Http(_)) => Err(Error::HttpFrame),
Some(NewStreamType::Unknown) => Ok(decoded), Some(NewStreamType::Unknown) => Ok(decoded),
Some(NewStreamType::Push(_) | NewStreamType::WebTransportStream(_)) => { Some(NewStreamType::Push(_) | NewStreamType::WebTransportStream(_)) => {
unreachable!("PushStream and WebTransport are mapped to None at this stage.") unreachable!("PushStream and WebTransport are mapped to None at this stage")
} }
} }
} }
@@ -237,8 +237,6 @@ impl RecvStream for NewStreamHeadReader {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::mem;
use neqo_common::{Encoder, Role}; use neqo_common::{Encoder, Role};
use neqo_qpack::{ use neqo_qpack::{
decoder::QPACK_UNI_STREAM_TYPE_DECODER, encoder::QPACK_UNI_STREAM_TYPE_ENCODER, decoder::QPACK_UNI_STREAM_TYPE_DECODER, encoder::QPACK_UNI_STREAM_TYPE_ENCODER,
@@ -253,7 +251,7 @@ mod tests {
use crate::{ use crate::{
control_stream_local::HTTP3_UNI_STREAM_TYPE_CONTROL, control_stream_local::HTTP3_UNI_STREAM_TYPE_CONTROL,
frames::{H3_FRAME_TYPE_HEADERS, H3_FRAME_TYPE_SETTINGS}, frames::{H3_FRAME_TYPE_HEADERS, H3_FRAME_TYPE_SETTINGS},
CloseType, Error, NewStreamType, ReceiveOutput, RecvStream, Res, CloseType, Error, NewStreamType, PushId, ReceiveOutput, RecvStream as _, Res,
}; };
struct Test { struct Test {
@@ -269,7 +267,7 @@ mod tests {
// create a stream // create a stream
let stream_id = conn_s.stream_create(stream_type).unwrap(); let stream_id = conn_s.stream_create(stream_type).unwrap();
let out = conn_s.process_output(now()); let out = conn_s.process_output(now());
mem::drop(conn_c.process(out.dgram(), now())); drop(conn_c.process(out.dgram(), now()));
Self { Self {
conn_c, conn_c,
@@ -292,7 +290,7 @@ mod tests {
.stream_send(self.stream_id, &enc[i..=i]) .stream_send(self.stream_id, &enc[i..=i])
.unwrap(); .unwrap();
let out = self.conn_s.process_output(now()); let out = self.conn_s.process_output(now());
mem::drop(self.conn_c.process(out.dgram(), now())); drop(self.conn_c.process(out.dgram(), now()));
assert_eq!( assert_eq!(
self.decoder.receive(&mut self.conn_c).unwrap(), self.decoder.receive(&mut self.conn_c).unwrap(),
(ReceiveOutput::NoOutput, false) (ReceiveOutput::NoOutput, false)
@@ -306,7 +304,7 @@ mod tests {
self.conn_s.stream_close_send(self.stream_id).unwrap(); self.conn_s.stream_close_send(self.stream_id).unwrap();
} }
let out = self.conn_s.process_output(now()); let out = self.conn_s.process_output(now());
mem::drop(self.conn_c.process(out.dgram(), now())); drop(self.conn_c.process(out.dgram(), now()));
assert_eq!(&self.decoder.receive(&mut self.conn_c), outcome); assert_eq!(&self.decoder.receive(&mut self.conn_c), outcome);
assert_eq!(self.decoder.done(), done); assert_eq!(self.decoder.done(), done);
} }
@@ -366,7 +364,7 @@ mod tests {
&[HTTP3_UNI_STREAM_TYPE_PUSH, 0xaaaa_aaaa], &[HTTP3_UNI_STREAM_TYPE_PUSH, 0xaaaa_aaaa],
false, false,
&Ok(( &Ok((
ReceiveOutput::NewStream(NewStreamType::Push(0xaaaa_aaaa)), ReceiveOutput::NewStream(NewStreamType::Push(PushId::new(0xaaaa_aaaa))),
true, true,
)), )),
true, true,
@@ -429,7 +427,7 @@ mod tests {
* HTTP3_UNI_STREAM_TYPE_PUSH */ * HTTP3_UNI_STREAM_TYPE_PUSH */
false, false,
&Ok(( &Ok((
ReceiveOutput::NewStream(NewStreamType::Push(0xaaaa_aaaa)), ReceiveOutput::NewStream(NewStreamType::Push(PushId::new(0xaaaa_aaaa))),
true, true,
)), )),
true, true,

View File

@@ -4,12 +4,9 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
use std::{ use std::time::{Duration, Instant};
mem,
time::{Duration, Instant},
};
use neqo_common::{event::Provider, qtrace, Datagram}; use neqo_common::{event::Provider as _, qtrace, Datagram};
use neqo_crypto::{AuthenticationStatus, ResumptionToken}; use neqo_crypto::{AuthenticationStatus, ResumptionToken};
use neqo_http3::{ use neqo_http3::{
Header, Http3Client, Http3ClientEvent, Http3OrWebTransportStream, Http3Parameters, Http3Server, Header, Http3Client, Http3ClientEvent, Http3OrWebTransportStream, Http3Parameters, Http3Server,
@@ -97,7 +94,7 @@ fn connect_peers(hconn_c: &mut Http3Client, hconn_s: &mut Http3Server) -> Option
let out = hconn_c.process_output(now()); // Initial let out = hconn_c.process_output(now()); // Initial
let out = hconn_s.process(out.dgram(), now()); // Initial + Handshake let out = hconn_s.process(out.dgram(), now()); // Initial + Handshake
let out = hconn_c.process(out.dgram(), now()); // ACK let out = hconn_c.process(out.dgram(), now()); // ACK
mem::drop(hconn_s.process(out.dgram(), now())); // consume ACK drop(hconn_s.process(out.dgram(), now())); // consume ACK
let authentication_needed = |e| matches!(e, Http3ClientEvent::AuthenticationNeeded); let authentication_needed = |e| matches!(e, Http3ClientEvent::AuthenticationNeeded);
assert!(hconn_c.events().any(authentication_needed)); assert!(hconn_c.events().any(authentication_needed));
hconn_c.authenticated(AuthenticationStatus::Ok, now()); hconn_c.authenticated(AuthenticationStatus::Ok, now());
@@ -189,14 +186,14 @@ fn fetch() {
let out = hconn_c.process(dgram, now()); let out = hconn_c.process(dgram, now());
qtrace!("-----server"); qtrace!("-----server");
let out = hconn_s.process(out.dgram(), now()); let out = hconn_s.process(out.dgram(), now());
mem::drop(hconn_c.process(out.dgram(), now())); drop(hconn_c.process(out.dgram(), now()));
process_server_events(&hconn_s); process_server_events(&hconn_s);
let out = hconn_s.process(None::<Datagram>, now()); let out = hconn_s.process(None::<Datagram>, now());
qtrace!("-----client"); qtrace!("-----client");
mem::drop(hconn_c.process(out.dgram(), now())); drop(hconn_c.process(out.dgram(), now()));
let out = hconn_s.process(None::<Datagram>, now()); let out = hconn_s.process(None::<Datagram>, now());
mem::drop(hconn_c.process(out.dgram(), now())); drop(hconn_c.process(out.dgram(), now()));
process_client_events(&mut hconn_c); process_client_events(&mut hconn_c);
} }
@@ -218,7 +215,7 @@ fn response_103() {
let out = hconn_c.process(dgram, now()); let out = hconn_c.process(dgram, now());
let out = hconn_s.process(out.dgram(), now()); let out = hconn_s.process(out.dgram(), now());
mem::drop(hconn_c.process(out.dgram(), now())); drop(hconn_c.process(out.dgram(), now()));
let request = receive_request(&hconn_s).unwrap(); let request = receive_request(&hconn_s).unwrap();
let info_headers = [ let info_headers = [
Header::new(":status", "103"), Header::new(":status", "103"),
@@ -228,7 +225,7 @@ fn response_103() {
request.send_headers(&info_headers).unwrap(); request.send_headers(&info_headers).unwrap();
let out = hconn_s.process(None::<Datagram>, now()); let out = hconn_s.process(None::<Datagram>, now());
mem::drop(hconn_c.process(out.dgram(), now())); drop(hconn_c.process(out.dgram(), now()));
let info_headers_event = |e| { let info_headers_event = |e| {
matches!(e, Http3ClientEvent::HeaderReady { headers, matches!(e, Http3ClientEvent::HeaderReady { headers,
@@ -239,7 +236,7 @@ fn response_103() {
set_response(&request); set_response(&request);
let out = hconn_s.process(None::<Datagram>, now()); let out = hconn_s.process(None::<Datagram>, now());
mem::drop(hconn_c.process(out.dgram(), now())); drop(hconn_c.process(out.dgram(), now()));
process_client_events(&mut hconn_c); process_client_events(&mut hconn_c);
} }
@@ -256,7 +253,7 @@ fn data_writable_events_low_watermark() -> Result<(), Box<dyn std::error::Error>
ConnectionParameters::default().max_stream_data(StreamType::BiDi, false, STREAM_LIMIT), ConnectionParameters::default().max_stream_data(StreamType::BiDi, false, STREAM_LIMIT),
)); ));
let mut hconn_s = default_http3_server(); let mut hconn_s = default_http3_server();
mem::drop(connect_peers(&mut hconn_c, &mut hconn_s)); drop(connect_peers(&mut hconn_c, &mut hconn_s));
// Client sends GET to server. // Client sends GET to server.
let stream_id = hconn_c.fetch( let stream_id = hconn_c.fetch(
@@ -298,7 +295,7 @@ fn data_writable_events_low_watermark() -> Result<(), Box<dyn std::error::Error>
exchange_packets(&mut hconn_c, &mut hconn_s, None); exchange_packets(&mut hconn_c, &mut hconn_s, None);
assert!(hconn_s.events().any(data_writable)); assert!(hconn_s.events().any(data_writable));
// Sending more fails, given that each data frame needs to be preceeded by a // Sending more fails, given that each data frame needs to be preceded by a
// header, i.e. needs more than 1 byte of send space to send 1 byte payload. // header, i.e. needs more than 1 byte of send space to send 1 byte payload.
assert_eq!(request.available()?, 1); assert_eq!(request.available()?, 1);
assert_eq!(request.send_data(&buf)?, 0); assert_eq!(request.send_data(&buf)?, 0);
@@ -330,7 +327,7 @@ fn data_writable_events() {
)); ));
let mut hconn_s = default_http3_server(); let mut hconn_s = default_http3_server();
mem::drop(connect_peers(&mut hconn_c, &mut hconn_s)); drop(connect_peers(&mut hconn_c, &mut hconn_s));
// Create a request. // Create a request.
let req = hconn_c let req = hconn_c
@@ -433,7 +430,7 @@ fn zerortt() {
let mut hconn_c = default_http3_client(); let mut hconn_c = default_http3_client();
hconn_c hconn_c
.enable_resumption(now(), &token) .enable_resumption(now(), &token)
.expect("Set resumption token."); .expect("Set resumption token");
let mut hconn_s = default_http3_server(); let mut hconn_s = default_http3_server();
// Create a request. // Create a request.

View File

@@ -6,7 +6,7 @@
use std::time::Instant; use std::time::Instant;
use neqo_common::event::Provider; use neqo_common::event::Provider as _;
use neqo_crypto::AuthenticationStatus; use neqo_crypto::AuthenticationStatus;
use neqo_http3::{ use neqo_http3::{
Header, Http3Client, Http3ClientEvent, Http3Server, Http3ServerEvent, Http3State, Priority, Header, Http3Client, Http3ClientEvent, Http3Server, Http3ServerEvent, Http3State, Priority,
@@ -25,7 +25,7 @@ fn exchange_packets(client: &mut Http3Client, server: &mut Http3Server) {
} }
} }
// Perform only Quic transport handshake. // Perform only QUIC transport handshake.
fn connect_with(client: &mut Http3Client, server: &mut Http3Server) { fn connect_with(client: &mut Http3Client, server: &mut Http3Server) {
assert_eq!(client.state(), Http3State::Initializing); assert_eq!(client.state(), Http3State::Initializing);
let out = client.process_output(now()); let out = client.process_output(now());
@@ -45,7 +45,7 @@ fn connect_with(client: &mut Http3Client, server: &mut Http3Server) {
assert!(client.events().any(connected)); assert!(client.events().any(connected));
assert_eq!(client.state(), Http3State::Connected); assert_eq!(client.state(), Http3State::Connected);
// Exchange H3 setttings // Exchange H3 settings
let out = server.process(out.dgram(), now()); let out = server.process(out.dgram(), now());
let out = client.process(out.dgram(), now()); let out = client.process(out.dgram(), now());
let out = server.process(out.dgram(), now()); let out = server.process(out.dgram(), now());
@@ -83,11 +83,7 @@ fn priority_update() {
}; };
match header_event { match header_event {
Http3ServerEvent::Headers { Http3ServerEvent::Headers { headers, fin, .. } => {
stream: _,
headers,
fin,
} => {
let expected_headers = &[ let expected_headers = &[
Header::new(":method", "GET"), Header::new(":method", "GET"),
Header::new(":scheme", "https"), Header::new(":scheme", "https"),

View File

@@ -6,7 +6,7 @@
use std::sync::OnceLock; use std::sync::OnceLock;
use neqo_common::event::Provider; use neqo_common::event::Provider as _;
use neqo_crypto::AuthenticationStatus; use neqo_crypto::AuthenticationStatus;
use neqo_http3::{ use neqo_http3::{
Error, Header, Http3Client, Http3ClientEvent, Http3OrWebTransportStream, Http3Server, Error, Header, Http3Client, Http3ClientEvent, Http3OrWebTransportStream, Http3Server,

View File

@@ -6,14 +6,14 @@
use std::{cell::RefCell, rc::Rc}; use std::{cell::RefCell, rc::Rc};
use neqo_common::{event::Provider, Header}; use neqo_common::{event::Provider as _, header::HeadersExt as _};
use neqo_crypto::AuthenticationStatus; use neqo_crypto::AuthenticationStatus;
use neqo_http3::{ use neqo_http3::{
Http3Client, Http3ClientEvent, Http3OrWebTransportStream, Http3Parameters, Http3Server, Http3Client, Http3ClientEvent, Http3OrWebTransportStream, Http3Parameters, Http3Server,
Http3ServerEvent, Http3State, WebTransportEvent, WebTransportRequest, WebTransportServerEvent, Http3ServerEvent, Http3State, WebTransportEvent, WebTransportRequest, WebTransportServerEvent,
WebTransportSessionAcceptAction, WebTransportSessionAcceptAction,
}; };
use neqo_transport::{StreamId, StreamType}; use neqo_transport::{ConnectionParameters, StreamId, StreamType};
use test_fixture::{ use test_fixture::{
anti_replay, fixture_init, now, CountingConnectionIdGenerator, DEFAULT_ADDR, DEFAULT_ALPN_H3, anti_replay, fixture_init, now, CountingConnectionIdGenerator, DEFAULT_ADDR, DEFAULT_ALPN_H3,
DEFAULT_KEYS, DEFAULT_SERVER_NAME, DEFAULT_KEYS, DEFAULT_SERVER_NAME,
@@ -59,7 +59,7 @@ fn connect() -> (Http3Client, Http3Server) {
assert_eq!(client.state(), Http3State::Connected); assert_eq!(client.state(), Http3State::Connected);
// Exchange H3 setttings // Exchange H3 settings
loop { loop {
out = server.process(out, now()).dgram(); out = server.process(out, now()).dgram();
let dgram_present = out.is_some(); let dgram_present = out.is_some();
@@ -96,12 +96,8 @@ fn create_wt_session(client: &mut Http3Client, server: &mut Http3Server) -> WebT
headers, headers,
}) => { }) => {
assert!( assert!(
headers headers.contains_header(":method", "CONNECT")
.iter() && headers.contains_header(":protocol", "webtransport")
.any(|h| h.name() == ":method" && h.value() == "CONNECT")
&& headers
.iter()
.any(|h| h.name() == ":protocol" && h.value() == "webtransport")
); );
session session
.response(&WebTransportSessionAcceptAction::Accept) .response(&WebTransportSessionAcceptAction::Accept)
@@ -127,7 +123,7 @@ fn create_wt_session(client: &mut Http3Client, server: &mut Http3Server) -> WebT
}) if ( }) if (
stream_id == wt_session_id && stream_id == wt_session_id &&
status == 200 && status == 200 &&
headers.contains(&Header::new(":status", "200")) headers.contains_header(":status", "200")
) )
) )
}; };
@@ -229,6 +225,17 @@ fn receive_data_server(
wt_stream.unwrap() wt_stream.unwrap()
} }
#[test]
fn wt_keepalive() {
let (mut client, mut server) = connect();
let _wt_session = create_wt_session(&mut client, &mut server);
let idle_timeout = ConnectionParameters::default().get_idle_timeout();
// Expect client and server to send PING after half of the idle timeout in order to keep
// connection alive.
assert_eq!(client.process_output(now()).callback(), idle_timeout / 2);
assert_eq!(server.process_output(now()).callback(), idle_timeout / 2);
}
#[test] #[test]
fn wt_client_stream_uni() { fn wt_client_stream_uni() {
const BUF_CLIENT: &[u8] = &[0; 10]; const BUF_CLIENT: &[u8] = &[0; 10];

View File

@@ -1 +1 @@
{"files":{"Cargo.toml":"f58a82c0437489fabe141b44181a181af4f0d0ff83855d4d844c7dece45fab4f","src/decoder.rs":"5c76a8407e02999a805ecaeae0ec2743121abe1e35b75126994d52ff7deb6af7","src/decoder_instructions.rs":"addf304fbd566ca387e5111cbe25784f7f50e0112ce0372b1ad6d18185b45a02","src/encoder.rs":"b6b9e3b87d2e56c07bf8e2ca3ad1688b313dd716f2239de89cf2b54242841a7a","src/encoder_instructions.rs":"746569e9f90f07af1aa49ed93f48c3fa3c41f13cea5f0ae88485a78a43a59470","src/header_block.rs":"47ad4a03d7a78e6d2269f8e004a884cb1e001c272a206121f479e29b8446f824","src/huffman.rs":"6976f1b4d3e5ef849a6b080cfb2e8804bf01cfe3b9bd9e3994a319d5405cd8f3","src/huffman_decode_helper.rs":"9ce470e318b3664f58aa109bed483ab15bfd9e0b17d261ea2b609668a42a9d80","src/huffman_table.rs":"06fea766a6276ac56c7ee0326faed800a742c15fda1f33bf2513e6cc6a5e6d27","src/lib.rs":"f9bad0fe7643c618d034c4941ebd30ad5f6015b8b87b484b0ea79681d13d8b49","src/prefix.rs":"d9ad12838d61b38dc2300948e3da01fd65371215edde1c370cf54ccd87d64d46","src/qlog.rs":"d8e8482624f0cc4388546169772846d37a0422ce10287ee2f716dfc5f7a56c3d","src/qpack_send_buf.rs":"48f8d0e011e0fb8e4bd0774279d3465e2be01fd9480eaf374ae2adada6be430d","src/reader.rs":"937e9db6ca4dd8d6f599e58bc2123f3b76b269b089b4e98e3922efd80861dd92","src/static_table.rs":"6e5ec26e2b6bd63375d2d77e72748151d430d1629a8e497ec0d0ea21c078524a","src/stats.rs":"624dfa3b40858c304097bb0ce5b1be1bb4d7916b1abfc222f1aa705907009730","src/table.rs":"2d2c9e6070a1e90048a4ad7c8279f9e1ce7615b44d7d8145fb0f140e554f5ca2"},"package":null} {"files":{"Cargo.toml":"5c1632f128048c857a1aceed231a744069576011d582a55271744f8e5d41a68e","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}

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