Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 26 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jobs:
# Go is required for FIPS builds
- uses: actions/setup-go@v5
with:
go-version: 'stable'
go-version: "stable"
# Prevent feature unification from selecting *ring* as the crypto provider
- run: RUST_BACKTRACE=1 cargo test --locked --manifest-path noq-proto/Cargo.toml --no-default-features --features rustls,aws-lc-rs
- run: RUST_BACKTRACE=1 cargo test --locked --manifest-path noq/Cargo.toml --no-default-features --features rustls,aws-lc-rs,runtime-tokio,__rustls-post-quantum-test
Expand Down Expand Up @@ -345,6 +345,30 @@ jobs:
run: |
cargo +$MSRV check --workspace --exclude fuzz --all-targets

external_types:
runs-on: ubuntu-latest
env:
RUSTC_WRAPPER: "sccache"
SCCACHE_GHA_ENABLED: "on"
# Pin to the nightly that the pinned `cargo-check-external-types`
# release was last tested against. Update both together.
CARGO_CHECK_EXTERNAL_TYPES_VERSION: "0.4.0"
TOOLCHAIN: "nightly-2025-10-18"
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env.TOOLCHAIN }}
- name: Install sccache
uses: mozilla-actions/sccache-action@v0.0.9
- name: Install cargo-binstall
uses: cargo-bins/cargo-binstall@v1.18.1
- uses: taiki-e/install-action@cargo-make
- name: Install cargo-check-external-types
run: cargo binstall cargo-check-external-types@${{ env.CARGO_CHECK_EXTERNAL_TYPES_VERSION }} --locked --no-confirm
- name: Check external types
run: cargo make check-external-types

cargo_deny:
timeout-minutes: 30
name: cargo deny
Expand Down Expand Up @@ -384,7 +408,7 @@ jobs:
# Go is required for FIPS builds
- uses: actions/setup-go@v5
with:
go-version: 'stable'
go-version: "stable"

- name: Setup Environment (PR)
if: ${{ github.event_name == 'pull_request' }}
Expand Down
30 changes: 30 additions & 0 deletions Makefile.toml
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,33 @@ env = { "PROPTEST_CASES" = "10000" }
description = "Run proptests in regression-only mode (runs for <5 seconds)"
command = "cargo"
args = ["nextest", "run", "--package=noq-proto", "-P", "proptests", "--no-fail-fast", "${@}"]

[tasks.check-external-types]
description = "Run cargo check-external-types on all workspace crates"
script = '''
set -e

# check-external-types needs a specific nightly version
# See https://github.com/awslabs/cargo-check-external-types#how-to-use
TOOLCHAIN="${TOOLCHAIN:-nightly-2025-10-18}"

# Disable -Dwarnings, we don't want to fail on unrelated errors here
RUSTDOCFLAGS=""

# Install the required toolchain
rustup -q toolchain install $TOOLCHAIN -c rustc,cargo,rust-std

# Run for each crate
fail=0
for crate in noq-udp noq-proto noq; do
echo "=== $crate ==="
if ! cargo +$TOOLCHAIN check-external-types \
--manifest-path "$crate/Cargo.toml" \
--features __all_without_fips; \
then
fail=1
fi
done

exit $fail
'''
13 changes: 12 additions & 1 deletion noq-proto/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ rustls-aws-lc-rs = ["rustls", "aws-lc-rs"]
# Don't rely on these whatsoever. They may disappear at any time.

__rustls-post-quantum-test = []
__all_without_fips = ["arbitrary", "aws-lc-rs", "rustls", "ring", "platform-verifier", "rustls-log", "qlog", "bench", "bloom", "tracing-log"]

[dependencies]
aes-gcm = { workspace = true, optional = true }
Expand Down Expand Up @@ -107,4 +108,14 @@ workspace = true

[package.metadata.docs.rs]
# all non-default features except fips (cannot build on docs.rs environment)
features = ["aws-lc-rs", "rustls", "ring", "platform-verifier", "rustls-log", "qlog", "bench"]
features = ["__all_without_fips"]

[package.metadata.cargo_check_external_types]
allowed_external_types = [
"arbitrary::Arbitrary", # gated behind `arbitrary` feature
"criterion::Criterion", # gated behind `bench` feature
"bytes::*",
"rustls",
"rustls::*",
"rustls_pki_types::*",
]
2 changes: 0 additions & 2 deletions noq-proto/src/connection/paths.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ impl std::hash::Hash for PathId {
}
}

impl identity_hash::IdentityHashable for PathId {}

impl Decodable for PathId {
fn decode<B: bytes::Buf>(r: &mut B) -> coding::Result<Self> {
let v = VarInt::decode(r)?;
Expand Down
28 changes: 20 additions & 8 deletions noq-proto/src/connection/timer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,25 @@ impl PathTimer {
];
}

/// Newtype around [`PathId`] that implements [`IdentityHashable`].
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
struct PathIdKey(PathId);

impl IdentityHashable for PathIdKey {}

impl std::hash::Hash for PathIdKey {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
state.write_u32(self.0.0);
}
}

/// Keeps track of the nearest timeout for each `Timer`
///
/// The [`TimerTable`] is advanced with [`TimerTable::expire_before`].
#[derive(Debug, Clone, Default)]
pub(crate) struct TimerTable {
generic: [Option<Instant>; ConnTimer::VALUES.len()],
path_timers: SmallMap<PathId, PathTimerTable, STACK_TIMERS>,
path_timers: SmallMap<PathIdKey, PathTimerTable, STACK_TIMERS>,
}

/// For how many paths we keep the timers on the stack, before spilling onto the heap.
Expand Down Expand Up @@ -278,11 +290,11 @@ impl TimerTable {
Timer::Conn(timer) => {
self.generic[timer as usize] = Some(time);
}
Timer::PerPath(path_id, timer) => match self.path_timers.get_mut(&path_id) {
Timer::PerPath(path_id, timer) => match self.path_timers.get_mut(&PathIdKey(path_id)) {
None => {
let mut table = PathTimerTable::default();
table.set(timer, time);
self.path_timers.insert(path_id, table);
self.path_timers.insert(PathIdKey(path_id), table);
}
Some(table) => {
table.set(timer, time);
Expand All @@ -295,7 +307,7 @@ impl TimerTable {
pub(super) fn get(&self, timer: Timer) -> Option<Instant> {
match timer {
Timer::Conn(timer) => self.generic[timer as usize],
Timer::PerPath(path_id, timer) => self.path_timers.get(&path_id)?.get(timer),
Timer::PerPath(path_id, timer) => self.path_timers.get(&PathIdKey(path_id))?.get(timer),
}
}

Expand All @@ -317,7 +329,7 @@ impl TimerTable {
self.generic[timer as usize] = None;
}
Timer::PerPath(path_id, timer) => {
if let Some(e) = self.path_timers.get_mut(&path_id) {
if let Some(e) = self.path_timers.get_mut(&PathIdKey(path_id)) {
e.stop(timer);
}
}
Expand All @@ -328,7 +340,7 @@ impl TimerTable {
/// Stops all per-path timers
pub(super) fn stop_per_path(&mut self, path_id: PathId, qlog: QlogSinkWithTime<'_>) {
for timer in PathTimer::VALUES {
if let Some(e) = self.path_timers.get_mut(&path_id) {
if let Some(e) = self.path_timers.get_mut(&PathIdKey(path_id)) {
e.stop(timer);
qlog.emit_timer_stop(Timer::PerPath(path_id, timer));
}
Expand Down Expand Up @@ -379,7 +391,7 @@ impl TimerTable {
}

let mut res = None;
for (path_id, timers) in self.path_timers.iter_mut() {
for (PathIdKey(path_id), timers) in self.path_timers.iter_mut() {
if let Some((timer, time)) = timers.expire_before(now) {
res = Some((Timer::PerPath(*path_id, timer), time));
break;
Expand Down Expand Up @@ -410,7 +422,7 @@ impl TimerTable {
}

for timer in PathTimer::VALUES {
for (path_id, timers) in self.path_timers.iter() {
for (PathIdKey(path_id), timers) in self.path_timers.iter() {
if let Some(time) = timers.timers[timer as usize] {
values.push((Timer::PerPath(*path_id, timer), time));
}
Expand Down
5 changes: 5 additions & 0 deletions noq-udp/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ log = ["dep:log"]
# Support private Apple APIs to send multiple packets in a single syscall.
fast-apple-datapath = []

# Internal (PRIVATE!) features used to aid testing.
# Don't rely on these whatsoever. They may disappear at any time.

__all_without_fips = ["tracing-log", "tracing", "log", "fast-apple-datapath"]

[dependencies]
libc = "0.2.175"
log = { workspace = true, optional = true }
Expand Down
15 changes: 14 additions & 1 deletion noq/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ rustls-aws-lc-rs = ["rustls", "aws-lc-rs"]
# Don't rely on these whatsoever. They may disappear at any time.

__rustls-post-quantum-test = ["rustls/prefer-post-quantum", "rustls", "aws-lc-rs", "proto/__rustls-post-quantum-test"]
__all_without_fips = ["lock_tracking", "aws-lc-rs", "rustls", "ring", "runtime-tokio", "runtime-smol", "tracing-log", "rustls-log", "bloom", "platform-verifier", "qlog", "fast-apple-datapath"]

[dependencies]
async-io = { workspace = true, optional = true }
Expand Down Expand Up @@ -144,4 +145,16 @@ required-features = ["rustls", "ring"]

[package.metadata.docs.rs]
# all non-default features except fips (cannot build on docs.rs environment)
features = ["lock_tracking", "aws-lc-rs", "rustls", "ring", "runtime-tokio", "runtime-smol", "tracing-log", "rustls-log"]
features = ["__all_without_fips"]

[package.metadata.cargo_check_external_types]
allowed_external_types = [
"noq_proto::*",
"noq_udp",
"noq_udp::*",
"bytes::bytes::Bytes",
"futures_core::stream::Stream",
"rustls",
"tokio::io::async_read::AsyncRead",
"tokio::io::async_write::AsyncWrite",
]
Loading